文章目录
基础部分
MVP
这部分直接照抄 PA1 的答案就行:
Eigen::Matrix4f get_model_matrix(float angle)
{
Eigen::Matrix4f rotation;
angle = angle * MY_PI / 180.f;
rotation <<
cos(angle), 0, sin(angle), 0,
0, 1, 0, 0,
-sin(angle), 0, cos(angle), 0,
0, 0, 0, 1;
Eigen::Matrix4f scale;
float coef = 1.0f; // 缩放因子
coef = 2.5f;
//coef = 5.0f;
//coef = 7.0f;
//coef = 8.0f;
scale <<
coef, 0, 0, 0,
0, coef, 0, 0,
0, 0, coef, 0,
0, 0, 0, 1;
Eigen::Matrix4f translate;
translate <<
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1;
return translate * rotation * scale;
}
Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos)
{
Eigen::Matrix4f view = Eigen::Matrix4f::Identity();
Eigen::Matrix4f translate;
translate <<
1, 0, 0, -eye_pos[0],
0, 1, 0, -eye_pos[1],
0, 0, 1, -eye_pos[2],
0, 0, 0, 1;
view = translate * view;
return view;
}
Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar)
{
Eigen::Matrix4f projection = Eigen::Matrix4f::Identity();
float Y = eye_fov / 180.0f * MY_PI; // degree2rad
float Y_over_2 = Y * 0.5f;
float cot_Y_over_2 = 1.0f / tan(Y_over_2);
// Another solution
Eigen::Matrix4f perspective_p;
perspective_p <<
-cot_Y_over_2 / aspect_ratio, 0, 0, 0,
0, -cot_Y_over_2, 0, 0,
0, 0, (zFar + zNear) / (zNear - zFar), 2.0f * zFar * zNear / (zFar - zNear),
0, 0, 1, 0;
return perspective_p * projection;
}
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;
}
Phong Shader
这部分直接照着 Blinn-Phong Rendering Equation 写就行:
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 = Eigen::Vector3f(0, 0, 0);
Eigen::Vector3f view_dir = (-point).normalized();
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 half_vector = ((light_dir + view_dir) / 2.0f).normalized();
float r2 = (light.position - point).squaredNorm();
auto ambient = ka.cwiseProduct(amb_light_intensity); // 间接光也是光源发出的,所以也要累加
auto diffuse = kd.cwiseProduct(light.intensity / r2 * MAX(0.0f, normal.dot(light_dir)));
auto specular = ks.cwiseProduct(light.intensity / r2 * std::pow(MAX(0.0f, normal.dot(half_vector)), p));
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
Texture Shader
将 Blinn-Phong Rendering Equation 中的 kd
置换为 texture_color / 255.f
即可:
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f return_color = Eigen::Vector3f(0, 0, 0);
if (payload.texture)
{
// TODO: Get the texture value at the texture coordinates of the current fragment
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
return_color = payload.texture->getColor(u, v);
}
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 = Eigen::Vector3f(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 = Eigen::Vector3f(0, 0, 0);
Eigen::Vector3f view_dir = (-point).normalized();
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 halfVector = ((light_dir + view_dir) / 2.0f).normalized();
float r2 = (light.position - point).squaredNorm();
auto ambient = ka.cwiseProduct(amb_light_intensity); // 间接光也是光源发出的,所以也要累加
auto diffuse = kd.cwiseProduct(light.intensity / r2 * MAX(0.0f, normal.dot(light_dir)));
auto specular = ks.cwiseProduct(light.intensity / r2 * std::pow(MAX(0.0f, normal.dot(halfVector)), p));
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
Bump Shader
照着推导方程写就行:
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 = Eigen::Vector3f(10, 10, 10);
Eigen::Vector3f eye_pos = Eigen::Vector3f(0, 0, 10);
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
// 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 kh = 0.2, kn = 0.1;
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.col(0) = t; TBN.col(1) = b; TBN.col(2) = normal;
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
int w = payload.texture->width;
int h = payload.texture->height;
float dU = kh * kn * (payload.texture->getColor(u + 1.f / w, v).norm() - payload.texture->getColor(u, v).norm());
float dV = kh * kn * (payload.texture->getColor(u, v + 1.f / h).norm() - payload.texture->getColor(u, v).norm());
Eigen::Vector3f ln = Eigen::Vector3f(-dU, -dV, 1.f);
Eigen::Vector3f n = (TBN * ln).normalized(); // Do not modify.
Eigen::Vector3f result_color = Eigen::Vector3f(0, 0, 0);
result_color = n;
return result_color * 255.f;
}
Displacement Shader
在 Bump Shader 的基础上再接入 Blinn-Phong 的部分:
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 = Eigen::Vector3f(10, 10, 10);
Eigen::Vector3f eye_pos = Eigen::Vector3f(0, 0, 10);
float p = 150;
Eigen::Vector3f color = payload.color;
Eigen::Vector3f point = payload.view_pos;
Eigen::Vector3f normal = payload.normal;
// 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 kh = 0.2, kn = 0.1;
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.col(0) = t; TBN.col(1) = b; TBN.col(2) = normal;
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
int w = payload.texture->width;
int h = payload.texture->height;
float dU = kh * kn * (payload.texture->getColor(u + 1.f / w, v).norm() - payload.texture->getColor(u, v).norm());
float dV = kh * kn * (payload.texture->getColor(u, v + 1.f / h).norm() - payload.texture->getColor(u, v).norm());
point += (kn * normal * payload.texture->getColor(u, v).norm());
Eigen::Vector3f ln = Eigen::Vector3f(-dU, -dV, 1.f);
normal = (TBN * ln).normalized(); // Do modify.
Eigen::Vector3f result_color = Eigen::Vector3f(0, 0, 0);
Eigen::Vector3f view_dir = (-point).normalized();
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 half_vector = ((light_dir + view_dir) / 2.0f).normalized();
float r2 = (light.position - point).squaredNorm();
auto ambient = ka.cwiseProduct(amb_light_intensity); // 间接光也是光源发出的,所以也要累加
auto diffuse = kd.cwiseProduct(light.intensity / r2 * MAX(0.0f, normal.dot(light_dir)));
auto specular = ks.cwiseProduct(light.intensity / r2 * std::pow(MAX(0.0f, normal.dot(half_vector)), p));
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}
提高部分
尝试更多模型
寻找中…
双线性纹理插值
取周围四个点的颜色做三次插值就行,我写了两个lambda
表达式clamp
和lerp
,用作截断和插值:
Eigen::Vector3f getColorBilinear(float u, float v)
{
int u_img = static_cast<int>(u * width);
int v_img = static_cast<int>((1 - v) * height);
auto clamp = [](int low, int high, int val) {
if (val < low)
return low;
else if (val > high)
return high;
else
return val;
};
int u_min = clamp(0, width - 1, floor(u_img));
int u_max = clamp(0, width - 1, ceil(u_img));
int v_min = clamp(0, height - 1, floor(v_img));
int v_max = clamp(0, height - 1, ceil(v_img));
/*
^ U01 U11
t U00 U10
s >
*/
cv::Vec3b U00 = image_data.at<cv::Vec3b>(v_max, u_min);
cv::Vec3b U10 = image_data.at<cv::Vec3b>(v_max, u_max);
cv::Vec3b U01 = image_data.at<cv::Vec3b>(v_min, u_min);
cv::Vec3b U11 = image_data.at<cv::Vec3b>(v_min, u_max);
auto lerp = [](auto& start, auto& end, float r) {
return (1.f - r) * start + r * end;
};
auto s = (u_img - u_min) / (u_max - u_min);
auto t = 1.f - (v_img - v_min) / (v_max - v_min);
auto color_bottom = lerp(U00, U10, s);
auto color_top = lerp(U01, U11, s);
auto color_binlinear = lerp(color_bottom, color_top, t);
return Eigen::Vector3f(color_binlinear[0], color_binlinear[1], color_binlinear[2]);
}
ps:要做一下采样才可以就看到明显的效果!