GAMES101 第8讲 着色(着色频率, 实时渲染管线, 纹理映射)
着色频率
把颜色应用在一个面上, 应用在一个顶点上, 或者应用在一个像素上, 这些都是着色的频率. 通常来说, 着色的频率越高, 渲染的效果就越好, 但是性能也就越差.
其中: 如果在顶点上着色(例如图中在矩形的四个顶点上着色), 那么在矩形内部的像素就会使用顶点的颜色进行插值.
逐三角形着色
又叫 平面着色 Flat Shading
一个三角面内的颜色完全相同, 根据三角形的法线(两条边的叉乘)等参数求得, 然后填充整个三角形.
- 三角形面是平坦的(只有一个法线方向)
- 对于平滑的曲面, 逐三角形着色会导致边缘的颜色不连续
逐顶点着色
又叫 Gouraud Shading
对任意一个顶点, 计算出它的法线, 以此为基础计算出顶点的颜色, 然后在三角形内部的像素上进行插值.
- 内部的颜色通过顶点颜色的插值得到
- 顶点法线的计算方法见后文
- 又叫Phong Shading, 不是Blinn-Phong反射模型, 是一种Shading算法, 都是以Phong这个人的名字命名的
逐像素着色
又叫 Phong Shading
对于每一个像素, 计算出它的法线, 以此为基础计算出像素的颜色.
- 逐像素着色是最精确的着色方法
- 但是计算量也是最大的
- 通常用于高质量的渲染
着色频率的选择
- 当面片数量已经很高时, 逐像素着色的计算量就会很大, 这时候可以考虑逐顶点着色或者逐三角形着色.
- 当面片数量较少时, 逐像素着色的计算量就会很小, 这时候可以考虑逐像素着色.
- 当然, 当面片数量大于了一定的阈值, 超过了像素数量, 那么逐像素着色就是最好的选择.
顶点法线的计算
顶点法线的计算方法有很多种, 当我们只需要计算一个绝对光滑的球形时, 法线方向就是球心指向顶点的方向.
当我们需要计算一个不规则的曲面时, 可以通过计算顶点的四周的面片的法线, 然后取平均值.
\[N_v = \frac{\sum_{i} N_i}{\|\sum_{i} N_i\|}\]但是想要更加精确的法线, 可以通过计算顶点的四周的面片的法线, 然后根据面片的面积加权平均.
逐像素的法线插值
顶点的法线是已知的, 但是像素的法线是未知的, 所以需要对顶点的法线进行重心坐标插值.
插值后需要对法线进行归一化.
实时渲染管线(图形管线)
从场景到屏幕的过程, 通常被称为图形管线.
- Application 应用: 先输入三维(模型)空间中的顶点数据
- 顶点处理 Vertex Processing: 将顶点数据变换到屏幕空间(投影变换)
- 三角形处理 Triangle Processing: 将顶点数据变换成三角形
- 光栅化 Rasterization: 将三角形变成片元(fragment, 又叫像素)
- 片元处理 Fragment Processing: 着色, 计算像素的颜色(这里也有深度测试等操作)
- 帧缓冲 Framebuffer Operations: 将像素写入帧缓冲
- 显示 Display: 将帧缓冲的内容显示到屏幕上
如果要着色:
- Gouraud Shading: 顶点处理阶段计算顶点颜色, 片元处理阶段插值得到像素颜色
- Phong Shading: 顶点处理阶段计算顶点法线, 片元处理阶段插值得到像素法线, 然后计算像素颜色
这两个阶段(顶点处理和片元处理)是可以自定义的, 也成为可编程着色器, 我们称其为着色器 Shader.
着色器 Shader
现代GPU允许我们编写自定义的着色器(Shader), 用于顶点处理和片元处理.
着色器 Shader 是一种可以在GPU硬件上执行的语言, OpenGL使用GLSL, DirectX使用HLSL.
- 顶点着色器 Vertex Shader: 用于顶点处理阶段, 计算顶点的位置, 颜色等
- 片元着色器 Fragment Shader: 用于片元处理阶段, 计算像素的颜色
片元着色器例子:
1
2
3
4
5
6
7
8
9
10
11
uniform sampler2D myTexture; // 纹理
varying vec3 lightDir; // 光线方向
varying vec2 uv; // 纹理坐标
varying vec3 norm; // 顶点法线
void diffuseShader() {
vec3 kd;
kd = texture2D(myTexture, uv); // 从纹理中获取颜色
kd *= clamp(dot(norm, lightDir), 0.0, 1.0); // 计算漫反射
gl_FragColor = vec4(kd, 1.0); // 设置像素颜色
}
纹理映射
纹理映射 Texture Mapping 是一种将纹理映射到三维模型表面的技术.
2D纹理
我们认为: 任何一个三维物体的表面都可以看作是一个二维平面, 我们可以将一个二维的纹理贴到这个平面上.
这个过程就是纹理映射 Texture Mapping, 一般由美术工程师完成, 也有自动化完成纹理映射的相关研究方向
纹理坐标(UV坐标)
- 纹理坐标是二维的, 通常用 $u$ 和 $v$ 表示
- $u$ 和 $v$ 的范围通常认为是都在 $[0, 1]$ 区间内
- 三角形每一个顶点都对应一个uv坐标
纹理的重复
在下图中, 我们显示了一个建筑场景的uv坐标的可视化结果:
我们可以很明显的看到, 纹理在建筑的表面上重复了很多次, 而且两个纹理的边界是很明显的; 但是在实际的渲染结果中, 并不能看到这种重复的现象:
这说明, 纹理本身在设计时是上下边界连续的, 这种纹理被称作可平铺纹理 Tileable texture.
三角形内部的像素的纹理坐标
我们知道三角形三个顶点对应的纹理坐标, 那么三角形内部的像素的纹理坐标是如何计算的呢?
这也是通过将顶点的纹理坐标进行插值得到的.