GAMES101:作业2记录

news2025/1/20 14:52:00

总览

在上次作业中,虽然我们在屏幕上画出一个线框三角形,但这看起来并不是那么的有趣。所以这一次我们继续推进一步——在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。该函数的内部工作流程如下:

  1. 创建三角形的 2 维 bounding box。
  2. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。
  3. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。
  4. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。

你需要修改的函数如下:
rasterize_triangle(): 执行三角形栅格化算法

static bool insideTriangle(): 测试点是否在三角形内。你可以修改此函数的定义,这意味着,你可以按照自己的方式更新返回类型或函数参数。

因为我们只知道三角形三个顶点处的深度值,所以对于三角形内部的像素,我们需要用插值的方法得到其深度值。我们已经为你处理好了这一部分,因为有关这方面的内容尚未在课程中涉及。插值的深度值被储存在变量 z_interpolated 中。

请注意我们是如何初始化 depth buffer 和注意 z values 的符号。为了方便同学们写代码,我们将 z 进行了反转,保证都是正数,并且越大表示离视点越远。

在此次作业中,你无需处理旋转变换,只需为模型变换返回一个单位矩阵。最后,我们提供了两个 hard-coded 三角形来测试你的实现,如果程序实现正确,你将看到如下所示的输出图像:

在这里插入图片描述

编写代码

static bool insideTriangle()函数的实现

首先我们来实现static bool insideTriangle():判断是否在三角形内

在这里插入图片描述

在这里插入图片描述
我们只需要看 A P × A B AP\times AB AP×AB B P × B C BP\times BC BP×BC C P × C A CP\times CA CP×CA是否是同号的,如果是同号的就说明P点在三角形里面(说明P在这些线段 A B AB AB B C BC BC C A CA CA的同侧,这里的 × \times ×表示的是叉乘)。

注意课程所讲的像素的坐标的定义和虎书不太一样,我认为是定义在像素的角点,我们要求像素是否在三角形内,应该是判断像素的中心是否在三角形里面,如图下面的像素的坐标可以从(0,0)到(width-1, height-1),这样我们就知道像素的中心是定义在(x+0.5,y+0.5)

在这里插入图片描述
我这里把insideTriangle的定义重新修改了一下,函数的参数列表的x和y都定义成了float型,我们调用这个函数的时候直接传入像素的中心位置(即对应的像素坐标为(x0,y0),我们传入(x0+0.5,y0+0.5)),同时也方便我们后续使用MSAA的处理。

static bool insideTriangle(float x, float y, const Vector3f* _v)
{   
    // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2]
    Eigen::Vector2f AP(x - _v[0].x(), y - _v[0].y());
    Eigen::Vector2f AB(_v[1].x() - _v[0].x(), _v[1].y()- _v[0].y());
    Eigen::Vector2f BP(x - _v[1].x(), y - _v[1].y());
    Eigen::Vector2f BC(_v[2].x()-_v[1].x(), _v[2].y()-_v[1].y());
    Eigen::Vector2f CP(x - _v[2].x(), y - _v[2].y());
    Eigen::Vector2f CA(_v[0].x() - _v[2].x(), _v[0].y() - _v[2].y());

    auto P_AB = AP.x() * AB.y() - AB.x() * AP.y();
    auto P_BC = BP.x() * BC.y() - BC.x() * BP.y();
    auto P_CA = CP.x() * CA.y() - CA.x() * CP.y();

    if ((P_AB >0 && P_BC >0 && P_CA >0) || (P_AB < 0 && P_BC < 0 && P_CA < 0)) {
        return true;
    }
    else {
        return false;
    }
}  

rasterize_triangle()函数的实现

我们根据提示来进行操作:

  1. 创建三角形的 2 维 bounding box。这一步很好实现,三角形被限制在一个bounding box,我们需要找到这个长方形的四个边,我们遍历的时候可以先遍历行再遍历列,所以需要知道这个长方形x坐标和y坐标的范围:
  • 长方形x坐标的对应的左边界是三角形的三个角点x坐标的最小值
  • 长方形x坐标的对应的右边界是三角形的三个角点x坐标的最大值
  • 长方形y坐标的对应的下边界是三角形的三个角点y坐标的最小值
  • 长方形y坐标的对应的上边界是三角形的三个角点y坐标的最大值

对应的代码是:

auto v = t.toVector4();
    // TODO : Find out the bounding box of current triangle.
int bounding_box_left_x = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
int bounding_box_right_x = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
int bounding_box_bottom_y = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
int bounding_box_top_y = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));
  1. 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中心的屏幕空间坐标来检查中心点是否在三角形内。

知道了bounding box的边界,我们就可以进行遍历了:

for (int x = bounding_box_left_x; x <= bounding_box_right_x; x++) {
    for (int y = bounding_box_bottom_y; y <= bounding_box_top_y; y++) {
        if (insideTriangle(x, y, t.v)) {
//代码逻辑
}
  1. 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度缓冲区 (depth buffer) 中的相应值进行比较。

现在我们实现比较的逻辑,首先我们需要求出插值的深度值(这部分已经给我们了),然后和缓冲区的值进行比较:

if (insideTriangle(x + 0.5, y + 0.5, t.v)) {
    // If so, use the following code to get the interpolated z value.   
    auto[alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 0.5, t.v);
    float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
    float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
    z_interpolated *= w_reciprocal;
    if (z_interpolated <  depth_buf[get_index(x, y)]) {
        //代码逻辑
    }
}

这里使用的computeBarycentric2D 对应的是下面的公式:

在这里插入图片描述

下面是虎书的公式,两者是差不多的:

在这里插入图片描述
然后我们把这三个系数对应乘上三角形三个点的深度就是插值以后的深度了。

其对应的代码是

static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector3f* v)
{
    float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
    float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
    float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
    return {c1,c2,c3};
}
  1. 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。

更靠近相机,由于这里的深度的值都设置为了正值,所以我们定义更靠近相机就是z_interpolated小于depth_buf对应的值。我们把深度缓冲区的深度更新为z_interpolated,把对应像素的颜色定义为三角形的颜色。

if (z_interpolated <  depth_buf[get_index(x, y)) {
        depth_buf[get_index(x, y)] = z_interpolated;
        // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
        set_pixel(Eigen::Vector3f(x, y, z_interpolated),  t.getColor());
    }

这里还需要注意一个小bug,如果单单用上面的代码运行会发现图形覆盖顺序不对:

这一点在讨论里有讲述
Home,Forums,GAMES在线课程(现代计算机图形学入门)i讨论区,Hw2的疑问

在这里插入图片描述
这样才是z越大表示越远。所以我们这里需要修改一下(原来没有添加vert.z()的负号):

vert.z() = -vert.z() * f1 + f2;

完整的代码是:

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    // TODO : Find out the bounding box of current triangle.
    int bounding_box_left_x = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
    int bounding_box_right_x = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
    int bounding_box_bottom_y = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
    int bounding_box_top_y = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));

    /*without MSAA*/
    
    // iterate through the pixel and find if the current pixel is inside the triangle

    for (int x = bounding_box_left_x; x <= bounding_box_right_x; x++) {
        for (int y = bounding_box_bottom_y; y <= bounding_box_top_y; y++) {
            if (insideTriangle(x + 0.5, y + 0.5, t.v)) {
                auto[alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 0.5, t.v);
			    float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
			    float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
			    z_interpolated *= w_reciprocal;
                if (z_interpolated <  depth_buf[get_index(x, y)) {
                    depth_buf[get_index(x, y)] = z_interpolated;
                    // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
                    set_pixel(Eigen::Vector3f(x, y, z_interpolated),  t.getColor());
                }
            }
        }
    }
}

可以看到三角形的效果渲染出来了,但是这里会出现锯齿:

在这里插入图片描述

在这里插入图片描述

然后是进阶的部分,我们使用 2x2 MSAA 缓解走样问题,即对每个像素进行 2x2 超采样,看一个像素的四个部分,有几个部分在三角形内,就把该点像素颜色乘上对应占比。(其实就是进行一次模糊操作)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

初代的代码没有维护每一个子采样点的深度,深度用的还是中心点的深度,而颜色对应是乘上了百分比:

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
    auto v = t.toVector4();
    // TODO : Find out the bounding box of current triangle.
    int bounding_box_left_x = std::min(v[0].x(), std::min(v[1].x(), v[2].x()));
    int bounding_box_right_x = std::max(v[0].x(), std::max(v[1].x(), v[2].x()));
    int bounding_box_bottom_y = std::min(v[0].y(), std::min(v[1].y(), v[2].y()));
    int bounding_box_top_y = std::max(v[0].y(), std::max(v[1].y(), v[2].y()));

    /*with MSAA*/
    // iterate through the pixel and find if the current pixel is inside the triangle
    
    int inNumber;
    float rd[4][2] ={
                    {0.25, 0.25}, 
                    {0.25, 0.75},
                    {0.75, 0.25},
                    {0.75, 0.75}};
    for (int x = bounding_box_left_x; x <= bounding_box_right_x; x++) {
        for (int y = bounding_box_bottom_y; y <= bounding_box_top_y; y++) {
            inNumber = 0;
            for (int i = 0; i < 4; i++){
                    if (insideTriangle(x+rd[i][0], y+rd[i][1], t.v)) {
                    	inNumber++;
                    }    
            }
            if (inNumber > 0){
                auto[alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 0.5, t.v);
                float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
                z_interpolated *= w_reciprocal;
                if (z_interpolated < depth_buf[get_index(x, y)]) {
                    depth_buf[get_index(x, y)] = z_interpolated;
                // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
	                Eigen::Vector3f pixelColor;
	                pixelColor << inNumber * t.getColor() / 4;
	                set_pixel(Eigen::Vector3f(x, y, z_interpolated), pixelColor);
                }
            }
        }
    }
    
}

但是这样渲染出来的是有黑边的。

在这里插入图片描述
在这里插入图片描述

这个讨论把黑边介绍得很好。

Home › Forums › GAMES在线课程(现代计算机图形学入门)讨论区 › 提高部分的解决方案 – 答案就在作业文档中

在这里插入图片描述
在这里插入图片描述

改进的方法就是我们要记录采样点的深度,像素的颜色根据采样点的颜色取均值,而深度检测的对象也是采样点

rasterizer.hpp定义采样点的颜色和采样点的深度的数组

std::vector<Eigen::Vector3f> sample_frame_buf;
std::vector<float> sample_depth_buf;

仿照原来类定义的frame_bufdepth_buf添加操作:

rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
    frame_buf.resize(w * h);
    depth_buf.resize(w * h);
    
    //MSAA
    sample_frame_buf.resize(4 * w * h);
    sample_depth_buf.resize(4 * w * h);
}
void rst::rasterizer::clear(rst::Buffers buff)
{
    if ((buff & rst::Buffers::Color) == rst::Buffers::Color)
    {
        std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0});
        //MSAA sample_frame_buf
        std::fill(sample_frame_buf.begin(), sample_frame_buf.end(), Eigen::Vector3f{0, 0, 0});
    }
    if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth)
    {
        std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits<float>::infinity());
        //MSAA sample_depth_buf
        std::fill(sample_depth_buf.begin(), sample_depth_buf.end(), std::numeric_limits<float>::infinity());
    }
}

并且在rasterizer.cpp定义了一个新函数(注意在rasterizer.hpp加上声明)方便我们获取采样点的索引:

int rst::rasterizer::get_sample_index(int x, int y)
{
    return (2 * height -1 -y) * 2 * width + x;
}

接着我们就可以实现MSAA了:

void rst::rasterizer::rasterize_triangle(const Triangle& t) {
	int inNumber;
	float x_sample;
	float y_sample;
	int sample_index[4];
	
	std::vector<Eigen::Vector3f> pixelvec;
	
	float rd[4][2] ={
	                {0.25, 0.25}, 
	                {0.25, 0.75},
	                {0.75, 0.25},
	                {0.75, 0.75}};//定义四个采样点的中心距离
	for (int x = bounding_box_left_x; x <= bounding_box_right_x; x++) {
	    for (int y = bounding_box_bottom_y; y <= bounding_box_top_y; y++) {
	        inNumber = 0;
	        for (int i = 0; i < 4; i++){
	                x_sample = x + rd[i][0];
	                y_sample = y + rd[i][1];
	                sample_index[i] =  get_sample_index(int(2 * x_sample), int(2 * y_sample));
	                if (insideTriangle(x_sample, y_sample, t.v)) {
	                // If so, use the following code to get the interpolated z value.   
	                    auto[sample_alpha, sample_beta, sample_gamma] = computeBarycentric2D(x_sample, y_sample, t.v);
	                    float sample_w_reciprocal = 1.0/(sample_alpha / v[0].w() + sample_beta / v[1].w() + sample_gamma / v[2].w());
	                    float sample_z_interpolated = sample_alpha * v[0].z() / v[0].w() + sample_beta * v[1].z() / v[1].w() + sample_gamma * v[2].z() / v[2].w();
	                    sample_z_interpolated *= sample_w_reciprocal;
	                    if (sample_z_interpolated <  sample_depth_buf[sample_index[i]]) {
	                        sample_depth_buf[sample_index[i]] = sample_z_interpolated;
	                    // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
	                        sample_frame_buf[sample_index[i]] = t.getColor();
	                        inNumber = inNumber + 1;
	                    }
	                }    
	        }
	        if (inNumber > 0){
	            // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted.
	            Eigen::Vector3f pixelColor;
	            pixelColor <<  (sample_frame_buf[sample_index[0]] + sample_frame_buf[sample_index[1]] + sample_frame_buf[sample_index[2]] + sample_frame_buf[sample_index[3]])/ 4;
	            set_pixel(Eigen::Vector3f(x, y, 0), pixelColor);
	        }
	    }
	} 
}  

图形的边缘就比较柔和了

在这里插入图片描述
明显看到边缘比较柔和了,而且是没有黑边的。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1280216.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis--13--缓存一致性问题

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 缓存一致性问题1、先更新缓存&#xff0c;再更新DB方案二&#xff1a;先更新DB&#xff0c;再更新缓存方案三&#xff1a;先删缓存&#xff0c;再写数据库推荐1&…

Python ctypes:揭秘高级Python与底层交互秘籍

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com ctypes是Python标准库中的外部函数库&#xff0c;允许Python调用动态链接库中的函数。它提供了与C兼容的数据类型和允许Python调用共享库中的函数&#xff0c;对系统级编程和与硬件交互非常有用。 基本用法 加…

基于Django框架搭建的协同过滤算法电影推荐网站-爬取的豆瓣电影数据

欢迎大家点赞、收藏、关注、评论啦 &#xff0c;由于篇幅有限&#xff0c;只展示了部分核心代码。 文章目录 一项目简介概述技术栈实现流程 二、功能三、系统四. 总结 一项目简介 # 电影推荐网站介绍 概述 该电影推荐网站是基于Django框架搭建的&#xff0c;旨在为用户提供个…

jdk1.8 hashmap源码阅读

目录 hashmap 成员变量 hashmap支持null键吗&#xff1f;为什么&#xff1f; 当扩容的时候&#xff0c;所有元素都会重新计算hash值吗&#xff1f; 怎么减少扩容次数 为什么node数组的大小是2的n次&#xff1f; 1.8和1.7的区别 1.8为啥要用红黑树&#xff1f; 扩容机制…

12.3_黑马MybatisPlus笔记(上)

目录 02 03 04 05 06 07 ​编辑 thinking:system.out::println?​编辑 thinking&#xff1a;list.of? 08 thinking&#xff1a;RequestParam和 ApiParam注解使用&#xff1f; thinking&#xff1a;RequestParam 和PathVariable的区别&#xff1f; ​编辑 ​编…

“B2B+OMS方案”,赋能家电巨头构建BC订单一体化能力,促进业务增长|徐礼昭

某国际知名家电电器品牌&#xff0c;年营收超过5000亿元。该电器企业其整体业务分三大类&#xff1a;线上线下B2B2C业务、线下B2B业务以及DTC零售业务。 随着业务的发展&#xff0c;该电器品牌对2B业务及DTC业务的数字化系统能力支撑需要更加全面和立体&#xff0c;以适应业务…

Sharding-Jdbc(4):Sharding-Jdbc分库

1 新建数据库 创建ds_0数据库和ds_1数据库&#xff0c;在两个数据库新建表如下&#xff1a; CREATE TABLE t_order (order_id bigint(20) NOT NULL,user_id bigint(20) NOT NULL,PRIMARY KEY (order_id) ) ENGINEInnoDB DEFAULT CHARSETutf8 COLLATEutf8_bin; 2 新建maven项目…

基于姿态估计的3D动画生成

在本文中&#xff0c;我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作&#xff0c;并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

SmartSoftHelp8,FrameCode极速二次开发框架源码

1.winform outlook style UI C/S 极速开发框架 netframework 2.0 2.winform toolbar style UI C/S 极速开发框架 netframework 2.0 3.WPF toolbar style UI C/S 极速开发框架 netframework 4.0 4.Xadmin-UI jquery B/S 极速开发框架 5.Vue element UI B/S…

ArrayList 与 顺序表 (附洗牌算法)!

曾经我也是一枚学霸&#xff0c;直到有一天想去学渣的世界看看&#xff0c;结果就找不到回去的路了。 目录 1. 线性表 2.顺序表 2.1 接口的实现 3. ArrayList简介 4. ArrayList使用 4.1 ArrayList的构造 4.2 ArrayList常见操作 4.3 ArrayList的遍历 4.4 ArrayList的扩…

永倍达电商模式分析:创新商业模式引领新时代购物潮

在2019年底&#xff0c;全球新冠疫情席卷&#xff0c;导致大量实体经济倒闭&#xff0c;人们纷纷居家躲避&#xff0c;经济陷入下行&#xff0c;企业家面临倒闭威胁。永倍达成立于2020年&#xff0c;是陕西永倍达电子商务有限公司的品牌&#xff0c;而其母公司实际上是天津铸源…

Stable Diffusion 系列教程 - 1 基础准备(针对新手)

使用SD有两种方式&#xff1a; 本地&#xff1a; 显卡要求&#xff1a;硬件环境推荐NVIDIA的具有8G显存的独立显卡&#xff0c;这个显存勉勉强强能摸到门槛。再往下的4G可能面临各种炸显存、炼丹失败、无法生成图片等各种问题。对于8G显存&#xff0c;1.0模型就不行&#xff0…

前端入门(四)Ajax、Promise异步、Axios通信、vue-router路由、组件库

文章目录 AjaxAjax特点 Promise 异步编程&#xff08;缺&#xff09;Promise基本使用状态 - PromiseState结果 - PromiseResult AxiosVue中使用AxiosAxios请求方式getpostput和patchdelete并发请求 Vue路由 - vue-router单页面Web应用&#xff08;single page web application&…

C++ string类(2)—成员访问、插入、删除、替换、查找和交换操作

目录 一、成员访问 1、[ ]&at 2、front( )&back( ) 二、插入元素 三、删除元素 四、替换元素 五、查找元素 1、查找第一次出现位置 2 、在指定范围内查找 六、交换字符串 七、c_str 八、rfind&substr 一、成员访问 1、[ ]&at 虽然二者功能一样&…

LeetCode | 101. 对称二叉树

LeetCode | 101. 对称二叉树 OJ链接 在本函数里不好进行判断&#xff0c;我们另外定义一个函数来如果两个都相等为空&#xff0c;就返回true一个为空&#xff0c;一个不为空都不为空,就比较值然后递归1的左&#xff0c;2的右&#xff0c;1的右&#xff0c;2的左 bool _isSymm…

javaee实验:MVC 框架技术应用——URL 映射及方法参数的使用

目录 urlmvc框架mvc框架的设计mvc流程 实验目的实验内容实验过程创建项目创建项目结构编写代码简单测试一下 url 和 Hypertext 以及 HTTP 一样&#xff0c;URL 是 Web 中的一个核心概念。它是浏览器用来检索 web 上公布的任何资源的机制 URL 代表着是统一资源定位符&#xff…

《洛谷深入浅出进阶篇》模意义下的乘法逆元+洛谷P3811

什么是乘法逆元&#xff1f; 算数意义上的乘法逆元指的是倒数&#xff0c;即&#xff1a;a*&#xff08;1/a&#xff09;1 所以 1/a 是 a在算数意义下的乘法逆元&#xff0c;或者可以说二者互为逆元。 这有什么用呢&#xff1f; 除以a就等于乘上a的乘法逆元&#xff0c;乘以…

keil5 --工程创建

一&#xff0c;文件夹介绍 首先去官网过去其他地方获取到官方提供的标准库文件 下面这个我是在官网进行下载的 我们在打开keil的时候会弹出一个在线下载的框&#xff08;这个框这里先不做说明&#xff0c;后面在继续讲解&#xff09;&#xff0c;我们不使用这个在线下载功能&a…

LangChain 19 Agents Reason+Action自定义agent处理OpenAI的计算缺陷

LangChain系列文章 LangChain 实现给动物取名字&#xff0c;LangChain 2模块化prompt template并用streamlit生成网站 实现给动物取名字LangChain 3使用Agent访问Wikipedia和llm-math计算狗的平均年龄LangChain 4用向量数据库Faiss存储&#xff0c;读取YouTube的视频文本搜索I…

配置SAP用户密码策略(不用重启服务器的方法)

最近公司审计&#xff0c;给我们提出了要优化密码策略的建议&#xff0c;原因是我们的密码策略太简单了。我稍稍研究了一下。之前是通过RZ10来配置&#xff0c;但是这种方法需要重启服务器&#xff0c;这就比较麻烦。其实有一种方法是通过配置密码策略&#xff0c;不要要重启的…