处理遮挡-Z-buffering

画画时,先画远的再画近的,前景遮挡背景;遇到两两覆盖的问题之前采用画家算法

Z-buffer

  • 存储当前像素的最小z值(假设z是正的,近的值小,远的值大)
  • 需要一个额外的缓冲区来存放深度值
    • 帧缓冲区存放颜色值
    • 深度缓冲区存放深度
1
2
3
4
5
6
7
for (each triangle T)
for (each sample(x,y,z) in T)
if (z < zbuffer[x,y]) // 记录最近的点,默认zbuffer.z为无穷大
framebuffer[x,y] = rbg; // 更新颜色
zbuffer[x,y] = z; // 更新深度
else
…… // 样本被遮挡,什么都不做

QQ截图20220829013050

Q:n个三角形如何排序,排序效率


Shading

不同物体用不同材质(Material - 和光线不同作用)表现的过程

照明和阴影(Blinn-Phong 模型)

漫反射

漫反射无论观察方向在哪里,看到该点的光是一样的,

  • Shading是局部的(只看该点,不考虑其他物体和阴影),计算在特定着色点反射到相机的光线(shading point),默认不会产生阴影

  • Lambertian (Diffuse) Shading 朗博着色:着色过程推导如下

QQ截图20220829015145

​ 设观察方向为 v,光线方向为 I,法线为 n,研究的着色点 shading point,I和n的夹角为

​ 假设反射为漫反射,反射后得到的光为多少呢?

QQ截图20220829015434

​ 根据朗博余弦定律,单位面积的光与成正比,而且将光源定义为球体,

​ 设总光源强度为I,则球壳上距离光源半径为r的点的强度为

QQ截图20220829015830

​ 因此,最后计算漫反射光为;

:漫反射光

:漫反射系数,颜色(灰度大小)

:到达着色点的光强,从光源出发点到达着色点衰减后的光强

:接受到的能量反射出去,0用于计算得到负值的情况取0


镜面反射

由于镜面反射着色点不吸收光照,接受多少反射多少,,观察方向与反射方向越近越接近高光,

QQ截图20220829104046

  • 求R和V的夹角也就可以转换为计算n与(I和v夹角/2)之间的夹角→半程向量,通过单位向量的点积测量远近。半程向量和法线足够近就可以看到高光,p为指数,p越大高光越集中,即h和v近

QQ截图20220829104337

​ 其中,为反射镜面光,为镜面系数


环境光

着色不依赖于任何东西

  • 添加恒定的颜色来考虑忽略的照明和黑色阴影
  • 近似的

​ 其中,为反射环境光,为环境光系数,为反射的环境光


Blinn-Phong反射模型(布林冯)

QQ截图20220829110754

那么,什么导致了着色差异?

例如,一个平面做一次shading和一个法线,一个顶点在做一次shading和一个法线,一个像素做一次shading和一个法线,像素效果最好

兰伯特模型

没有高光(镜面反射)


着色频率

  • 三角形平面着色(平面着色):一个平面做一次shading和一个法线,不适用于表面平滑的物体
  • 顶点着色(高洛德着色):一个顶点在做一次shading和一个法向量,需要内部插值
  • 每个像素着色(冯氏着色,双线性插值着色):每个三角形的法向量插值,计算所有像素,不属于布模型

面频率非常高的情况下,着色差异不大

  • 定义逐顶点法向量

    • 优先从几何结构直接计算出法线(例如圆切面)
    • 否则就从三角形面推断法线,例如平均周围的面法线(顶点所在面加权平均)

    QQ截图20220829145038

    • 已知顶点求内部法线:顶点法线的重心插值,规范化方向

    QQ截图20220829145433


图形(实时渲染)管线

图片1

  • Shader 程序
    • 程序顶点和片段处理阶段
    • 描述对单个顶点(或片段)的操作
    • 步骤
      • Shader函数每个片段执行一次
      • 输出当前片段屏幕采样位置表面的颜色
      • 这个着色器执行一个纹理查找来获得表面的材质颜色,然后执行一个漫反射照明计算(先获得颜色再处理光照)
1
2
3
4
5
6
7
8
9
10
11
12
uniform sampler2D myTexture;
uniform vec3 lightDir;
varying vec2 uv; // 每个片段的值(光珊化插值)
varying vec3 norm; // 每个片段的值(光珊化插值),法向量n

void diffuseShader()
{
vec3 Kd;
kd = texture2D(myTexture,uv); // 获取材质
kd *= clamp(dot(-lightDir,norm),0.0,1.0); // 朗博Shader模型,I·n
gl_FragColor = vec4(kd,1.0); // 输出片段颜色
}

Texture Mapping 纹理映射

纹理(Texture):即三角形的内部填充图,纹理图表面是2D。

  • 每个3D表面点在2D图像(纹理)中也有一个位置。例如地图展开图

    将2D纹理展开放在(U,V)坐标系中,则3D对应的点在该坐标会有(u,v)位置

三角形插值算法

插值:利用它可通过函数在有限个点处的取值状况,估算出函数在其他点的近似值,用来填充图像变换时像素之间的空隙

  • 为什么需要插值算法?

    • 指定顶点值

    • 需要获得三角形上平滑过度的不同值

  • 需要对什么做插值?

    • 纹理坐标,颜色,法向量
  • 如何做插值?

    • Barycentric Coordinates 重心坐标

重心坐标

​ 设一个三角形坐标系 ,三个坐标点非负;三个顶点为A,B,C,内部点为

​ 若在A点,则 为0

​ 若按面积表示

QQ截图20220830002027

  • 重心将三角形分成三个等面积三角形,计算任意点面积,公式为
  • 顶点上的线性插值算法

QQ截图20220830003302

重心坐标在投影后是不变的,重心坐标和任何点插值投影后不一定是一个点,V可以表示位置,纹理等


简单的纹理映射:漫反射颜色贴图

步骤

  • 对每一个光栅化后屏幕的采样点(x, y),一般是像素点中心纹理映射
  • (u,v)表示纹理坐标,查找屏幕上这个点(x, y)对应的纹理坐标
  • texcolor = texture.sample(u, v)
  • 设置采样点颜色为纹理颜色,通常是漫反射系数

纹理太小

对于纹理太小的情况

双线性插值:屏幕上的像素点映射到纹理坐标上不是整数(不是中心)

  • 周围4个样本的纹理值,设周围4个点到该点的水平,垂直距离为(s, t)

QQ截图20220830005335

线性插值

已知,从强度加上到的差之间与x相乘

水平上下方向的线性插值

双线性插值(水平+垂直):可以实现平滑过度


纹理太大

纹理太大出现采样不够,出现摩尔纹锯齿等走样情况

  • 超采样:更高质量,更高消耗,信号频率在一个像素内过大,需要更高的采样频率

尝试不采样,计算一个范围内的平均值

例如在一个场景中有前景和背景,前景物体大,一个像素所占的纹理少;背景信息多,所占纹理更多

  • Mipmap

​ 一张图生成一系列图,分辨率依次降低,类似 U-Net 先下降分辨率提取不同分辨率的特征,实际上不同的特征就是不同的纹理

QQ截图20220830014857

​ 通过自己的中心(蓝点)和邻居的中心(红点)映射到纹理空间上位置的与屏幕空间位置之间的差异做插值

QQ截图20220830015122

​ 计算第 D 层的 Mipmap

  • 三线性插值

    在双线性插值的基础上层与层之间做插值,处理层与层之间的渐变,不同深度下场景渐变

    QQ截图20220830015918

  • 个性异向过滤具有更好的效果


纹理应用

  • 环境映射(贴图)

  • 环境光

  • 球环境映射

    • 立方体映射:球体信息记录在立方体上

      QQ截图20220830110748

纹理会影响着色:纹理并不只表示颜色

  • 存储高度和法线
  • 法线变化,着色变化;相对高度引起法线差异
  • 虚假的几何信息
  • 凹凸贴图(法线贴图—任一像素的法线扰动)
    • 每像素的法线扰动(只影响shading)
    • 对纹理定义的每个纹理“高度偏移”
    • 高度变化后的法线如何计算?p点坐标为(u,v)
      • 2D平面的情况
        • 先固定切线,通过切线定义法线,设原始切线
        • p处的导数是
        • 扰动法线为

QQ截图20220830115303

  • 3D平面的情况
    • 原始表面法线
    • p处的导数
    • 扰动法线为
    • 且这是在本地坐标的情况
  • 位移映射(位移贴图)
    • 在凹凸贴图上使用相同的纹理
    • 实际上是移动顶点

QQ截图20220830120139

  • 3D噪声生成+实体建模
  • 提供预先计算好的Shading
  • 3D纹理和体积渲染