Press "Enter" to skip to content

图形学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 :屏幕纵横比

 

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

 

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

 

 

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

 

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

 

 

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

 

 

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

 

 

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

 

 

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

 

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

 

View Coordinate——Projection Coordinate;

 

正交投影和透视投影;

 

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

 

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

 

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

 

 

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);

 

 

四、几何着色器(Geometry Shader)

 

可选阶段;

 

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

 

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

 

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

 

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

 

五、图元组装(Primitive Assembly)

 

1.裁剪(Clipping)

 

 

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为窗口相对于屏幕的位置;

 

 

六、光栅化(Rasterization)

 

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

 

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

 

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

 

1.三角形组装(Triangle Setup)

 

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

 

2.三角形遍历(Triangle Traversal)

 

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

 

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

 

3.线段的扫描转化

 

Bresenham光栅化算法

 

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

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

 

 

4.多边形填充

扫描线填充算法(Scanline Filling)
重心计算多边形片元颜色

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

 

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

 

计算每个三角形的重心(三个顶点的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;

 

半兰伯特模型(Half Lambert)

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

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

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

 

3)镜面反射高光

 

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

Phong光照模型

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

 

高光指数:e

 

 

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

Blinn-Phong光照模型

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

 

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

 

 

材质

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滤波的技术;

 

法线贴图 (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;

 

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

 

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

 

 

因为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

 

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

 

 

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

隐藏面消除 (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)——两个方向剥离,一个从前往后,一个从后往前,两个方向效率提高;

Be First to Comment

发表评论

您的电子邮箱地址不会被公开。 必填项已用*标注