目录
写在前面
main 函数中 std::function<Eigen::Vector3f(fragment_shader_payload)> active_shader = displacement_fragment_shader 这一句是指定当前的着色方式, 将等号后的displacement_fragment_shader 改为 phong_fragment_shader 等方法函数名即可, 或者在属性中修改命令行参数也可以。
第一题 三角形重心坐标
题目: 对三角形顶点处的各属性进行插值, 并进行着色。
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);
}
解析:
1. 三角形重心坐标的推导:
首先推导直线重心坐标, 然后由直线推广到三角形。
1.1 直线重心坐标:
设有向量 AB, A点坐标(a1, a2), B点坐标(b1, b2), 直线上有任意一点 C(c1, c2)。如何用 A, B 两点来表示 C?可以用点起始点坐标加上 k 倍的向量得到
因为 kAB 这个向量的起点是原点,所以 A + kAB 也就是把 kAB 这个向量的起点从原点挪到了 A 点。又因为 C 点在 AB 上, 所以要满足 0 <= k <= 1。公式中有 (1-k) + k = 1, 即 A, B 前的系数之和为 1。
1.2 三角形重心坐标:
设三角形的三个顶点分别为 A(a1, a2, a3), B(b1, b2, b3), C(c1, c2, c3), AB 上有一点 D, CD上有一点 E。
由直线重心坐标得到, D = (1 - m)A + mB, E = (1 - n)C + nD, 将 D 代入 E, 得:
E = (1 - n)C + n((1 - m)A + mB) = (n - mn)A + mnB + (1 - n)C
A, B, C 前面的系数之和为 n - mn + mn + 1 - n = 1, 则用 α, β, γ 分别替换 A, B, C 前面的系数, 可得 E = αA + βB + γC, 且当时, E 点位于三角形内部。
此时可以联立方程(Ax 为 A 的 x 坐标值, Ay 为 A 的 y 坐标值, 以此类推):
最后可以得到闫老师给出的公式, 这里偷下懒, 直接贴图。
2. 代码:
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;
auto v = t.toVector4();
float x_max = std::max(std::max(v[0].x(), v[1].x()), v[2].x());
float y_max = std::max(std::max(v[0].y(), v[1].y()), v[2].y());
float x_min = std::min(std::min(v[0].x(), v[1].x()), v[2].x());
float y_min = std::min(std::min(v[0].y(), v[1].y()), v[2].y());
for (int i = x_min; i < x_max + 1; ++i)
{
for (int j = y_min; j < y_max + 1; ++j)
{
if (insideTriangle(i, j, t.v))
{
auto [alpha, beta, gamma] = computeBarycentric2D(i, j, 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;
if (depth_buf[get_index(i, j)] > z_interpolated) //获取当前点在深度缓存中的值, 如果大于插值计算出的 z 值, 则上色
{
//计算顶点的插值属性
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);
//初始化 payload 这个可供之后 Bling-Phong 模型和贴图使用
fragment_shader_payload payload(interpolated_color, interpolated_normal.normalized(), interpolated_texcoords, texture ? &*texture : nullptr);
payload.view_pos = interpolated_shadingcoords;
depth_buf[get_index(i, j)] = z_interpolated;
Vector2i temp(i, j);
//fragment_shader(payload) 的含义是: 获得当前激活的着色方法返回的颜色值
set_pixel(temp, fragment_shader(payload));
}
}
}
}
// 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);
}
第二题 Blinn-Phong光照模型
题目: 实现 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.
}
return result_color * 255.f;
}
解析:
1. 漫反射:
kd: 漫反射系数, 个人认为是入射点对于不同颜色光的反射率
I / r²: 到达入射点的光照强度, r 是光源到入射点的距离, 因为在三维空间中, 所以光的强度与 r²成反比, 如果是二维空间, 则光强与 r 成反比。
max(0, n · l): 因为 n 和 l 都是单位向量, 所以 n · l 就是光照方向与法线的余弦值, 而取 0 和 余弦值的最大值的原因是余弦值可能是负值, 此时漫反射的结果会变为负值, 这显然不正确, 此时结果应该为 0 才对。
2. 镜面反射(高光)
当观察方向和镜面反射方向十分接近时, 则法线方向和半程向量 h 也很接近, 因为计算半程向量的难度远小于计算镜面反射方向, 因此使用半程向量来计算高光项 Ls。此处 Ks 时镜面反射系数, 通常来说高光为白色, 因此 Ks 一般也设置为白色。系数 p 是为了增大 Ls 对α 的敏感度, p越大, Ls 衰减的速度就越快, 如下图。
3. 环境光
环境光是一个常数, 在任何一个地方看到的颜色都是一个常数结果, 因此环境光的结果就是将这一点上反射的所有光都加起来(暂时这样认为, 实际上计算要用到全局光照技术)。
4. Blinn-Phong 反射模型
将前三个计算结果相加, 则得到了 Blinn-Phong 反射模型的结果。
5. 代码:
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 l = light.position - point;
Eigen::Vector3f v = eye_pos - point;
Eigen::Vector3f h = (l + v).normalized();
Vector3f diffuse = kd.cwiseProduct(light.intensity / (l.dot(v)) * std::max(0.0f, (normal.normalized().dot(l.normalized()))));
Vector3f specular = ks.cwiseProduct(light.intensity / (l.dot(l)) * pow(std::max(0.0f, payload.normal.normalized().dot(h)), 200));
Vector3f ambient = ka.cwiseProduct(amb_light_intensity);
result_color += diffuse + specular + ambient;
}
return result_color * 255.f;
}
第三题 纹理贴图
题目: 实现纹理贴图
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
}
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.
}
return result_color * 255.f;
}
解析:
将 Blinn-Phong 中的漫反射系数换为片元在该点的纹理颜色向量即可。
Eigen::Vector3f texture_fragment_shader(const fragment_shader_payload& payload)
{
Eigen::Vector3f return_color = { 0, 0, 0 };
if (payload.texture)
{
// 获取纹理坐标的颜色
return_color = payload.texture->getColor(payload.tex_coords.x(), payload.tex_coords.y());
}
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 };
Eigen::Vector3f ambient = ka * amb_light_intensity[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;
float r = light_dir.dot(light_dir);
// ambient
Eigen::Vector3f La = ka.cwiseProduct(amb_light_intensity);
// diffuse
Eigen::Vector3f Ld = kd.cwiseProduct(light.intensity / r);
Ld *= std::max(0.0f, normal.normalized().dot(light_dir.normalized()));
// specular
Eigen::Vector3f h = (light_dir + view_dir).normalized();
Eigen::Vector3f Ls = ks.cwiseProduct(light.intensity / r);
Ls *= std::pow(std::max(0.0f, normal.normalized().dot(h)), p);
result_color += (La + Ld + Ls);
}
return result_color * 255.f;
}
第四题 凹凸贴图实现及法线贴图推导
题目: 实现凹凸贴图
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)
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
return result_color * 255.f;
}
解析:
关于法线贴图这部分我的理解不一定准确, 只能现在这里记录, 如果日后发现错误再来修改。
1. 凹凸贴图的原理:
利用凹凸贴图来改变原本光滑的平面的法线, 使原本光滑的平面产生凹凸感。
2. TBN 矩阵:
这道题的难点应该就是 TBN 矩阵的理解。
想理解 TBN 矩阵, 首先要理解切线空间, 我读了一些博客和帖子, 觉得这篇讲的最详尽且容易理解——切线空间(Tangent Space)完全解析。
下图是 UV 空间(切线空间), 假设映射到纹理上的三角形如图中所示, 其中向量
E1 = ΔU1U + ΔV1V
E2 = ΔU2U + ΔV2V
再看 E1, E2 向量在三维空间中的表示, 如下图: 此时可以得到(再次偷懒, 公式都是从切线空间(Tangent Space) 的计算与应用复制来的):
E1 在三维空间中的坐标记为 (E1x, E1y, E1z), E2 的坐标记为 (E2x, E2y, E2z),所以上面的等式用矩阵形式表达。
对 T 和 B 求解,计算比较简单:
设:
则有:
因为 E1, E2 可以由三角形顶点在三维空间中的坐标(顶点坐标信息记录在属性中,可以直接读到)求出, 所以可以解出 T。最后求解 B,因为 B 同时垂直于 T 和法向量,而法向量又能直接读到所以直接用 T 叉乘法向量即可求出。
题目代码中给出的公式:Vector t = (x*y/sqrt(x*x+z*z),sqrt(x*x+z*z),z*y/sqrt(x*x+z*z)),个人猜测(没有推导求证, 因为实际计算中不一定使用这个公式): 此处的 x, y, z 是当前 payload 的法线坐标,而这个法线坐标是根据三角形重心坐标插值求出来的, 也就是说法线其实包含了三个顶点的坐标信息,所以此处 T 采用的不是前面推导出来的计算公式, 而是用法线坐标来计算。
如果有大佬知道这个公式的推导过程也请不吝赐教。
3. 代码:
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)
auto n = normal;
auto x = n.x();
auto y = n.y();
auto z = n.z();
Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
Vector3f b = n.cross(t);
auto u = payload.tex_coords.x(); //在切线空间中的 x 坐标
auto v = payload.tex_coords.y(); //在切线空间中的 y 坐标
//切线空间向世界空间的转换矩阵
Matrix3f tbn;
tbn << t.x(), t.y(), t.z(),
b.x(), b.y(), b.z(),
n.x(), n.y(), n.z();
// 纹理的实际宽度, 因为 u, v实际是通过重心插值计算出的值, 需要乘以实际宽度才是对应纹理中的坐标
auto w = payload.texture->width;
// 同理, v 也要乘以纹理的实际高度
auto h = payload.texture->height;
//U 方向的切线(导数)
auto dU = kh * kn * (payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm());
//V 方向的切线(导数)
auto dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
//当前点的法向量
Vector3f ln(-dU, -dV, 1);
//使用tbn矩阵将法线变换到世界空间中
normal = (tbn * ln).normalized(); //normal = ln.normalized();
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
return result_color * 255.f;
}
得到的结果:
法线贴图的原理在代码中也可以实验, 将 normal = (tbn * ln).normalized() 这一句替换为 normal = ln.normalized(), 运行代码后得到结果:
此时 normal 就是每个点在切线空间中的法线, 可以看到图片中基本都是蓝色, 也就证明这些点在切线空间中的法线基本都是 (0,0,1), 从 dU 和 dV 的计算过程也可以看出, 凹凸贴图的凹凸感来自颜色的变化, 当这个变化越剧烈(导数越大), 则凹凸感也越明显。这点可以通过将auto texture_path 这一句改为 auto texture_path = "spot_texture.png" 得到, 在贴图黑白交界处, 凹凸感最强烈。
第五题 位移贴图
题目: 在 bump_fragment_shader 的基础上实现 displacement_fragment_shader。
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 p = p + kn * n * h(u,v)
// Normal n = normalize(TBN * ln)
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.
}
return result_color * 255.f;
}
解析:
1. 代码:
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 p = p + kn * n * h(u,v)
// Normal n = normalize(TBN * ln)
auto n = normal;
auto x = n.x();
auto y = n.y();
auto z = n.z();
Vector3f t(x * y / sqrt(x * x + z * z), sqrt(x * x + z * z), z * y / sqrt(x * x + z * z));
Vector3f b = n.cross(t);
auto u = payload.tex_coords.x();
auto v = payload.tex_coords.y();
Matrix3f tbn;
tbn << t.x(), t.y(), t.z(),
b.x(), b.y(), b.z(),
n.x(), n.y(), n.z();
auto w = payload.texture->width;
auto h = payload.texture->height;
auto dU = kh * kn * (payload.texture->getColor(u + 1.0f / w, v).norm() - payload.texture->getColor(u, v).norm());
auto dV = kh * kn * (payload.texture->getColor(u, v + 1.0f / h).norm() - payload.texture->getColor(u, v).norm());
Vector3f ln(-dU, -dV, 1);
auto temp = payload.texture->getColor(u, v).norm();
point += (kn * normal * payload.texture->getColor(u, v).norm()); //这一步产生位移
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.
float rr = (light.position - point).dot(light.position - point);
auto l = light.position - point;
auto v = eye_pos - point;
auto h = (l + v).normalized();
auto ambient = ka.cwiseProduct(amb_light_intensity);
auto diffuse = kd.cwiseProduct(light.intensity / rr * std::max(0.0f, l.normalized().dot(normal.normalized())));
auto specular = ks.cwiseProduct(light.intensity / rr * std::pow(std::max(0.0f, normal.dot(h)), 200));
result_color += ambient + diffuse + specular;
}
return result_color * 255.f;
}