欢迎来到我的博客
本篇文章主要是对这篇博客——Unity Shader-遮挡处理——的最后一种方法进行优化
主要添加了接受阴影以及投射阴影部分的处理。
以下是我进行了部分优化的截图:
用的格式工厂,结果动图糊成狗了
首先是代码,这里的代码我已经基于原来博客的做了改进,加了接受阴影和投射阴影,修改的部分我会在代码里详细注释
Shader "Learn/OcclusionDissolve"
{
Properties
{
_Diffuse("Diffuse", Color) = (1,1,1,1)
_DissolveColorA("Dissolve Color A", Color) = (0,0,0,0) //边缘颜色a
_DissolveColorB("Dissolve Color B", Color) = (1,1,1,1) //边缘颜色b
_MainTex("Base 2D", 2D) = "white"{}
_DissolveMap("DissolveMap", 2D) = "white"{} //噪声贴图
_DissolveThreshold("DissolveThreshold", Range(0,2)) = 0
_ColorFactorA("ColorFactorA", Range(0,1)) = 0.7 //边缘颜色a宽度
_ColorFactorB("ColorFactorB", Range(0,1)) = 0.8 //边缘颜色b宽度
_DissolveDistance("DissolveDistance", Range(0, 20)) = 14 // 溶解距离
_DissolveDistanceFactor("DissolveDistanceFactor", Range(0,3)) = 3 //溶解程度
}
CGINCLUDE
#pragma multi_compile_fwdbase//编译指令,不然“SHADOW_COORDS(5)//阴影变量”无法赋值将报错
#include "Lighting.cginc"
#include "AutoLight.cginc"//SHADOW_COORDS,TRANSFER_SHADOW,UNITY_LIGHT_ATTENUATION宏声明文件
fixed4 _Diffuse;
fixed4 _DissolveColorA;
fixed4 _DissolveColorB;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _DissolveMap;
float4 _DissolveMap_ST;
fixed _DissolveThreshold;
fixed _ColorFactorA;
fixed _ColorFactorB;
float _DissolveDistance;
float _DissolveDistanceFactor;
struct v2f
{
float4 pos:SV_POSITION;
float3 worldNormal:TEXCOORD0;
float2 uv:TEXCOORD1;
float4 screenPos:TEXCOORD2;
float3 viewDir : TEXCOORD3;
float3 worldPos:TEXCOORD4;
SHADOW_COORDS(5)//阴影变量
};
v2f vert(appdata_base v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex);
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
o.worldNormal= mul(v.normal, (float3x3)unity_WorldToObject);
o.viewDir=ObjSpaceViewDir(v.vertex);
//计算屏幕坐标
o.screenPos=ComputeGrabScreenPos(o.pos);
//计算顶点世界坐标,用于接受阴影计算
o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz;
TRANSFER_SHADOW(o);//计算上一步中声明的阴影纹理坐标
return o;
}
fixed4 frag(v2f i):SV_Target
{
float2 screenPos=i.screenPos.xy/i.screenPos.w;
//计算距离中心点距离作为一个控制系数
float2 dir=float2(0.5,0.5)-screenPos;
float distance=sqrt(dir.x*dir.x+dir.y*dir.y);
//计算一下像素点到相机距离作为另一个控制系数
float screenSpaceDistance=0.5-sqrt(dir.x*dir.x+dir.y*dir.y);
float viewDistance = max(0,(_DissolveDistance - length(i.viewDir)) / _DissolveDistance) * _DissolveDistanceFactor;
//用两个控制系数作为最终溶解的系数
float disolveFactor = viewDistance * screenSpaceDistance * _DissolveThreshold;
//采样Dissolve Map
fixed disolveValue=tex2D(_DissolveMap,i.uv).r;//这里我直接用r
//小于阈值的部分直接discard,调用内置函数clip
clip(disolveValue-disolveFactor);
fixed3 worldNormal = normalize(i.worldNormal);
fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
//光照这一部分我重写了一下方便自己理解和阅读
fixed3 albedo=tex2D(_MainTex,i.uv).rgb*_Diffuse.rgb;
fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;
fixed3 lambert = saturate(dot(worldNormal, worldLightDir));
//这个不好解释,详见《unity shader入门精要》9.4或 p205,可以帮我们声明并计算atten,用于计算接受阴影
UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);
fixed3 diffuse=_LightColor0.xyz*albedo*lambert;
fixed3 color =ambient+diffuse*atten;
//这里为了比较方便,直接用color和最终的边缘lerp了
float lerpValue=disolveFactor/disolveValue;
if(lerpValue>_ColorFactorA)
{
if(lerpValue>_ColorFactorB)
return _DissolveColorB;
return _DissolveColorA;
}
return fixed4(color,1);
}
//下面的两个着色器用于计算投射阴影部分
//有计算discard部分所需的变量和计算投射阴影的变量便可
struct v2f_shadow
{
V2F_SHADOW_CASTER;//计算投影阴影时各个数据
float4 screenPos:TEXCOORD1;
float3 viewDir : TEXCOORD2;
float2 uv:TEXCOORD3;
};
v2f_shadow vert_shadow(appdata_base v)
{
//和第一部分差不多
v2f_shadow o;
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);
o.screenPos=ComputeGrabScreenPos(o.pos);
o.viewDir=ObjSpaceViewDir(v.vertex);
return o;
}
fixed4 frag_shadow(v2f_shadow i):SV_Target
{
//和第一部分差不多
float2 screenPos=i.screenPos.xy/i.screenPos.w;
float2 dir=float2(0.5,0.5)-screenPos;
float distance=sqrt(dir.x*dir.x+dir.y*dir.y);
float screenSpaceDistance=0.5-sqrt(dir.x*dir.x+dir.y*dir.y);
float viewDistance = max(0,(_DissolveDistance - length(i.viewDir)) / _DissolveDistance) * _DissolveDistanceFactor;
float disolveFactor = viewDistance * screenSpaceDistance * _DissolveThreshold;
fixed disolveValue=tex2D(_DissolveMap,i.uv).r;
clip(disolveValue-disolveFactor);
//内置宏计算投射阴影
//详见《unity shader入门精要》9.4或 p199
SHADOW_CASTER_FRAGMENT(i);
}
ENDCG
SubShader
{
Pass
{
Cull off
Tags{"LightMode"="ForwardBase"}//设置渲染路径,原来博客的代码没有设置光照模型,导致变量没法正确,导致光照计算错误
//具体情况是模型光照随着镜头移动暗部和亮部改变(不知道这两个美术术语有没有说对)
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
ENDCG
}
Pass//
{
//自定义投影函数
Tags{"LightMode"="ShadowCaster"}//设置渲染路径
Cull off//这两个pass的剔除模式自己爱咋弄咋弄吧
CGPROGRAM
#pragma vertex vert_shadow
#pragma fragment frag_shadow
#pragma multi_compile_shadowcaster//ShadowCaster编译指令
ENDCG
}
}
//FallBack "Diffuse"//默认回调已用不上
}
为啥我要执着于用自己实现的接受阴影和投射阴影,而不用博客原来的FallBack “Diffuse”,当然是为了装B…
哈哈,不是,直接用FallBack “Diffuse”,模型将不能接受其他模型投射的阴影
如图所示
太违和了,有没有
对比一下
而为什么又要实现投射阴影函数呢,这不都是已经能投射阴影了不是。但是,又破绽
看图,这是其一
当你透过消溶的模型的阴影面去看场景时,都罩上了不该有的阴影。不仅如此,接受别的模型阴影的部分也是,看图
还有一个问题,不知道算不算问题,《untiy shader入门精要》上也有提到,上图
模型消融后,阴影没有改变。但这如果是场景中的模型,比如是堵墙,那它这个就是合理的,因为它只是让玩家看到被墙挡住的主角或是什么,并不是真的消融了,阴影没有改变是正常的。但一写了自定义投影计算,这两个现象都不见了。
如图:
透过缺口去看场景出现的问题也解决了。
感谢你的阅读,如有不正确的地方欢迎指出!