【Shader入门精要】第八章——透明效果

深入解析Unity Shader中透明渲染的核心概念,包括深度测试、透明度测试、透明度混合、双面渲染等关键技术点,以及如何在实践中应用这些技术解决常见问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Shader入门精要项目链接:

https://github.com/candycat1992/Unity_Shaders_Book

学习目的:理解一些常见的透明物体渲染问题,其中主要有:透明度测试、透明度混合、深度测试、深度写入、混合因子、混合操作、双面渲染等知识点。

一、深度测试、深度写入

以像素(片元)为对象,对片元深度值和已有的深度值进行数值比较,若满足条件,则进行渲染该片元,并检查是否开启深度写入,如果开启了,则覆盖已有的深度值;若不满足条件,则不会进行渲染,也不会写入(无论是否开启写入)。

已有的深度值:深度缓冲区的深度值;(深度缓冲区相当于一个屏幕二维深度数组,保存了屏幕每一个像素点的深度值!)

PS:每一个像素(片元)都会进行如上操作,其中,深度测试是必须开启的,深度写入可以不开启;只有在深度测试和深度写入都开启了的情况下,才能做到万无一失的渲染,不然就可能会出现渲染错乱问题;默认都是开启的。

二、透明度测试

原理:透明度测试是直接使用一个函数进行裁剪片元来达到透明效果的一种手段。

void clip(float4); 其中参数可以是float4, float3, float2, float1, float

参数:裁剪时使用的标量或矢量条件。

描述:如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。它等同于:

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

可通过在片元着色器里,使用clip函数对物体进行透明化,效果可以自己测试:

fixed4 frag(v2f i) : SV_Target {
    //...
    clip(o_col.a - 0.5); //意味着:透明度比0.5低的,会被裁剪(不渲染)
    //...
}

三、透明度混合

原理:使用当前片元的颜色值和已有的颜色值进行混合混合操作+混合因子构成的混合算法),来达到透明效果。

在Transparent渲染队列(Tags标签"Queue"="Transparent"),Unity2017版本的Shader会强制性关闭深度写入。(这句话是错的!为什么我当初会这样理解,在最下方解释了。)

我们的不透明物体是绝对先于半透明和透明物体渲染的,而且不透明物体是绝对渲染正确的,因为它们都开启了深度测试和深度写入,进行的是像素级别精确渲染!等不透明物体都渲染结束后,剩下的工作才是渲染半透明和透明的物体,此时这些物体基本上都需要关闭深度写入才能正常渲染,如果你开启了深度写入并且渲染队列不是Transparent(看上方红字),就可能会出现,当有一个半透明物体渲染在最前方(指最靠近摄像机视口),此时在后方的半透明物体都不会渲染了,因为后方的半透明物体深度值比最前方的半透明物体深度值要低!(只有满足深度测试等式:当前片元深度值>缓冲区深度值才渲染)所以,我们要关掉深度写入,不要让最前方的半透明物体的深度值进行写入(也是为了不要让任何半透明物体的深度值进行写入),此时为了确保谁先谁后进行渲染的问题,还是要看物体距离摄像机的远近,越远的半透明物体越先渲染出来,逐渐往摄像机靠近不断地渲染出这些半透明物体,这样一个叠加的顺序才是符合现实的。

为什么要由远处到近处的方式进行渲染半透明物体?因为假设有一个半透明物体A(深度大)和一个半透明物体B(深度小),B躲在A的后面,此时渲染顺序不确定,假设我先渲染B物体,再渲染A物体,这样渲染是没有问题的(层级明显正确),但是,先渲染A再渲染B,因为我们没有开启深度写入,所以假设A,B都满足大于深度缓冲区的深度值(即都能够渲染),但是,此时因为关闭了深度写入,A先进行渲染后(A的深度不会进行写入深度缓冲区),(所以B还是满足渲染条件的)B再进行渲染混合A颜色值,就会导致B好像是在A的前面一样。

PS:在所有不透明物体渲染结束后,此时深度缓冲区的内容是最大的不透明物体深度值,所以那些能够渲染出来的不透明物体或透明物体的深度值 都是比这个 最大的不透明物体深度值 还要的!即比那些不透明物体还要靠近摄像机的物体。其实深度值可能就是与摄像机的距离,越近就越大(可能本人还没有清楚是不是这个回事)

混合命令Blend开头的,它是一个RenderSetup命令可写在SubShader{} 或 Pass{}中例如:

SubShader{
    ZWrite Off
    //BlendOp Add //默认混合操作:相加混合
    Blend SrcAlpha OneMinusScrAlpha
    Pass{
        Tags { "LightMode" = "ForwardBase" }
        Blend OneMinusSrcAlpha SrcAlpha 
    }
}

[RenderSetup]可以看回之前的Unity Shader基础温习一遍。

可见,上方的代码,在SubShader开启了ZWrite Off深度写入关闭,并且Blend Alpha OnMinusSrcAlpha开启混合并设置混合因子,其中有一个默认代码:BlendOp Add(混合操作:相加混合命令),使用了Blend XXX YYY时,一定要有一个混合操作,默认的就是相加混合命令BlendOp Add;

相加混合公式:O(rgb) = SrcFactor * S(rgb) + DstFactor * D(rgb), O(a) = SrcFactorA * S(a) + DstFactorA * D(a)

提前补充说明,下方所提及的 源颜色值 是指当前渲染的片元(像素)的颜色值, 目标颜色值 是指已在颜色缓冲区的颜色值(即已存在于屏幕上的),源RGB通道混合因子是指这个混合因子是作用于源RGB通道的乘法系数,以此类推其他的同理。

SrcFacot是源RGB通道混合因子,DstFacot是目标RGB通道混合因子,SrcFacotrA是源A通道混合因子,DstFacotrA是目标A通道混合因子,S是源颜色值,D是目标颜色值。

Blend来控制SrcFactor和DstFactor数值,例如:Blend SrcAlpha OneMinusSrcAlpha 等同于

SrcFactor = SrcAlpha(源颜色A通道数值)

DstFactor = OneMinusSrcAlpha (1-源颜色A通道数值)

此时SrcFactorA和DstFactorA分别等于SrcFacotor和DstFactor的数值。

如果要额外指定SrcFactorA和DstFactorA,可以使用如下命令:

Blend SrcFactor DstFactor, SrcFactorA DstFactorA(也就是在后面加多一个逗号,再接着写A通道的混合因子即可)

Blend是[RenderSetup]的一个指令!

混合因子
One1
Zero0
SrcColor源颜色值(RGBA):当用于混合RGB通道时,混合因子为SrcColor.rgb;当混合A通道时,混合因子为SrcColor.a
SrcAlpha源颜色的透明度值(A通道)
DstColor目标颜色值(RGBA):当用于混合RGB通道时,混合因子为DstColor.rgb;当混合A通道时,混合因子为DstColor.a
DstAlpha目标颜色的透明度值(A通道)
OneMinusSrcScolor(1 - 源颜色值(RGBA)):当用于混合RGB通道时,混合因子为(1 - 源颜色值(RGBA)).rgb;当混合A通道时,混合因子为(1 - 源颜色值(RGBA)).a
OneMinusSrcAlpha1 - 源颜色的透明度值(A通道)
OneMinusDstScolor

(1 - 目标颜色值(RGBA)):当用于混合RGB通道时,混合因子为(1 - 目标颜色值(RGBA)).rgb;当混合A通道时,混合因子为(1 - 目标颜色值(RGBA)).a 

OneMinusDstAlpha1 - 目标颜色的透明度值(A通道)

实际上你记住的东西是非常简单的,就是Src 和 Dst, Src 是 Source缩写版(中文译为源), Dst 是 destination缩写版(中文译为目标),Minus是 减法符号(-),这个单词译为 减操作,这样去理解记忆混合因子就简单了不少!

例如:OneMinusSrcColor 就是 One  Minus SrcColor 译为 1 减 源颜色rgba。

其他例子:SrcAlpha 就是 Src  Alpha  = Source Alpha 译为 源颜色透明通道(A通道)

混合操作
Add

将混合后的源颜色和混合后的目标颜色相加。默认的混合操作。使用的混合等式是:

O(rgb) = SrcFactor * S(rgb) + DstFactor * D(rgb)

O(a) = SrcFactorA * S(a) + DstFactorA * D(a)

Sub

用混合后的源颜色值减去混合后的目标颜色。使用的混合等式是:

O(rgb) = SrcFactor * S(rgb) - DstFactor * D(rgb)

O(a) = SrcFactorA * S(a) - DstFactorA * D(a)

RevSub

用混合后的目标颜色值减去混合后的源颜色。使用的混合等式是:

O(rgb) = DstFactor * D(rgb) - SrcFactor * S(rgb)

O(a) = DstFactorA * D(a) - SrcFactorA * S(a)

Min

使用源颜色和目标颜色中较小的值,是逐分量比较的。使用的混合等式是:

O(rgba) = ( min( S(r), D(r) ), min(S(g),D(g)), min(S(b),D(b)), min(S(a),D(a)) )

Max

使用源颜色和目标颜色中较大的值,是逐分量比较的。使用的混合等式是:

O(rgba) = ( max( S(r), D(r) ), max(S(g),D(g)), max(S(b),D(b)), max(S(a),D(a)) )

其他逻辑操作仅在DirectX 11.1 中支持

注意:上方表格提及的 混合后的源颜色值是指 用源颜色值 * 一个混合因子SrcFactor 之后的结果 就是 所谓的混合后的源颜色值。混合后的目标颜色值同理。

而透明度混合中的混合,是指整个混合等式的结果(包括了先对两个颜色值进行与一个系数(混合因子)相乘,再根据混合操作来对两个相乘后的颜色值进行相加或相减等操作,最终得出一个颜色值放入颜色缓冲区,等待刷新到屏幕上!)

四、双面渲染

4.1 在透明度测试下,实现双面渲染的透明效果

使用Cull指令控制需要剔除哪个面的渲染图元,例如:

Cull Front | Back | Off     分别为 剔除正面 、剔除背面、不进行剔除

在Unity中,默认是剔除背面 即 Cull Back

我们可以在使用clip实现的半透明渲染Shader的Pass{}中加上 Cull Off 不进行剔除,来实现双面渲染,伪代码如下:

Shader "MilkShader/Sevent/AlphaTestCullOff"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		//...
	}
	SubShader
	{
		Tags { "RenderType"="AlphaTest" "IgnoreProjector"= "True" "RenderType" = "TransparentCutout" }
		LOD 100				

		Pass
		{    
                        //不进行剔除(即双面都会进行渲染!)
			Cull Off
			
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"

			struct appdata
			{
			        //...			
			};

			struct v2f
			{
		       		//...
			};

		        //...
						
			v2f vert (appdata v)
			{
    			//...
			}
			
			fixed4 frag (v2f i) : SV_Target
			{		
                              //采样片元颜色值						
				fixed4 col = tex2D(_MainTex, i.uv);
                              //使用clip函数裁剪透明度少于0.5的片元
				clip(col.a - 0.5); //忘记这个函数是干嘛的?请看博客透明度测试有解释。
                              //...
			}
			ENDCG
		}
	}
        Fallback "xxx"
}

效果图:

这里可看见内部结构(即反面也渲染出来了),完成了双面渲染,我们仅仅是在加了一句代码Cull Off;Cull指令是RenderSetup里面的指令;注意:这是在开启了深度测试和深度写入的情况下才正常的。

假如我关了深度写入,会如何?

假如我关了深度测试和深度写入,会如何?(测试结果同上图)

下面有一段话解释这个问题的发生,一般来讲,你都能听懂只是有点长。(因为这个问题这样说才能最清晰)

这种问题的发生是因为关闭深度写入的后果,导致的渲染错乱,在上面我有说到透明度混合为什么要关闭深度写入,以及关闭后引发的每个半透明物体之间渲染错乱问题和Unity的处理方式:从远到近渲染。而这里的问题是因为关闭深度写入,导致自身像素渲染顺序错乱问题,原本是不会进行渲染自身内部的,但是由于我进行了Cull Off,导致内部也要渲染,此时还关闭了深度写入,就会导致Unity无法得知哪个像素点先进行渲染,哪个后进行渲染了,因为没有更新深度缓冲区的深度值,每一个像素无论先还是后都会被渲染,例如:同一个屏幕坐标点上,一个像素点A(深度:1000)先进行了渲染,然后另一个像素点B(深度:10)再进行渲染,此时2个像素点深度值都比缓冲区的深度值要大(即满足渲染条件,可以进行渲染了),B就会覆盖掉A像素点(明明A才是最靠近摄像机的!)。你可以说,我不覆盖,我使用B和A进行混合行不?唔好意思啊,这个问题在透明度混合的时候也说明了,这里是透明度测试,所以不考虑混合,而是覆盖!

4.2 在透明度混合下,实现双面渲染

透明度混合是关闭了深度写入,你会问,不会出问题吗?我就有一种方法来解决上述问题!

那就是使用2个Pass!第一个Pass渲染背面,第二个Pass渲染正面,这样就完成了双面渲染而且不会出现自身像素渲染错乱问题

伪代码直接给出来(看不懂算我输)

Shader "MilkShader/Sevent/AlphaBlendShaderCullOff"
{
	
	SubShader
	{
        //注意:"Queue"="Transparent"会强制关闭深度写入
		Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
             //你可以试试把ZWrite Off 注释掉!因为会强制关闭的
		ZWrite Off
		Pass
		{

                    //第一个Pass剔除正面,只渲染背面(先进行渲染后面的像素点)			
			Cull Front
                    //别忘了开启混合
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			...
			ENDCG
		}

	Pass
		{
            //第二个Pass剔除背面,只渲染正面(再进行渲染前面的像素点)
			Cull Back
                       //别忘了开启混合
			Blend SrcAlpha OneMinusSrcAlpha

			CGPROGRAM
			...
			ENDCG
		}
	}
	FallBack "Transparent/VertexLit"
}

可发现,渲染的很完美,此时我试试关闭深度测试(实际上绝对不会关闭深度测试的!)

还是正确的渲染出来了,其中Unity可能做了什么手脚,比如强制性开启了深度测试或者目前这个例子无法验证,实际上是绝对!不会关闭深度测试的!因为想保证渲染顺序还是要经过一个测试才能确保,关闭深度测试后,所有物体都能够渲染,不管一个是否被遮挡,统统都会渲染,这时肯定就会出问题,除非你人为控制每一个物体不会产生遮挡、重叠等现象。(impossible!)

五、开启深度写入的半透明效果

写在尾部是因为,我们在前面理解了,半透明是要关闭深度写入的,而且也弄明白了,怎么关闭深度写入后进行双面渲染。

(在冯乐乐的开源项目中,一个名为:Knot 的物体)

现在有这么一个物体,注意它是一个整体!我们用普通的半透明混合方式实现透明化(即透明度混合方式,要关闭深度写入!)

这次给出完整代码(透明度混合):

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "MilkShader/Eight/KnotAlphaBlend"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_AlphaScale ("Alpha Scale ", Range(0,1)) = 0.5
		_Color ("Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }
		ZWrite Off
		Pass
		{			
			Tags { "LightMode" ="ForwardBase" }
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

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

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			fixed4 _Color;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{	
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));				
				fixed4 color = tex2D(_MainTex, i.uv);
				fixed3 albedo = color.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));				
				return fixed4(ambient + diffuse, color.a * _AlphaScale);
			}
			ENDCG
		}
	}
	FallBack "Transparent/VertexLit"
}

【Shader入门精要】Unity中的基础光照 进行了详细的解释上方的漫反射代码+透明度混合;

可发现:物体虽然实现了半透明,但是内部的渲染顺序乱了,什么你没发现乱了?好吧!我给出下图改良后的!

这种就是开启了深度写入的透明度混合效果,看起来没上面那个帅,但是问题是解决了!

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "MilkShader/Eight/KnotAlphaBlend"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_AlphaScale ("Alpha Scale ", Range(0,1)) = 0.5
		_Color ("Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }
        //第一个Pass进行只写入片元深度,好让Unity能确定像素之间的渲染顺序!
		Pass
		{
			ZWrite On   //开启深度写入
			ColorMask 0 //不进行输出像素颜色值
		}
        //下面的Pass 和原本的一样
		Pass
		{			
			//ZWrite Off  //我注释掉了这个,因为Transparent是默认关闭深度写入的		
			Tags { "LightMode" ="ForwardBase" }
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

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

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			fixed4 _Color;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{	
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));				
				fixed4 color = tex2D(_MainTex, i.uv);
				fixed3 albedo = color.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));				
				return fixed4(ambient + diffuse, color.a * _AlphaScale);
			}
			ENDCG
		}
	}
	FallBack "Transparent/VertexLit"
}

神奇的事情发生了!我经过一个测试,如下代码:

// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld'

Shader "MilkShader/Eight/KnotAlphaBlend"
{
	Properties
	{
		_MainTex ("Texture", 2D) = "white" {}
		_AlphaScale ("Alpha Scale ", Range(0,1)) = 0.5
		_Color ("Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "Queue"="Transparent" "RenderType"="Transparent" }
		Pass
		{
            //我注释掉了深度写入,也就是会变成关闭深度写入?
			//ZWrite On
			ColorMask 0
		}
		Pass
		{			
			//ZWrite Off			
			Tags { "LightMode" ="ForwardBase" }
			Blend SrcAlpha OneMinusSrcAlpha
			CGPROGRAM			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "UnityCG.cginc"
			#include "Lighting.cginc"

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

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

			sampler2D _MainTex;
			float4 _MainTex_ST;
			fixed _AlphaScale;
			fixed4 _Color;
			
			v2f vert (appdata v)
			{
				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				o.worldNormal = UnityObjectToWorldNormal(v.normal);				
				return o;
			}
			
			fixed4 frag (v2f i) : SV_Target
			{	
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));				
				fixed4 color = tex2D(_MainTex, i.uv);
				fixed3 albedo = color.rgb * _Color.rgb;
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;
				fixed3 diffuse = _LightColor0.rgb * albedo * saturate(dot(worldNormal, worldLightDir));				
				return fixed4(ambient + diffuse, color.a * _AlphaScale);
			}
			ENDCG
		}
	}
	FallBack "Transparent/VertexLit"
}

按照我的理解,Transparent渲染队列是默认关闭深度写入的,所以此时第一个Pass应该是关闭深度写入的,但是,如上图所示,第一个Pass还是履行了之前开启深度写入的职责(即写入了深度确保渲染顺序正确),这就实测打脸,Transparent并非是默认关闭渲染写入的,上图代码注释掉了ZWrite On还正常的原因是因为默认ZWrite就是开启的!

当我把第一个Pass修改为如下,关闭深度写入!

此时,第一个Pass的作用就没了,导致发生了渲染错乱问题。

可能,有人不懂我为什么会认为Transparent渲染队列的物体会默认关闭深度写入,那是因为我在实现透明度混合时,发现不写ZWrite Off,也是正常渲染了,而且还不会挡住后面的“不透明物体”!此时我理所当然地认为,Unity默认给Transparent渲染队列上的物体关闭了深度写入!不然怎么可能不会挡住后面的物体呢!但是!我忽略了一点,那就是不透明物体是在所有半透明物体渲染之前进行的渲染。(我TM!扇了自己一巴掌)

你可以自行测试一下,放一个半透明物体和一个不透明物体,不透明物体在半透明物体后面,然后你将半透明物体改成开启深度写入,会发现完全没问题。

                         

左图是关闭深度写入的,  右图是开启深度写入的

是不是没有任何区别?所以我当时就直接以为Unity将它强制改为了关闭深度写入。实际上没有!

                    

左图关闭深度写入                               右图开启深度写入

右图情况是因为开启了深度写入,导致最前面的透明正方箱子挡住了后面的长方形透明箱子。而不透明物体是最先渲染的所以没问题,正常显示。
2020年4月11日20:16:53
补充:“相交穿透的透明物体开启深度写入,其中,离摄像机越远的一方会遮挡另一方”

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值