渲染管线

Per-Sample Processing

Posted by Bob on January 19, 2019

渲染管线

image

Scissor Test

在OpengGl中通过如下方法设置裁剪

//设置裁剪矩形的位置和大小

void glScissor (GLint x, GLint y, GLsizei width, GLsizei height);

Alpha Test

使用fragment程序时ShaderLab Alpha Test命令无效,用cg clip函数来处理。

Shader实例

树透明区域丢弃掉。

clip函数


void clip(float4 x)
{
  if (any(x < 0))
    discard;
}

image

Shader "Unlit/Clip"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_CutOff("CutOff",Range(0,1)) = 0.5
	}
	SubShader
	{
		Tags { "RenderType"="Opaque" }
		LOD 100

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;

				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				return o;
			}
			
			float _CutOff;

			fixed4 frag (v2f i) : SV_Target
			{
				fixed4 col = tex2D(_MainTex, i.uv);
				clip(col.a-_CutOff);
				return col;
			}
			ENDCG
		}
	}
}


模板测试 Stencil Test

stencil是印刷业中的版面模子,模子上抠出需要的图案,然后将模子盖在要被印刷的材质上,对抠出的洞涂颜色就可以了。

如果将屏幕上所有像素初始为0,那么stencil buffer的作用就是将某些0变为特定值1,2,3…255,每个pass之前可以决定只渲染某个特定stencil值的像素并抛弃其他像素,就像一块模板一样屏蔽所有其他非该stencil值的像素区域,只对当前stencil值的像素进行操作。

image

Stencil Test语法如下:

stencil{

    Ref referenceValue 
	
    ReadMask  readMask 
	
    WriteMask writeMask 

    Comp comparisonFunction 
	
    Pass stencilOperation 
	
    Fail stencilOperation 
	
    ZFail stencilOperation 
}

判断依据:


if(referenceValue&readMask) comparisonFunction (stencilBufferValue&readMask)

通过像素

else

抛弃像素

  • ReferenceValue:该值用来比较(当 Comp 设置为非always)并/或 用来写入到buffer(如果任何Pass,Fail,ZFail被设置来代替他)。为 0–255 整数.

  • ReadMask:从字面意思的理解就是读遮罩,readMask将和referenceValue以及stencilBufferValue进行按位与(&)操作,readMask取值范围也是0-255的整数,默认值为255,二进制位11111111,即读取的时候不对referenceValue和stencilBufferValue产生效果,读取的还是原始值。

  • WriteMask:一个8 bit的mask,值为0-255的整数,当写入buffer时使用。注意,和其他写掩码一样,它指定的模版缓冲(stencil buffer)会被写操作影响。(例如:WriteMask 0 意味着没有bit会受影响,而不是0值会被写入)。 默认值为: 255.

  • comparisonFunction:该函数用来比较reference值和当前buffer中的内容。默认为: always.

取值 含义
Greater 相当于“>”操作,即仅当左边>右边,模板测试通过,渲染像素
GEqual 相当于“>=”操作,即仅当左边>=右边,模板测试通过,渲染像素
Less 相当于“<”操作,即仅当左边<右边,模板测试通过,渲染像素
LEqual 相当于“<=”操作,即仅当左边<=右边,模板测试通过,渲染像素
Equal 相当于“=”操作,即仅当左边=右边,模板测试通过,渲染像素
NotEqual 相当于“!=”操作,即仅当左边!=右边,模板测试通过,渲染像素
Always 不管公式两边为何值,模板测试总是通过,渲染像素
Never 不敢公式两边为何值,模板测试总是失败 ,像素被抛弃
  • stencilOperation: 1、Pass:表示要做什么,当buffer内容通过了模板测试(以及深度测试)。 默认: keep.

    2、Fail:表示要做什么,当buffer的内容没有通过模板测试。 默认: keep.

    3、ZFail:表示要做什么,当buffer内容通过了模板测试,但是没通过深度测试。 默认: keep.

取值 含义
Keep 保留当前缓冲中的内容,即stencilBufferValue不变。
Zero 将0写入缓冲,即stencilBufferValue值变为0。
Replace 将参考值写入缓冲,即将referenceValue赋值给stencilBufferValue。
IncrSat stencilBufferValue加1,如果stencilBufferValue超过255了,那么保留为255,即不大于255。
DecrSat stencilBufferValue减1,如果stencilBufferValue超过为0,那么保留为0,即不小于0。
Invert 将当前模板缓冲值(stencilBufferValue)按位取反
IncrWrap 当前缓冲的值加1,如果缓冲值超过255了,那么变成0,然后继续自增。
DecrWrap 当前缓冲的值减1,如果缓冲值已经为0,那么变成255,然后继续自减。
Shader实例

通过窗透过墙看后面的大海

image


//window

ZWrite Off

Stencil {
            Ref 1
            Comp always
            Pass replace
}

//wall

Stencil {
            Ref 1
            Comp NotEqual
}

//sea

Stencil {
            Ref 1
            Comp Equal
}

Depth Test

深度

由于渲染顺序的问题,远处的物体会被近处的物体,所遮挡。 远近不是物体直接到摄像机的距离,而是做摄像机朝向垂线的距离。

深度值的精度一般有16位、24位和32位float,比较常用的深度精度为24位。

深度缓存中存储着准备要绘制在屏幕上的像素点的深度值。

  • ZWrite on / off : 开启写入深度缓冲/关闭写入深度缓冲,一般情况不透明的物体都会使用Zwrite on 而半透明的物体都会使用 ZWrite off 这样半透明的物体就直接比较ZTest了。

  • 在深度缓冲区中包含深度值介于0.0和1.0之间.但是和z的关系一般不按线性关系处理。而是一个附近的物体是小的 z 值因此给了我们很高的深度精度,很远的给很低精读的深度值。

image

在深度测试中,默认情况是将要绘制的新像素的z值与深度缓冲区中对应位置的z值进行比较,如果比深度缓存中的值小,那么用新像素的颜色值更新深度缓存(Detph buffer)中对应像素的颜色值(Color buffer中)。

  • ZTest 分类:当前的像素来对深度缓冲中的z值进行比较的条件。

ZTest Less:深度小于当前缓存则通过

ZTest Greater:深度大于当前缓存则通过

ZTest LEqual:深度小于等于当前缓存则通过

ZTest GEqual:深度大于等于当前缓存则通过

ZTest Equal:深度等于当前缓存则通过

ZTest NotEqual:深度不等于当前缓存则通过

ZTest Always:不论如何都通过

ZWrite和ZTest使用情况:

1.当ZWrite为On时,ZTest通过时,该像素的深度才能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值也会写入颜色缓存。

2.当ZWrite为On时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。

3.当ZWrite为Off时,ZTest通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest通过了,该像素的颜色值会写入颜色缓存。

4.当ZWrite为Off时,ZTest不通过时,该像素的深度不能成功写入深度缓存,同时因为ZTest不通过,该像素的颜色值不会写入颜色缓存。

Shader实例

1.红绿模型shader均为默认值,ZWrite默认值为On,ZTest默认值为LEqual

先渲染红色模型,再渲染绿色模型。

image

image

2.均为ZWrite默认值为On,ZTest为Always

image

3.均为ZWrite默认值为On,ZTest为GEqual

image

4.红色模型默认值,绿色模型ZWrite为Off,ZTest为GEqual image

Offset

Z-fighting

下图可以看出z_eye与z_ndc并非线性关系。也就是说近平面具有非常高的精度,而远平面的精度很低。如果[-near,-far]的范围变得很大,会引起深度精度问题(Z-fighting):远平面附近ze的小变化不会影响z_ndc值。为了最小化深度缓存精度问题,n与f的距离应该尽可能小。

image

多边形偏移是一个挺有用的技巧,有时会用来解决Z-fighting。z-fighting在开启深度测试时,如果两个重叠物体的深度值非常接近,两次绘制到同一个像素时,深度值相差非常小,就无法正确的辨别究竟哪一次绘制的深度值更小.那么就也会产生z-fighting。 image

shader 语法

Offset Factor, Units

允许您使用两个参数指定深度偏移,Factor和Units,这允许您强制将一个多边形绘制在另一个多边形上,尽管它们实际上位于相同位置。

offset = (m * factor) + (r * units)
  • Factor相对于多边形的X或Y缩放最大Z斜率.
  • Units缩放最小可分辨深度缓冲值。
  • m是多边形的深度的斜率(在光栅化阶段计算得出)中的最大值。这句话难以理解,你只需知道,一个多边形越是与近裁剪面(near clipping plan)平行,m就越接近0。
  • r是能产生在窗口坐标系的深度值中可分辨的差异的最小值,r是由具体实现OpenGL的平台指定的一个常量。

一个大于0的offset 会把模型推到离你(摄像机)更远一点的位置,相应地,一个小于0的offset 会把模型拉近。

黄色模型设置为如下值解决二个重叠模型绘制z-fighting问题:

offset -1,0

image

Alpha Blend

混合命令
取值 含义
Blend Off 关闭混合(默认)
Blend SrcFactor DstFactor 片元产生的颜色xSrcFactor+屏幕上已有的颜色xDstFactor=最终的颜色(写入颜色缓存)
Blend SrcFactor DstFactor, SrcFactorA DstFactorA 同上,只不过使用单独的因子SrcFactorA和DstFactorA来混合透明度通道
BlendOp BlendOperation 用其他的操作来取代加法混合。
BlendOp OpColor, OpAlpha 同上,只不过对于透明度通道使用不同的操作。
混合操作(BlendOp)
取值 含义
Add 加法: FinalColor=SrcFactorSrcColor+DstFactorDstColor
Sub 减法(源-目标): FinalColor=SrcFactorSrcColor-DstFactorDstColor
RevSub 减法(目标-源): FinalColor=DstFactorDstColor-SrcFactorSrcColor
Min 较小值(逐个通道比较)
Max 较大值(逐个通道比较)
混合因子(Blend)
取值 含义
One 混合因子1,表示完全的源颜色或目标颜色
Zero 混合因子0,舍弃掉源颜色或目标颜色
SrcColor 源颜色值
SrcAlpha 源透明度
DstColor 目标颜色值
DstAlpha 目标透明度
OneMinusSrcColor 1-SrcColor
OneMinusSrcAlpha 1-SrcAlpha
OneMinusDstColor 1-DstColor
OneMinusDstAlpha 1-DstAlpha
Shader实例
		Tags { "Queue"="Transparent" "RenderType"="Transparent" "IgnoreProjector"="True" }
		Blend SrcAlpha OneMinusSrcAlpha

image

blend Mode

image

RenderQueue

RenderQueue会改变物体被渲染的先后顺序,但是并不会改变物体的空间位置。只是针对同一个camera渲染的物体。

Tags { "Queue"="Geometry" }

需在SubShader中显示声明ZWrite Off,通知Unity我们会重写物体的渲染深度排序。

属性 渲染队列描述
Background 1000 这个队列通常被最先渲染(比如 天空盒)。
Geometry 2000 这是默认的渲染队列。它被用于绝大多数对象。不透明几何体使用该队列。从前往后渲染
AlphaTest 2450 需要开启透明度测试的物体。Unity5以后从Geometry队列中拆出来,因为在所有不透明物体渲染完之后再渲染会比较高效。
GeometryLast 所有Geometry和AlphaTest队列的物体渲染完后
Transparent 3000 所有Geometry和AlphaTest队列的物体渲染完后,再按照从后往前的顺序进行渲染,任何使用了透明度混合的物体都应该使用该队列(例如玻璃和粒子效果)
Overlay 4000 该队列用于实现一些叠加效果,适合最后渲染的物体(如镜头光晕)。

参考资料:

Per-Sample_Processing

Per-Fragment_Operations

Cg 3.1 Toolkit Documentation

Khronos glPolygonOffset

MSDN glPolygonOffset

Using Polygon Offset