GAMES101现代计算机图形学作业
课后作业0:
给定一个点 P=(2,1), 将该点绕原点先逆时针旋转 45◦,再平移 (1,2), 计算出 变换后点的坐标(要求用齐次坐标进行计算)。
代码:
#include<iostream>
#include<cmath>
#include<Eigen/Core>
#include<Eigen/Dense>
using namespace std;
using namespace Eigen;
const double PI=acos(-1);
//旋转函数
Vector3f rotation(Vector3f x,double a)
{
Matrix3f f;
f<<cos(a),-sin(a),0,sin(a),cos(a),0,0,0,1;
return f*x;
}
//平移函数
Vector3f move(Vector3f x,Vector3f pos)
{
return x+pos;
}
int main()
{
//p点,用向量表示,第三个值为1
Vector3f p(2,1,1);
//方向向量,第三个值为0
Vector3f pos(1,2,0);
p=rotation(p,PI*45/180);
cout<<"旋转过后:"<<endl;
cout<<p<<endl;
p=move(p,pos);
cout<<"平移过后"<<endl;
cout<<p<<endl;
return 0;
}
结果:
也可以把旋转和平移都写到一个矩阵里
代码:
#include<iostream>
#include<cmath>
#include<Eigen/Core>
#include<Eigen/Dense>
using namespace std;
using namespace Eigen;
const double PI=acos(-1);
//旋转后平移函数
Vector3f rotation_move(Vector3f x,double a,Vector3f pos)
{
Matrix3f f;
f<<cos(a),-sin(a),pos[0],sin(a),cos(a),pos[1],0,0,1;
return f*x;
}
int main()
{
//p点,用向量表示,第三个值为1
Vector3f p(2,1,1);
//方向向量,第三个值为0
Vector3f pos(1,2,0);
p=rotation_move(p,45*PI/180,pos);
cout<<p;
return 0;
}
结果正确
课后作业1:
填写一个旋转矩阵和一个透视投影矩阵。给定三维下三个点 v0(2.0, 0.0, −2.0), v1(0.0, 2.0, −2.0), v2(−2.0, 0.0, −2.0), 你需要将这三个点的坐标变换为屏幕坐标并在屏幕上绘制出对应的线框三角形 (在代码框架中,我们已经提供了 draw_triangle 函数,所以你只需要去构建变换矩阵即可)。简而言之,我们需要进行模型、视图、投影、视口等变换来将三角形显示在屏幕上。
代码:
//返回把摄像机移到原点的矩阵
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
Eigen::Matrix4f translate;
translate<<
1,0,0,-eye_pos[0],
0,1,0,-eye_pos[1],
0,0,1,-eye_pos[2],
0,0,0,1;
view=translate*view;
return view;
}
//返回绕着z轴旋转一个角度的矩阵
Eigen::Matrix4f get_model_matrix(float rotation_angle)
{
Eigen::Matrix4f model = Eigen::Matrix4f::Identity();
Eigen::Matrix4f rotate;
rotate<<
cos(rotation_angle),-sin(rotation_angle),0,0,
sin(rotation_angle),cos(rotation_angle),0,0,
0,0,1,0,
0,0,0,1;
model=rotate*model;
return model;
}
//透视投影矩阵,先挤压到[-1,1]^3,再做一次正交投影 45, 1, 0.1, 50
Eigen::Matrix4f get_projection_matrix(float eye_fov,float aspect_ratio,float zNear,float zFar)
{
Eigen::Matrix4f projection=Eigen::Matrix4f::Identity();
Eigen::Matrix4f M_persp2ortho(4,4);
Eigen::Matrix4f M_ortho_scale(4,4);
Eigen::Matrix4f M_ortho_trans(4,4);
//角度转换成弧度制,这里/2表示的是视锥的上半部分的角度
float angle=eye_fov*MY_PI/180.0/2;
//求出屏幕的长和宽
float height=zNear*tan(angle)*2;
float width=height*aspect_ratio;
//求出屏幕的坐标边界
auto t=-zNear*tan(angle);//上截面
auto r=t*aspect_ratio;//右截面
auto l=-r;//左截面
auto b=-t;//下截面
//透视矩阵"挤压"
M_persp2ortho<<
zNear,0,0,0,
0,zNear,0,0,
0,0,zNear+zFar,-zNear*zFar,
0,0,1,0;
//下面两个矩阵用于正交投影
// 正交矩阵-缩放
M_ortho_scale<<
2/(r-l),0,0,0,
0,2/(t-b),0,0,
0,0,2/(zNear-zFar),0,
0,0,0,1;
//正交矩阵-平移
M_ortho_trans<<
1,0,0,-(r+l)/2,
0,1,0,-(t+b)/2,
0,0,1,-(zNear+zFar)/2,
0,0,0,1;
projection=M_ortho_scale*M_ortho_trans*M_persp2ortho*projection;
return projection;
}
结果:
三维空间中的物体,通过MVP变换之后,再经过视口变换后显示在屏幕上,这就是光栅化。
课后作业1:
在屏幕上画出一个实心三角形,换言之,栅格化一个三角形。上一次作业中,在视口变化之后,我们调用了函数rasterize_wireframe(const Triangle& t)。但这一次,你需要自己填写并调用函数 rasterize_triangle(const Triangle& t)。
该函数的内部工作流程如下:
- 创建三角形的 2 维 bounding box。
- 遍历此 bounding box 内的所有像素(使用其整数索引)。然后,使用像素中
心的屏幕空间坐标来检查中心点是否在三角形内。 - 如果在内部,则将其位置处的插值深度值 (interpolated depth value) 与深度
缓冲区 (depth buffer) 中的相应值进行比较。 - 如果当前点更靠近相机,请设置像素颜色并更新深度缓冲区 (depth buffer)。
判断一个点是否在三角形里
static bool insideTriangle(int x, int 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::Vector3f point(x,y,1);
Eigen::Vector3f p1=_v[1]-_v[0];
Eigen::Vector3f p2=_v[2]-_v[1];
Eigen::Vector3f p3=_v[0]-_v[2];
double a=p1.cross(point-_v[0]).z();
double b=p2.cross(point-_v[1]).z();
double c=p3.cross(point-_v[2]).z();
return a>=0&&b>=0&&c>=0||a<=0&&b<=0&&c<=0;
}
重心坐标插值
float getarea(Vector3f a,Vector3f b)
{
return abs(a.x()*b.y()-b.x()*a.y())/2;
}
//计算重心坐标,插值
static std::tuple<float,float,float> computeBarycentric2D(float x,float y,const Vector3f* v)
{
Vector3f a={
v[0].x()-x,v[0].y()-y,0};
Vector3f b={
v[1].x()-x,v[1].y()-y,0};
Vector3f c={
v[2].x()-x,v[2].y()-y,0};
Vector3f a1={
v[0].x()-v[1].x(),v[0].y()-v[1].y(),0};
Vector3f b1={
v[0].x()-v[2].x(),v[0].y()-v[2].y(),0};
//三角形面积
float totalArea=getarea(a1,b1);
float c1=getarea(a,b)/totalArea;
float c2=getarea(b,c)/totalArea;
float c3=getarea(c,a)/totalArea;
return {
c1,c2,c3};
}
光栅化三角形
void rst::rasterizer::rasterize_triangle(const Triangle& t)
{
auto v=t.toVector4();
float alpha,beta,gamma,lmin=INT_MAX,rmax=INT_MIN,tmax=INT_MIN,bmin=INT_MAX;
for(auto &k:v)//找到bounding box的边界坐标
{
lmin=int(std::min(lmin,k.x()));
rmax=std::max(rmax,k.x());
rmax=rmax==int(rmax)?int(rmax)-1:rmax;
tmax=std::max(tmax,k.y());
tmax=tmax==int(tmax)?int(tmax)-1:tmax;
bmin=int(std::min(bmin,k.y()));
}
for(float i=lmin;i<=rmax;i++)
{
for(float j=bmin;j<=tmax;j++)
{
if(insideTriangle(i,j,t.v))
{
//插值求重心坐标
std::tie(alpha,beta,gamma)=computeBarycentric2D(i+0.5,j+0.5,t.v);
float w_reciprocal=1.0/(alpha/v[0].w()+beta/v[1].w()+gamma/v[2].w());
//把z插值出来了
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(i,j)])//如果当前z值比像素z值小(这里是把z值换成正数比较的)
{
set_pixel({
i,j,1},t.getColor());
depth_buf[get_index(i,j)]=-z_interpolated;//设置像素颜色,修改像素当前深度
}
}
}
}
}
结果: