这个基本上是昨天看完今天复习的,理论篇。
1、渲染流水线
概念性的三个阶段:应用阶段->(输出渲染图元)->几何阶段->(输出屏幕空间的顶点信息)->光栅化阶段
-应用阶段
CPU负责实现,开发者有绝对掌控权。
三个主要任务:
-准备场景数据(eg.摄像机位置、视锥体、场景中哪些模型、哪些光源,etc)
-culling粗粒度剔除
-设置好模型的渲染状态(材质、纹理、shader,etc)
最重要的输出的几何信息:渲染图元
-几何阶段
通常GPU实现,决定绘制图元是什么、怎样绘制、哪里绘制
负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作
一个重要任务:
-顶点坐标变换到屏幕空间
输出:屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色
-光栅化阶段
通常GPU实现,利用上阶段数据产生屏幕像素,渲染出最终图像
决定哪些像素被绘制在屏幕上,对上一阶段的逐顶点数据进行差值,进行逐像素处理
两个重要目标:
-计算每个图元覆盖了哪些像素
-位像素计算它们的颜色
2、CPU和GPU间的通讯
主要涉及到三个阶段:
-把数据加载到显存中
-设置渲染状态
-调用Draw Call
2.1 数据加载到显存
数据流向:硬盘(HDD)->系统内存(RAM)->显存(VRAM)【大多显卡对RAM无直接访问权】
需要通过CPU指导GPU如何进行渲染工作
2.2 设置渲染状态
网格如何进行渲染(eg. 使用哪个顶点/片源着色器、光源属性、材质,etc)
2.3 调用Draw Call
指向需要被渲染的图元列表,不包含任何材质信息
3、GPU流水线
3.1 流水线实现:
顶点数据->顶点着色器->(曲面细分着色器)->(几何着色器)->裁剪->屏幕映射
->三角形设置->三角形遍历->片元着色器->逐片元操作->屏幕图像
完全可编程控制 可配置但不可编程 无任何控制权
---------------------------------------------------------------------------------------------------
顶点着色器:完全可编程。实现顶点空间变换、顶点着色等
曲面细分着色器:可选,细分图元
几何着色器:可选,执行逐图元操作/产生更多图元
裁剪:将不在视野内顶点剪裁,剔除某些三角图源面片
屏幕映射:每个图元坐标转换到屏幕坐标系
---------------------------------------------------------------------------------------------------
三角形设置、三角形遍历:固定函数
偏远着色器:完全可编程,实现逐片着色操作
逐片元操作:eg. 修改颜色、深度缓冲、混合
---------------------------------------------------------------------------------------------------
-顶点着色器
处理单位:顶点。输入进来的每个顶点都调用一次vertex shader。
无法知道顶点间的关系(eg.是否属于同一三角网格),但由于独立GPU处理速度很快
完成的工作任务:
-坐标变换
-逐顶点光照(计算顶点颜色?)
-输出后续阶段所需数据
-坐标变换
改变顶点位置-顶点动画
最基本的工作:把顶点坐标从模型空间转换到齐次裁剪空间
如:o.pos = mul(UNITY_MVP, v.position);[注:MVP,MV应该是指Model View Matrix, P应该是指Projection Matrix,望指正]
(顶点坐标转换到齐次裁剪坐标系,硬件做透视除法,最终得到归一化设备坐标)
(PS:OpenGL也是Unity用的NDC,z分量[-1,1],DirectX在[0,1])
输出路径经光栅化后交给片元着色器进行处理,还可以把数据发送给曲面细分着色器或几何着色器
-裁剪
剪裁不在摄像机视野范围的物体
图元与视野的三种关系:完全在视野内、部分在视野内、完全在视野外
可以自定义裁剪操作进行配置
-屏幕映射
把图元的x,y坐标转换到屏幕坐标系下,决定了这个顶点对应屏幕上哪个像素以及距离这个像素有多远。对输入z的坐标不作任何处理。
屏幕坐标系与z坐标构成了窗口坐标系,一起传入到光栅化阶段
【PS:OpenGL左下角是最小的窗口坐标值,DirectX是左上角】
---------------------------------------------------------------------------------------------------
-三角形设置
光栅阶段,上一阶段输出的信息时屏幕坐标系下的顶点位置+深度值(z)、法线方向、视角方向
是一个计算三角网格表示数据的过程
上一阶段输出的是三角网格的顶点,通过两边顶点计算每条边上的像素坐标,得到三角形边界的表示方法。
-三角形遍历
检查每个像素是否被三角网格覆盖,如果覆盖就生成片元
找到哪些像素被三角网格覆盖的过程,也被叫做扫描变换
输出片元序列
片元fragment:状态集合,包括不限于屏幕坐标、深度信息、从几何阶段输出的顶点信息(法线、纹理)
-片元着色器
前面的光栅化阶段不会影响到每个pixel的眼素质,而是产生数据信息来表述一个三角网格如何覆盖每个像素。
输入时上一阶段对顶点信息(顶点着色器输出)插值得到的结果,输出一个或多个颜色值
渲染技术:纹理采样(在顶点着色器阶段输出每个顶点对应的纹理坐标,通过对三个顶点的纹理坐标插值得到覆盖的片元纹理)
局限:影响单个片元,不可以将任何结果(导数信息除外)发给邻居
根据上一步茶之后的片源信息,计算该片元的输出颜色
-逐片元操作
Merger的作用,合并,即输出合并阶段
两个主要任务:
-决定每个片元的可见性,涉及到深度测试、模板测试等
-片元通过所有测试,就把偏远颜色值和已存储在颜色缓冲区中的颜色进行合并/混合
-片元可见性问题
-模板测试
和颜色缓冲、深度缓冲类似,开启测试GPU读取掩码和模板缓冲区中该片元位置的模板值,将该值与读取的参考值进行比较。比较函数可以开发者指定(如小于或大于某值舍弃该片元)。不论是否通过测试,均可根据测试结果修改模板缓冲区(如失败时模板缓冲区保持不变,同时将模板缓冲区对应位置值+1)
通常用于限制渲染区域、渲染阴影、轮廓渲染等
-深度测试
深度值大于等于当前深度缓冲区的值则舍弃它,通常只显示出离摄像机最近的物体,而那些被其他物体遮挡的就不需要出现在屏幕上。与模板测试不同的是若没有通过深度测试就没权利更改深度缓冲区中的值。若通过了测试,则指定是否要用这个片元的深度值覆盖掉原有的深度值,通过开启、关闭深度写入做到。(透明效果与深度测试和深度写入关系密切)
-合并/混合
通过渲染时,颜色缓冲中往往已经有上次渲染之后的颜色结果,我们使用这次渲染得到的颜色完全覆盖之前的结果还是进行其他处理
对于不透明物体,关闭blend操作,则片元计算得到的颜色值直接覆盖;而半透明就需要混合操作。
开启混合功能,则GPU取出源颜色(片元着色器)和目标颜色(存在于颜色缓冲区的颜色值),进行混合
测试顺序不唯一,逻辑上测试是在片元着色器后进行,但GPU尽可能在执行片元着色器之前遍进行测试。但测试提前的话,检测结果可能会与片元着色器中的操作冲突。若有冲突则禁用提前测试,造成性能上下降,透明度测试导致性能下降
GPU使用双缓冲策略,场景被渲染到后置缓冲中,GPU会交换后置缓冲区和前置缓冲区中的内容,使看到图像连续
3.2 总结
unity封装了诸多功能,但导致变成自由度下降
4、总结
4.1 OpenGL和DirectX
编程接口,应用程序向接口发送渲染命令(draw call),接口依次向显卡驱动(显卡的OS)发送渲染命令,显卡驱动翻译成GPU可理解的代码。因为有驱动的存在,即支持OpenGL也支持DirectX
显卡有GPU外,还有内存即显存(VRAM)
4.2 HLSL、GLSL和CG
DirectX - HLSL,OpenGL - GLSL,NVIDIA - CG(C for Graphic)
GLSL,跨平台,编译结果取决于硬件供应商的做法
HLSL,基本是微软自身产品,微软控制着色器编译
CG,真正意义跨平台
4.3 Draw Call
CPU调用图像编程接口
-CPU如何与GPU实现并行工作
命令缓冲区,draw call是其中一种,其余有改变使用的着色器、使用不同的纹理等
-为什么draw call多了影响帧速率
类比总容量一样的数量不同的文件压缩,GPU渲染速度快鱼CPU提交命令速度
-如何减少draw call
批处理,需要在CPU内存中合并网格,更适合于静态物体
可以:避免使用大量很小网格,不可避免时考虑可否合并;避免使用过多材质
4.4 固定管线渲染
淘汰
5、Shader是什么
GPU流水线上一些可高度编程的阶段;
有一些特定类型的着色器(vertex shader & fragment shader);
依靠着色器可控制流水线中的渲染细节,如顶点着色器的顶点变换以及传递数据,片元着色器进行像素渲染