着色

定义

  1. 通过绘画或者程序控制明暗变化或者颜色变化
  2. 在物体上应用不同的材质的一个过程

布林冯反射模型

  1. 高光
  2. 漫反射
  3. 环境光
  4. 并非完全符合物理的

通过摄像机计算一个特定点的光线反射

  1. 输入
    1. 观测方向: v,从特定点到相机的向量
    2. 法线:n,假设曲面上的一个点在极小的范围内可以视为平面,则该点有法线
    3. 光照方向: l,从特定点到光源的向量
    4. 其他参数:颜色,亮度
  2. 着色局部性:不考虑阴影

布林冯反射模型 - 漫反射

  1. 均匀的把光线反射到各个方向
  2. 计算某一点的光能量(产生)有多少:假设为点光源,距离圆心距离为1的某一点的能量为I,则距离圆心为r的能量为I/Math.pow(r, 2)
  3. 计算有多少的光能量被接收到:Ld = kd * (I/Math.pow(r,2)) * max(0, n · l),Ld表示漫反射光强度,kd表示漫反射系数(受颜色影响,如果是黑的,则系数为0,用三维向量表示则是(r, g, b)),n · l表示光能量接收量,如果小于0,则光照方向与法线夹角大于90度,没有意义,所以最小值取0
  4. 漫反射跟v没有任何关系

布林冯反射模型 - 高光

  1. 什么时候能看到高光?: 观察方向与镜面反射方向比较接近的时候
  2. 观察方向与镜面反射方向接近 === 关照方向与观察方向的半程向量与法线接近,即: h(半程向量) = bisector(v, l) = (v + l) / ||v + l||
  3. Ls = ks * (I/Math.pow(r, 2)) * max(0, cos)^p = ks * (I/Math.pow(r, 2)) * max(0, n · h)^p
  4. 指数p的作用: 正常余弦函数到45度的时候,还是接近1的数字,可以看到高光,与实际不符,所以增加p的余弦值的指数,来加速变化的速度,一般在100-200左右

布林冯反射模型 - 环境光

  1. 假设不管从哪个角度看,得到的反射光是一样强度的,La = ka * Ia,主要用于提升亮度
  2. 布林冯反射模型: 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

着色频率

  1. flat shading: 基于三角形平面着色
  2. gouraud shading: 基于三角形顶点着色,中间用插值
  3. phong shading: 基于三角形每个像素点着色
  4. 当几何体足够复杂的情况下,可以使用相对简单的着色模型

如何定义顶点法线?

  1. 相邻面的法线求加权平均: 可以按面的面积比例来加权

如何定义像素的法线?

  1. 重心坐标

实时渲染管线

  1. 输入三维空间的顶点
  2. 转换为屏幕空间的顶点(顶点着色器)
  3. 转换为屏幕空间的三角形
  4. 每个三角形转换为光栅化的片段
  5. 针对每个片段着色(片段着色器)
  6. 片源组合输出图形
  7. shadertoy: 适合练习shader编写

gpu

  1. 提供了完整的一套渲染管线的硬件
  2. 提供了顶点和片段着色器可编程
  3. compute shader: 用于通用gpu计算,也叫gpgpu,挖矿必备
  4. 独立显卡,集成显卡
  5. 高度并行化处理器

纹理映射

  1. 任何一个3d表面的点,可以看作是一个2d平面
  2. 纹理即是一张图,3d表面上的点与纹理上的点一一对应
  3. 纹理坐标系: uv,均在0-1之内
  4. 纹理图四方连续,可以在各个方向重复,平滑过渡

重心坐标

  1. 用于三角形内部插值
  2. 一个点在三角形内,且满足 (x, y) = aA + bB + cC,a + b + c = 1,则(a,b,c)为重心坐标
  3. A点的重心坐标为(1, 0, 0)
  4. 任一点的重心坐标a = A点对面三角形的面积 / ABC的面积,其他同理
  5. 特殊点,三角形中心点的重心坐标为(1/3, 1/3, 1/3)
  6. 插值规则:V = aVa + bVb +cVc,Va,Vb,Vc可以使对应点的任意属性,颜色或法线
  7. 投影之后,重心坐标发生变化,所以要在投影之前做插值

纹理应用

  1. 对于光栅化屏幕上的点,求对应的uv坐标
  2. 取纹理上对应uv的颜色值
  3. 将颜色值作为kd系数,求布林冯模型的最终值

纹理的放大

  1. texel: 纹理上的一个像素
  2. 如果遇到屏幕分辨率大于纹理分辨率的情况,纹理坐标可能会出现小数
  3. 线性插值: lerp(x, v0, v1) = v0 + x*(v1-v0)
  4. 双线性插值(bilinear): 取texel周围四个临近点,先取水平方向的2点,使其与texel连成的直线刚好垂直,再根据这两个点求texel的线性插值
  5. u0 = lerp(s, u00, u10), u1 = lerp(s, u01, u11), f(x, y) = lerp(t, u0, u1)
  6. bicubic: 取周围16个临近点

点采样纹理

  1. 会导致锯齿和摩尔纹
  2. 近处一个点覆盖的纹理较小,可能就一个texel,但远处一个点覆盖的纹理较大,可能会涉及到一片texel
  3. 超采样可以降低锯齿,但开销太大
  4. 如果不采样,如何得到一个点的纹理区域的平均值: 点查询vs范围查询

mipmap

  1. 允许做范围查询: fast, approx, square,近似的快速的正方形的范围查询
  2. level0: 128x128, level1: 64x64, level2: 32x32, level3: 16x16, level4: 8x8, level5: 4x4, level6: 2x2, level7: 1x1
  3. mipmap多了1/3的存储量
  4. 假设D代表level的层级, L为uv坐标上的点对应的近似正方形的边长,D = log2(L), L = max(sqrt((du/dx)^2 + (dv/dx)^2), max(sqrt((du/dy)^2 + (dv/dy)^2))
  5. 三线性插值: 由于D是小数的,可以使用两层(floor(D), ceil(D))上对应点的双线性插值,在计算连续的D对应的线性插值
  6. 各向异性过滤: 解决屏幕上的一个点,对应纹理坐标区域非正方形,而是长条形的情况, 内存占用为原来的3倍
  7. ewa过滤: 用无数个椭圆来表示任意倾斜的四边形,让采样更准确,计算量更大

纹理应用

  1. 环境贴图: 犹他茶壶,认为环境光只有方向,没有深度信息
  2. 把整个环境光记录在球上,展开,存在扭曲问题
  3. 球形贴图: 假设环境光记录在球体对应的包围盒上
  4. 凹凸贴图: 法线贴图,没改变点的位置,只是产生了视觉效果,在球体边缘就失效了
    1. 首先需要一个切线空间TBN,假设任一点的切线都是固定(0, 0, 1)
    2. 计算该点的dU,dV
    3. 则法线为(-dU, -dV, 1)
    4. 再将法线左乘TBN矩阵,转换到世界空间坐标
  5. 位移贴图: 改变所有点的位置,directx动态曲面细分
    1. 和凹凸贴图一样,需要TBN切线空间
    2. 额外要做的是改变点的位置
  6. 三维空间噪声函数,柏林噪声可以表示一些不规则纹理,如大理石,山脉
  7. 计算好的shader,屏蔽环境光