
着色
定义
- 通过绘画或者程序控制明暗变化或者颜色变化
- 在物体上应用不同的材质的一个过程
布林冯反射模型
- 高光
- 漫反射
- 环境光
- 并非完全符合物理的
通过摄像机计算一个特定点的光线反射
- 输入
- 观测方向: v,从特定点到相机的向量
- 法线:n,假设曲面上的一个点在极小的范围内可以视为平面,则该点有法线
- 光照方向: l,从特定点到光源的向量
- 其他参数:颜色,亮度
- 着色局部性:不考虑阴影
布林冯反射模型 - 漫反射
- 均匀的把光线反射到各个方向
- 计算某一点的光能量(产生)有多少:假设为点光源,距离圆心距离为1的某一点的能量为I,则距离圆心为r的能量为I/Math.pow(r, 2)
- 计算有多少的光能量被接收到:Ld = kd * (I/Math.pow(r,2)) * max(0, n · l),Ld表示漫反射光强度,kd表示漫反射系数(受颜色影响,如果是黑的,则系数为0,用三维向量表示则是(r, g, b)),n · l表示光能量接收量,如果小于0,则光照方向与法线夹角大于90度,没有意义,所以最小值取0
- 漫反射跟v没有任何关系
布林冯反射模型 - 高光
- 什么时候能看到高光?: 观察方向与镜面反射方向比较接近的时候
- 观察方向与镜面反射方向接近 === 关照方向与观察方向的半程向量与法线接近,即: h(半程向量) = bisector(v, l) = (v + l) / ||v + l||
- Ls = ks * (I/Math.pow(r, 2)) * max(0, cos)^p = ks * (I/Math.pow(r, 2)) * max(0, n · h)^p
- 指数p的作用: 正常余弦函数到45度的时候,还是接近1的数字,可以看到高光,与实际不符,所以增加p的余弦值的指数,来加速变化的速度,一般在100-200左右
布林冯反射模型 - 环境光
- 假设不管从哪个角度看,得到的反射光是一样强度的,La = ka * Ia,主要用于提升亮度
- 布林冯反射模型: L = La + Ld + Ls = ka * Ia + kd * (I/Math.pow(r, 2)) * max(0, n · l) + ks * (I/Math.pow(r, 2)) * max(0, n · h)^p
着色频率
- flat shading: 基于三角形平面着色
- gouraud shading: 基于三角形顶点着色,中间用插值
- phong shading: 基于三角形每个像素点着色
- 当几何体足够复杂的情况下,可以使用相对简单的着色模型
如何定义顶点法线?
- 相邻面的法线求加权平均: 可以按面的面积比例来加权
如何定义像素的法线?
- 重心坐标
实时渲染管线
- 输入三维空间的顶点
- 转换为屏幕空间的顶点(顶点着色器)
- 转换为屏幕空间的三角形
- 每个三角形转换为光栅化的片段
- 针对每个片段着色(片段着色器)
- 片源组合输出图形
- shadertoy: 适合练习shader编写
gpu
- 提供了完整的一套渲染管线的硬件
- 提供了顶点和片段着色器可编程
- compute shader: 用于通用gpu计算,也叫gpgpu,挖矿必备
- 独立显卡,集成显卡
- 高度并行化处理器
纹理映射
- 任何一个3d表面的点,可以看作是一个2d平面
- 纹理即是一张图,3d表面上的点与纹理上的点一一对应
- 纹理坐标系: uv,均在0-1之内
- 纹理图四方连续,可以在各个方向重复,平滑过渡
重心坐标
- 用于三角形内部插值
- 一个点在三角形内,且满足 (x, y) = aA + bB + cC,a + b + c = 1,则(a,b,c)为重心坐标
- A点的重心坐标为(1, 0, 0)
- 任一点的重心坐标a = A点对面三角形的面积 / ABC的面积,其他同理
- 特殊点,三角形中心点的重心坐标为(1/3, 1/3, 1/3)
- 插值规则:V = aVa + bVb +cVc,Va,Vb,Vc可以使对应点的任意属性,颜色或法线
- 投影之后,重心坐标发生变化,所以要在投影之前做插值
纹理应用
- 对于光栅化屏幕上的点,求对应的uv坐标
- 取纹理上对应uv的颜色值
- 将颜色值作为kd系数,求布林冯模型的最终值
纹理的放大
- texel: 纹理上的一个像素
- 如果遇到屏幕分辨率大于纹理分辨率的情况,纹理坐标可能会出现小数
- 线性插值: lerp(x, v0, v1) = v0 + x*(v1-v0)
- 双线性插值(bilinear): 取texel周围四个临近点,先取水平方向的2点,使其与texel连成的直线刚好垂直,再根据这两个点求texel的线性插值
- u0 = lerp(s, u00, u10), u1 = lerp(s, u01, u11), f(x, y) = lerp(t, u0, u1)
- bicubic: 取周围16个临近点
点采样纹理
- 会导致锯齿和摩尔纹
- 近处一个点覆盖的纹理较小,可能就一个texel,但远处一个点覆盖的纹理较大,可能会涉及到一片texel
- 超采样可以降低锯齿,但开销太大
- 如果不采样,如何得到一个点的纹理区域的平均值: 点查询vs范围查询
mipmap
- 允许做范围查询: fast, approx, square,近似的快速的正方形的范围查询
- level0: 128x128, level1: 64x64, level2: 32x32, level3: 16x16, level4: 8x8, level5: 4x4, level6: 2x2, level7: 1x1
- mipmap多了1/3的存储量
- 假设D代表level的层级, L为uv坐标上的点对应的近似正方形的边长,D = log2(L), L = max(sqrt((du/dx)^2 + (dv/dx)^2), max(sqrt((du/dy)^2 + (dv/dy)^2))
- 三线性插值: 由于D是小数的,可以使用两层(floor(D), ceil(D))上对应点的双线性插值,在计算连续的D对应的线性插值
- 各向异性过滤: 解决屏幕上的一个点,对应纹理坐标区域非正方形,而是长条形的情况, 内存占用为原来的3倍
- ewa过滤: 用无数个椭圆来表示任意倾斜的四边形,让采样更准确,计算量更大
纹理应用
- 环境贴图: 犹他茶壶,认为环境光只有方向,没有深度信息
- 把整个环境光记录在球上,展开,存在扭曲问题
- 球形贴图: 假设环境光记录在球体对应的包围盒上
- 凹凸贴图: 法线贴图,没改变点的位置,只是产生了视觉效果,在球体边缘就失效了
- 首先需要一个切线空间TBN,假设任一点的切线都是固定(0, 0, 1)
- 计算该点的dU,dV
- 则法线为(-dU, -dV, 1)
- 再将法线左乘TBN矩阵,转换到世界空间坐标
- 位移贴图: 改变所有点的位置,directx动态曲面细分
- 和凹凸贴图一样,需要TBN切线空间
- 额外要做的是改变点的位置
- 三维空间噪声函数,柏林噪声可以表示一些不规则纹理,如大理石,山脉
- 计算好的shader,屏蔽环境光