Unity Shader——遮挡溶解

本文对UnityShader遮挡处理进行优化,重点在于接受阴影和投射阴影的改进。通过自定义着色器代码,实现了更精确的阴影效果,解决了原有模型无法接收其他模型阴影的问题,同时改善了透视线程下的阴影表现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

欢迎来到我的博客

本篇文章主要是对这篇博客——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入门精要》上也有提到,上图
在这里插入图片描述
模型消融后,阴影没有改变。但这如果是场景中的模型,比如是堵墙,那它这个就是合理的,因为它只是让玩家看到被墙挡住的主角或是什么,并不是真的消融了,阴影没有改变是正常的。但一写了自定义投影计算,这两个现象都不见了。
如图:
在这里插入图片描述
在这里插入图片描述
透过缺口去看场景出现的问题也解决了。
感谢你的阅读,如有不正确的地方欢迎指出!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值