games101-作业
作业1
games101 作业1及作业2分析及解决 详解透视矩阵 - dyccyber - 博客园
GAMES101——作业1 旋转与投影(含提高:罗德里格斯旋转公式)_games101作业1-CSDN博客
GAMES101——作业1 旋转与投影(含提高:罗德里格斯旋转公式)_games101作业1-CSDN博客
任务
实现
get_model_matrix
绕x,y,z轴的变换矩阵如下,算出两个三角函数值后直接代入即可
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity(); //创建一个单位矩阵
float angle = rotation_angle / 180.0f * MY_PI; //将角度制的角度转化为弧度制的角度,因为c++默认的三角函数用的是弧度制
float s = std::sin(angle);
float c = std::cos(angle);
model << c, -s, 0, 0, s, c, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0; //根据旋转矩阵的公式,直接给模型矩阵赋值
return model;
}
get_projection_matrix
zNear是近平面到摄像机的距离,已知视角,可以根据正切函数求出一半的高度,最后乘2就可以得到近平面的高度,根据宽高比,求出近平面的宽度。再根据推导出的矩阵,代入数据即可求得所需的结果。
/*
float eye_fov; Y view ,观看
float aspect_ratio ;宽高比
*/
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio,
float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f persp_to_ortho; //公式中的persp->ortho矩阵
Eigen::Matrix4f ortho; //ortho矩阵
float height = zNear*std::tan(eye_fov/2) * 2; //视锥体挤成立方体,立方体的高
float width = height * aspect_ratio; //立方体的宽
persp_to_ortho << zNear, 0, 0, 0, //根据公式求得persp->ortho矩阵
0,zNear,0,0,
0,0,zNear + zFar,-zNear*zFar,
0,0,1,0;
ortho << 2.0/width , 0 ,0, 0, //根据公式求得ortho矩阵,这里直接将平移和缩放两步写在一起了
0, 2.0/height,0,0,
0,0,2.0/(zNear-zFar),-(zNear+zFar)/(zNear-zFar),
0,0,0,1;
projection = ortho * persp_to_ortho * projection;
return projection;
}
绘制过程:
代码分析
简要分析一下整体的一个绘制流程
首先定义了绘制的视口 同时初始化了像素缓冲区 与 深度缓冲区:
rst::rasterizer r(700, 700);
rst::rasterizer::rasterizer(int w, int h) : width(w), height(h)
{
frame_buf.resize(w * h);
depth_buf.resize(w * h);
}
定义相机位置、三角形三个顶点在空间中的位置,三个顶点的索引顺序,注意我这里相机位置和顶点位置设置的都和原来不一样,这里后面再提:
Eigen::Vector3f eye_pos = {0, 0, 0};
std::vector<Eigen::Vector3f> pos{
{2, 0, 12}, {0, 2, 12}, {-2, 0, 12}};
std::vector<Eigen::Vector3i> ind{
{0, 1, 2}};
然后创建对应三角形的顶点缓冲区以及索引缓冲区:
auto pos_id = r.load_positions(pos);
auto ind_id = r.load_indices(ind);
rst::pos_buf_id rst::rasterizer::load_positions(const std::vector<Eigen::Vector3f> &positions)
{
auto id = get_next_id();
pos_buf.emplace(id, positions);
return {id};
}
rst::ind_buf_id rst::rasterizer::load_indices(const std::vector<Eigen::Vector3i> &indices)
{
auto id = get_next_id();
ind_buf.emplace(id, indices);
return {id};
}
然后就是设置模型、观察以及透视矩阵,最后绘制
绘制部分:
void rst::rasterizer::draw(rst::pos_buf_id pos_buffer, rst::ind_buf_id ind_buffer, rst::Primitive type)
{
if (type != rst::Primitive::Triangle)
{
throw std::runtime_error("Drawing primitives other than triangle is not implemented yet!");
}
读取对应的三角形的顶点以及索引信息
auto& buf = pos_buf[pos_buffer.pos_id];
auto& ind = ind_buf[ind_buffer.ind_id];
float f1 = (100 - 0.1) / 2.0;
float f2 = (100 + 0.1) / 2.0;
Eigen::Matrix4f mvp = projection * view * model;
for (auto& i : ind)
{
Triangle t;
将读取到的顶点坐标转换成齐次坐标的形式,并进行MVP变换,然后存储下来。
Eigen::Vector4f v[] = {
mvp * to_vec4(buf[i[0]], 1.0f),
mvp * to_vec4(buf[i[1]], 1.0f),
mvp * to_vec4(buf[i[2]], 1.0f)
};
透视除法
for (auto& vec : v) {
vec /= vec.w();
}
转换到像素空间
for (auto & vert : v)
{
vert.x() = 0.5*width*(vert.x()+1.0);
vert.y() = 0.5*height*(vert.y()+1.0);
vert.z() = vert.z() * f1 + f2;
}
设置三角形的各个顶点
for (int i = 0; i < 3; ++i)
{
t.setVertex(i, v[i].head<3>());
t.setVertex(i, v[i].head<3>());
t.setVertex(i, v[i].head<3>());
}
设置各个顶点的颜色
t.setColor(0, 255.0, 0.0, 0.0);
t.setColor(1, 0.0 ,255.0, 0.0);
t.setColor(2, 0.0 , 0.0,255.0);
绘制 这里是用线框形式绘制 使用的画线算法是Bresenham
rasterize_wireframe(t);
}
}