Games101-Homework3

本章内容

本章讲述Games101的作业3,主要涉及着色部分。

基础部分

实现

  • [10 分] 参数插值: 正确插值颜色、法向量、纹理坐标、位置 (Shading Position) 并将它们传递给 fragment_shader_payload.
  • [20 分]Blinn-phong 反射模型: 正确实现 phong_fragment_shader 对应的反射模型。
  • [5 分] Texture mapping: 将 phong_fragment_shader 的代码拷贝到 texture_fragment_shader, 在此基础上正确实现 Texture Mapping,使用下述公式即可,注意公式中light_dir和view_dir都是单位向量。

  • [10 分] Bump mapping 与 Displacement mapping: 正确实现 Bump mapping 与 Displacement mapping.

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
auto v = t.toVector4();
int x_max, x_min, y_max, y_min;
x_min = MIN(floor(v[0].x()), MIN(floor(v[1].x()), floor(v[2].x())));
x_max = MAX(ceil(v[0].x()), MAX(ceil(v[1].x()), ceil(v[2].x())));
y_min = MIN(floor(v[0].y()), MIN(floor(v[1].y()), floor(v[2].y())));
y_max = MAX(ceil(v[0].y()), MAX(ceil(v[1].y()), ceil(v[2].y())));

for (int x = x_min; x <= x_max; x++)
{
for (int y = y_min; y <= y_max; y++)

{
if (insideTriangle(x + 0.5, y + 0.5, t.v))
{
// If so, use the following code to get the interpolated z value.
auto [alpha, beta, gamma] = computeBarycentric2D(x + 0.5, y + 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;

auto index = get_index(x, y);
if (z_interpolated < depth_buf[index] || std::isinf(depth_buf[index]))
{
depth_buf[index] = z_interpolated;

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).normalized();
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);

// 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;
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);
set_pixel(Eigen::Vector2i(x, y), pixel_color);
}
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//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.

//光的方向,视线方向,半程向量,衰减因子
Eigen::Vector3f light_dir = (light.position - point);
Eigen::Vector3f view_dir = eye_pos - point;
Eigen::Vector3f half_dir = (view_dir + light_dir).normalized();
float r = light_dir.dot(light_dir);

light_dir = light_dir.normalized();
view_dir = view_dir.normalized();

//La-环境光照,Ld-漫反射,Ls-高光
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);

Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);
Ld *= std::max(0.0f, normal.dot(light_dir));

Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);
Ls *= pow(std::max(0.0f, normal.dot(half_dir)), p);

result_color += La + Ld + Ls;
}

return result_color * 255.f;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//bump
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

float x = normal.x(), y = normal.y(), z = normal.z();
Eigen::Vector3f t = Eigen::Vector3f(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN0, TBN;
TBN0 << t, b, normal;
TBN = TBN0.transpose();

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);
Eigen::Vector3f n = (TBN * ln).normalized();

Eigen::Vector3f result_color = {0, 0, 0};
result_color = n;

return result_color * 255.f;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
//Displacement
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;

float x = normal.x(), y = normal.y(), z = normal.z();
Eigen::Vector3f t = Eigen::Vector3f(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
Eigen::Vector3f b = normal.cross(t);
Eigen::Matrix3f TBN0, TBN;
TBN0 << t, b, normal;
TBN = TBN0.transpose();

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)
{
//光的方向,视线方向,半程向量,衰减因子
Eigen::Vector3f light_dir = light.position - point;
Eigen::Vector3f view_dir = eye_pos - point;
Eigen::Vector3f half_dir = (view_dir + light_dir).normalized();
float r = light_dir.dot(light_dir);

light_dir = light_dir.normalized();
view_dir = view_dir.normalized();

//La-环境光照,Ld-漫反射,Ls-高光
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);

Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);
Ld *= std::max(0.0f, normal.dot(light_dir));

Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);
Ls *= pow(std::max(0.0f, normal.dot(half_dir)), p);

result_color += La + Ld + Ls;
}

return result_color * 255.f;
}

运行结果

Normal

Blinn-Phong

image-20220829005504268

Texture

image-20220829005532971

Bump

Displacement

提高部分

实现

  • [Bonus 3 分] 尝试更多模型: 找到其他可用的.obj 文件,提交渲染结果并 把模型保存在 /models 目录下。这些模型也应该包含 Vertex Normal 信息。
  • [Bonus 5 分] 双线性纹理插值: 使用双线性插值进行纹理采样, 在 Texture 类中实现一个新方法 Vector3f getColorBilinear(float u, float v) 并 通过 fragment shader 调用它。为了使双线性插值的效果更加明显,你应该考虑选择更小的纹理图。请同时提交纹理插值与双线性纹理插值的结果,并进行比较。

代码

运行结果

遇到的问题

Blinn-Phong

  • light_dir和view_dir没有单位化,就会产生下图情况

  • 高光没有使用衰减因子p,会产生下图情况

image-20220829005618078

Texture

运行时报错,原因是纹理坐标u, v计算出现负数,超出[0, 1]的区间,修改getColor函数如下

1
2
3
4
5
6
7
8
9
10
11
12
Eigen::Vector3f getColor(float u, float v)
{
// 坐标限定
if (u < 0) u = 0.0f;
if (u > 1) u = 0.999f;
if (v < 0) v = 0.0f;
if (v > 1) v = 0.999f;
auto u_img = u * width;
auto v_img = (1 - v) * height;
auto color = image_data.at<cv::Vec3b>(v_img, u_img);
return Eigen::Vector3f(color[0], color[1], color[2]);
}