GAMES101作业解答-作业03-Pipeline and Shading
GAMES101作业解答-作业03-Pipeline and Shading
1. 问题描述
- 本次作业需要完成的内容稍多,具体每个问题如下:
-
- 修改函数 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.
2. 问题解答
- 总得来说,上述问题无非就是在前面作业基础上,加载模型进行光栅化,着色,模型渲染,实现光照模型以及纹理映射,以及修改纹理图片进行重新编译。
- 首先实现normal shader,具体代码部分如下:
Eigen::Vector3f normal_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f return_color = (payload.normal.head<3>().normalized() + Eigen::Vector3f(1.0f, 1.0f, 1.0f)) / 2.f;
Eigen::Vector3f result;
result << return_color.x() * 255, return_color.y() * 255, return_color.z() * 255;
return result;
}
- 结果如下:

- 实现 Blinn-Phong 反射模型代码:
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.
float r2 = (point - light.position).squaredNorm();
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f v = (eye_pos - point).normalized();
Eigen::Vector3f h = (l + v).normalized();
float cosd = std::max(0.0f, normal.dot(l));
float coss = std::pow(std::max(0.0f, normal.dot(h)), p);
Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f ld = kd.cwiseProduct(light.intensity / r2 * cosd);
Eigen::Vector3f ls = ks.cwiseProduct(light.intensity / r2 * coss);
result_color += Eigen::Vector3f(la + ld + ls);
}
return result_color * 255.f;
}
-
结果如下:

-
实现纹理映射部分代码:
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->getColorBilinear(payload.tex_coords.x(), payload.tex_coords.y());
// 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();
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.
float r2 = (point - light.position).squaredNorm();
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f v = (eye_pos - point).normalized();
Eigen::Vector3f h = (l + v).normalized();
float cosd = std::max(0.0f, normal.dot(l));
float coss = std::pow(std::max(0.0f, normal.dot(h)), p);
Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f ld = kd.cwiseProduct(light.intensity / r2 * cosd);
Eigen::Vector3f ls = ks.cwiseProduct(light.intensity / r2 * coss);
result_color += Eigen::Vector3f(la + ld + ls);
}
return result_color * 255.f;
}
# 需要写双线性插值函数
// 双线性插值
Eigen::Vector3f getColorBilinear(float u, float v)
{
float u_img = u * (width - 1);
float v_img = (1 - v) * (height - 1);
Eigen::Vector2f u00{std::floor(u_img), std::floor(v_img)};
Eigen::Vector2f u01{std::ceil(u_img), std::floor(v_img)};
Eigen::Vector2f u10{std::floor(u_img), std::ceil(v_img)};
Eigen::Vector2f u11{std::ceil(u_img), std::ceil(v_img)};
float s = u_img - std::floor(u_img);
float t = v_img - std::floor(v_img);
Eigen::Vector3f u0 = getColor2(u00.x(), u00.y()) + s * (getColor2(u01.x(), u01.y()) - getColor2(u00.x(), u00.y()));
Eigen::Vector3f u1 = getColor2(u10.x(), u10.y()) + s * (getColor2(u11.x(), u11.y()) - getColor2(u10.x(), u10.y()));
return u0 + t * (u1 - u0);
}
Eigen::Vector3f getColor2(int u, int v)
{
int u_i = std::min(width - 1, u);
u_i = std::max(0, u_i);
int v_i = std::min(height - 1, v);
v_i = std::max(0, v_i);
auto color = image_data.at<cv::Vec3b>(v_i, u_i);
return Eigen::Vector3f(color[0], color[1], color[2]);
}
-
实现纹理后的结果为:

-
实现 Bump Mapping代码如下:
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)
Eigen::Vector3f n = normal;
float x = n.x();
float y = n.y();
float z = n.z();
Eigen::Vector3f t = {x*y/sqrt(x*x+z*z), sqrt(x*x+z*z), z*y/sqrt(x*x+z*z)};
t.normalize();
Eigen::Vector3f b = n.cross(t);
b.normalize();
Eigen::Matrix3f TBN;
TBN << t.x(), b.x(), n.x(),
t.y(), b.y(), n.y(),
t.z(), b.z(), n.z();
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float h = payload.texture->height;
float uc = payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm();
float vc = payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm();
float du = kh * kn * uc;
float dv = kh * kn * vc;
Eigen::Vector3f ln = Eigen::Vector3f(-du, -dv, 1.0f);
ln.normalize();
normal = (TBN * ln).normalized();
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
return result_color * 255.f;
}
-
实现后,你可以看到这样的结果:

-
实现 Displacement Mapping 部分代码如下:
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)
Eigen::Vector3f n = normal;
float x = n.x();
float y = n.y();
float z = n.z();
Eigen::Vector3f t = {x*y/sqrt(x*x+z*z), sqrt(x*x+z*z), z*y/sqrt(x*x+z*z)};
t.normalize();
Eigen::Vector3f b = n.cross(t);
b.normalize();
Eigen::Matrix3f TBN;
TBN << t.x(), b.x(), n.x(),
t.y(), b.y(), n.y(),
t.z(), b.z(), n.z();
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float h = payload.texture->height;
float uc = payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm();
float vc = payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm();
float du = kh * kn * uc;
float dv = kh * kn * vc;
Eigen::Vector3f ln = Eigen::Vector3f(-du, -dv, 1.0f);
ln.normalize();
normal = (TBN * ln).normalized();
point = point + kn * n * (payload.texture->getColor(u, v).norm());
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.
float r2 = (point - light.position).squaredNorm();
Eigen::Vector3f l = (light.position - point).normalized();
Eigen::Vector3f v = (eye_pos - point).normalized();
Eigen::Vector3f h = (l + v).normalized();
float cosd = std::max(0.0f, normal.dot(l));
float coss = std::pow(std::max(0.0f, normal.dot(h)), p);
Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f ld = kd.cwiseProduct(light.intensity / r2 * cosd);
Eigen::Vector3f ls = ks.cwiseProduct(light.intensity / r2 * coss);
result_color += Eigen::Vector3f(la + ld + ls);
}
return result_color * 255.f;
}
-
实现结果如下:

-
最后main函数部分代码如下:
int main(int argc, const char** argv)
{
std::vector<Triangle*> TriangleList;
float angle = 140.0;
bool command_line = false;
std::string filename = "output.png";
objl::Loader Loader; // 对象加载对象
std::string obj_path = "/Users/level/CLionProjects/Assignment3/models/spot/"; // 对象路径
// Load .obj File
bool loadout = Loader.LoadFile("../models/spot/spot_triangulated_good.obj");
for(auto mesh:Loader.LoadedMeshes)
{
for(int i=0;i<mesh.Vertices.size();i+=3)
{
Triangle* t = new Triangle();
for(int j=0;j<3;j++)
{
t->setVertex(j,Vector4f(mesh.Vertices[i+j].Position.X,mesh.Vertices[i+j].Position.Y,mesh.Vertices[i+j].Position.Z,1.0));
t->setNormal(j,Vector3f(mesh.Vertices[i+j].Normal.X,mesh.Vertices[i+j].Normal.Y,mesh.Vertices[i+j].Normal.Z));
t->setTexCoord(j,Vector2f(mesh.Vertices[i+j].TextureCoordinate.X, mesh.Vertices[i+j].TextureCoordinate.Y));
}
TriangleList.push_back(t);
}
}
rst::rasterizer r(700, 700);
auto texture_path = "hmap.jpg";
r.set_texture(Texture(obj_path + texture_path));
std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = phong_fragment_shader;
if (argc >= 2)
{
command_line = true;
filename = std::string(argv[1]);
if (argc == 3 && std::string(argv[2]) == "texture")
{
std::cout << "Rasterizing using the texture shader\n";
active_shader = texture_fragment_shader;
texture_path = "spot_texture.png";
r.set_texture(Texture(obj_path + texture_path));
}
else if (argc == 3 && std::string(argv[2]) == "normal")
{
std::cout << "Rasterizing using the normal shader\n";
active_shader = normal_fragment_shader;
}
else if (argc == 3 && std::string(argv[2]) == "phong")
{
std::cout << "Rasterizing using the phong shader\n";
active_shader = phong_fragment_shader;
}
else if (argc == 3 && std::string(argv[2]) == "bump")
{
std::cout << "Rasterizing using the bump shader\n";
active_shader = bump_fragment_shader;
}
else if (argc == 3 && std::string(argv[2]) == "displacement")
{
std::cout << "Rasterizing using the bump shader\n";
active_shader = displacement_fragment_shader;
}
}
Eigen::Vector3f eye_pos = {0,0,10};
r.set_vertex_shader(vertex_shader);
r.set_fragment_shader(active_shader);
int key = 0;
int frame_count = 0;
if (command_line)
{
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
r.set_model(get_model_matrix(angle));
r.set_view(get_view_matrix(eye_pos));
r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));
r.draw(TriangleList);
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
cv::imwrite(filename, image);
return 0;
}
while(key != 27)
{
r.clear(rst::Buffers::Color | rst::Buffers::Depth);
r.set_model(get_model_matrix(angle));
r.set_view(get_view_matrix(eye_pos));
r.set_projection(get_projection_matrix(45.0, 1, 0.1, 50));
//r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle);
r.draw(TriangleList);
cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data());
image.convertTo(image, CV_8UC3, 1.0f);
cv::cvtColor(image, image, cv::COLOR_RGB2BGR);
cv::imshow("image", image);
cv::imwrite(filename, image);
key = cv::waitKey(10);
if (key == 'a' )
{
angle -= 0.1;
}
else if (key == 'd')
{
angle += 0.1;
}
}
return 0;
}
3. 注意问题
- 如果所渲染的模型是上下倒置的,则需要修改投影变换部分代码如下:
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
// 始终看向-z方向
zNear = -zNear;
zFar = -zFar;
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
Eigen::Matrix4f p;
p << zNear, 0, 0, 0,
0, zNear, 0, 0,
0, 0, zNear + zFar, -zNear * zFar,
0, 0, 1, 0;
float l, r, t, b = 0.0f;
t = tan(eye_fov / 2 / 180 * MY_PI) * (- zNear);
r = t * aspect_ratio;
b = -t;
l = -r;
Eigen::Matrix4f ot;
ot << 1, 0, 0, -(r + l) / 2,
0, 1, 0, -(t + b) / 2,
0, 0, 1, -(zNear + zFar) / 2,
0, 0, 0, 1;
Eigen::Matrix4f os;
os << 2 / (r - l), 0, 0, 0,
0, 2 / (t - b), 0, 0,
0, 0, 2 / (zNear - zFar), 0,
0, 0, 0, 1;
// 对z进行取反
Eigen::Matrix4f z;
z << 1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,
0, 0, 0, 1;
Eigen::Matrix4f o;
o = os * ot;
projection = z * o * p * projection;
return projection;
}
天青色等烟雨,而我在等你

浙公网安备 33010602011771号