实时渲染中实现透明效果,通常会在渲染模型时控制透明通道。当开启透明混合后,一个物体被渲染到屏幕上时,每个片元除了颜色值和深度值之外,还有另外的属性,透明度。透明度为1表示像素完全不透明,当为0时,表示像素完全不显示。
Unity中有两种方法实现透明效果,一种是透明度测试,这种方法无法得到真正的半透明效果,另一种是透明度混合。
对于不透明物体,不考虑渲染顺序也能得到正确的排序效果,是因为强大的深度缓冲的存在。实时渲染中,深度缓冲用于解决可见性问题,决定了哪个物体的哪些部分会被渲染在前面,哪些部分会被其他物体遮挡。基本思想是,根据深度缓存中的值来判断该片元距离摄像机的距离,渲染一个片元时,需要把他的深度值和已经存在于深度缓冲中的值比较(开启深度测试),如果他的值距离摄像机更远,就说明这个片元不应该被渲染在屏幕上。否则,这个片元应该覆盖掉此时颜色缓冲中的像素值,并把他的深度更新到深度缓冲(开启了深度写入)但如果想要实现透明效果,就没那么简单,因为使用透明度混合时,关闭了深度写入(ZWrite)
透明度测试和混合的基本原理
透明度测试:采用一种极端的机制,一个片元透明度不满足条件(通常小于某个阈值),他对应的片元就会被舍弃。被舍弃的片元不会再进行任何处理,也不会对颜色缓冲产生任何影响。否则就会按照不透明物体的处理方式来处理,进行深度测试,深度写入。也就是说,透明测试不需要关闭深度写入,跟其他不透明物体最大的不同就是会根据透明度舍弃一些片元。虽然简单,产生的效果也很极端,要么完全透明,要么完全不透明。
透明度混合:使用当前片元的透明度作为混合因子,与已经存在存储在颜色缓冲中的颜色进行混合,得到新的颜色。但是半透明混合需要关闭深度写入,使得我们要非常小心物体的渲染顺序。需要注意的是,透明度混合只关闭了深度写入,没有关闭深度测试。意味着,我们使用透明度混合渲染一个片元时,还是会比较他的深度值与当前深度缓冲中的深度值,如果深度值距离摄像机更远,就不会再进行混合操作。这一点决定了当一个不透明物体出现在一个透明物体的前面,我们 先渲染了不透明物体,仍然可以正常的遮挡透明物体,也就是说,对于透明度混合来说,深度缓冲是只读的。
渲染顺序
透明度混合技术,需要关闭深度写入,这时候需要小心处理透明物体的渲染顺序。如果不关闭深度写入,一个半透明表面背后的表面本来是可以透过他看到的,但是由于深度测试判断结果是该半透明表面距离摄像机更近,导致后面的表面将会被剔除,也就无法透过半透明表面看到后面的物体了。因此关闭深度写入和渲染顺序就变得非常重要
场景两个物体A和B A半透明 B不透明
1.先渲染B再渲染A。
由于不透明物体开启深度测试和写入,此时的缓冲中没有任何有效数据。因此B会先写入颜色缓冲和深度缓冲。随后渲染A,透明物体仍然会进行深度测试,因此我们发现和B相比A距离摄像机更近,因此我们会使用A的透明度来和颜色缓冲中B的颜色进行混合,得到正确的半透明效果。
2先渲染A再渲染B
渲染A时候,深度缓冲区中没有任何数据,因此A直接写入颜色缓冲,但是由于对半透明物体关闭了深度写入,因此A不会修改深度缓冲,等到渲染B,B进行深度测试,B发现深度缓冲还没有数据,就直接写入颜色缓冲,结果就是B会直接覆盖掉A的颜色。视觉上来看,B就出现在A前面,错误的效果。
因此,应该在不透明物体渲染完之后再渲染半透明物体。
假设场景中两个物体A和B都是半透明
1.先渲染B 再渲染A 那么B就会正常写入颜色缓冲,A会和颜色缓冲中的B颜色混合,得到正确结果
2.先渲染A 再渲染B A先写入 B会和A进行混合,结果相反,看起来B再A前面,就是错误的半透明效果。
因此渲染引擎一般都会对物体进行排序,再渲染,常用方法是:
1.先渲染不透明物体 并且开启深度测试和写入
2.半透明物体按距离摄像机的远近进行排序,然后按照从后往前的顺序渲染这些半透明物体,并且开启深度测试,但关闭深度写入。
但是如果半透明物体相互重叠,上面第二条并不能解决,这时候选择把物体拆分为两个部分再进行排序。
Unity Shader渲染的顺序
Unity提供了渲染队列这一解决方案,我们可以使用SubShader的Queue标签来决定我们的模型归于哪个渲染队列。Unity在内部使用一系列的整数索引来表示每个渲染队列,并且索引号越小,越早被渲染。
因此,想要通过透明度测试来实现透明效果,
SubShader
{
Tags { "Queue" = "AplhaTest"}
Pass {
}
}
想要通过透明度混合来实现透明效果 代码应该包括类似下面代码
SubShader
{
Tags { "Queue" = "Transparent"}
Pass {
ZWrite Off
}
}
ZWrite Off关闭深度写入,也可以写再SubShader 这样该SubShader下的Pass都会关闭深度写入。
透明度测试
只要一个片元透明度不满足条件,那么对应的片元就会被舍弃,被舍弃不再进行任何处理,也不会对颜色缓冲产生任何作用;否则,就会按普通的不透明物体的处理方式对待。
通常,会在片元着色器中使用clip函数来进行透明度测试。clip是cg的一个函数
函数 void clip(float4 x);void clip(float3 x);void clip(float2 x);void clip(float1 x);void clip(float x);
参数 裁剪使用标量或者矢量
描述 如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。
Shader "Unlit/Alpha Test"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Tint",Color) = (1,1,1,1)
_Cutoff ("Alpha Cutoff",Range(0,1)) = 0.5 //决定我们调用Clip进行透明度测试使用的判断条件 范围[0,1] 因为纹理像素的透明度在这个范围内
}
SubShader
{
Tags { "Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout"}
Pass {
Tags {"LightMode" = "ForwardBase"}
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _Cutoff;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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);
//Alpha Test
clip(texColor.a - _Cutoff);
//Equal to
//if ((texColor.a-_Cutoff)<0.0)
//{
// discard;
//}
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,1.0);
}
ENDCG
}
}
Fallback "Transparent/Cutout/VertexLit"
}
Unity在透明度测试使用的渲染队列是名为AlphaTest的队列,因此我们需要把Queue标签设置为AlphaTest。RenderType标签通常被用于着色器替换功能。我们还把IgnoreProjector设置为True,意味着Shader不会受到投射器影响,通常使用了透明度测试的Shader都应该设置这三个标签,最后LightMode标签是Pass标签的一种,用于定义该Pass在Unity光照流水线的角色。
上面提到Clip函数的定义,判断参数是否为负数,如果为负数就会舍弃该片元的输出,也就是说当texColor.a小于材质参数_Cutoff时,片元完全透明。
透明度测试得到的效果很极端,要么完全透明,要么完全不透明,效果就像在一个不透明物体上挖了一个空洞,得到透明效果边缘处参差不齐,有锯齿,这个是精度问题,为了得到更加柔滑的透明效果,可以使用透明度混合。
透明度混合
这种方法可以得到正真的半透明效果,会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入,也要小心物体的渲染顺序。为了进行混合,需要使用Unity的混合命令。Blend是Unity的混合命令,实现半透明效果就需要把当前自身的颜色和已经存在于颜色缓冲中的颜色进行混合,混合时使用的函数就是由该指令决定的,下表给出Blend命令语义:
本节使用 Blend SrcFactor DstFactor混合,这个命令在设置混合因子的同时也开启了混合模式,只有开启了混合,片元的透明通道才有意义。我们会把源颜色的混合因子SrcFactor设为SrcAlpha 目标颜色的混合因子DstFactor为OneMinusSrcAlpha,意味着混合后的新颜色是
Shader "Unlit/Alpha Blend"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Tint",Color) = (1,1,1,1)
_AlphaScale("Alpha Scale",Range(0,1)) = 1
}
SubShader
{
Tags { "Queue" = "Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent"}
Pass {
Tags {"LightMode" = "ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha //同时开启混合命令 如果不用Blend 透明度没有意义
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _AlphaScale;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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
}
}
Fallback "Transparent/VertexLit"
}
使用新的属性_AlphaScale来替代原来的_Cutoff属性。_AlphaScale用于在透明纹理的基础上控制整体的透明度。
透明度混合的渲染队列是Transparent。因此需要把Queue标签设置为Transparent。RenderType标签可以让Unity把这个Shader归入提前定义的组,用来指明该Shader是一个使用了透明度混合的Shader。同样该Shader也不受投影器的影响,通常使用了透明度混合的Shader都应该在SubShader中设置这三个标签。
还需要在Pass中为透明度混合进行合适的混合状态设置。Pass的深度写入关闭,开启并设置Pass的混合模式。
偏远着色器移除了透明度测试代码,设置了该片元着色器返回值中的透明通道,只有使用Blend命令打开混合后,设置透明通道才有意义,否则不会对片元的透明有任何效果。
开启深度写入的半透明效果
由于关闭深度写入而造成错误排序的情况:一种解决办法是使用两个Pass来渲染模型,第一个Pass开启深度写入,但不输出颜色,目的仅仅是为了把该模型的深度值写入深度缓冲中;第二个Pass进行正常的透明度混合,由于上一个Pass已经得到了逐像素的正确深度信息,该Pass就可以按照像素级别的深度排序结果进行透明渲染。但这种方法缺点,多使用了一个Pass会对性能造成一定影响。
Shader "Unlit/Alpha BlendZWriteOn"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Tint",Color) = (1,1,1,1)
_AlphaScale("Alpha Scale",Range(0,1)) = 1
}
SubShader
{
Tags { "Queue" = "Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent"}
Pass {
ZWrite On
ColorMask 0
}
Pass {
Tags {"LightMode" = "ForwardBase"}
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha //同时开启混合命令 如果不用Blend 透明度没有意义
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _AlphaScale;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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
}
}
Fallback "Diffuse"
}
新增加Pass目的仅仅是为了把模型的深度信息写入深度缓冲,从而剔除模型中被自身遮挡的片元。因此,Pass的第一行开启了深度写入,第二行使用了ColorMask,在ShaderLab中ColorMask用于设置颜色通道的写掩码,语义如下
ColorMask RGB | A | 0 | 其他任何RGBA组合
ColorMask为0 意味该Pass不写入任何通道颜色,不会输出颜色,该Pass只需写入深度缓存即可。
ShaderLab混合命令
混合还有很多其他用处,不仅仅用于透明度混合。
当片元着色器产生一个颜色的时候,可以选择与颜色缓冲中的颜色进行混合。这样一来,混合就和两个操作数有关:源颜色和目标颜色。源颜色S,是指片元着色器产生的颜色;目标颜色D,是指从颜色缓冲中读取到的颜色。进行混合得到的颜色,用O表示,重新写入颜色缓冲。颜色混合包含了RGBA四个通道,并非仅仅是RGB通道。使用之前一定要开启混合。
混合等式和参数
混合时一个逐片元操作,不可编程可配置。
已知两个操作数,源颜色S和目标颜色D,得到颜色O就必须使用一个等式来计算。当进行混合时候,必须使用两个混合等式,一个用于混合RGB一个用于混合A通道。当设置混合状态时,实际上设置的就是混合等式中的操作和因子。在默认情况下,混合等式使用的操作都是加操作,只需要在设置一下混合因子即可。由于需要两个等式,每个等式有两个因子,因此一共需要四个因子,下表给出Shaderlab中设置混合因子命令。
可以发现,第一个命令只提供了两个因子,意味着将使用同样的混合因子来混合RGB通道和A通道,此时SrcFactorA将等于SrcFactor,DstFactorA将等于DstFactor,下面就是使用这些因子进行加法混合时候使用的混合公式
那么这些混合因子有哪些值,下面给出了支持的几种混合因子
使用上面的指令进行设置时,RGB通道的混合银子和A通道的混合因子都是一样的,如果希望使用不同的参数混合A通道,这时候可以利用Blend SrcFactor DstFactor,SrcFactorA DstFactorA指令。如果混合后,输出颜色的透明度就是源颜色的透明度,可以使用下面命令:
Blend SrcAlpha OneMinusSrcAlpha, One Zero
混合操作
当把源颜色和目标颜色与他们对应的混合因子相乘后,把他们的结果加起来作为输出颜色,也选择不使用加法,使用减法,使用ShaderLab的BlendOp BlendOperation命令,混合操作命令,下表给出了ShaderLab支持的混合操作
混合操作命令通常是与混合因子命令一起工作,需要注意的是,使用Min和Max混合操作时,混合因子实际上是不起任何作用,仅会判断原始的源颜色和目的颜色之间的比较
常见的混合类型
通过混合操作和混合因子命令的组合,可以得到一些类似Ps混合模式中的混合效果
双面渲染透明效果
现实生活,一个物体是透明的,意味着不仅可以透过他看到其他物体的样子,也可以看到他内部的结果,之前实现的透明效果中,无论是透明度测试还是透明度混合,都无法观察到正方体内部以及背面的形状,导致物体看起来就好像只有半个一样。这是因为,默认情况下渲染引擎剔除了物体背面的渲染图元,只渲染了物体的正面,如果想要得到双面渲染的效果,可以使用Cull指令来控制,Unity中,Cull指令的语法如下;
设置为Back 背对摄像机的渲染图元就不会被渲染,这也是默认情况下的剔除状态;如果设置为Front,那么朝向摄像机的渲染图元就不会被渲染;如果设置为Off就会关闭剔除共嗯,所有的渲染图元都会被渲染,但由于这时候需要渲染的图元数目会成倍增加,因此除非用于特殊效果,通常不会关闭剔除功能。
透明度测试的双面渲染
Shader "Unlit/Alpha Test Cull Off"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Tint",Color) = (1,1,1,1)
_Cutoff ("Alpha Cutoff",Range(0,1)) = 0.5 //决定我们调用Clip进行透明度测试使用的判断条件 范围[0,1] 因为纹理像素的透明度在这个范围内
}
SubShader
{
Tags { "Queue" = "AlphaTest" "IgnoreProjector"="True" "RenderType" = "TransparentCutout"}
Pass {
Tags {"LightMode" = "ForwardBase"}
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _Cutoff;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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);
//Alpha Test
clip(texColor.a - _Cutoff);
//Equal to
//if ((texColor.a-_Cutoff)<0.0)
//{
// discard;
//}
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,1.0);
}
ENDCG
}
}
Fallback "Transparent/Cutout/VertexLit"
}
增加一行 Cull Off即可
透明度混合双面渲染
和透明度测试相比,透明度混合会更复杂一些,因为透明度混合需要关闭深度写入,想要得到正确的透明效果,渲染顺序非常重要,保证图元从后往前渲染。透明度测试来说没有关闭深度写入,因此可以利用深度缓冲按逐像素的粒度进行深度排序,从而保证渲染的正确性。然而一旦关闭了深度写入,就需要小心的控制渲染顺序来得到正确的深度关系。为此,选择把双面 渲染的工作分为两个pass,第一个只渲染背面,第二个只渲染正面,由于Unity会顺序执行SubShader中的各个Pass,因此可以保证背面总是在正面之前渲染,保证了正确的深度渲染关系
Shader "Unlit/Alpha Blend"
{
Properties
{
_MainTex ("Main Tex", 2D) = "white" {}
_Color ("Main Tint",Color) = (1,1,1,1)
_AlphaScale("Alpha Scale",Range(0,1)) = 1
}
SubShader
{
Tags { "Queue" = "Transparent" "IgnoreProjector"="True" "RenderType" = "Transparent"}
Pass {
Tags {"LightMode" = "ForwardBase"}
Cull Back
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha //同时开启混合命令 如果不用Blend 透明度没有意义
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _AlphaScale;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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"}
Cull Front
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha //同时开启混合命令 如果不用Blend 透明度没有意义
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
fixed _AlphaScale;
sampler2D _MainTex;
float4 _MainTex_ST;
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 = UnityObjectToClipPos(v.vertex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld,v.vertex).xyz;
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
}
}
Fallback "Transparent/VertexLit"
}