此处的渲染流程只是一个概念流水线。大概分为应用阶段、几何阶段、光栅化阶段。
主要输出渲染所需的几何信息,包括点、线、三角面等,传递给下一阶段使用;这一阶段主要CPU处理,该阶段产生的产物就是渲染图元。
主要工作
负责和每个渲染图元打交道,主要任务顶点坐标转变到屏幕空间中,再交给光栅化处理,这阶段会输出二维顶点坐标及对应的深度值等,这阶段主要在GPU。
主要工作
OpenGL和unity:NDC坐标的z分量范围[-1,1]——DirectX:NDC坐标的z分量范围[0,1]
窗口坐标系=屏幕坐标系+z坐标
OpenGL把屏幕的左下角当成最小的窗口坐标值;DirectX把屏幕的左上角当成最小的窗口坐标值
产生屏幕上的像素,渲染最终图像.这阶段主要在GPU。
主要工作
片元不是真正意义上的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色
测试顺序并不一定唯一,unity中技术Early-Z技术:深度测试在片元着色器之前
为了避免我们看到正在进行光栅化的图元,GPU使用双重缓冲
unity shader是一种抽象,我们和这个抽象打交道的途径——ShaderLab
此处主要以顶点片元着色器为例
Shader "Unlit/NewUnlitShader"
{Properties{//外部可以访问//声明一个Color类型颜色_Color ("颜色", Color) = (1,1,1,1)}SubShader{//配置SubShader标签,设置分类Tags { "RenderType"="Opaque" }//配置深度写入与裁切Zwrite Off Cull Off//配置混合模式Blend One ZeroPass{CGPROGRAM//编译指令:告诉Unity哪个函数包含了顶点着色器的代码#pragma vertex vert//编译指令:告诉Unity哪个函数包含了片元着色器的代码#pragma fragment frag//引入写好的内置文件#include "UnityCG.cginc"//在CG代码中,我们需要定义一个与属性名称和类型都匹配的变量float4 _Color;//定义顶点着色器输入struct appdata{//把数据从应用(application)阶段传递到顶点着色器(vertex shader)中float4 vertex : POSITION;float2 uv : TEXCOORD0;/**以上语义内容均来自MeshRender组件提供*在每帧调用DrawCall的时候,MR组件会把它负责渲染的模型数据发送给UnitySHader*一个模型包含一组三角面片,每个三角面由三个顶点组成,定点包含各种信息*/};//定义顶点着色器输出struct v2f{float4 vertex : SV_POSITION;float3 color : COLOR0;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.color=fixed3(0.5,0.5,0.5);return o;}fixed4 frag (v2f i) : SV_Target{fixed3 c = i.color;c*=_Color.rgb;return fixed4(c,1);}ENDCG}}
}
shaderLab中Properties语句块定义了着色器所需的各种属性
Properties 语义块支持的属性类型 | ||
---|---|---|
属性类型 | 默认值的定义语法 | 例子 |
Int | number | _Int("Int",Int)=2 |
Float | number | _Float("Float",Float)=0.2 |
Range(min,max) | number | _Range("Range",Range(0.0,2.0))=1.2 |
Color | (number,number,number,number) | _Color("Color",Color)=(1,1,1,1) |
Vector | (number,number,number,number) | _Vector("Vector",Vector)=(1,2,5,3) |
2D | "defaulttexture" {} | _2D("2D",2D)="" {} |
Cube | "defaulttexture" {} | _Cube("Cube",Cube)="white" {} |
3D | "defaulttexture" {} | _3D("3D",3D)="black" {} |
SHaderLab属性类型和CG变量类型的匹配关系 | |
---|---|
ShaderLab属性类型 | CG变量类型 |
Color,Vector | float4,half4,fixed4 |
Range,Float | float,half,fixed |
2D | sampler2D |
Cube | samplerCube |
3D | sampler3D |
- 有时,在CG变量前会出现uniform关键字,它是一种修饰词,它仅用于提供一些关于该变量的初始值是如何指定和存储的相关信息。在unity shader中,uniform关键字可以忽略。
- float:高精度浮点值,通常是32位。
- half:中精度浮点值。通常是16位,范围是-60000至+60000,它适合存储UV坐标,颜色值等。
- fixed:低精度浮点值。通常是11位,范围是-2.0至+2.0,精度为1/256。这是三者中最小的一个,可以用于光照计算、颜色等。
- sampler*:分为6类 :sampler、sampler1D、sampler2D、sampler3D、samplerCUBE、samplerRECT。DirectX profiles不支持samplerRECT类型。
每个UnityShader文件至少有一个SubShader,unity会选择第一个可以在目标平台运行的SubShader,都不支持就运行Fallback。
SubShader的标签类型 | ||
---|---|---|
标签类型 | 说明 | 例子 |
Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有透明物体可以在所有不透明物体后面被渲染,我们也可以自定义使用的渲染队列来控制物体渲染顺序。Unity在内部使用一系列整数索引来表示每个渲染队列,且索引号越小表示越早被渲染。 | Tags{"Queue"="Geometry"} |
RenderType | 对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器等,这可以被用于着色器替换(Shader Replacement)功能。 | Tags{"RenderType"="Opaque"} |
DisableBatching | 一些SubShader在使用Unity的批处理功能时会出现问题,例如使用了模型空间下的坐标进行顶点动画,这时可以通过该标签来直接指明是否对该SubShader使用批处理。 | Tags{"DisableBatching"="True"} |
ForceNoShadowCasting | 控制使用该SubShader的物体是否会投射阴影 | Tags{"ForceNoShadowCasting"="True"} |
IgnoreProjector | 如果该标签值为“true”,那么使用该SubShader的物体将不会受Projector的影响,通常用于半透明物体。 | Tags{"IgnoreProjector"="True"} |
CanUseSpriteAtlas | 当该SubShader是用于精灵(Sprites)时,将该标签设为"Flase"。 | Tags{"CanUseSpriteAtlas"="False"} |
PreviewType | 指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球形,我们可以通过把该标签的值设为"Plane"来改变预览类型。 | Tags{"PreviewType"="Plane"} |
名称 | 队列索引 | 描述 |
Background | 1000 | 这个渲染队列会在任何其他队列之前被渲染,我们通常使用该队列来渲染那些需要绘制在背景上的物体 |
Geometry | 2000 | 默认的渲染队列,大多数物体都使用这个队列,不透明物体使用这个队列 |
AlphaTest | 2450 | 需要透明度测试的物体使用这个队列,在Unity5中它从Geometry队列中被单独分出来了,这是因为在所有不透明物体渲染之后再渲染它们会更加高效。 |
Transparent | 3000 | 这个队列中的物体会在所有Geometry和AlphaTest物体渲染后,再按从后往前的顺序进行渲染,任何使用了透明度混合(例如关闭了深度写入的Shader)的物体都应该使用这个队列 |
Overlay | 4000 | 该队列用于实现一些叠加效果。任何需要在最后渲染的物体都应该使用该队列 |
常见的渲染状态设置选项 | ||
---|---|---|
状态名称 | 设置指令 | 解释 |
Cull | Cull Back | Front | Off | 设置剔除模式:剔除背面/正面/关闭剔除 |
ZTest | ZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always | 设置深度测试时使用的函数 |
ZWrite | ZWrite On | Off | 开启/关闭深度写入 |
Blend | Blend One Zero | 开启并设置混合模式 |
参数 | 描述 |
One | 因子为1 |
Zero | 因子为0 |
SrcColor | 因子为源颜色值。当用于混合RGB的混合等式时,使用SrcColor的RGB分量作为混合因子;当用于A的混合等式时,使用SrcColor的A分量作为混合因子 |
SrcAlpha | 因子为源颜色的透明值(A通道) |
DstColor | 因子为目标颜色值。当用于混合RGB的混合等式时,使用DstColor的RGB分量作为混合因子;当用于A的混合等式时,使用DstColor的A分量作为混合因子 |
DstAlpha | 因子为目标颜色的透明度值(A通道) |
OneMinusSrcColor | 因子为(1-源颜色) |
OneMinusSrcAlpha | 因子为(1-源颜色的透明度值) |
OneMinusDstColor | 因子为(1-目标颜色) |
OneMinusDstAlpha | 因子为(1-目标颜色的透明度值) |
操作 | 描述 |
Add | 将混合后的源颜色和目标颜色相加,默认的混合操作 |
Sub | 用混合后的源颜色减去混合后的目标颜色 |
RevSub | 用混合后的目标颜色减去混合后的源颜色 |
Min | 使用源颜色和目标颜色中较小的值,是逐分量比较的 |
Max | 使用源颜色和目标颜色中较大的值,是逐分量比较的 |
3.5. 通过混合操作和混合因子命令的组合,我们可以得到Photoshop混合模式中的混合效果(可以在网上单独查找,应该会有很多)
Blend SrcAlpha OneMinusSrcAlpha // Alphablending alpha混合
Blend One One // Additive 相加混合
Blend One OneMinusDstColor // Soft Additive柔和相加混合
Blend DstColor Zero // 正片叠底Multiplicative 相乘混合
BlendDstColor SrcColor // 2x Multiplicative 2倍相乘混合
Use Pass “MyShader/MYPASSNAME”//必须使用大写形式
Pass的标签类型 | ||
标签类型 | 说明 | 例子 |
LightMode | 定义该Pass在Unity的渲染流水线中的角色 | Tags{"LightMode"="ForwardBase"} |
RequireOptions | 用于指定当满足某些条件时才渲染该Pass,它的值是一个由空格分隔的字符串。目前,Unity 支持的选项有:SoftVegetation。在后面的版本中,可能会增加更多选项。 | Tags{"RequireOptions"="SoftVegetation"} |
语义:Fallback "name"或者Fallback Off
Ⅰ、在OpenGL中,(0,0)点对应了屏幕的左下角,在DirectX中,(0,0)点对应了屏幕左上角
Ⅱ、我们不仅可以将渲染结果渲染到屏幕上,还可以输出到不同的渲染目标上,但当我们把屏幕图像渲染到一张渲染纹理中时,如果不采取任何措施,就会因为①的原因造成纹理翻转的情况,不过,Unity在背后为我们处理了这种反转问题,不过当我们在Unity中开启了抗锯齿,此时如果基于DirectX平台使用渲染纹理技术,同时处理多张渲染图象的时候,我们就需要自己处理反转问题。
前言
实现方法
采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件,那么它对应的片元就会被舍弃,被舍弃的片元不再进行任何处理,也不会对颜色缓冲产生任何影响;否则就会按照普通的不透明物体的处理方法来处理它。
这种方法可以得到真正的半透明效果,它会使用当前片元的透明度作为混合因子,与已经存在的颜色缓冲中的颜色值进行混合,得到新的颜色。但是透明度混合仅需要关闭深度写入,没有关闭深度测试,这使得我们要小心物体的渲染顺序。对于透明度混合,深度缓冲是只读的。(如果不关闭深度写入,那么就会发生前面的透明物体遮住了后面的不透明物体)
2.1. 渲染顺序
因为有时关闭深度写入,那么就导致渲染顺序将变得非常重要。所以渲染引擎一般会先对物体进行排序,再渲染。常用方法:
2.1.1. 先渲染所有不透明物体,并开启它们的深度测试和深度写入。
2.1.2. 把半透明物体按他们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。
纹理最初的目的就是使用一张图片来控制模型的外观。使用纹理映射技术,我们可以把一张图黏在模型表面,逐纹素(纹素的名字是为了和像素进行区分)地控制模型的颜色。
属性面板
Wrap Mode
1.1. Repeat:这种模式下,如果纹理坐标超过了1,那么它的整数部分将会被舍弃,而直接使用小数部分采样,这样的结果是纹理将会不断重复。
1.2. Clamp:这种模式下,如果纹理坐标大于1,那么将截取1,如果小于0,就截取到0。
Filter Mode
它决定了当纹理由于变换而产生拉伸时将会采用哪种滤波模式。有三种模式:它们得到的图片滤波效果依次提升,但需要耗费的性能也依次增大。
· Point:使用了最近邻滤波,图像会看起来有种像素风格,
· Bilinear:使用线性滤波,图像看起来被模糊了。
· Trilinear:和Bilinear滤波几乎一样,只是在多级渐远纹理之间进行混合了。
Max Texture Size:如果纹理大小超过了这个值,Unity将会把该纹理缩放到这个最大分辨率,理想情况下,纹理可以不是正方形,但是长宽的大小应该是2的幂,如果使用非2的幂大小(Non Power of Two ,NPOT)的纹理,那么会占用更多内存空间,GPU读取速度也会下降。甚至有些平台不支持。
多级渐远纹理技术:将原纹理提前用滤波处理得到很多更小的图像,形成一个图像金字塔,每一层都是对上一层图像降采样的结果。这是一种典型的用空间换取时间的方法。在Unity中,勾选Generate Mip Maps即开启多级渐远纹理技术。
应用
就是使用一张纹理来修改模型表面的法线,以便为模型提供更多的细节。
实现方法
1.1. 高度映射:使用一张高度纹理来模拟表面位移,然后得到一个修改后的法线值。
高度图中存储着强度值,它用来表示模型表面局部的海拔高度。颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹。
高度图通常会和法线映射一起使用,用于给出表面凹凸的额外信息。
1.2. 法线映射:使用一张法线纹理来直接存储表面法线。通常使用法线映射
法线纹理中存储的就是表面的法线方向。由于法线方向的分量范围在[-1,1],而像素的分量
范围为[0,1],因此我们需要做一个映射,但是我们在Shader中对法线纹理进行纹理采样
后,还需要对结果进行一次反映射的过程,以得到原先的法线方向:
1.3. 法线纹理的坐标空间
1.3.1. 模型空间的法线纹理
优点:实现简单,更加直观。我们甚至都不需要模型原始的法线和切线等信息。
缺点:在纹理坐标的缝合处和尖锐的边角部分,可见的突变较少,既可以提供平滑的边 界。切线空间下的法线纹理中的法线信息是依靠纹理坐标的方向得到的结果,可能会在边缘处或尖锐的部分造成更多可见的缝合迹象。
1.3.2. 切线空间的法线纹理
优点:自由度高,模型空间的法线纹理是绝对法线信息,而切线空间下的法线纹理记录的是相对法线信息。可进行UV动画;可以重用法线纹理;可压缩。
1.4. 在利用法线纹理做凹凸映射的时候,实际上他只有两个通道是真正必不可少的,第三个通道可以通过另外两个推导出来,所以使用DXT5nm的压缩方法可以减少纹理的占用内存。
但是再利用高度图生成法线纹理的时候,除了需要将纹理类型设置为Normal map外,还需要勾选Create from Grayscale,这样我们就可以把它和切线空间下的法线纹理同等对待了。
勾选Create from Grayscale后:
Bumpiness:控制凹凸程度
Filtering:Smooth:使生成后的法线纹理比较平滑
Shard:它会使用Sobel滤波来生成法线。
在很多卡通风格的渲染中会用到一种冷到暖色调的着色技术,这种技术会用到渐变纹理。
对于Unity自带的Skybox材质:
1.1. 6张图片位置要摆正,同时为了让天空盒子正常渲染,我们需要把这6张纹理的WrapMode设置为Clamp,以防止在接缝处出现不匹配的现象。
1.2. 材质界面除了6张图还有3个属性:
Tint Color:用于控制该材质的整体颜色;
Exposure:用于调整天空盒子的亮度;
Rotation:用于调整天空盒子沿+y轴方向的旋转角度。
如果我们想某些摄像机可以使用不同的天空盒子,可以通过向该摄像机添加Skybox组件,覆盖掉之前的设置。
在Unity中,天空盒子是在所有不透明物体之后渲染的,而其背后使用的网格是一个立方体或一个细分后的球体。
根据视角方向控制反射程度。
定义:unity为渲染目标纹理定义了一种专门的纹理类型——渲染纹理。在unity中使用渲染纹理通常有两种方式:
定义:指的是那些由计算机生成的图像,我们通常使用一些特定的算法来创建个性化图案或者非常真实的自然元素,例如石头、木头等。
我们直接讲解标准光照模型,它的基本理念就是只关心直接光照,也就是那些直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线。它的基本方法就是把进入到摄像机的光线分为4个部分,分别是自发光、高光反射、漫反射、环境光,每个部分使用一种方法来计算它的贡献度。
定义:这部分用于描述其他所有的间接光照。
定义:这部分用于描述当给定一个方向时,一个表面本身会向该方向发射多少辐射量。
定义:这部分用于描述当光线从光源照射到模型表面时,该表面会向每个方向散射多少辐射量。
cdiffuse=(clight · mdiffuse)max(0,n · I)
其中,n是表面法线,I是指向光源的单位矢量,mdiffuse是材质的漫反射颜色,clight是光源颜色。需要注意的是,我们需要防止法线和光源方向点乘的结果为负值,为此,我们使用取最大值的函数来将其截取到0,这可以防止物体被从后面来的光源照亮。
定义:这部分用于描述当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量。