这个demo放出来之后,对其中的角色阴影部分的技术十分感兴趣,我就赶紧下过来研究了一下。官方的技术博客里有对这一部分技术的介绍,链接在这里:独特的角色阴影,
其中写到只要在其他shader里添加
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow_ShadowSample.cginc"
这两行代码就能使用这个高级阴影了。 我之前也因为在做一些demo的时候发现unity默认阴影达不到理想效果,和ue4比起来还是有一定差距的。于是就想到使用这个阴影技术了。于是我兴致勃勃的把这两行代码和几个库文件加到我的shader和项目里去,一运行发现,高级阴影并没有出现。哎,看样子只能自己研究了。
我们发现原来有一个非常简单的方法来对常见的Unity shader所用的阴影方法进行重写。 于是我琢磨着应该是他们也用了这三个方法,于是打开 UniqueShadow_ShadowSample.cginc 文件,果不其然,在最下面找到了这么一段代码
#if defined(UNITY_PASS_FORWARDBASE) || defined(UNITY_PASS_FORWARDADD) || defined(UNIQUE_SHADOW_FORCE_REPLACE_BUILTIN)
#undef SHADOW_COORDS
#undef TRANSFER_SHADOW
#undef SHADOW_ATTENUATION
#define SHADOW_COORDS(i) UNIQUE_SHADOW_INTERP(i)
#define TRANSFER_SHADOW o.uniqueShadowPos = mul(u_UniqueShadowMatrix, float4(worldPos.xyz, 1.f));
#define SHADOW_ATTENUATION(i) UNIQUE_SHADOW_SAMPLE(i);
#endif
这里的意思就是,如果判断pass的名字是
UNITY_PASS_FORWARDBASE、NITY_PASS_FORWARDADD、UNIQUE_SHADOW_FORCE_REPLACE_BUILTIN
这三个的话,就会把
SHADOW_COORDS(i) 替换成UNIQUE_SHADOW_INTERP(i),
SHADOW_ATTENUATION(i)替换成UNIQUE_SHADOW_SAMPLE(i);
TRANSFER_SHADOW的值替换成后面的 o.uniqueShadowPos
这样的话就好说了,之后只要把自己的shader里生成阴影的pass名字改成三个里的任意一个,生成阴影的代码还是按照原来的方式写就可以了。
于是我立马新建了一个Unlit Shader,添加上生成阴影的代码后,把pass的名字改成了UNITY_PASS_FORWARDBASE,代码如下
Shader "Custom/simpleSuperShadow"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags { "RenderType"="Opaque" }
LOD 100
Pass
{
Tags{ "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_fwdbase
#include "UnityCG.cginc"
#include "AutoLight.cginc"
#define UNITY_PASS_FORWARDBASE
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
struct v2f
{
float2 uv : TEXCOORD0;
float4 pos : SV_POSITION;
float3 worldPos : TEXCOORD1;
SHADOW_COORDS(2)
};
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(_Object2World, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
fixed4 frag (v2f i) : SV_Target
{
// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
fixed shadow = SHADOW_ATTENUATION(i);
return col * shadow;
}
ENDCG
}
}
FallBack "Diffuse"
}
点击运行之后,shader报错,提示我顶点函数里worldPos未定义,报错的地方是在TRANSFER_SHADOW(o)这一行,但是我明明在v2f里定义了这个变量,并且在上一行赋值了才对。这个提示不应该出现才对。对于这个问题,我毫无头绪,不知道自己哪里出了问题。于是我又新建了一个standard surface shader,这次我只在里面添加了
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
两行,运行一下,居然成功了,于是我在编辑器里点开了surface shader的原始代码,通过仔细对比阴影生成的三个函数发现,surface shader在
顶点函数使用worldPos的时候,并不是直接给o.worldPos赋值的,而是先定义了一个float3的worldPos变量,计算出来以后再让o.worldPos = worldPos,这里感觉应该是一样的才是,并没有什么特别的地方啊。虽然我不是很清楚surface shader里为啥要这么写,但是抱着试一试的心情,我把自己的shader的顶点函数从原来的
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
o.worldPos = mul(_Object2World, v.vertex).xyz;
TRANSFER_SHADOW(o);
return o;
}
这样,改成了
v2f vert (appdata_full v)
{
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
float3 worldPos = mul(_Object2World, v.vertex).xyz;
o.worldPos = worldPos;
TRANSFER_SHADOW(o);
return o;
}
这样,于是我运行一下。卧槽竟然成功了。按道理来说定义一个临时变量和直接赋值应该没有区别才是,为啥这里不定义一个就不行呢,
mul(_Object2Wrold,v.vertex).xyz返回的本来就是一个float3的变量才是,为毛非要这么做才能正确显示呢。实在是不知道要怎么解释。希望知道的大大们能够帮我解释一下。
总结一下这个高级阴影的使用方法
1.给计算阴影的pass添加
#define UNITY_PASS_FORWARDBASE
#pragma multi_compile _ UNIQUE_SHADOW UNIQUE_SHADOW_LIGHT_COOKIE
#include "UniqueShadow/UniqueShadow_ShadowSample.cginc"
这三行代码,然后在顶点函数使用TRANSFES_SHADOW的地方改成
float3 worldPos = mul(_Object2World, v.vertex).xyz;
o.worldPos = worldPos;
TRANSFER_SHADOW(o);
这样自定义的shader也能够使用这个超高分辨率的阴影了。
这里我用了一个卡通shader做测试
未使用高级阴影:
使用高级阴影:
这样就能做出媲美ue4的高级阴影来了。