Shader
// jave.lin 2019.06.05
Shader "Custom/Player"
{
Properties
{
_TintColor ("Tint Color", Color) = (1,1,1,1)
_Tex ("Tex", 2D) = "white" {}
_BackTex("BackTex", 2d) = "white" {}
_MaskColor ("Mask Color", Color) = (1,1,1,0.5)
_BackIntensity ("BackIntensity", Range(0.0, 1.0)) = 0.2
_FrontIntensity ("FrontIntensity", Range(0.0, 1.0)) = 0.2
}
SubShader
{
// Project Shadow
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
CGPROGRAM
#pragma vertex vertShadow
#pragma fragment fragShadow
#pragma multi_compile_shadowcaster
#include "UnityCG.cginc"
struct v2f
{
V2F_SHADOW_CASTER;
};
v2f vertShadow(appdata_base v)
{
v2f o;
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
return o;
}
fixed4 fragShadow(v2f i) : SV_Target
{
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
}
// Draw Back
Pass
{
Name "DrawBack"
Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWRITE Off
Cull Front
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile SHADOWS_SCREEN
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct a2f
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
float2 uv : TEXCOORD3;
fixed3 diffuse : COLOR0;
fixed3 ambient : COLOR1;
};
fixed4 _TintColor;
sampler2D _Tex;
float4 _Tex_ST;
float _BackIntensity;
v2f vert(a2f v)
{
v2f o = (v2f)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _Tex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.diffuse = _LightColor0.rgb * ((dot(lightDir,normalDir) + 1) * 0.5);
o.ambient = UNITY_LIGHTMODEL_AMBIENT;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
//fixed shadow = SHADOW_ATTENUATION(i);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 albedo = tex2D(_Tex, i.uv) * _TintColor.rgb;
fixed3 diffuse = i.diffuse * albedo * _BackIntensity;
atten = max(0.5, atten);
return fixed4(i.ambient + diffuse * atten, _TintColor.a);
}
ENDCG
}
// Draw Front
Pass
{
Name "DrawFront"
Tags { "Queue" = "Transparent" "RenderType" = "Transparent" }
Blend SrcAlpha OneMinusSrcAlpha
ZWRITE Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile SHADOWS_SCREEN
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
struct a2f
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal : NORMAL;
};
struct v2f
{
float4 pos : SV_POSITION;
float3 worldNormal : TEXCOORD0;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
float2 uv : TEXCOORD3;
fixed3 diffuse : COLOR0;
fixed3 ambient : COLOR1;
};
fixed4 _TintColor;
sampler2D _Tex;
float4 _Tex_ST;
float _FrontIntensity;
v2f vert(a2f v)
{
v2f o = (v2f)0;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _Tex);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 normalDir = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
o.diffuse = _LightColor0.rgb * ((dot(lightDir,normalDir) + 1) * 0.5);
o.ambient = UNITY_LIGHTMODEL_AMBIENT;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag(v2f i) : SV_TARGET
{
//fixed shadow = SHADOW_ATTENUATION(i);
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);
fixed3 albedo = tex2D(_Tex, i.uv) * _TintColor.rgb;
fixed3 diffuse = i.diffuse * albedo * _FrontIntensity;
atten = max(0.5, atten);
return fixed4(i.ambient + diffuse * atten, _TintColor.a);
}
ENDCG
}
// Draw Mask
Pass
{
Name "DrawMask"
ZTest Greater
ZWrite Off
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
};
struct v2f
{
float4 vertex : SV_POSITION;
};
float4 _MaskColor;
v2f vert (appdata v)
{
v2f o = (v2f)0;
o.vertex = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
return _MaskColor;
}
ENDCG
}
}
FallBack "Diffuse"
}
Runtime
- 下面的GIF中墙不是透明的
- 只有那个长方体是半透明的
- 图中可以看到有投影阴影
- 有遮挡绘制(一般很多3D游戏主角被挡住都会绘制一些半透明的轮廓)
查看内部的背面绘制
总结
- Unity 自带的Standard 材质的RenderMode:Fade/Transparent也是接收不了阴影的
- 而且Fade/Transparent中,没有绘制背面,对半透明物体来,效果不是很好,除非说半透明物体薄的像张纸,那还好一些
- 理论上是可以半透明 + 接收阴影的
- 甚至还可以实现半透明阴影的强弱,和投影的颜色
- 不过目前我对块底层不了解
因为目前我们是通过Unity 自带的 ShadowMap实现方式
ShadowMap大致原理:
- 将所有的需要投影的对象通过从灯光的角度来拍射出深度图
- 然后再开始渲染非透明对象,在片段着色阶段,得出像素的坐标,再转换到灯光坐标系,对比顶点坐标Z与灯光角度投影出来的深度图的值,如果深度图的值比顶点Z值小,说明此片段像素在阴影中,确认中阴影中后,在绘制时用一些数值来影响最终的RGB值(亮度)即可,阴影亮度会第一下所以讲RGB同时降低,这个降低系数值一般配置中定,如unity Light中的Shadow的Intensity决定,0.0~1.0,次值越小说明阴影越淡,越大则越暗的阴影,所以可以推断出,unity最终使用时:
// fragment phase
float _ShadowIntensity; // 类似上面的Intensity
fixed4 frag(...)
{
fixed4 color = ...; // may be : ambient + tex2D * diffuse * tintColor;
color.rgb = color.rgb * (1 - _ShadowIntensity);
return color;
}
unity中第一点是从shader中找第一个Pass的Tags { “LightMode” = “ShadowCaster” }的作为投影出深度图的处理。(如:我们上面提供的第一个Pass),如果用户自定义的Pass没找到,unity将会到Fallback中指定的内置Shader去找,如果没有指定Fallback shader或是再Fallback shader也没有找到,那么将取消此对象投影处理
再上面我们自定义的ShadowCaster 的Pass中,可以看到此Pass看不到太多的实现细节,基本都再#include对应的宏处理了(我尝试过看Unity的GCInclude下的shader,看得我云里雾里的,太多,如果要看某个宏,可以使用Sublime、Notepad++使用字符方式来搜索,即可马上看到对应代码)
如果说,可以控制处理ShadowCaster的Pass中,除了写入深度图缓存外,再将颜色值写入类似帧缓存的地方,在渲染对象是,读取深度图确定在阴影后,在读取帧缓存取颜色,那就可以实现带颜色的阴影叠加了(虽然这个透射光的功能可以用光线追踪来实现,但光线追踪性能消耗太大,一般只能用于离线渲染的非实时渲染),此外阴影强弱还可以通过帧缓存中的alpha来处理了
Shadow Map实现
我之后实现了ShadowMap:Unity Shader - Custom DirectionalLight ShadowMap 自定义方向光的ShadowMap
现在终于知道为何Unity内置的半透明物体为何接收不了阴影了,因为Unity的阴影是在:SSS(Screen Space Shadow,屏幕空间的阴影)中收集的。
而SSS需要先取屏幕不透明物体的深度纹理,然后对每个像素还原到WorldSpace,再转换到LightSpace处计算是否阴影中。
而我们半透明物体是不写深度的,所以屏幕深度纹理就没有半透明的信息。
这就是原因了。
我在那篇文章中有实现:
虽然可以接收阴影了。
但是半透明的物体,没有投射阴影是很奇怪的。
Project
链接: https://pan.baidu.com/s/1YNB1YykA0cXsKby3VWM4Qg 提取码: awdg 复制这段内容后打开百度网盘手机App,操作更方便哦
测试时的Unity 版本为2018.3.0f2
打开TriggerTestScene场景
选中Hierarchy中的"Player",就是我们GIF中的长方体