700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【unity shader 入门精要 读书笔记】透明

【unity shader 入门精要 读书笔记】透明

时间:2021-07-11 12:24:16

相关推荐

【unity shader 入门精要 读书笔记】透明

一、透明

1、透明度测试【Alpha Test】

它采用一种“霸道极端”的机制:只要有一个片元的透明度不满足条件【通常是小于某个阈值】,那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。

也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。

2、透明度混合【Alpha Blending】

这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储再颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这需要非常小心物体的渲染顺序。要注意的是,透明度混合只是关闭了深度写入,但没有关闭深度测试。这意味着,但使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果它的深度值距离摄像机更远,那么就不会再进行混合操作。这一点决定了,当一个不透明物体出现在一个透明物体的前面,如果先渲染了不透明物体,它仍然可以正常的遮挡住透明物体,也就是说,对于透明度混合来说,深度缓冲是只读的。

说明:

1、先渲染 B, B 会首先写如 颜色缓冲和深度缓。随后会渲染 A 。

如果这个时候,A开启了颜色缓冲和深度缓冲,会进行深度测试,发现 A 比 B 更靠近摄像机,然后就会使用 A 的透明度来和颜色缓冲中的 B的颜色进行混合,得到正确的半透明效果。

2、先渲染A,由于 A 关闭了深度缓冲和颜色缓冲,所以它不会把数据写入 深度缓冲和颜色缓冲,然后再渲染 B 的时候,由于深度缓冲和颜色缓冲中没有数据,B 进行写入的时候,就会把 造成 B 在 A 的前面 这样的错觉了。这是不正确的渲染顺序

1、先渲染 B, 再渲染 A , 那么 B 会正常的写入颜色缓冲,然后 A 会和颜色缓冲中的 B 颜色进行混合,得到正确的半透明效果。

2、先渲染 A ,再渲染 B。那么 A 会先写入 颜色缓冲,随后 B 会和颜色缓冲中的 A 进行混合,这样混合结果会完全反过来, 看起来就好像 B 在 A 的前面,得到的是错误的半透明效果。

从这个例子可以看出,半透明物体之间也是要符合一定的渲染顺序的。

基于这两点,渲染引擎一般都会对物体进行排序,再渲染。常用的方法是:

1、先渲染所有不透明物体,并开启它们的深度测试和深度写入。

2、把半透明物体按它们距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并开启它们的深度测试,但关闭深度写入。

但是,由于深度缓冲中的值其实是像素级别的,即每个像素有一个深度值,但是之前的例子都是对单个物体级别进行排序的,这意味着排序结果是,要么 A 全部在 B 前面渲染,要么 A 全部在 B 后面渲染。但如果存在循环重叠的情况,那么使用这种方法就永远无法得到正确的结果。

比如:

二、Unity Shader 的渲染顺序。

1、透明度测试【Alpha Test】

它采用一种“霸道极端”的机制:只要有一个片元的透明度不满足条件【通常是小于某个阈值】,那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理,也不会对颜色缓冲产生任何影响;否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。

也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。

Unity 为了解决渲染顺序的问题,提供了渲染队列 【render queue】这一解决方案。

可以使用 SubShader 的 Queue 标签来决定模型将归于哪个渲染队列。

Unity 在内部使用了一系列整数索引来表示 每个渲染队列,且索引号越小表示越早被渲染。如下:

其中 ZWrite Off 用于关闭深度写入,在这里选择把它写在 Pass 中。也可以把它写在

SubShader 中,这意味着该 SubShader 下的所有 Pass 都会关闭深度写入。

通常,会再片元着色器中使用 clip 函数来进行透明度测试。

clip 是 CG 中的一个函数。

void clip(float4 x);

void clip(float3 x);

void clip(float2 x);

void clip(float x);

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

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

void clip(float4 x)

{

if(any(x < 0))

discard;

}

Shader "Custom/AlphaTest" {Properties{_Color("Main Tint", color) = (1, 1, 1, 1)_MainTex("Mian Tex", 2d) = ""{}//_Cutoff 参数用于决定调用 clip 进行透明度测试的判断条件。[0, 1]_Cutoff("Alpha Cutoff", range(0, 1)) = 0.5}SubShader{//unity 中透明度测试使用的渲染队列是名为 AlphaTest 的队列,因此需要把//Queue 的标签设置为 AlphaTest , 而 RenderType 标签可以让 unity 把//这个 shader 归入提前定义的租【TransparentCutout】,以指明该 shader 是使用//了一个透明度测试的 shader。 //RenderType 标签通常用于着色器替换功能//把 IgnoreProjector 设置为 True 是为了让这个 shader 不会受到投影器【Projectors】//通常,使用了透明度测试的 shader 都应该再 SubShader 中设置这3个标签。Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}Pass{//LightMode 标签是Pass 标签中的一种,//它用于定义该 Pass 再 unity 的光照流水线中的角色//只有定义了正确的 LightMode,//才能够正确得到一些 unity 内置的光照变量,//例如_LightColor0Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;//由于 _Cutoff 的范围再[0, 1]之中,可以使用 fixed 的精度来定义它fixed _Cutoff;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);//在顶点着色器中计算处世界空间的法线方向和顶点位置//以及变换后的纹理坐标,再把它传递给片元着色器。o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightPos = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);//clip 函数非判断它的参数,即 texColor.a - _Cutoff是否为负数,//如果是,就会舍弃该片元的输出。clip(texColor.a - _Cutoff);//if((texColor.a - _Cutoff) < 0.0){//discard; //discard 指令用来显示的剔除片元。//}fixed3 albeod = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albeod;fixed3 diffuse = _LightColor0.rgb * albeod.rgb * max(0, dot(worldNormal, worldLightPos));return fixed4(ambient + diffuse, 1.0);}ENDCG}}//与之前的 Diffuse 和 Specular 不同,这次使用//Transparent/Cutout/VertexLit 来作为回调函数//这不仅能够保证写编写的 SubShader 无法在当前显卡工作//时可以适应的 代替 Shader,还可以保证使用透明度//测试的物体可以正确地向其他物体投射阴影。FallBack "Transparent/Cutout/VertexLit"}

从上图可以看出,透明度测试得到的透明效果很 “极端” ——要么完全透明,要么完全不透明,效果上往往像在一个不透明物体上挖了一个空洞。而且得到的透明效果在边缘处往往参差不齐,有锯齿,这是因为在边界处纹理的透明度变化精度问题。

为了更好的效果,可以使用 透明度 混合。

三、透明度混合

透明度混合:

这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲种的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,这使得必须非常小心物体的渲染顺序。

Blend 命令:

想要实现半透明的效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色值进行混合,混合时使用的函数就是由该指令决定的。

Blend SrcFactor DstFactor

开启混合,并设置混合因子。

源颜色【该片元产生的颜色】会 乘以 SrcFactor,

而目标颜色【已经存在于颜色缓存的颜色】会乘以 DstFactor,

然后把两者相加后再存入颜色缓冲中。

简单表示:

CurColor * SrcFactor + LastColor * DstFactor

在这里,会使用第二种语义,即 Blend SrcFactor DstFactor 进行混合。

需要注意的是,这个命令在设置混合因子的同时也开启了混合模式。

这是因为,只有开启了混合之后,设置片元的透明通道才有意义。

在使用这个命令的时候,unity会帮我们自动打开了。

这里例子会把源颜色的混合因子 SrcFactor 设为 SrcAlpha ,

而目标颜色的混合因子 DstFactor 设为 OneMinusSrcAlpha。

这意味着,经过混合后的新颜色是:

Shader "Custom/AlphaBlend" {Properties{_Color("Main Tint", color) = (1, 1, 1, 1)_MainTex("Main Tex", 2d) = "" {}//使用一个新的属性_AlphaScale 用于在透明纹理的基础上控制整体的透明度。_AlphaScale("Alpha Scale", range(0, 1)) = 1}SubShader{//前面内容中已经知道在 unity 中透明度混合使用的渲染队列是名为// Transparent 的,因此需要把 Queue 标签设置为 Transparent。//RenderType 标签可以让 unity 把这个shader归入到提前定义的组//【这里就是 Transparent】中,用来指明该 shader 是一个使用了//透明度混合的 shader。Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}//设置合适的透明度混合状态//关闭深度写入【作用是不把当前的内容写进去,是显示之前的内容】//【简单来说,就是显示在这个物体后面的物体(相对于摄像机来说)】//【Blend SrcAlpha OneMinusSrcAlpha 设置混合的值】ZWrite Off//将源颜色【该片元着色器产生的颜色】的混合因子设为//SrcAlpha//把目标颜色【已经存在于颜色缓冲中的颜色】的混合因子设为//OneMinusSrcAlpha//以得到合适的半透明效果Blend SrcAlpha OneMinusSrcAlphaPass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;} fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb *_Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));//只有开启了Blend 混合命令后,这里设置 透明通道才有意义//否则,这些透明度并不会对片元的透明效果有任何影响。return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}Fallback "Transparent/VertexLit"}

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

由于关闭了深度写入,当模型本身有复杂的遮挡关系或是包含了复杂的非凸网格的时候,就会有各种各样因为排序错误而产生的错误的透明效果。【例如上图】

一种解决办法是使用两个 Pass 来渲染模型:

第一个 Pass 开启深度写入,但不输出颜色,它的目的仅仅是为了把该模型的深度值写入深度缓冲中,

第二个Pass进行正常的透明度混合.

由于上一个 Pass 已经得到了逐像素的深度信息, 该 Pass 就可以按照像素级别的深度排序结果进行透明渲染。

但这种方法的缺点在于,多使用一个 Pass 会对性能造成一定的影响。而且,

模型内部之间不会有任何真正的半透明效果。

Shader "Custom/AlphaBlendZWrite" {Properties{_Color("Main Tint", color) = (1, 1, 1, 1)_MainTex("Main Tex", 2d) = "" {}_AlphaScale("Alpha Scale", range(0, 1)) = 1}SubShader{Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}ZWrite OffBlend SrcAlpha OneMinusSrcAlpha//新添加的 Pass 的目的仅仅为了把模型的深度信息写入深度缓冲中,//从而剔除模型中被自身遮挡的片元。Pass{//开启深度写入ZWrite On//ColorMask用于设置颜色通道的掩码(write mask)ColorMask 0}Pass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};struct v2f {float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;} fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb *_Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));//只有开启了Blend 混合命令后,这里设置 透明通道才有意义//否则,这些透明度并不会对片元的透明效果有任何影响。return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}Fallback "Transparent/VertexLit"}

一个新的渲染命令 —— ColorMask,

ColorMask 用于设置颜色通道的写掩码【write mask】,它的语义如下

ColorMask RGB | A | 0 | 其他任何R、G、B、A的组合

当 ColorMask 设为 0 时,意味着该Pass 不写入任何颜色通道,即不会输出任何颜色。

这正是所需要的 —— 该 Pass 只需写入深度缓存即可

【通过测试,ColorMask R(模型显示红色) G(模型显示绿色),以此类推】

五、 ShaderLab 的混合命令

如何实现混合?

当片元着色器产生一个颜色的时候,可以选择与颜色缓存中的颜色进行混合。这样一来,混合就和两个操作数有关:

源颜色(source color) :指的是由片元着色器产生的颜色值。【S】

目标颜色(destination color):指的是从颜色缓冲中读取到的颜色值。【D】

对它们进行混合后得到的输出颜色【O】,会重新写入到颜色缓冲中。

S、D、O 都包含了 ARGB 四个通道的数据。

混合等式:

当进行混合时,需要要使用两个混合等式:一个用于RGB通道,一个用于 A 通道。

当设置混合状态时,实际上设置的就是混合等式中的操作和因子。

默认情况下,混合等式使用的操作是加操作。【也可以使用其他操作】

第一个命令只提供了两个因子,这意味着将使用同样的混合因子来混合 RGB 通道和 A 通道。

即此时 SrcFactorA = SrcFactor,DstFactorA = DstFactor。效果就是如下:

下面的表给出了 ShaderLab 支持的几种混合因子:

混合操作:

在上面涉及到的混合等式中,当把源颜色和目标颜色与它们对应的混合因子相乘后,都是把它们的结果加起来作为输出颜色的。还有其他的操作命令:

BlendOp BlendOperation

例如:

需要注意的是: 当使用 Min 或 Max 混合操作时,

混合因子实际上是不起任何作用的,它们仅会判断原始的源颜色和目的颜色之间的比较结果。

常见的混合类型:

以及对应的效果:

六、双面渲染的透明效果

1、在前面的实现的透明效果中,无论是透明度测试,还是透明度混合,

都无法观察到物体内部以及背面的形状,导致物体看起来就好像只有半个,这是因为

在默认情况下,渲染引擎剔除了物体背面【相对于摄像机方向】的渲染图元,只是渲染了物体的正面。如果想要得到双面渲染的效果,可以使用 Cull 指令来控制需要剔除哪个面的渲染图元。

命令如下:

Back:物体背向摄像机的部分不会被渲染【默认状态】

Front :物体朝向摄像机的部分不会被渲染。

Off:所有的图元都会被渲染

Shader "Custom/AlphtTestBothSide" {Properties{_Color("Main Tint", color) = (1, 1, 1, 1)_MainTex("Mian Tex", 2d) = ""{}_Cutoff("Alpha Cutoff", range(0, 1)) = 0.5}SubShader{Tags{"Queue" = "AlphaTest" "IgnoreProjector" = "True" "RenderType" = "TransparentCutout"}//关闭渲染剔除cull offPass{Tags{"LightMode" = "ForwardBase"}CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _Cutoff;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float4 texcoord : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex).xyz;o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightPos = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);clip(texColor.a - _Cutoff);//if((texColor.a - _Cutoff) < 0.0){//discard; //discard 指令用来显示的剔除片元。//}fixed3 albeod = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albeod;fixed3 diffuse = _LightColor0.rgb * albeod.rgb * max(0, dot(worldNormal, worldLightPos));return fixed4(ambient + diffuse, 1.0);}ENDCG}}FallBack "Transparent/Cutout/VertexLit"}

2、

和透明度测试相比,想要浪透明混合实现双面渲染会更复杂一些,这是因为透明度混合需要关闭深度写入。

如果仍然采用关闭剔除功能,那么久无法保证同一个物体的正面和背面图元的渲染顺序,就有可能得到错误的半透明效果。

为此,得选择把双面渲染的工作分成两个Pass:

第一个 Pass 只渲染背面,

第二个 Pass 只渲染正面。

由于 Unity 会顺序执行SubShader 中的各个 Pass,因此可以保证背面总是在正面

被渲染之前渲染,从而得到正确的渲染关系。

Shader "Custom/AlphaBlendBothSide" {Properties{_Color("Color", color) = (1, 1, 1, 1)_MainTex("MianTex", 2d) = "" {}_AlphaScale("Alpha Scale", range(0, 1)) = 1}SubShader{Tags{"Queue" = "Transparent" "IgnoreProjector" = "True" "RenderType" = "Transparent"}Pass{Tags{"LightMode" = "ForwardBase"}ZWrite offBlend SrcAlpha OneminusSrcAlpha //先渲染剔除前面的 Pass cull front CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}Pass{Tags{"LightMode" = "ForwardBase"}ZWrite offBlend SrcAlpha OneminusSrcAlpha //再渲染剔除后面的 Pass//【其实也可不剔除,视角效果无差别,主要是为了性能】cull back CGPROGRAM#pragma vertex vert#pragma fragment frag#include "Lighting.cginc"fixed4 _Color;sampler2D _MainTex;float4 _MainTex_ST;fixed _AlphaScale;struct a2v{float4 vertex : POSITION;float3 normal : NORMAL;float2 texcoord : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldNormal : TEXCOORD0;float3 worldPos : TEXCOORD1;float2 uv : TEXCOORD2;};v2f vert(a2v v){v2f o;o.pos = mul(UNITY_MATRIX_MVP, v.vertex);o.worldNormal = UnityObjectToWorldNormal(v.normal);o.worldPos = mul(_Object2World, v.vertex);o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);return o;}fixed4 frag(v2f i) : SV_Target{fixed3 worldNormal = normalize(i.worldNormal);fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));fixed4 texColor = tex2D(_MainTex, i.uv);fixed3 albedo = texColor.rgb * _Color.rgb;fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(worldNormal, worldLightDir));return fixed4(ambient + diffuse, texColor.a * _AlphaScale);}ENDCG}}}

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。