Games01作业
GAMES101 作业
作业1
作业1的目的是绘制线框三角形并让其旋转,对于这一目标,框架已经完成了大部分的工作,我们只需要填充MVP变换中的Model和Projection两个个矩阵即可。
首先是Model矩阵:
按照绕z轴旋转的矩阵填入即可。
Eigen::Matrix4f get_model_matrix(float rotation_angle) {
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
float cosa = std::cos(rotation_angle / 180.0 * MY_PI);
float sina = std::sin(rotation_angle / 180.0 * MY_PI);
model << cosa, -sina, 0.0, 0.0, sina, cosa, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0;
return model;
}
之后是Projection矩阵:
投影过程分为三个步骤:
- 第一步将视锥压缩成为长方体
- 第二部将长方体平移,使其中心位于坐标原点
- 第三步将长方体压缩为[-1,1]³ 的一个立方体(注意-1到1意味着边长为2)
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar) {
Eigen::Matrix4f squash;
squash << zNear, .0, .0, .0, .0, zNear, .0, .0, .0, .0, zNear + zFar,
-zNear * zFar, .0, .0, 1.0, .0;
Eigen::Matrix4f translate;
float t = -zNear * std::tan(eye_fov / 180.0 * MY_PI / 2);
float b = -t;
float r = t * aspect_ratio;
float l = -r;
translate <<
1.0, .0, .0, -(l+r)/2,
.0, 1.0, .0, -(b+t)/2,
.0, .0, 1.0, -(zNear + zFar) / 2,
.0, .0, .0, 1.0;
Eigen::Matrix4f zoom;
zoom <<
2 / (r - l), 0., 0., 0.,
.0, 2 / (t - b), 0., .0,
.0, .0, 2 / (zNear - zFar), .0,
.0, .0, .0, 1.0;
return zoom * translate * squash;
}
作业2
作业2需要实现的部分有两个,一是判断点是否在三角形内部,二是光栅化。
判断点在三角形内部:
对于三角形ABC与任意点P,判断其是否在三角形内部可以用AB×AP,BC×BP,CA×CP这三项叉乘是否在同一方向上来进行。
鉴于我们只需要考虑xy平面上的三角形,可以使用二维的叉乘来判断,设两个向量分别为(x1,y1)与(x2,y2),即是计算(x1y2 - x2y1)。
static bool insideTriangle(float x, float y,const Vector3f (&v) [3])
{
// point A, point B reutrn AB × AP
auto cross = [x,y](float x1, float y1, float x2, float y2){
return (x2 - x1)*(y - y1) -(y2 - y1)*(x - x1);
};
// AB × AP
float a = cross(v[0][0], v[0][1], v[1][0], v[1][1]);
float b = cross(v[1][0], v[1][1], v[2][0], v[2][1]);
float c = cross(v[2][0], v[2][1], v[0][0], v[0][1]);
return a > 0 ? (b>0 && c>0 ) : (b<0 && c<0);
}
这里cross传入的是两个点A和B,捕获的是P点,计算结果是AB×AP。
计算完成后判断三个结果是否同号即可。这里采用浮点数作为参数可以便于后续的SSAA计算。
光栅化:
如果前面的Projection矩阵有问题,那么这里可能得不到正确的结果(方向和深度关系会与示例不同)
需要完成的工作包括:
-
计算bounding box,这一步比较简单,遍历三角形的三个顶点取到最小的x,最小的y,最大的x和最大的y即可。
-
判断点是否在三角形内部,以及判断深度是否小于深度缓冲中的值:
if(insideTriangle(x+.5, y+.5, t.v)){ auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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(depth_buf[get_index(x,y)] > z_interpolated){ depth_buf[get_index(x,y)] = z_interpolated; frame_buf[get_index(x, y)] = t.getColor(); } }
SSAA优化
2×2 SSAA的做法简单来说就是对于原始的采样,使用4倍的分辨率进行,因此我们需要定义4倍大小的frame_buffer和z_buffer,为便于计算,我们可以将原来的frame_buffer和z_buffer的元素替换成对应的数组:
std::vector<std::array<float, SSAA*SSAA>> ssaa_depth_buf;
std::vector<std::array<Eigen::Vector3f, SSAA*SSAA>> ssaa_frame_buf;
然后光栅化的过程与原来的方式几乎没有区别,只需要在每一个(x,y)点处计算4个采样点即可。最后是在绘制之前将ssaa_frame_buffer中的颜色取平均填入到frame_buffer中去。
// 对于每个点x,y,需要采样 n*n次
for(int i = 0; i< SSAA; ++i){
for(int j = 0;j<SSAA;++j){
float xx = x+1.0/SSAA/2 + i * 1.0/SSAA;
float yy = y+1.0/SSAA/2 + j * 1.0/SSAA;
if(!insideTriangle(xx, yy, t.v)){
continue;
}
auto[alpha, beta, gamma] = computeBarycentric2D(xx, yy, 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;
int ssaa_index = i*SSAA +j;
if(ssaa_depth_buf[get_index(x, y)][ssaa_index] > z_interpolated){
ssaa_depth_buf[get_index(x, y)][ssaa_index] = z_interpolated;
ssaa_frame_buf[get_index(x,y)][ssaa_index] = t.getColor();
}
}
}
下图是4×4 SSAA、2×2 SSAA和不使用SSAA的对比:
可以看到效果还是比较明显的
posted on 2023-03-26 21:28 xiaomingcc 阅读(30) 评论(0) 编辑 收藏 举报
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律