VFShader总结--渲染流程

要谈Unity shader我们首先要了解下整个的渲染流水线
其实我们现在写的unityshader并等于shader只是整个渲染过程的一部分
渲染可以概念性的分为3个部分
应用阶段,几何阶段,光栅化阶段
类似这个图
VFShader总结--渲染流程_第1张图片
每个阶段又可以分出很多小的阶段
其中 应用阶段是CPU 几何阶段和光栅化阶段载体则是GPU

一.应用阶段 顾名思义 由我们开发者控制

它主要作用有 1.将渲染需要的数据加载至显存中, 2.设置渲染状态, 3.以及调用draw call

1.将渲染需要的数据加载至显存中;

所需要渲染的数据都有哪些?

有场景的数据,比如摄像机位置,视锥体,场景中包含哪些模型,哪些光源等等, 网格,纹理,顶点的所携带的信息(颜色,uv坐标,法线等);

如何加载至显存?

所有渲染所需的数据都需要从硬盘( Hard Disk Drive, HDD )中加载到系统内存( Random Access Memory, RAM )中。然后,网格和纹理等数据又被加载到显卡上的存储空间一一显存( Video Random Access Memory, VRAM)中。这是因为,显卡对于显存的访问速度更快,而且大多数显卡对于RAM 没有直接的访问权利

2.设置渲染状态

什么是渲染状态呢? 一个通俗的解释就是,这些状态定义了场景中的网格是怎样被渲染的。
使用哪个顶点着色器(Vertex Shader) /片元着色器(Fragment Shader)、光源属性、材质等
使用哪个shader,光照的模式,材质球这些
比如一个cube 一个sphere两个都受光源一样,材质球一样渲染状态就一样的.

3.指导GPU如何工作,即调用Drawcall

发起方就是CPU,接收方是CPU,实现两者之间的通信.
CPU作用:发送一个命令,这个命令仅仅指向一个显存中的被渲染的图元列表,告诉GPU需要渲染这个.不需要包含材质信息,在设置渲染状态已经做了,
GPU作用:接到命令后根据渲染状态和顶点数据进行计算,这个计算过程就是重要的GPU流水线

几何阶段和光栅化阶段就是在GPU流水线上完成的;

二.几何阶段 光栅化阶段

VFShader总结--渲染流程_第2张图片

这里就只讲uniyshader包括的

几何阶段

顶点着色器,裁剪,屏幕映射

  • 顶点着色器(完全可编程)
    作用:把接收的顶点数据进行一系列操作传给下一阶段
    在vfshader中主要这一部分

    #pragma vertex vert  //定义vert函数就是代表顶点着色器
    struct vertexInput{   
    	
    };
    struct vertexOutput{
    
    };
    vertexOutput vert(vertexInput v){
    	vertexOutput o;
    	/*----
    	一系列顶点上的操作
    	顶点动画,顶点空间转换,顶点着色等
    	----*/
    	return o;
    }
    

    首先看第一个结构体

    struct vertexInput{   	
    	float4 vertex:POSITION;
    };
    

    它的作用就是把之前应用阶段传过来的顶点数据存进去
    我这里先只列举其中一个数据:vertex(顶点位置)
    后面跟着的一个大写的POSITION,称为语义,作用就是告诉GPU把顶点的位置数据用这个参数接收
    再看这个函数

    vertexOutput vert(vertexInput v){
    	vertexOutput o;
    	o.pos=UNITYObjectToClipPos(v.vertex);
    	//o.pos=mul(UNITY_MATRIX_MVP,v.vertex);
    	return o;
    }
    

    主要必须要有中间这句代码
    o.pos=UNITYObjectToClipPos(v.vertex);
    它的作用就是顶点坐标转换到齐次裁剪坐标系下,接着通常再由硬件做透视除法后,最终得到归一化的设备坐标(Normalized Device Coordinates , NDC ) 然后存入第二个结构体
    VFShader总结--渲染流程_第3张图片
    图2.8 给出的坐标范围是OpenGL 同时也是Unity 使用的NDC, 它的z 分量范围在[-1, 1 ]之间,而在DirectX 中, NDC 的z 分量范围是[0, 1];
    最后看第二个结构体

    struct vertexInput{   	
    	float4 pos:SV_POSITION;
    };
    

    它的作用就是把顶点着色器一系列操作后的数据存起来,经过光栅化阶段提供给之后的片元着色器

  • 裁剪 (可配置)
    这一阶段的目的是将那些不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片。
    VFShader总结--渲染流程_第4张图片
    以及通过指令控制裁剪三角图元的正面还是背面

    Cull Off|Back(default)|Front
    
  • 屏幕映射(不可配置和编程)
    负责把每个图元的坐标转换到屏幕坐标系中,屏幕坐标系是一个二维坐标系,它和我们用于显示画面的分辨率有很大关系
    VFShader总结--渲染流程_第5张图片
    注意:屏幕映射不会对输入的z坐标做任何处理,它会保留并传到下一阶段
    另外一个需要注意的点:
    屏幕坐标系在OpenGL 和DirectX 之间的差异问题。OpenGL把屏幕的左下角当成最小的窗口坐标值,而DirectX 则定义了屏幕的左上角为最小的窗口坐标值,所有如果发现得到的图像是倒转的,那么很有可能就是这个原因造成的.
    接下来就会进入光栅化阶段.

光栅化阶段

光栅化阶段有两个最重要的目标: 计算每个图元覆盖了哪些像素,以及为这些像素计算它们的颜色
三角形设置,三角形遍历,片元着色器,逐片元操作

  • 三角形设置 (不可编程和配置)
    作用计算光栅化一个三角网格所需的信息.具体来说, 上一个阶段输出的都是三角网格的顶点(深度,法线,位置,颜色等),即我们得到的是三角网格每条边的两个端点。但如果要得到整个三角网格对像素的覆盖情况, 我们就必须计算每条边上的像素坐标。为了能够计算边界像素的坐标信息,我们就需要得到三角形边界的表示方式。这样一个计算三角网格表示数据的过程就叫做三角形设置。它的输出是为了给下一个阶段做准备
  • 三角形遍历 (不可编程和配置)
    作用:检查每个像素是否被一盒三角网格覆盖,如果覆盖就生成一个片元,这个过程称为三角形遍历
    然后使用三角网格3个顶点的顶点信息对整个覆盖区域像素插值,输出一个片元的序列.
    VFShader总结--渲染流程_第6张图片
    注意:一个片元并不是真正意义上的像素,而是包含了很多状态的集合, 这些状态用于计算每个像素的最终颜色。这些状态包括了(但不限于)它的屏幕坐标、深度信息,以及其他从几何阶段输出的顶点信息, 例如法线、纹理坐标等。
  • 片元着色器 (可编程)
    #pragma fragment frag   //定义frag函数就是代表片元着色器
    fixed4 frag(vertexOutput i):SV_Target{
    	//fixed4 finalColor=tex2D(_MainTex,i.uv);
    	return fixed4(1,1,1,1);
    }
    
    片元着色器的输入是上一个阶段对顶点信息插值得到的结果, 更具体来说,是根据那些从顶点着色器中输出的数据插值得到的。而它的输出是一个或者多个颜色值。技术之一就是纹理采样。为了在片元着色器中进行纹理采样,我们通常会在顶点着色器阶段输出每个顶点对应的纹理坐标, 然后经过光栅化阶段对三角网格的3 个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标了。(只能影响单个片元)
  • 逐片元操作
    作用主要有两个:
    1,决定每个片元的可见性.也就是对其进行测试(可配置)
    深度测试,模板测试等 (ZTest ZWrite Stenil)
    这里先给出一个简单的图示,测试的详细内容在这看到深度测试,模板测试.
    VFShader总结--渲染流程_第7张图片
    模板测试通常用于限制渲染的区域.模板测试还有一些更高级的用法, 如渲染阴影、轮廓渲染等
    透明效果和深度测试以及深度写入的关系非常密切.
    2.如果通过测试,就把这个片元的颜色值和储存在颜色缓冲区的颜色合并或混合 (Blend)
    这是一个混合的简单图示,详细的混合可以在这里看到 Blend.
    VFShader总结--渲染流程_第8张图片
    虽然逻辑上这些测试都是在片元着色器之后执行,但实际上这些测试在大多数GPU上都是在执行片元着色器之前执行的, 因为如果想充分提高性能,尽可能早地知道哪些片元是会被舍弃的,对于这些片元就不需要再使用片元着色器来计算它们的颜色是必需的.
    在unity的渲染流水线上,它的深度测试就是在片元着色器之前,这种深度测试提前执行就称为Early-Z技术.
    存在的问题:片元着色器中有一种透明度测试的方法AlphaTest(消耗很大的原因),如果一个片元没有通过透明度测试,会手动调用API(clip函数)将其舍弃掉,就导致GPU无法提前执行各种测试.
    解决方案:现代的GPU 会判断片元着色器中的操作是否和提前测试发生冲突,如果有冲突,就会禁用提前测试。但是,这样也会造成性能上的下降,因为有更多片元需要被处理了。这也是透明度测试会导致性能下降的原因.
            最后:为了避免我们看到那些正在进行光栅化的图元, GPU 会使用双重缓冲( Double Buffering ) 的策略。这意味着,对场景的渲染是在幕后发生的,即在后置缓冲(Back Buffer ) 中。一旦场景已经被渲染到了后置缓冲中, GPU 就会交换后置缓冲区和l前置缓冲(Front Buffer ) 中的内容,而前置缓冲区是之前显示在屏幕上的图像。由此,保证了我们看到的图像总是连续的。

你可能感兴趣的:(Shader的学习总结,Shader)