# 作业要求
完善以下函数:
rasterize_triangle(const Triangle& t) // 在此处实现与作业 2 类似的插值算法,实现法向量、颜色、纹理颜色的插值。 | |
get_projection_matrix() // 将你自己在之前的实验中实现的投影矩阵填到此处,此时你可以运行./Rasterizer output.png normal 来观察法向量实现结果。 | |
phong_fragment_shader() // 实现 Blinn-Phong 模型计算 Fragment Color. | |
texture_fragment_shader() // 在实现 Blinn-Phong 的基础上,将纹理颜色视为公式中的 kd,实现 Texture Shading Fragment Shader. | |
bump_fragment_shader() // 在实现 Blinn-Phong 的基础上,仔细阅读该函数中的注释,实现 Bump mapping. | |
displacement_fragment_shader() // 在实现 Bump mapping 的基础上,实现 displacement mapping. |
# 作业实现
# 三角形光栅化
直接按照注释中的提示,实现三角形光栅化即可。
void rst::rasterizer::rasterize_triangle( | |
const Triangle &t, const std::array<Eigen::Vector3f, 3> &view_pos) { | |
auto v = t.toVector4(); | |
const int x_min = | |
static_cast<int>(std::min(v[0].x(), std::min(v[1].x(), v[2].x()))); | |
const int x_max = | |
static_cast<int>(std::max(v[0].x(), std::max(v[1].x(), v[2].x()))); | |
const int y_min = | |
static_cast<int>(std::min(v[0].y(), std::min(v[1].y(), v[2].y()))); | |
const int y_max = | |
static_cast<int>(std::max(v[0].y(), std::max(v[1].y(), v[2].y()))); | |
for (auto x = x_min; x <= x_max; x++) { | |
for (auto y = y_min; y <= y_max; y++) { | |
if (insideTriangle(x, y, t.v)) { | |
auto [alpha, beta, gamma] = computeBarycentric2D(x, y, 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; | |
Z = -Z; | |
zp = -zp; | |
int ind = get_index(x, y); | |
if (zp < depth_buf[ind]) { | |
depth_buf[ind] = 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); | |
set_pixel(Vector2i(x, y), pixel_color); | |
} | |
} | |
} | |
} | |
} |
# 实现 Blinn-Phong 模型计算 Fragment Color
其中
Eigen::Vector3f light_vector = (light.position - point).normalized(); | |
Eigen::Vector3f view_vector = (eye_pos - point).normalized(); | |
Eigen::Vector3f half_vector = (light_vector + view_vector).normalized(); | |
Eigen::Vector3f n_vector = normal.normalized(); | |
float r2 = (light.position - point).dot(light.position - point); | |
// ambient | |
Eigen::Vector3f la = ka.cwiseProduct(amb_light_intensity); | |
// diffuse | |
Eigen::Vector3f ld = kd.cwiseProduct(light.intensity / r2) * | |
std::max(0.0f, n_vector.dot(light_vector)); | |
// specular | |
Eigen::Vector3f ls = ks.cwiseProduct(light.intensity / r2) * | |
std::pow(std::max(0.0f, n_vector.dot(half_vector)), p); | |
result_color += la + ld + ls; |
# 实现 Texture Shading Fragment Shader
把纹理颜色视为公式中的 ,实现 Texture Shading Fragment Shader。
return_color = payload.texture->getColor(payload.tex_coords.x(), | |
payload.tex_coords.y()); |
# 实现 Bump mapping
Bump mapping 是一种通过法线贴图来模拟表面细节的技术。在实现 Bump mapping 的基础上,我们需要计算出偏移后的法线。这里参考注释。
float x = normal.x(); | |
float y = normal.y(); | |
float z = normal.z(); | |
Eigen::Vector3f t = {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 TBN; | |
TBN << t, b, normal; | |
float u = payload.tex_coords.x(); | |
float v = payload.tex_coords.y(); | |
float dU = | |
kh * kn * | |
(payload.texture->getColor(u + 1.0f / payload.texture->width, v).norm() - | |
payload.texture->getColor(u, v).norm()); | |
float dV = | |
kh * kn * | |
(payload.texture->getColor(u, v + 1.0f / payload.texture->height).norm() - | |
payload.texture->getColor(u, v).norm()); | |
Eigen::Vector3f ln = {-dU, -dV, 1}; | |
normal = (TBN * ln).normalized(); | |
Eigen::Vector3f result_color = {0, 0, 0}; | |
result_color = normal; |
# 实现 displacement mapping
在实现 Bump mapping 的基础上,我们需要计算出偏移后的顶点位置。这里参考注释。
point = point + kn * normal * payload.texture->getColor(u, v).norm(); |
# 双线性纹理插值
Eigen::Vector3f getColorBilinear(float u, float v) { | |
u = u * width; | |
v = (1 - v) * height; | |
int u0 = (int)u; | |
int v0 = (int)v; | |
int u1 = std::min(u0 + 1, width - 1); | |
int v1 = std::min(v0 + 1, height - 1); | |
float du = u - u0; | |
float dv = v - v0; | |
auto color0 = image_data.at<cv::Vec3b>(v0, u0); | |
auto color1 = image_data.at<cv::Vec3b>(v0, u1); | |
auto color2 = image_data.at<cv::Vec3b>(v1, u0); | |
auto color3 = image_data.at<cv::Vec3b>(v1, u1); | |
Eigen::Vector3f color; | |
for (int i = 0; i < 3; i++) { | |
color[i] = (1 - du) * (1 - dv) * color0[i] + du * (1 - dv) * color1[i] + | |
(1 - du) * dv * color2[i] + du * dv * color3[i]; | |
} | |
return color; | |
} |