透明效果二
对于我来说,透明效果的编写非常重要,甚至于比一些高级的渲染效果还要重要。那么我需要实现的效果就是,给一张含透明通道的贴图,能够实现双面渲染,并且双面不会出现透明效果的错误。之前很多次尝试都是错误的(主要是没有正儿八经的学过shader)因此,这次学了之后好好总结一下。
一、透明度混合
从名词中可以看出,透明度混合能够实现透明度与其他不透明物体的混合。为了实现半透明效果我们考虑到:
1、关闭了深度写入。如果不关闭深度写入的话,那么前面的物体就会完全的覆盖后面的,也就是说,靠近摄像机的物体会挡住原理摄像机的物体。这不是我们所需要的,我们需要就算是远一点的物体也能被渲染到,因此我们需要关闭深度写入。然而关闭深度写入会带来一些列的麻烦。
2、我们希望的是,远一点的物体能够先渲染,这样就能实现先渲染远一点的,再将现在的透明颜色与现有的颜色混合。实现这个目的也是我们为什么要关闭时深度写入的原因。先渲染的颜色通过了深度测试存到了颜色缓冲中,而后渲染的颜色,在受到关闭深度写入后,也到了颜色缓冲中,将两者混合得到了我们的半透明效果。
3、透明度混合需要开启混合。开启混合很简单,然后还需要设置混合方式。在unity中混合方式通过BlendOp来设置,设置后再通过设置混合因子来计算:
BlendOp Add
Blend SrcAlpha OneMinusSrcAlpha
因为默认就是Add,所以第一句话其实可以忽略,而第二句话就是设置计算因子。计算因子是影响最后渲染效果的重要参数。
二、透明度混合例子
Shader "Custom/4.1.2" {//贴图效果,但是在复杂模型中存在问题
Properties{
_Color("Color Tint", Color) = (1, 1, 1, 1)
_MainTex("Main Tex",2D) = "white"{}
_AlphaScale("Alpha Value",Range(0,1)) = 0.5
}
SubShader{
Tags{"Queue"="AlphaTest" "IgnoreProjector" = "True" "RenderType"="Transparent"}//透明度测试的标签
Pass{
Tags{ "LightMode" = "ForwardBase" }
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
fixed4 _Color;
sampler2D _MainTex;
float4 _MainTex_ST;//一张贴图就应该对应一个_ST变量,可以共用一个缩放变量
fixed _AlphaScale;
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.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;//缩放后再位移
//为之后计算凹凸纹理做准备先将光线和视线方向转到切线空间
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 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
fixed4 texColor = tex2D(_MainTex, i.uv);
//开始剔除
fixed3 albedo = texColor.rgb * _Color;
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo;//环境光
fixed3 diffuse = _LightColor0.rgb * ambient * max(0, dot(worldNormal, lightDir));//漫反射
return fixed4(ambient + diffuse, texColor.a * _AlphaScale);
}
ENDCG
}
}
FallBack "Specular"
}
首先,透明度混合需要设置渲染方式:”RenderType”=”Transparent”。然后设置关闭深度写入以及混合方式:
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
最后在输出的时候,再片元着色器中返回需要的透明度。所需要的设置缺一不可,不可缺少但是可以修改内容。
三、双面透明度渲染
双面透明度渲染是我最感兴趣的地方。实现双面渲染其实很简单,只需要添加Cull Off就行了,但是如果需要添加透明效果的时候,由于一个物体的双面渲染的流程不同就会出现问题:
两个相同材质的物体,就会出现背面渲染的失误。
如果通过双pass先渲染前面,后渲染后面也会出现问题:
只有先渲染后面,再渲染前面才能得到正确的效果:
图上面方块是先渲染前面再渲染后面,然后混合。下面方块是先渲染后面,再渲染前面,最后混合。得到结果中可以看出,下面方块效果更加真实。
这里需要用双pass渲染,因为在subshder中,每一个pass都回依次执行,适合这种一步一步渲染的需求。