目录
作业要求
补充代码
代码框架预览
rasterize_triangle()
结果
interpolated--做插值
关于代码中插值属性出现的shadingcoords
phong_fragment_shader()
结果
texture_fragment_shader()
结果
bump_fragment_shader()
先放上代码
每一步的详细解释
kh和kn是什么
为什么不是u+1而是u+1/w
.norm()
结果
一些话
displacement_fragment_shader()
代码
结果
作业要求如果什么都不修改运行后会提示:
下面开始补充代码。
rasterize_triangle()实现法向量、颜色、纹理插值。
//Screen space rasterization
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array& view_pos)
{
//构建bounding box
// 这里跟作业2里的深度插值差不多,v.w()就是该顶点深度值,用Z和zp代替w_reciprocal和z_interpolated
auto v = t.toVector4();
int min_x = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
int min_y = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
int max_x = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
int max_y = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
for (int x = min_x; x {20, 20, 20}, {500, 500, 500}}; //light是之前定义的struct包含了position和intensity
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vector{20, 20, 20}, {500, 500, 500}};
auto l2 = light{{-20, 20, 0}, {500, 500, 500}};
std::vectorgetColor(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());
// Vector ln = (-dU, -dV, 1)
// Normal n = normalize(TBN * ln)
Eigen::Vector3f ln;
lngetColor(u, v).norm());
float dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
/*一步步的拆分:
1.kh*kn就不解释了,上面有说
2.payload是所写函数输入的一个struct,这个struct包含了texture等信息,这个结构是在shader.hpp中定义的
3.payload.texture ———— 取payload这个struct里的texture
texture ———— 是Texture.hpp中定义的一个class,里面包含了纹理的宽高(width/height)和getColor()函数等信息
payload.texture->getColor() ———— 访问到定义的这个getColor()函数
4.为什么要用“u+1.0f/w”而不是直接“u+1”?我们仔细看Texture.hpp对getColor()的一段定义:
...
auto u_img = u * width;
auto v_img = (1 - v) * height;
auto color = image_data.at(v_img, u_img);
return Eigen::Vector3f(color[0], color[1], color[2]);
...
这里的u v值都×了纹理对应的宽高,变换过来的话移动一个单位应该是“u*width+1”因此在我们的函数里1个单位对应的应该是1/width,1/h同理
5.getColor().norm() ———— .norm()是Eigen库里定义的一个求范数的函数,就是求所有元素²的和再开方。
向量的范数则表示的是原有集合的大小,范数的本质是距离,存在的意义是为了实现比较。
这部分为什么要给个norm,我的理解是:getColor返回的是一个储存颜色值的向量:(color[0],color[1],color[2])对应的是RGB值
dU和dV都是一个float值,并不是Vector,想要实现h()表示的实数高度值,就要用到norm.()将向量映射成实数(个人理解,不确定对不对)
6.还需要注意,这里的dU和dV对应的是老师课上给的dp/du和dp/dp/dv
*/
// Vector ln = (-dU, -dV, 1)
// Normal n = normalize(TBN * ln)
Eigen::Vector3f ln;
ln 1) v = 1;
auto u_img = u * width;
auto v_img = (1 - v) * height;
auto color = image_data.at(v_img, u_img);
return Eigen::Vector3f(color[0], color[1], color[2]);
}
这里的u v值都×了纹理对应的宽高,变换过来的话移动一个单位应该是“u*width+1”因此在我们的函数里1个单位对应的应该是1/width,1/h同理。
.norm().norm()是Eigen库里定义的一个求范数的函数,就是求所有元素²的和再开方。向量的范数则表示的是原有集合的大小,范数的本质是距离,存在的意义是为了实现比较。
至于这部分为什么要给个norm,我的理解是:getColor返回的是一个储存颜色值的向量(color[0],color[1],color[2]),对应的是RGB值,dU和dV都是一个float值,并不是Vector3f,想要实现h()表示的实数高度值,就要用到norm.()将向量映射成实数(个人理解,不确定对不对)
结果想把贴图理解完整不容易,我也是下了很大的功夫但是还只了解到了皮毛,有一点我也不是很清楚,就是这个函数虽然名称是Bump Map凹凸贴图,但是做的事情似乎跟Normal Map法线贴图一样,我不知道是我理解有误还是是这就是表达的法线贴图。
关于凹凸贴图/法线贴图/位移贴图的一些学习我也做了一些整理,有兴趣的可以去看看:
GAMES101扩展——法线贴图详细学习_flashinggg的博客
displacement_fragment_shader()与bump相比位移贴图多了一个修改point的步骤:
point += kn * normal * payload.texture->getColor(u, v).norm();
直接修改了顶点坐标的高度值。
代码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 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)
float x = normal.x();
float y = normal.y();
float z = normal.z();
Eigen::Vector3f t, b;
t height;
// dU = kh * kn * (h(u+1/w,v)-h(u,v)),
// dV = kh * kn * (h(u,v+1/h)-h(u,v))
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());
// Vector ln = (-dU, -dV, 1)
// Position p = p + kn * n * h(u,v)
// Normal n = normalize(TBN * ln)
Eigen::Vector3f ln;
ln 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.
//向量l,v,h
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();
//光源到物体的距离————light到point的
float r2 = (light.position - point).dot(light.position - point);//利用了 a·b/|a||b|=cos
//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;
}
return result_color * 255.f;
}
结果