Unity半透明特效原理讲解(为什么半透明设置渲染顺序和深度写入这么重要)

写在前面

最近要实现一个透明的特效,所以更仔细的去学了一下。但是网上更多的是用文字讲解,实验做的相对较少,因此我在这里会更多的附上实验效果展示和结论。若有理解错误的地方,欢迎讨论。

实验场景

摄像头看向两个Cube,红Cube在前,蓝Cube在后
在这里插入图片描述

实验1:红(不透明)+蓝(不透明)+默认渲染顺序(先渲染蓝Cube)

效果:
在这里插入图片描述
解析: 红蓝Cube使用相同的Shader,只是颜色不一样。可以看到红色挡住了颜色。这很好理解。因为Unity默认从离摄像机原的地方开始渲染。那蓝Cube会先进行深度测试,发现深度缓冲区里面是空的,所以将蓝Cube的深度写入深度缓冲区,并将自己的颜色Blue写入颜色缓冲区。之后红Cube深度测试,发现自己的深度值比深度缓冲区里的蓝Cube值要小,所以通过了深度测试,于是将自己的深度值写入深度缓冲区中覆盖掉以前蓝Cube的深度缓冲区,并将自己的颜色Red写入颜色颜色缓冲区。从而实现覆盖效果。
结论:
1、Unity的默认渲染顺序是从离摄像机远的地方开始渲染。
2、通过深度测试,进行深度写入时,颜色写入颜色缓冲区也会同时进行。

Shader代码:

Shader "My/Blue"
{
    
    
	Properties
	{
    
    
		_Color("Color", Color) = (0,0,1,1)
	}
	SubShader
	{
    
    
		Tags {
    
     "Queue"="Geometry" }
	    
		LOD 100
        Pass
        {
    
    
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            fixed4 _Color; 
            struct appdata
            {
    
    
                float4 vertex : POSITION;
            };

            struct v2f
            {
    
    
                float4 vertex : SV_POSITION;
            };
            
            v2f vert (appdata v)
            {
    
    
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
    
    
                return fixed4(_Color.r,_Color.g,_Color.b,_Color.a);
            }
            ENDCG
        }
	}
}

实验2:红(不透明+优先渲染)+蓝(不透明)

效果:
在这里插入图片描述
解析:
把蓝色Cube的渲染队列值调到2001大于红色Cube的渲染队列值。从而使得红色Cube优先渲染。但是结果并没有发生改变。这是因为先渲染红色时,红色的深度值被写入深度缓冲区,再渲染蓝色时,蓝色深度值比红色大,因此不会写入深度缓冲区,所以效果不变。
在这里插入图片描述
在这里插入图片描述
结论:
1、改变渲染队列值能改变Unity对物体的渲染顺序。
2、对于不透明物体来说,因为深度测试的存在,所以不用关心渲染顺序。

实验3:红(透明+关闭深度写入)+蓝(不透明)+默认渲染顺序(先渲染蓝Cube)

效果:
在这里插入图片描述
解析:
我们仅在红Cube的Shader中加入透明度混合代码、把红色Cube的渲染队列改为3000、关闭深度写入,并且把红Cube的透明度改为150。此时可以看到红色Cube呈现透明效果。原因也很简单。因为先渲染蓝色,所以当红色Cube进行深度测试通过时本应该把深度值写入深度缓冲区但是由于我们关闭了深度写入,所以深度值还是蓝色Cube的深度值。但是混合的颜色依然写入颜色缓冲区。
在本场景Blend SrcAlpha OneMinusSrcAlpha表示红色红色透明度+蓝色(1-红色透明度)

//红CubeShaer加
Tags {
    
    "Queue"="Transparent" }
Blend SrcAlpha OneMinusSrcAlpha

在这里插入图片描述
结论:
1、关闭深度写入,只是代表如果深度测试通过时不会将深度值写入深度缓冲,但是颜色写入依然会进行。

实验4:红(透明+关闭深度写入+优先渲染)+蓝(不透明)

效果:
在这里插入图片描述
解析:
我们将蓝色Cube的渲染队列值调到3001大于红色的300。因此先渲染红色Cube,但是由于红色Cube关闭了深度写入,所以深度值不会写入深度缓冲区,所以深度缓冲区依然为空,但是颜色依然会写入颜色缓冲区。然后渲染蓝Cube时,发现深度缓冲区为空,这时就会误以为蓝Cube是第一个渲染的,所以将蓝Cube深度值写入深度缓冲区,颜色值写入颜色缓冲区,从而导致了蓝Cube在红Cube前的效果。
在这里插入图片描述
结论:
1、对于半透明物体来说,渲染顺序是相当重要的,不同的渲染顺序会出现不同的效果。

实验5:红(透明+开启深度写入)+蓝(不透明)+默认渲染顺序(先渲染蓝Cube)

效果:
在这里插入图片描述
解析:
将蓝Cube渲染队列值调回2000,使得蓝Cube优先渲染,但开启红Shader的深度写入。所以在渲染红Cube时,深度测试通过后会将红色Cube的深度值写入深度缓冲区,混合颜色会写入颜色缓冲区。
在这里插入图片描述
结论:
1、显示结果和实验3红色Cube关闭深度写入是一样的,有人会觉得,开不开启深度写入好像效果都一样,只是最终存到深度缓冲区里面的值不一样罢了,实验3中深度缓冲区离得值是蓝Cube得深度值,而实验5深度缓冲区里得值是红Cube的值。那为什么还要关闭红Shader的深度写入呢?这不多此一举吗?这是因为此时还没考虑当蓝色Cube也是透明的情况,请看实验6、7

实验5.5:红(透明+开启深度写入+优先渲染)+蓝(不透明)

效果:
在这里插入图片描述
解析: 调大蓝色的渲染队列值(3001)使其大于红色的渲染队列值(3000),让红色先渲染。红色深度值和颜色被写入。然后渲染蓝色,因为蓝色在后面,深度值小于红色所以不写入深度缓冲区,因此即便红色半透明,也看不到蓝色。

实验6:红(透明+开启深度写入)+蓝(透明+开启深度写入)+默认渲染顺序(先渲染蓝Cube)

效果:
在这里插入图片描述
解析:
把蓝色也变为透明,可以看到红蓝重叠的地方,完全看不到红色。为什么呢?见https://blog.csdn.net/a1047120490/article/details/106744930/
写得很详细,解释了为什么。
结论:
当两个透明物体发生重叠时如果开启深度写入,那么重叠的部分显示会出错。

实验7:红(透明+关闭深度写入)+蓝(透明+关闭深度写入)+默认渲染顺序(先渲染蓝Cube)

在这里插入图片描述
解析:
可以看到红蓝重叠的地方,得出了透明的效果,但感觉红色是在蓝色前面的。为什么呢?同样见
https://blog.csdn.net/a1047120490/article/details/106744930/
结论:
当两个透明物体发生重叠时如果不开启深度写入,那么重叠的部分显示照样会出错,但总比开启深度写入强。

总结

1、对于不透明物体的,因为深度缓冲区的原因,渲染顺序不太重要(实验1、2)。
2、对于透明物体,渲染顺序会直接影响渲染结果,所以要设置渲染队列 Tags { "Queue"="Transparent" }(实验3、4)
3、对于透明物体,如果开启深度写入,当两个透明物体重合时,会出现渲染错误,如果关闭深度写入,也会出现渲染错误,但比开启时错误要少。所以还是关闭深度写入的好。
4、因此对于半透明物体的默认写法为设置渲染队列为"Queue"="Transparent",设置透明混合Blend SrcAlpha OneMinusSrcAlpha,关闭深度写入ZWrite Off

//设置渲染队列和渲染类型
Tags {
    
    "Queue"="Transparent"  "RenderType"="Transparent" }
//设置透明混合
Blend SrcAlpha OneMinusSrcAlpha
//关闭深度写入
ZWrite Off

5、Unity在进行渲染时一般会对物体进行排序,再渲染,渲染顺序为:
(1)先渲染所以不透明物体("Queue"="Geometry"的物体),从到相机的距离,由远到近渲染,并且开启深度测试和深度写入。
(2)再渲染半透明物体("Queue"="Transparent"的物体),从到相机的距离,由远到近渲染,并且开启深度测试但关闭深度写入。
(3)但由于Unity对物体距离相机距离排序是基于object level的(基于物体中心的排序),所以当两个透明物体出现重合时,会出现渲染的错误,这是应该以像素为单位排序渲染(用顺序无关透明(order independent transparency, OIT)技术)。

写在后面

今天2023/02/21是出考研成绩的日子,虽然博主已经走过了考研的独木桥,但回望过去考研的经历还是不禁感叹,考研这条路注定是孤独的,注定是几家欢喜几家愁。愿,星光不问赶路人,时光不负有心人。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/iiiiiiimp/article/details/129138385