GAMES101 HW3
这里回顾GAMES101 HW3,这次作业的内容是Pipeline and Shading。
课程主页:
课程作业:
课程视频:
参考资料:
- https://logic-three-body.github.io/post/games101-greaterassignment3-shader-and-graphic-pipeline/
- https://games-cn.org/forums/topic/zuoye3faxiantietugenweiyitietulidekhhekndoushisha/
- http://games-cn.org/forums/topic/%e4%bd%9c%e4%b8%9a3%e6%9b%b4%e6%ad%a3%e5%85%ac%e5%91%8a/
rasterize_triangle
对上一讲的代码进行了修改,主要改动如下:
- 支持对其他指标的插值;
- 支持ssaa;
- 返回的结果为$\sum_{i\in triangleq} s_i / \sum_{i\in triangleq}$;
- 上一讲返回的结果为$\sum_{i\in triangleq} s_i / n^2$;
- 即只关于三角形内部的点求均值;
- ssaa的分别率通过最后一个命令行参数传入;
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();
// TODO : Find out the bounding box of current triangle.
// bounding box
float x_min = 1e10;
float x_max = -1e10;
float y_min = 1e10;
float y_max = -1e10;
for (int i = 0; i < 3; i++) {
x_min = std::min(x_min, t.v[i](0));
x_max = std::max(x_max, t.v[i](0));
y_min = std::min(y_min, t.v[i](1));
y_max = std::max(y_max, t.v[i](1));
}
int x_min_int = int(x_min);
int x_max_int = int(x_max) + 1;
int y_min_int = int(y_min);
int y_max_int = int(y_max) + 1;
// 支持ssaa
float x, y;
// 间距
float d = 1.0 / n;
for (int i = x_min_int; i <= x_max_int; i++) {
for (int j = y_min_int; j <= y_max_int; j++) {
// 记录super-sample在三角形内的数量
int cnt = 0;
// 记录块内的最小深度
float z_interpolated_min = 1e10;
// msaa内的平均值
Eigen::Vector3f interpolated_color = Eigen::Vector3f::Zero();
Eigen::Vector3f interpolated_normal = Eigen::Vector3f::Zero();
Eigen::Vector2f interpolated_texcoords = Eigen::Vector2f::Zero();
Eigen::Vector3f interpolated_shadingcoords = Eigen::Vector3f::Zero();
// 记录在区域内的个数
float m = 0;
// 区域内的平均坐标
float x_mean = 0;
float y_mean = 0;
// 遍历每个super-sample
for (int p = 0; p < n; p++) {
for (int q = 0; q < n; q++) {
x = i + (p + 0.5) * d;
y = j + (q + 0.5) * d;
x_mean += x;
y_mean += y;
// 在三角形内则更新cnt
if (insideTriangle(x, y, t.v)) {
cnt++;
// 计算插值深度值
auto[alpha, beta, gamma] = computeBarycentric2D(x, y, 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;
z_interpolated_min = std::min(z_interpolated_min, z_interpolated);
// 计算个指标的插值结果
interpolated_color = interpolated_color + interpolate(alpha, beta, gamma, t.color[0], t.color[1], t.color[2], 1);
interpolated_normal = interpolated_normal + interpolate(alpha, beta, gamma, t.normal[0], t.normal[1], t.normal[2], 1);
interpolated_texcoords = interpolated_texcoords + interpolate(alpha, beta, gamma, t.tex_coords[0], t.tex_coords[1], t.tex_coords[2], 1);
interpolated_shadingcoords = interpolated_shadingcoords + interpolate(alpha, beta, gamma, view_pos[0], view_pos[1], view_pos[2], 1);
// 更新个数
m += 1;
}
}
}
// 计算均值
interpolated_color = interpolated_color / m;
interpolated_normal = interpolated_normal / m;
interpolated_texcoords = interpolated_texcoords / m;
interpolated_shadingcoords = interpolated_shadingcoords / m;
x_mean /= m;
y_mean /= m;
int index = get_index(i, j);
// 如果平均点在三角形内则生成结果
if (insideTriangle(x_mean, y_mean, t.v)) {
// 越小越近
if (z_interpolated_min < depth_buf[index]) {
// 得到颜色
auto payload = fragment_shader_payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
auto pixel_color = fragment_shader(payload);
// 设置颜色
Eigen::Vector2i point(i, j);
set_pixel(point, pixel_color);
// 更新深度
depth_buf[index] = z_interpolated_min;
}
}
}
}
}
测试:
./Rasterizer normal_1.png normal 1
./Rasterizer normal_2.png normal 2
phong_fragment_shader
首先结合下图进行参数说明:
- point: 光线打在物体上位置,即上图中三条线的交点;
- light.position: 光源位置;
- normal: $\mathrm n$;
- eye_pos:图中眼睛的位置;
计算公式:
代码:
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_direction = (light.position - point);
Eigen::Vector3f viewer_direction = (eye_pos - point);
Eigen::Vector3f intensity = light.intensity / light_direction.squaredNorm();
light_direction = light_direction.normalized();
viewer_direction = viewer_direction.normalized();
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f diffuse = std::max(0.0f, normal.dot(light_direction)) * kd.cwiseProduct(intensity);
Eigen::Vector3f h = (light_direction + viewer_direction).normalized();
Eigen::Vector3f specular = std::pow(std::max(0.0f, normal.dot(h)), p) * ks.cwiseProduct(intensity);
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
测试:
./Rasterizer phong_1.png phong 1
./Rasterizer phong_2.png phong 2
texture_fragment_shader
和之前唯一的区别是改变color,具体方式为利用getColor方法:
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[0], payload.tex_coords[1]);
}
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 light_direction = (light.position - point);
Eigen::Vector3f viewer_direction = (eye_pos - point);
Eigen::Vector3f intensity = light.intensity / light_direction.squaredNorm();
light_direction = light_direction.normalized();
viewer_direction = viewer_direction.normalized();
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f diffuse = std::max(0.0f, normal.dot(light_direction)) * kd.cwiseProduct(intensity);
Eigen::Vector3f h = (light_direction + viewer_direction).normalized();
Eigen::Vector3f specular = std::pow(std::max(0.0f, normal.dot(h)), p) * ks.cwiseProduct(intensity);
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
测试:
./Rasterizer texture_1.png texture 1
./Rasterizer texture_2.png texture 2
bump_fragment_shader
这一部分主要参考lecture 10的15到17页的内容,这里要做一点说明:
- $h$为一个预设的函数,这里助教设置为h(u,v)=texture_color(u,v).norm,该函数是人为随意定义的,没有理论含义;
- $kh, kn$是为了控制生成结果;
- $t$为切线方向,即曲线方程为$f(x,y, z) = y\sqrt{x ^2 +z^2 }$,暂时不知道该方程是人为设置的,还是推导出来的;
代码:
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(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.col(0) = t;
TBN.col(1) = b;
TBN.col(2) = normal;
float u = payload.tex_coords[0];
float v = payload.tex_coords[1];
float w = payload.texture->width;
float h = payload.texture->height;
// 人为选择的函数
float huv = payload.texture->getColor(u, v).norm();
// kh, kn为预设的值, 是为了控制最后颜色
float dU = kh * kn * (payload.texture->getColor(u + 1.0 / w, v).norm() - huv);
float dV = kh * kn * (payload.texture->getColor(u, v + 1.0 / h).norm() - huv);
Eigen::Vector3f ln(-dU, -dV, 1);
normal = (TBN * ln).normalized();
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
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_direction = (light.position - point);
Eigen::Vector3f viewer_direction = (eye_pos - point);
Eigen::Vector3f intensity = light.intensity / light_direction.squaredNorm();
light_direction = light_direction.normalized();
viewer_direction = viewer_direction.normalized();
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f diffuse = std::max(0.0f, normal.dot(light_direction)) * kd.cwiseProduct(intensity);
Eigen::Vector3f h = (light_direction + viewer_direction).normalized();
Eigen::Vector3f specular = std::pow(std::max(0.0f, normal.dot(h)), p) * ks.cwiseProduct(intensity);
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
测试:
./Rasterizer bump_1.png bump 1
./Rasterizer bump_2.png bump 2
displacement_fragment_shader
和前一部分类似,唯一的区别是这里更新的是point:
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 point = point + 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(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.col(0) = t;
TBN.col(1) = b;
TBN.col(2) = normal;
float u = payload.tex_coords[0];
float v = payload.tex_coords[1];
float w = payload.texture->width;
float h = payload.texture->height;
// 人为选择的函数
float huv = payload.texture->getColor(u, v).norm();
// kh, kn为预设的值, 是为了控制最后颜色
float dU = kh * kn * (payload.texture->getColor(u + 1.0 / w, v).norm() - huv);
float dV = kh * kn * (payload.texture->getColor(u, v + 1.0 / h).norm() - huv);
Eigen::Vector3f ln(-dU, -dV, 1);
point = point + kn * normal * huv;
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_direction = (light.position - point);
Eigen::Vector3f viewer_direction = (eye_pos - point);
Eigen::Vector3f intensity = light.intensity / light_direction.squaredNorm();
light_direction = light_direction.normalized();
viewer_direction = viewer_direction.normalized();
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f diffuse = std::max(0.0f, normal.dot(light_direction)) * kd.cwiseProduct(intensity);
Eigen::Vector3f h = (light_direction + viewer_direction).normalized();
Eigen::Vector3f specular = std::pow(std::max(0.0f, normal.dot(h)), p) * ks.cwiseProduct(intensity);
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
测试:
./Rasterizer bump_1.png bump 1
./Rasterizer bump_2.png bump 2
getColorBilinear
和texture_fragment_shader类似,区别是将getColor修改为getColorBilinear:
Eigen::Vector3f bilinear_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[0], payload.tex_coords[1]);
}
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 light_direction = (light.position - point);
Eigen::Vector3f viewer_direction = (eye_pos - point);
Eigen::Vector3f intensity = light.intensity / light_direction.squaredNorm();
light_direction = light_direction.normalized();
viewer_direction = viewer_direction.normalized();
Eigen::Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
Eigen::Vector3f diffuse = std::max(0.0f, normal.dot(light_direction)) * kd.cwiseProduct(intensity);
Eigen::Vector3f h = (light_direction + viewer_direction).normalized();
Eigen::Vector3f specular = std::pow(std::max(0.0f, normal.dot(h)), p) * ks.cwiseProduct(intensity);
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
然后是getColorBilinear的实现,这部分需要参考lecture 9的17到22页,具体代码如下:
Eigen::Vector3f getColorBilinear(float u, float v)
{
float u_img = u * width;
float v_img = (1 - v) * height;
int u_img_min = (int) u_img;
int u_img_max = u_img_min + 1;
int v_img_min = (int) v_img;
int v_img_max = v_img_min + 1;
float s = u_img - u_img_min;
float t = v_img - v_img_min;
auto c00 = img2Color(v_img_min, u_img_min);
auto c10 = img2Color(v_img_min, u_img_max);
auto c01 = img2Color(v_img_max, u_img_min);
auto c11 = img2Color(v_img_max, u_img_max);
auto c0 = lerp(s, c00, c10);
auto c1 = lerp(s, c01, c11);
auto color = lerp(t, c0, c1);
return color;
}
Eigen::Vector3f lerp(float s, Eigen::Vector3f a, Eigen::Vector3f b)
{
return a + s * (b - a);
}
测试:
./Rasterizer bump_1.png bump 1
./Rasterizer bump_2.png bump 2
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Doraemonzzz!
评论
ValineLivere