渲染顺序

​ 先画不透明物体,再画透明物体,渲染顺序非常重要,不能随意改动,如果没有一个渲染顺序则会导致部分物体无法被画到屏幕上。先渲染不透明物体,再渲染透明物体,之后再对不透明物体从后往前排序渲染

透明度测试:

概念

​ 只要一个片元的透明度不满足条件(通常小于某个阈值),那么就舍弃对应的片元。被舍弃的片元不会在进行任何的处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体来处理,即进行深度测试,深度写入等等。虽然简单,但是很极端,要么完全透明,要么完全不透明。

例如下图

​ 假如说我们代码中缩写为当α小于0.5是便会舍去上半部分,指挥渲染下班α大于0.5的部分,但之后不会再去干其α的值为多少,统一将其按照不透明物体渲染。最后渲染出来便为一座山。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
Shader "Unlit/014"
{
Properties
{
_MainTex("MainTex", 2D) = "white"{}
_Diffuse("Diffuse", Color) = (1,1,1,1)
_Cutoff("Alpha Cutoff", Range(0,1)) = 0.5
}
SubShader
{
Tags { "Queue"="AlphaTest" "IgnoreProjector"="True"}
LOD 100

Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"

fixed4 _Diffuse;
sampler _MainTex;
float4 _MainTex_ST; //不需要在Properties里定义,用于存放Tiling(缩放)和Offset(位移)的数值
float _Cutoff;


struct v2f
{
float4 vertex : SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};

v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
//o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //为了时Tiling和Offset的数值影响到uv
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

//图片采样
fixed4 testColor = tex2D(_MainTex, i.uv);

if((testColor.a - _Cutoff) < 0)
{
discard;
}

//漫反射
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed3 diffuse = _LightColor0.rgb * testColor.rgb * _Diffuse.rgb * (max(0, dot(worldLightDir, normalize(i.worldNormal)))* 0.5 + 0.5);

fixed3 color = ambient + diffuse;
return float4(color, 1);
}
ENDCG
}
}
}

​ 其中最重要的时要把渲染顺序改成AlphaTest,虽然此处不影响,但是以后写代码时需要改。

透明度混合:

概念

​ 可以得到真正的半透明效果,它会使当前片元的透明度作为混合因子,与已经储存在颜色缓冲中的颜色值进行混合么,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得我们要非常小心物体的渲染顺序。注意:透明度混合只关闭了深度写入,但没有关闭深度测试。这表示当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果深度值距离摄像机更远,那么就不会在进行混合操作。比如一个不透明物体在透明物体前面,我们先渲染不透明物体,可以正常的挡住不透明物体。

​ 为什么要关闭深度写入?如下图:

image-20241114180542085

​ 不透明物体小物块已经画好了,在话不透明物体时,深度测试正常进行,其透明度测试小于不透明度深度,会关掉深度写入,此时好处便为,若想要在两个物理之间再画一个透明物体,此时该物体的深度要大于蓝色的深度,若刚才的深度写入开启列,那么这个物体便不会画在屏幕上。若关闭之后便可正常画出。

​ 两个不透明物体的渲染

image-20241115102805818

​ 若此时先渲染A再徐然B,则会导致B再A的前面,那么就会导致错误,所以再渲染时会先渲染不透明物体,再渲染透明物体,之后再对不透明物体从后往前排序渲染。

混合

只要有关键字Blend,就代表开启混合(除Blend off以外)

计算公式

image-20241115111427378

最后混合只要使用Blend SrcFator DstFactor就行了。

混合因子

混合操作

常见的混合操作类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//正常(Normal)透明度混合
Blend SrcAlpha OneMinusSrcAlpha
//柔和相加
Blend OneMinusDstColor One
//正片叠底
Blend DstColor Zero
//两倍相乘
Blend DstColor SrcColor
//变暗
BlendOp min
Blend One One
//变亮
Blend OneMinusDstColor One
Blend One OneMinusSrcColor
//线性减淡
Blend One One

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
Shader "Unlit/015"
{
Properties
{
_MainTex("MainTex", 2D) = "white"{}
_Diffuse("Diffuse", Color) = (1,1,1,1)
_AlphaScale("Alpha Scale", Range(0,1)) = 1
}
SubShader
{
Tags { "Queue"="transparent" "IgnoreProjector"="True" "RenderType"="Transparent"}
LOD 100

ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
tags{"LightMode"="ForwardBase"}

CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"

fixed4 _Diffuse;
sampler _MainTex;
float4 _MainTex_ST; //不需要在Properties里定义,用于存放Tiling(缩放)和Offset(位移)的数值
float _AlphaScale;


struct v2f
{
float4 vertex : SV_POSITION;
fixed3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
float2 uv : TEXCOORD2;
};

v2f vert (appdata_base v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldNormal = worldNormal;
o.worldPos = mul(unity_ObjectToWorld, v.vertex);
//o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //为了时Tiling和Offset的数值影响到uv
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}

fixed4 frag (v2f i) : SV_Target
{
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;

//图片采样
fixed4 testColor = tex2D(_MainTex, i.uv);

//漫反射
//fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 worldLightDir = UnityWorldSpaceLightDir(i.worldPos);
fixed3 diffuse = _LightColor0.rgb * testColor.rgb * _Diffuse.rgb * (dot(worldLightDir, normalize(i.worldNormal))* 0.5 + 0.5);

fixed3 color = ambient + diffuse;
return float4(color, testColor.a * _AlphaScale);
}
ENDCG
}
}
Fallback "Transparent/Vertexlit"
}

特殊问题处理

当以上代码用于下图所示图形时,便会出现如下图所示的渲染错误的情况出现。

所以此时需要另写一个通道,去做深度写入。

1
2
3
4
5
6
7
8
9
Pass
{
ZWrite On //在此通道进行深度写入
ColorMask 0 //代表该通道不写入任何颜色值。
}

//而对于一下代码需要写到原先的Pass通道内,因为我们只想让其对原先Pass通道做影响
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha

做完以上改动后就可以正常渲染了