作业要求
- 修改函数 rasterize_triangle(const Triangle& t) in rasterizer.cpp: 在此
处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。
- 修改函数 get_projection_matrix() in main.cpp: 将你自己在之前的实验中
实现的投影矩阵填到此处,此时你可以运行 ./Rasterizer output.png normal
来观察法向量实现结果。 - 修改函数 phong_fragment_shader() in main.cpp: 实现 Blinn-Phong 模型计
算 Fragment Color. - 修改函数 texture_fragment_shader() in main.cpp: 在实现 Blinn-Phong
的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment
Shader. - 修改函数 bump_fragment_shader() in main.cpp: 在实现 Blinn-Phong 的
基础上,仔细阅读该函数中的注释,实现 Bump mapping. - 修改函数 displacement_fragment_shader() in main.cpp: 在实现 Bump
mapping 的基础上,实现 displacement mapping.
具体实现
- rasterize_triangle()函数,关键是要知道interpolated_color 、interpolated_normal、interpolated_texcoords、interpolated_shadingcoords这几个参数是根据interpolate()函数计算得出的。
//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();
int xmin = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
int ymin = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
int xmax = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
int ymax = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
for(int i=xmin;i<=xmax;i++)
{
for(int j=ymin;j<=ymax;j++)
{
if(insideTriangle(i,j,t.v)){ //点在三角形内部
auto[alpha, beta, gamma] = computeBarycentric2D(i, j, t.v);
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;
if(zp < depth_buf[get_index(i,j)]){
depth_buf[get_index(i,j)] = zp;
auto interpolated_color = interpolate(alpha,beta,gamma,t.color[0],t.color[1],t.color[2],1);
auto interpolated_normal = interpolate(alpha,beta,gamma,t.normal[0],t.normal[1],t.normal[2],1);
auto interpolated_texcoords = interpolate(alpha,beta,gamma,t.tex_coords[0],t.tex_coords[1],t.tex_coords[2],1);
auto interpolated_shadingcoords = interpolate(alpha,beta,gamma,view_pos[0],view_pos[1],view_pos[2],1);
fragment_shader_payload payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
auto pixel_color = fragment_shader(payload);
Vector2i p(i,j);
set_pixel(p,pixel_color);
}
}
}
}
}
- get_projection_matrix()函数直接把作业1中的代码搬过来
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
// Students will implement this function
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
// TODO: Implement this function
// Create the projection matrix for the given parameters.
// Then return it.
//创建透视变正交矩阵
Eigen::Matrix4f m = Eigen::Matrix4f::Identity();;
m << zNear,0,0,0,
0,zNear,0,0,
0,0,zNear+zFar,-zNear*zFar,
0,0,1,0;
//创建正交投影矩阵
Eigen::Matrix4f q1 = Eigen::Matrix4f::Identity(),q2 = Eigen::Matrix4f::Identity();
float top = tan(eye_fov/2/180*acos(-1)) * -zNear;
float bottom = -top;
float right = top * aspect_ratio;
float left = -right;
//缩放
q1 << 2.0/(right - left),0,0,0,
0,2.0/(top - bottom),0,0,
0,0,2.0/(zNear - zFar),0,
0,0,0,1;
//平移
q2 << 1,0,0,-(right+left)/2.0,
0,1,0,-(top+bottom)/2.0,
0,0,1,-(zNear+zFar)/2.0,
0,0,0,1;
projection = q1 * q2 * m;
return projection;
}
- phong_fragment_shader()函数,在这之前你需要知道Blinn-Phong反射模型的计算公式:参考博客
下面解释一下该函数中的一些参数,知道这些参数的意义之后再去做会清晰很多:amb_light_intensity代表环境光的强度,对应公式中的Ia;
lights表示两个光源,它的强度对应公式中的 I(不是向量l);
eye_pos代表眼睛的位置;
color、point、normal表示着色点的颜色、位置、法向量;
用光源的位置减去着色点的位置point,然后标准化一下就得到了向量l;
用眼睛的位置减去着色点的位置标准化一下得到向量v;
(v+l)/2就得到了向量h
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;
//着色点的信息
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
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.
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
//距离r的平方
float r = (light.position - point).dot(light.position - point);
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f v = (eye_pos - point).normalized();
Eigen::Vector3f h = (l + v).normalized();
double c = normal.normalized().dot(l);
Eigen::Vector3f diffuse = kd.cwiseProduct(light.intensity/r) * std::max(0.0,c);
c = normal.normalized().dot(h);
Eigen::Vector3f specular = ks.cwiseProduct(light.intensity/r) * pow(std::max(0.0,c),p);
result_color = result_color + ambient + diffuse + specular;
}
return result_color * 255.f;
}
运行之后你会得到这样的结果
- texture_fragment_shader()相对来说就比较好实现了
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
Eigen::Vector3f color = payload.texture->getColor(payload.tex_coords.x(),payload.tex_coords.y());
return_color += color;
//std::cout<<color;
}
Eigen::Vector3f texture_color;
texture_color << return_color.x(), return_color.y(), return_color.z();
Eigen::Vector3f ka = Eigen::Vector3f(0.005, 0.005, 0.005);
Eigen::Vector3f kd = texture_color / 255.f;
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 = texture_color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
Eigen::Vector3f result_color = {0, 0, 0};
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.
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
//距离r的平方
float r = (light.position - point).dot(light.position - point);
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f v = (eye_pos - point).normalized();
Eigen::Vector3f h = (l + v).normalized();
double c = normal.normalized().dot(l);
Eigen::Vector3f diffuse = kd.cwiseProduct(light.intensity/r) * std::max(0.0,c);
c = normal.normalized().dot(h);
Eigen::Vector3f specular = ks.cwiseProduct(light.intensity/r) * pow(std::max(0.0,c),p);
result_color = result_color + ambient + diffuse + specular;
}
return result_color * 255.f;
}
结果如下
后两个函数emmmm不太懂,代码如下
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)
float x = normal.x();
float y = normal.y();
float z = normal.z();
Eigen::Vector3f t = Eigen::Vector3f(x * y / std::sqrt(x * x + z * 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();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float 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 ln = Eigen::Vector3f(-dU, -dV, 1.0f);
point += (kn * normal * payload.texture->getColor(u, v).norm());
normal = (TBN * ln).normalized();
Eigen::Vector3f result_color = {0, 0, 0};
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.
Eigen::Vector3f light_dir = (light.position - point).normalized();
Eigen::Vector3f view_dir = (eye_pos - point).normalized();
Eigen::Vector3f half_vector = (light_dir + view_dir).normalized();
// 距离衰减
float r2 = (light.position - point).dot(light.position - point);
//环境光
//cwiseProduct():矩阵点对点相乘
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
//漫反射
Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r2);
Ld *= std::max(0.0f, normal.normalized().dot(light_dir));
//高光
Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r2);
Ls *= std::pow(std::max(0.0f, normal.normalized().dot(half_vector)), p);
result_color += (La + Ld + Ls);
}
return result_color * 255.f;
}
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)
float x = normal.x();
float y = normal.y();
float z = normal.z();
Eigen::Vector3f t = Eigen::Vector3f(x * y / std::sqrt(x * x + z * 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();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float 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 ln = Eigen::Vector3f(-dU, -dV, 1.0f);
normal = TBN * ln;
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal.normalized();
return result_color * 255.f;
}