图形学3D渲染管线学习

图形学3D渲染管线

DX和OpenGL左右手坐标系不同,会有一些差距,得出的矩阵会不一样;

OpenGL的投影平面不是视景体的近截面;

顶点(vertexs)

顶点坐标,颜色,法线,纹理坐标(UV),连线索引;

图元(primitives)

几何顶点被组合为图元(点,线段或多边形),图元装配;

片元(fragments)

图元被分几步转换为片元:图元被适当的裁剪,颜色和纹理数据也相应作出必要的调整,相关的坐标被转换为窗口坐标。最后,光栅化将裁剪好的图元转换为片元;

一、顶点数据(Vertex)

顶点坐标,颜色,法线,纹理坐标(UV),连线索引(三角形连线顺序-左右手坐标系顺序 不同);

顶点数据在流水管线中以图元处理:

点(GL_POINTS)、线(GL_LINES)、线条(GL_LINE_STRIP)、三角面(GL_TRIANGLES);

四边形顶点信息:顶点位置、颜色、UV、四个顶点顺序;

索引可以避免共享顶点间数据的多余——顶点缓存对象(Vertex Buffer Object,VBO)

二、顶点着色器(Vertex Shader)

1.矩阵变换

通过矩阵变换将世界坐标系下的顶点变换到视口坐标系下显示;

齐次变换:将一个原本是n维的向量用一个n+1维向量来表示(是不是很像投影);

互为逆矩阵:相乘结果为1;

转置矩阵:行列互换;

正交矩阵:行列向量互相垂直,正交矩阵的逆矩阵就是他的转置矩阵;

1)模型矩阵到世界矩阵

Local Coordinate——World Coordinate;

顶点在世界坐标系下可以进行平移(Translation),旋转(Rotation),缩放(Scaling)操作;

按顺序矩阵连乘得到世界矩阵下顶点的坐标;

如果进行缩放操作要考虑法线变换(非均匀缩放-逆矩阵的转置矩阵获得变换法线-切线空间);

OpenGL Normal Vector Transformation

2)世界矩阵到摄像机矩阵

World Coordinate——View Coordinate;

摄像机矩阵也叫观察矩阵;

摄像机的一些参数:

EyePosition:摄像机位置

FocusPosition:观察目标点(at)

UpDirection:摄像机上方向(不是Y,相对世界)

Near:近截面、Far:远截面

FOV(vertical field of view):垂直视场角

Aspect Ratio :屏幕纵横比

以上参数定义了视景体(台体);

顶点坐标转换到摄像机矩阵逆向思维,理解为将当前顶点平移摄像机当前位置向量的反向量;

image-20211208234540797

摄像机*摄像机变换矩阵肯定会得到一个单位矩阵(坐标系都是单位矩阵);

摄像机当前的x,y,z轴组成的矩阵和摄像机变换矩阵互为逆矩阵;

image-20211209000201960

通过摄像机参数求得摄像机的xyz轴向量;

image-20211208234204150

由于正交矩阵的逆矩阵就是他的转置矩阵,再添加单位1;

image-20211209000609321

将摄像机变换矩阵和平移矩阵相乘得到摄像机矩阵;

image-20211209000720748

以上为左手坐标系下的摄像机矩阵;

3)摄像机矩阵到投影矩阵

View Coordinate——Projection Coordinate;

正交投影和透视投影;

在Dx中近截面就是投影平面;

正交投影每个坐标点只是丢弃了z坐标,就不推导了;

透视投影:通过相似三角形求出视景体内顶点x,y坐标在投影平面的相对位置,保留z坐标;

image-20211209002653905

D3DXMATRIX matProject;
// 这个函数是设置正交投影矩阵
D3DXMatrixOrthoLH(&matProject, width, height, Znear, Zfar);
pD3dDevice->SetTransform(D3DTS_PROJECTION, &matProject);

OpenGL投影矩阵

从右到左将以上矩阵连乘获得局部空间到裁剪空间的变换矩阵:

V(clip) = M(projection)*M(view)*M(model)*V(local)

2.着色计算

Flat Shading——一个顶点代表三角形颜色,默认索引中第一个顶点颜色;

Gouraud Shading——逐顶点着色,只计算三个顶点关照信息,在光栅化阶段做插值得到各个片段光照信息,缺点:插值导致高光非线性;

Phong Shading——冯氏光照,逐片段着色(像素)法线插值出各个片元的法线信息,在片元着色器中使用法线、UV、位置等计算光照;

三、曲面细分着色器(Tessellation Shader)

可选阶段;

外壳着色器(Hull Shader)、镶嵌器(Tessellation)、域着色器(Domain Shader)

不创建高模,根据与摄像机距离使用镶嵌器细化模型,添加三角面(LOD);

Image [20]

四、几何着色器(Geometry Shader)

可选阶段;

以图元作为输入数据,可以创建销毁几何图元;

法线可视化——毛发效果;

根据距离摄像机远景动态调整多边形边数实现LOD效果;

公告牌技术(BillBoards)——3D图片替代模型,总是朝向摄像机;

五、图元组装(Primitive Assembly)

1.裁剪(Clipping)

Image [22]

1)线段裁剪

点面关系,线面关系,通过向量在面上的投影,以及点是否在矩形内;

八方位裁剪法;

2)三角形裁剪

平顶三角和平底三角形,判断出界平移顶点;

裁剪算法有Cohen-Sutherland算法Liang-Barsky算法Sutherland-Hodgman多边形裁剪算法

2.背面剔除(Back-Face Culling)

只和三角形与摄像机的距离有关,不依赖摄像机朝向;

根据三角形顶点顺序,使用左右手坐标系,叉乘求出法向量;

顶点顺时针法向量朝外剔除,逆时针朝内剔除;

3屏幕映射 (Screen Mapping)

1)透视剔除(Perspective Division)

将视景体空间顶点,除以w分量获得标准设备空间,-1到1的立方体空间(CVV-标准视体);

w分量保留的z坐标的信息,正交投影w分量为1,w分量也就是z值代表了深度信息;

OpenGL变换OpenGL投影矩阵

2)视口变换(View Transform)

通过执行透射除法获得归一化的设备坐标NDC(Normalized Device Coordinate);

z值在视口变换过程中被映射,OpenGL映射到[-1,1],DirectX[0,1];

NDC坐标映射到窗口坐标要通过平移和缩放过程(相机的宽高比);

视口矩阵:xy为窗口相对于屏幕的位置;

image-20211209013022842

六、光栅化(Rasterization)

将图元离散为片元的过程;

图元覆盖一个片元(Overlap)——点采样(Point Sampling),像素中间点在三角形内;

超级采样和多重采样技术,涉及到抗锯齿;

1.三角形组装(Triangle Setup)

三角形组装会对顶点的输入数据(比如,颜色、法线、纹理坐标)进行插值,得到各个片段对应的数据值,为后面的片元着色器提供片元数据;

2.三角形遍历(Triangle Traversal)

遍历这些三角图元覆盖了哪些片元的采样点,随后得到该图元所对应的片元;

顶点数据插值获得片元的颜色,法线,UV,深度等信息,用于投射正交插值获得正确的透视颜色纹理等信息;

3.线段的扫描转化

  • Bresenham光栅化算法

  • 数字微分画线算法DDA (Digital Differential Analyzer)

    直线公式,斜率小于1,y增量为整数,大于1,x增量为整数;

4.多边形填充

求多边形的几何重心,以重心为顶点,向多变的各个顶点连线,分割为三角形;

重心颜色:所有三角形顶点颜色的平均值;

计算每个三角形的重心(三个顶点的x,y分别相加除以3);

将三角形重心坐标和面积关联起来;

sx += curx*curs;

sy += cury*curs;

重心的坐标为:tatal.s总面积

center.x = sx/total.s;

center.y = sy/total.s;

七、片元着色器(Fregment Shader)

色彩混合:颜色的RGB值相加,用于混合所有光照效果

色彩调制:色彩乘法,RGB值分别相乘,相当于给RGB值都乘以一个系数,将颜色变亮或变暗;

1.Phong光照模型

1)环境光

用来模拟全局光照效果;在物体光照信息基础上叠加较小的光照常量;

2)漫反射

光线进入物体内部重新散射出来,看做均匀分部所以和观察者位置无关;只影响亮度;

  • 兰伯特余弦定律(Lambert Consine Law)

    取决表面法线和光线的夹角,夹角越大分量越小,漫反射越小;90度漫反射几乎为0;

    image-20211209095512523

  • 半兰伯特模型(Half Lambert)

    v社做半条命时提出,改变物体暗区域的光照信息;

    将漫反射系数从[0,1]改为[0.5,1],提高暗部的亮度信息;

    //法线和光线的夹角,
    float diflight = dot(s.Normal,lightDir);
    float hLambert = difLight * 0.5 + 0.5;
    

3)镜面反射高光

和观察者的位置有关,不同角度观察结果不同;

  • Phong光照模型

光线反射向量和观察向量的夹角;

高光指数:e

image-20211209102210717

在夹角大于90度的情况,会造成高光丢失现象,光线会不连续,有明显的明暗分界线;

  • Blinn-Phong光照模型

光线向量和观察向量的中间位置(半角向量)和法线的夹角;

在任何角度观察,夹角都不会大于90度;不会出现高光不连续现象;

img

  • 材质

    struct Material
    {
    	vector3 ambient;	//环境光
    	vector3 diffuse;	//漫反射
    	vector3 Specular;	//镜面反射 
    	vector3 emissive;	//自发光
    	float e;			//镜面反射系数
    }
    

    物体最终颜色 = 环境光结果*环境光反射系数 + 漫反射结果*漫反射系数+镜面反射结果(计算了高光指数)*镜面反射系数+材质自发光颜色*自放光系数;

2.纹理贴图 (Textures)

纹理映射,将图像信息映射到三角形网格;

凹凸贴图(bump mapping)、法线贴图(normal mapping)、高度纹理(height mapping)、视差贴图(parallax mapping)、位移贴图(displacement mapping)、立方体贴图(cubemap)、阴影贴图(shadowmap);

  • UV坐标的寻址方式

归一化到[0,1]之间,像素大小为2的次方,方便计算处理;

寻址方式也叫平铺方式:重复寻址(repeat)、边缘钳制寻址(clamp)和镜像寻址(mirror);

uv超出0-1,该如何寻址(就是图片的平铺,边缘像素扩展,镜像);

  • 纹理采样方式

纹理像素和图元像素不是一一对应,要用到纹理的滤波方式;

点过滤(point)、线性过滤(linear)、最近领点过滤(nearest neighbor point)和双线性过滤(bilinear),Unity的Trilinear滤波的技术;

Image [58]

  • 法线贴图 (Normal Mapping)

物体空间(object space)和切线空间(tangent space)

根据物体空间计算的法线,在物体旋转移动后回得到错误的光照信息;

切线空间:相对于顶点坐标存储计算;

顶点本身法线为N轴,模型给定定义一条和该顶点相切的切线T轴,N和T叉乘得到B轴;

法线(N)、切线(T)和副切线(B) ,三个轴组成切线空间;

法线贴图就是在切线空间中记录了法线扰动的方向;

以顶点法线N为z轴坐标系,扰动后的法线z轴也总是朝向(0,0,1),所以得得到法线贴图总是淡蓝色(RGB);

法线纹理最终值需要做个映射,由于维度向量取值[-1,1],纹理通道范围在[0,1],最终记录结果为:(normal+1)/2;

切线空间坐标系到世界坐标系的转换矩阵

物体移动旋转时,法线乘以这个矩阵就可以得到改变后的法线;

Image [61]

因为z总是朝向(0,0,1)纹理就可以直接记录xy——纹理压缩:DXT1,DXT5;

3.锯齿和抗锯齿 (Aliasing and Anti-aliasing)

  • 超级采样抗锯齿 (Super-Sampling Anti-aliasing——SSAA)

    将原图分辨率放大一倍,再采样;光栅化和片元着色都是原来的4倍,渲染缓存也是4倍;

  • 多重采样抗锯齿 (Multi-Sampling Anti-aliasing——MSAA)

    每个片元有多个采样点,计算采样点的覆盖率(Coverage),光栅化阶段计算采样点覆盖率,在片元着色器计算颜色值后乘以这个覆盖率;

    MSAA和延迟渲染(deferred render)不兼容(延迟渲染需要Geometry 和Lighting两个Pass,lighting阶段无法通过GBuffer获得片元覆盖率);

4.阴影 (Shadows)

光照烘焙获得Shadowmap;先光照烘焙获得深度信息,再通过阴影贴图判断那些片元落在阴影中;

Shadowmap的精度会导致阴影粉刺,需要便宜深度来消除粉刺现象;

阴影锯齿通过百分比渐进过滤(PCF)实现软化阴影(softshadow);

八、测试和混合(Tests & Blending)

1.裁切测试 (Scissor Test)

裁切测试可以避免当视口比屏幕窗口小时造成的渲染浪费问题;一般默认不开启,

2.Alpha测试 (Alpha Test)

​ 片段着色器中丢弃alpha值小于0.1的片段;

  • Early-Z Culling

    硬件厂商用来加速渲染的手段;在片元着色之前提出被遮挡的片元;

    但是生效要求只能通过光栅化插值得到深度,不能再片元着色器阶段去修改深度缓冲;

3.模板测试 (Stencil Test)

模板测试有一个对应的缓存, 即模板缓存(Stencil Buffer), 用于记录所有像素的模板值, 默认值为0;

片元携带的参考值和模板缓存中的值比较,满足比较函数,调用操作函数更新模板值;

  • 模板值: 模板缓存中已经存在的值

  • 参考值: 在渲染该物体前, 由程序设置的指定值

  • 比较函数: 决定如何将两个值作比较的函数

  • 操作函数: 定义通过或者不通过测试后对模板值的更新操作

Unity中模板测试不可编程,可配置管线阶段;

Stencil
{
    Ref refValue		//参考值
    Comp always			//比较函数	
    Pass keep			//模板测试和深度测试都通过后的操作
    Fail keep			//模板测试和深度测试都未通过后的操作
    ZFail keep			//模板测试通过而深度测试未通过后的操作
    WriteMask 255		//使用参考值更新模板值之前, 在模板值与掩码按位与之后再更新		255代表不做处理
    ReadMask 255		//读取模板值后, 将其与掩码按位与之后再与参考值作比较		  255代表不做处理
}

/*
UnityEngine.Rendering.CompareFunction比较函数枚举
0(Disabled): 关闭模板测试, 等同于全部通过测试, 经过测试发现不是真的关闭.
1(Never): 全部不能通过测试
2(Less): 待比较的值小于缓存中的值时通过测试
3(Equal): 待比较的值等于缓存中的值时通过测试
4(LessEqual): 待比较的值小于等于缓存中的值时通过测试
5(Greater): 待比较的值大于缓存中的值时通过测试
6(NotEqual): 待比较的值不等于缓存中的值时通过测试
7(GreaterEqual): 待比较的值大于等于缓存中的值时通过测试
8(Always): 全部通过测试, 默认值
*/

/*
UnityEngine.Rendering.StencilOp操作函数枚举
0(Keep): 保持模板缓存中的值不变
1(Zero): 将模板缓存中的值置为0
2(Replace): 使用参考值替换模板缓存中的值
3(IncrementSaturate): 使模板缓冲区值增大, 最大限制为可表示的最大无符号值
4(DecrementSaturate): 使模板缓冲区值减小, 最小限制为0
5(Invert): 对模板缓冲区值按位求反
6(IncrementWrap): 与IncrementSaturate类似, 只是达到最大后继续增大将重新设置为 0
7(DecrementWrap): 与DecrementSaturate类似, 只是达到最小后继续减小将重新设置为可表示的最大无符号值
*/

4.深度测试 (Depth Test)

比较当前片段的深度值是否比深度缓冲中预设的值小(默认比较方式),如果是更新深度缓冲和颜色缓冲;否则丢弃片段不更新缓冲区的值;

Early-Z Culling也是利用Z-Buffer的技术来进行深度测试的,只不过该测试是在片段着色器之前进行;

深度测试是可配置的阶段——ZTest和ZWrite

  • Z-Fighting

深度缓冲精度不够,深度值相近的片元会造成重叠模糊问题:

Image [83]

解决:物体不要靠太近;使用高精度的深度缓冲;

  • 隐藏面消除 (Hidden Surface Removal, HSR)

前面的图元组装裁剪,背面拣选,和Z-Buff都属于HSR,裁剪针对图元,Z-Buffer针对像素点;目的都是为了减少到达片元着色器的片元个数,提高渲染性能;

1)视椎体剔除 (Viewing-Frustum Culling)

利用物体包围盒来做交差检测,常见的包围盒有轴对齐包围盒(AABB)和有向包围盒(OBB)两种;

需要是由高效数据结构来提升碰撞检测的效率——八叉树(OcTree)、二分空间划分(Binary Space Partitioning)、四叉树(Quad Tree)、场景图(Scene Graphs)、kd树(K-Dimensional Tree)和层次包围(Bounding Volume Hierarchies);

计算:

点法式判断三维空间点和面的关系,裁剪掉不在视景体内的包围盒顶点;

求视景体六个面:

通过投影矩阵,求出投影和摄像机的逆矩阵,反向求出视景体对应面;

求逆矩阵——行列式,代数余子式,伴随矩阵,行列式的值,有固定公式;

近截面裁剪:

三角形和面的关系,用向量表示三角形和面;两条共起点的向量可以表示三角形;分解成线和面的关系;

三角形和近截面的关系:

1个角在视景体内——偏移另外两个顶点到视景体平面上;

2个角在视景体内——三角形拆分为2个三角形,注意三角形顶点连线顺序;

2)入口剔除 (Portal Culling)

将室内的门或者窗户看做视椎体来进行裁剪;实现”笼中窥梦”的效果;

3)遮挡剔除 (Occlusion Culling)

通过离线烘焙的犯法来预先计算出潜在可视集合(Potentially Visible Set,PVS);

PVS记录了每个地形块(Tiles)可能看到的物体的集合,用于运行时查找计算;

场景划分为小地形块,每个块上随机取N个采样点;

从采样点发射射线获取场景中和射线相交的物体,记录物体ID;

根据摄像机位置,使用采样点几率的物体id列表进行渲染;

采样精度和烘焙效率问题;

6.Alpha混合 (Alpha Blending)

经过前面所有的测试才能进入Alpha混合阶段,一个测试过不了都到不了Alpha混合;

Alpha混合实现物体半透明效果,渲染顺利非常重要,可能需要手动改Queue(渲染队列)的值;

  • 渲染顺序:

先渲染不透明物体,从前往后渲染;——不透明物体渲染前先进行深度检测,先后近的物体,远处物体通不过深度检测,就不用进行深度写入操作;

再渲染半透明物体,从后往前渲染;——半透明物体渲染需要知道前一层的颜色信息进行混合,先渲染远处物体,在画近处物体时可以通过颜色缓存获取前一层颜色信息;

画家算法:先画物体会被后画物体覆盖;

Unity ShaderLab中渲染队列设置;

Queue 其他预定义的值为:Background = 1000 , AlphaTest = 2450,Overlay = 4000。默认值是Geometry =2000;

ShaderLab默认开启深度测试和深度写入;

//将本 Shader 计算出的颜色值(源颜色值,即蓝色) * 源Alpha值(0.6) + 目标颜色值(可以理解为背景色) * (1-0.6)
Blend SrcAlpha OneMinusSrcAlpha
// Transparent (透明) = 3000,值越小越先渲染,而后渲染( Queue 值大)的物体会覆盖先渲染的物体    
Tags {"Queue" = "Transparent"}

ZTest LEqual 	        //小于等于
ZWrite On 		//打开
//ZTest 可取值为:Greater , GEqual , Less , LEqual , Equal , NotEqual , Always , Never , Off,默认是 LEqual,ZTest Off 等同于 ZTest Always
//ZWrite 可取值为:On , Off,默认是 On
  • 顺序无关半透明算法(Order-independent transparency,OIT)

深度剥离(Depth Peeling)

双向剥离(Dual Depth Peeling)——两个方向剥离,一个从前往后,一个从后往前,两个方向效率提高;

Tags: