作业三
作业
光栅化插值算法
实现对三角形三个点绑定的法向量、颜色、纹理颜色的插值
重心坐标插值
重心坐标
对于三角形内部的点我们都可以通过形如:
$(x, y) =\alpha A + \beta B + \gamma C$
其中 $\alpha + \beta + \gamma = 1$
我们称$(\alpha, \beta, \gamma)$为重心坐标(三维坐标依旧成立)
计算重心坐标
三角形内的点的$\alpha \beta \gamma$的值是其三角形顶点的对边与三角形内点构成的三角形的面积 下图所示
通过上面推到,重心坐标计算公式如图
有了重心坐标 我们就可以对三角形顶点上标定的向量愉快的插值了^^
插值对象可以是深度 法线 颜色等 如图所示
代码
// 计算重心坐标
static std::tuple<float, float, float> computeBarycentric2D(float x, float y, const Vector4f* v){
float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y());
float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y());
float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y());
return {c1,c2,c3};
}
// 三维向量插值 二维等类似
static Eigen::Vector3f interpolate(float alpha, float beta, float gamma, const Eigen::Vector3f& vert1, const Eigen::Vector3f& vert2, const Eigen::Vector3f& vert3, float weight)
{
return (alpha * vert1 + beta * vert2 + gamma * vert3) / weight;
}
实现Blinn-Phong模型计算
Bline-Phong模型主要分为三种反射
光的强度
光的强度衰减和距离的平方成反比,公式为 $I/r^2$ 其中$I$是原始光强,$r$是光于物体之间的距离
环境光照
环境光照描述的是间接光照的影响 由于间接光照计算过于困难 因此Blinn-Phong模型用个常量表示间接光照
漫反射
漫反射描述的是光打在物体表面上向四面八方反射
因此可以想到漫反射只会受光的入射$L$和物体表面法线$N$影响
镜面反射
镜面反射描述光反射时物体的高光
这于观察者的视线V和入射光线的反射R相关(Phone)
我们也可以用半程向量H代替R的计算(Blinn-Phone)
代码
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.
auto light_dir = (light.position - point);
auto V = (eye_pos - point).normalized();
auto L = (light.position - point).normalized();
auto H = (V + L).normalized();
float r = light_dir.dot(light_dir);
auto diffuse = std::max(L.dot(normal), 0.0f) * kd.cwiseProduct((light.intensity / r));
auto specular = std::pow(std::max(0.0f, H.dot(normal)), p) * ks.cwiseProduct((light.intensity / r));
auto ambient = ka.cwiseProduct(amb_light_intensity);
result_color += (diffuse + specular + ambient);
}
return result_color * 255.f;
}
纹理查找
- 纹理查询主要是通过插值获得纹理坐标([0.0..1.0]) 然后去
texture
中找寻对应的像素 - 由于纹理坐标是浮点(且归一化了) 且每个纹理坐标不一定能一一对应
texture
中找的像素 - 这样我们需要对查询的纹理进行过滤 过滤主要分为邻近点过滤和线性过滤
邻近点过滤
顾名思义,找纹理坐标最对应的点(即当纹理坐标浮点数转换为像素坐标的定点时 最靠近顶点的像素)
线性插值
对于纹理坐标中对应四个像素进行双线性插值
凹凸贴图
凹凸贴图主要是通过贴图的高度差来算出法线位置(感觉和法线贴图作用一样 处理比法线贴图麻烦?) 通过光照实现视觉效果 需要将其转换到切线空间
通过求导算出切线 将其旋转90就能得出法线位置
代码
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)
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 << t.x(), b.x(), normal.x(),
t.y(), b.y(), normal.y(),
t.z(), b.z(), normal.z();
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float h = payload.texture->height;
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());
point += kn * normal * payload.texture->getColor(u, v).norm();
Eigen::Vector3f ln{-dU, -dV, 1.0f};
normal = TBN * ln;
normal = normal.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.
auto light_dir = (light.position - point);
auto V = (eye_pos - point).normalized();
auto L = (light.position - point).normalized();
auto H = (V + L).normalized();
float r = light_dir.dot(light_dir);
auto diffuse = std::max(L.dot(normal), 0.0f) * kd.cwiseProduct((light.intensity / r));
auto specular = std::pow(std::max(0.0f, H.dot(normal)), p) * ks.cwiseProduct((light.intensity / r));
auto ambient = ka.cwiseProduct(amb_light_intensity);
result_color += (diffuse + specular + ambient);
}
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)
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 << t.x(), b.x(), normal.x(),
t.y(), b.y(), normal.y(),
t.z(), b.z(), normal.z();
float u = payload.tex_coords.x();
float v = payload.tex_coords.y();
float w = payload.texture->width;
float h = payload.texture->height;
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());
Eigen::Vector3f ln{-dU, -dV, 1.0f};
normal = TBN * ln;
auto n = normal;
Eigen::Vector3f result_color = {0, 0, 0};
result_color = normal;
return result_color * 255.f;
}
此处评论已关闭