GAMES101-Assignment3
参考文章:
1.《GAMES101》作业框架问题详解
2. Games101:作业3(管线分析、深度插值、libpng warning、双线性插值等)
3.【GAMES101】作业3(提高)与法线贴图原理和渲染管线框架分析
文件清单:
CMakeLists.txt(项目配置清单,cmake根据此清单进行系统构建、编译、测试)
OBJ_Loader.h(用于加载三维模型)
global.hpp(定义全局变量PI)
rasterizer.hpp(光栅器头文件)
Texture.hpp(声明纹理宽高和根据纹理坐标获取像素颜色的函数getColor)
Triangle.hpp(三角形的头文件,定义其相关属性)
Shader.hpp(声明颜色、法向量、纹理、纹理坐标,支持纹理映射,定义了fragment_shader_payload,其中包括了 Fragment Shader 可能用到的参数)
rasterizer.cpp(生成渲染器界面与绘制)
Texture.cpp(目前为空文件)
Triangle.cpp(画出三角形)
main.cpp
1.实验要求
2.实验内容及其结果
2.1 rasterize_triangle in rasterizer.cpp
barycentric
初始化空间中三角形三个点的颜色、法向量、纹理坐标、shading point位置,通过重心公式对三角形内的各个属性进行插值,计算得到的各个属性传入fragment_shader_payload
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
// TODO: From your HW3, get the triangle rasterization code.
// TODO: Inside your rasterization loop:
// * v[i].w() is the vertex view space depth value z.
// * Z is interpolated view space depth for the current pixel
// * zp is depth between zNear and zFar, used for z-buffer
// float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
// float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
// zp *= Z;
// TODO: Interpolate the attributes:
// auto interpolated_color
// auto interpolated_normal
// auto interpolated_texcoords
// auto interpolated_shadingcoords
// Use: fragment_shader_payload payload( interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
// Use: payload.view_pos = interpolated_shadingcoords;
// Use: Instead of passing the triangle's color directly to the frame buffer, pass the color to the shaders first to get the final color;
// Use: auto pixel_color = fragment_shader(payload);
auto v = t.toVector4();
// Define bound of box
//INT_MAX: infnity represented by top limitation of integer
int BoxMin_X =INT_MAX,BoxMin_Y=INT_MAX;
//INT_MIN: infnity represented by bottom limitation of integer
int BoxMax_X =INT_MIN,BoxMax_Y=INT_MIN;
//iterate to find bound
for (int i = 0; i < 3; i++)
{
BoxMin_X=std::min(BoxMin_X,(int)v[i][0]);
BoxMin_Y=std::min(BoxMin_Y,(int)v[i][1]);
BoxMax_X=std::max(BoxMax_X,(int)v[i][0]);
BoxMax_Y=std::max(BoxMax_Y,(int)v[i][1]);
}
//iterate pixel inside of bounding box
for (int i = BoxMin_X; i <= BoxMax_X; i++)
{
for (int j = BoxMin_Y; j <= BoxMax_Y; j++)
{
//float x=i+0.5,y=i+0.5;
//check if center of current pixel is inside the triangle
if (insideTriangle(i,j,t.v))//if centric pixel(x,y) is insidez. three point v[0],v[1],v[2]in triangle
{
//interpolated depth value
auto[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());
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;
//interpolated depth value is compared with depth_buffer
if (-z_interpolated < depth_buf[get_index(i,j)])//get buffer index of pixel(x,y)
{
//color interpolate
auto interpolated_color = interpolate(alpha,beta,gamma,t.color[0],t.color[1],t.color[2],1);
//normal vector interpolate
auto interpolated_normal = interpolate(alpha,beta,gamma,t.normal[0],t.normal[1],t.normal[2],1).normalized();
//texture coordinates interpolate
auto interpolated_texcoords = interpolate(alpha,beta,gamma,t.tex_coords[0],t.tex_coords[1],t.tex_coords[2],1);
//shading point coordinates interpolate
auto interpolated_shadingcoords = interpolate(alpha,beta,gamma,view_pos[0],view_pos[1],view_pos[2],1);
//interpolated attributes send to fragment_shader_payload
fragment_shader_payload payload(interpolated_color,interpolated_normal.normalized(),interpolated_texcoords,texture ? &*texture :nullptr);
//send orignal coord to view_pos
payload.view_pos = interpolated_shadingcoords;
//update depth buffer
depth_buf[get_index(i,j)] = -z_interpolated;
//update frame buffer
frame_buf[get_index(i,j)] = fragment_shader(payload);
//get color of 1 out of 3 vertex in triangle,then set this color to current pixel
set_pixel({i,j},frame_buf[get_index(i,j)]);
//set_pixel({i,j,1},t.getColor());//(x,y,1) homogeneous coord,num 1 represent a point
}
}
}
}
}
2.2 get_projection_matrix in main.cpp
MVP变换
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
// TODO: Use the same projection matrix from the previous assignments
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the projection matrix for the given parameters.
// Then return it.
// n=zNear f=zFar
// calculate r l t b according to eye_fov and aspect_ratio
// tan(eye_fov/2)=t/|n|,aspect_ratio=r/t
// t=2|n|tan(eye_fov/2),r=t*aspect_ratio,l=-r,b=-t
float t=2*abs(zNear)*tan(eye_fov/2),b=-t;
float r=aspect_ratio*t,l=-r;
eye_fov=(eye_fov/180.0)*MY_PI;//angle to rad
//perspective projection M_{projection}= M_{orthographics}*M_{persp to ortho}
Eigen::Matrix4f M_persp_to_ortho,M_ortho,M_scale,M_translate;
M_persp_to_ortho << zNear,0,0,0,
0,zNear,0,0,
0,0,zNear+zFar,-zNear*zFar,
0,0,1,0;
//M_{orthographics}=scale*translate
M_scale << 2/(r-l),0,0,0,
0,2/(t-b),0,0,
0,0,2/(zNear-zFar),0,
0,0,0,1;
M_translate << 1,0,0,-(r+l)/2,
0,1,0,-(t+b)/2,
0,0,1,-(zNear+zFar)/2,
0,0,0,1;
M_ortho = M_scale*M_translate;
projection = M_ortho*M_persp_to_ortho*projection;
return projection;
}
in main.cpp 激活相应 shader
编译后运行
./Rasterizer output.png normal
2.3 phong_fragment_shader in main.cpp
Blinn-phong 反射模型: 正确实现 phong_fragment_shader 对应的反射模型
Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;//p value control range of specular
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
Eigen::Vector3f l,v,dif,h,spe,amb;
for (auto& light : lights)
{
// TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
// components are. Then, accumulate that result on the *result_color* object.
//light vector l
l = (light.position - point).normalized();
//view vector v
v = (eye_pos - point).normalized();
//distance r from light to shading point,namely norm of light vector
//r = (light.position - point).dot(light.position - point);
//diffuse L_d= kd(I/r^2)max(0,n dot l)
dif = kd.cwiseProduct(light.intensity / (light.position - point).dot(light.position - point))*std::fmax(0, normal.dot(l));//normal is n vector
//half vector h=(v+l)/norm(v+l)
h = (v+l) / ((v+l).dot(v+l));
//specular L_s=ks(I/r^2)max(0,n dot h)^p
spe = ks.cwiseProduct(light.intensity / ((light.position - point).dot(light.position - point)))*pow(std::fmax(0,normal.dot(h.normalized())),p);
//ambient La=kaIa
amb = ka.cwiseProduct(amb_light_intensity);
result_color += (dif + spe + amb);
}
return result_color * 255.f;
}
in main.cpp 激活相应 shader
编译后运行
./Rasterizer output.png phong
2.4 texture_fragment_shader in main.cpp
Texture mapping: 将 phong_fragment_shader 的代码拷贝到 texture_fragment_shader, 在此基础上正确实现 Texture Mapping.
phong_fragment_shader中color = payload_color
texture_fragment_shader中color = texture_color
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f return_color = {0, 0, 0};
if (payload.texture)
{
// TODO: Get the texture value at the texture coordinates of the current fragment
return_color = payload.texture->getColor(payload.tex_coords.x(),payload.tex_coords.y());
}
Eigen::Vector3f texture_color;
texture_color << return_color.x(), return_color.y(), return_color.z();
//ambient term coefficient ka
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
//diffuse reflection coefficient kd
Eigen::Vector3f kd = texture_color / 255.f;
//specular term coefficient ks
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
//light's position and intensity
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
//color of texture
Eigen::Vector3f color = texture_color;
//coord of shading point
Eigen::Vector3f point = payload.view_pos;
//normal vector of shading point
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
Eigen::Vector3f l,v,dif,h,spe,amb;
for (auto& light : lights)
{
// TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
// components are. Then, accumulate that result on the *result_color* object.
//light vector l
l = (light.position - point).normalized();
//view vector v
v = (eye_pos - point).normalized();
//distance r from light to shading point,namely norm of light vector
//(light.position - point).dot(light.position - point)
//diffuse L_d= kd(I/r^2)max(0,n dot l)
dif = kd.cwiseProduct(light.intensity / ((light.position - point).dot(light.position - point)))*std::fmax(0, normal.dot(l));//normal is n vector
//half vector h=(v+l)/norm(v+l)
h = (v+l) / ((v+l).dot(v+l));
//specular L_s=ks(I/r^2)max(0,n dot h)^p
spe = ks.cwiseProduct(light.intensity / ((light.position - point).dot(light.position - point)))*pow(std::fmax(0,normal.dot(h.normalized())),p);
//ambient La=kaIa
amb = ka.cwiseProduct(amb_light_intensity);
result_color += (dif + spe + amb);
}
return result_color * 255.f;
}
in main.cpp 激活相应shader
编译后运行
./Rasterizer output.png texture
2.5 bump mapping in main.cpp
Eigen::Vector3f bump_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
float kh = 0.2, kn = 0.1;
// TODO: Implement bump mapping here
// Let n = normal = (x, y, z)
// Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
// Vector b = n cross product t
// Matrix TBN = [t b n]
// dU = kh * kn * (h(u+1/w,v)-h(u,v))
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
// Vector ln = (-dU, -dV, 1)
// Normal n = normalize(TBN * ln)
//------------------------
//perturb the normal in 3D
//original surface normal n(p)=(0,0,1)
//derivative at p are
//dp/du=c1*[h(u+1)-h(u)]
//dp/dv=c2*[h(v+1)-h(v)]
//perturbed normal is then n(p)=(-dp/du,-dp/dv,1).normalized()
float x=normal.x(),y=normal.y(),z=normal.z();
Eigen::Vector3f t{x*y / std::sqrt(x*x+z*z), x*z / std::sqrt(x*x+z*z), z*y / std::sqrt(x*x+z*z)};
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN;
TBN << t.x(),b.x(),normal.x(),
t.y(),b.y(),normal.y(),
t.z(),b.z(),normal.z();
float u = payload.tex_coords.x(),v = payload.tex_coords.y();
float w = payload.texture->width,h = payload.texture->height;
float dU = kh*kn*(payload.texture->getColor(u+1.0f/w,v).norm() - payload.texture->getColor(u,v).norm());
float dV = kh*kn*(payload.texture->getColor(u,v+1.0f/h).norm() - payload.texture->getColor(u,v).norm());
Eigen::Vector3f perturbed_n{-dU,-dV,1.0f};
normal = TBN * perturbed_n;
//-------------------------
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal.normalized();
return result_color * 255.f;
}
编译后运行
./Rasterizer output.png bump
2.6 displacement mapping in main.cpp
Eigen::Vector3f displacement_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = payload.color;
Eigen::Vector3f ks = Eigen::Vector3f(0.7937, 0.7937, 0.7937);
auto l1 = light{{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector<light> lights = {l1, l2};
Eigen::Vector3f amb_light_intensity{10, 10, 10};
Eigen::Vector3f eye_pos{0, 0, 10};
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
float kh = 0.2, kn = 0.1;
// TODO: Implement displacement mapping here
// Let n = normal = (x, y, z)
// Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z))
// Vector b = n cross product t
// Matrix TBN = [t b n]
// dU = kh * kn * (h(u+1/w,v)-h(u,v))
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
// Vector ln = (-dU, -dV, 1)
// Position p = p + kn * n * h(u,v)
// Normal n = normalize(TBN * ln)
//------------------------
//perturb the normal in 3D
//original surface normal n(p)=(0,0,1)
//derivative at p are
//dp/du=c1*[h(u+1)-h(u)]
//dp/dv=c2*[h(v+1)-h(v)]
//perturbed normal is then n(p)=(-dp/du,-dp/dv,1).normalized()
float x=normal.x(),y=normal.y(),z=normal.z();
Eigen::Vector3f t{x*y / std::sqrt(x*x+z*z), x*z / std::sqrt(x*x+z*z), z*y / std::sqrt(x*x+z*z)};
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN;
TBN << t.x(),b.x(),normal.x(),
t.y(),b.y(),normal.y(),
t.z(),b.z(),normal.z();
float u = payload.tex_coords.x(),v = payload.tex_coords.y();
float w = payload.texture->width,h = payload.texture->height;
float dU = kh*kn*(payload.texture->getColor(u+1.0f/w,v).norm() - payload.texture->getColor(u,v).norm());
float dV = kh*kn*(payload.texture->getColor(u,v+1.0f/h).norm() - payload.texture->getColor(u,v).norm());
Eigen::Vector3f perturbed_n{-dU,-dV,1.0f};
normal = TBN * perturbed_n;
point += (kn * normal * payload.texture->getColor(u,v).norm());
//-------------------------
Eigen::Vector3f result_color = {0, 0, 0};
Eigen::Vector3f light_vec,view_vec,dif,half_vec,spe,amb;
for (auto& light : lights)
{
// TODO: For each light source in the code, calculate what the *ambient*, *diffuse*, and *specular*
// components are. Then, accumulate that result on the *result_color* object.
//light vector l
light_vec = (light.position - point).normalized();
//view vector v
view_vec = (eye_pos - point).normalized();//+l??
//distance r from light to shading point,namely norm of light vector
//r = (light.position - point).dot(light.position - point);
//diffuse L_d= kd(I/r^2)max(0,n dot l)
dif = kd.cwiseProduct(light.intensity / (light.position - point).dot(light.position - point))*std::fmax(0, normal.dot(light_vec));//normal is n vector
//half vector h=(v+l)/norm(v+l)
half_vec = (view_vec+light_vec) / ((view_vec+light_vec).dot(view_vec+light_vec));
//specular L_s=ks(I/r^2)max(0,n dot h)^p
spe = ks.cwiseProduct(light.intensity / ((light.position - point).dot(light.position - point)))*pow(std::fmax(0,normal.dot(half_vec.normalized())),p);
//ambient La=kaIa
amb = ka.cwiseProduct(amb_light_intensity);
result_color += (dif + spe + amb);
}
return result_color * 255.f;
}
编译后运行
./Rasterizer output.png displacement