Assignment3
前置知识
Blinn-Phong
一种局部着色模型(不会生成阴影,因为不涉及多物体)
光源方向I和视角方向v均是从片元出发(指向光源或摄像机)
diffuse(漫反射):与视角无关,只与接受到的光的总量有关
L
d
=
k
d
(
I
/
r
2
)
m
a
x
(
0
,
n
∗
I
)
L_d=k_d(I/r^2)max(0,n*I)
Ld=kd(I/r2)max(0,n∗I)
specular(高光):与视角有关,用光源和视角的半角和法向量作比较
L
s
=
k
s
(
I
/
r
2
)
m
a
x
(
0
,
n
∗
h
)
p
L_s=k_s(I/r^2)max(0,n*h)^p
Ls=ks(I/r2)max(0,n∗h)p
ambient(环境光):直接系数 * 颜色
Graphics Pipeline(实时渲染管线)
顶点阶段(Vertex Processing、Triangle Processing)
输入顶点信息(坐标)【MVP变换】,将坐标变换为顶点,再根据顶点间的对应关系将顶点连成三角形【屏幕空间】
->
光栅化(Rasterization)
将三角形离散成像素【三角形采样】,并用深度测试剔除某些像素
->
着色阶段(Fragment Processing、Framebuffer Operations)
根据不同着色方法决定像素的颜色【Shading】,最后输出
重心坐标插值(从顶点向像素)
重心坐标:三角形平面上的点都能用三个顶点的线性组合表示(三个系数之和为1);若点在三角形内,则需满足三个系数都是非负数
如何求三个系数:假设点在三角形内,将该点和三个点连起来,各个点的系数 = 对面的小三角形(不以自己做顶点的)/ 总三角形面积
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1erBSM8M-1645590206704)(C:\Users\dell\AppData\Roaming\Typora\typora-user-images\image-20220222195539800.png)]
求得系数之后,目标插值属性可直接用系数乘以三个点的对应属性得到
投影过后的重心坐标会变,所以在深度插值时应该用三维坐标插值(先将屏幕坐标投影回三维坐标),作业不要求
Texture(纹理)
纹理坐标uv反映了顶点坐标和纹理贴图之间的对应关系,将一张纹理图上的颜色着色到模型上(作为漫反射系数kd)
code
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
...
if (insideTriangle(x + 0.5f, y + 0.5f, t.v))
{
...
if(z_interpolated < z_depth)
{
z_depth = z_interpolated;
// TODO: Interpolate the attributes:
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).normalized();
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;
// 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;
auto pixel_color = fragment_shader(payload);
set_pixel(Vector2i(x, y), pixel_color); // 这里参数变了
}
}
}
Eigen::Vector3f phong_fragment_shader(const fragment_shader_payload& payload)
{
...
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.
Vector3f ambient = ka * amb_light_intensity;
Vector3f lightDir = light.position - point;
Vector3f lightEnergy = light.intensity / std::sqrt(lightDir.dot(lightDir));
Vector3f viewDir = eye_pos - point;
Vector3f diffuse = kd * lightEnergy * std::max(.0f, normal.dot(lightDir));
Vector3f halfDir = (viewDir + lightDir).normalized();
Vector3f specular = ks * lightEnergy * std::pow(std::max(.0f, normal.dot(halfDir)), p);
result_color += (ambient + diffuse + specular);
}
...
}
// displacement_fragment_shader和bump_fragment_shader直接照着伪代码改就行,就不放上来了
注意事项
纹理的放大和缩小
放大会使纹理出现锯齿,可用抗锯齿手段或各种线性插值解决
缩小会更复杂,可用mipmap等解决(和作业联系不大,不展开了)
在之前项目中使用过的函数的参数发生变化
set_pixel()第一个参数从Vector3f变成Vector2f
OpenCV(4.5.1) Error: Assertion failed (!_src.empty()) in cv::cvtColor
是main()中读取模型和贴图路径的问题