实现一个有衰减的 Planar Shadow

本文介绍了一种在Unity 3D中实现平面阴影和衰减效果的方法,通过修改shader代码,避免使用C#脚本,直接在shader中完成阴影的计算和渲染。作者强调了劳逸结合的重要性,并提供了完整的shader代码示例,包括阴影的计算公式和渲染过程。
摘要由CSDN通过智能技术生成

最近正好有时间,需要研究增强一下项目中的阴影效果,顺带看到了这种实现,记一下笔记。

最近真是加班加太多,人都加傻了,回家睡了一觉突然醒悟,我为什么要写一个c#脚本呀,tmd矩阵变化一下不就得了。。。。。。
在这里奉劝大家,一定要注意劳逸结合,不然非常容易犯许多低级错误。
以下是修改后的shader代码,一个文件全部搞定,不用还非得搞一个c#脚本出来了。

/*
如何求顶点投影到平面上的点(阴影点)
当平面上取不相等的任意两个点组成一个向量,与平面的法线总是垂直的,向量垂直点乘为0,因此可以通过一个点和一个法线来定义,
plane方程如下:( P - P0 )·N = 0,N=normal,P0表示平面上的一个点,P表示平面上的任意点,当P = P0时 0·N = 0
射线方程 P = o + t * D,(o为射线起点,t为标量,表示射线原点到和平面交点的距离)联立两个方程式可求交点。方程如下:

          ( O + D·t - P0 )·N = 0
       => ( O - P0 )·N + D·N·t = 0
       => t = ( O - P0 )·N / D·N  ( 其中D·N ≠0 ,向量点积满足分配律)

P0表示平面上一点中心点(0,0,0) o:顶点世界坐标  N:平面的法向量(0,1,0)D:直射光方向
注意两点:
当 D·N = 0 时,表示射线与平面垂直,则射线与平面平行。
解出 t < 0 时,表示 射线沿着平面相反的半平面发射,也是不相交的(当然如果是直线就没关系啦)
*/

Shader "Test/PlanarShadow"
{
    Properties
    {
        _MainTex ("纹理贴图", 2D) = "white" {}
        
        _CenterWorldPos ("中心点的世界坐标", Vector) = (0, 0.001, 0, 0)
        _PlanarNormal ("平面的法线方向", Vector) = (0, 1, 0, 0)
        
        _ShadowColor ("阴影的颜色", Color) = (0.25, 0.25, 0.25, 0.25)
        _ShadowFalloff ("阴影的衰减系数", float) = 0.3
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" "LightMode"="ForwardBase" }

        // 渲染模型
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD0;
            };

            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
		
        // 渲染平面阴影Pass
        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite On
            ZTest LEqual
            
            // 解决double blending,保证一个点只被渲染一次
            Stencil
            {
                Ref 0 // 设定参考值0,stencilbuffer里面的值会跟它进行比较,stencilBuffer值默认为0  
                Comp Equal // 比较方式为"相等"
                Pass IncrWrap // 当模版测试和深度测试都通过的时候,当前模板缓冲中的是值+1
            	
            	WriteMask 255		
				ReadMask 255
                Fail Keep
                ZFail Keep
            }

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag    
            #include "UnityCG.cginc"
            
            struct appdata
            {
                float4 vertex : POSITION;
            };
            
            struct v2f
            {
                float4 vertex : SV_POSITION;
                float4 shadowWorldPos : TEXCOORD0;
                float4 shadowObjectPos : TEXCOORD1;
            };
            
            uniform float4 _CenterWorldPos;
            uniform float4 _PlanarNormal;
            
            uniform float4 _ShadowColor;
            uniform float _ShadowFalloff;
            
            v2f vert (appdata v)
            {
                v2f o;
                
                float4 worldPos = mul(unity_ObjectToWorld, v.vertex); // 世界空间坐标
                float4 lightDir = normalize(_WorldSpaceLightPos0); // 平行光的方向

                // 把模型拍平了
                float distance = dot(_CenterWorldPos.xyz - worldPos.xyz, _PlanarNormal.xyz) / dot(lightDir.xyz, _PlanarNormal.xyz);
                o.shadowWorldPos = worldPos + lightDir * distance;
                
                o.vertex = mul(UNITY_MATRIX_VP, o.shadowWorldPos); // 乘以视图投影变换矩阵,转换到裁剪空间坐标
                o.shadowObjectPos = mul(unity_WorldToObject, o.shadowWorldPos); // 在把坐标变换回模型空间,做衰减计算用
                
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
				fixed4 color;
				color.rgb = _ShadowColor.rgb;
                
                half attenuate = 1 - saturate(distance(_CenterWorldPos, i.shadowObjectPos) * _ShadowFalloff);
				color.a = _ShadowColor.a * attenuate;
                
				return color;
            }

            ENDCG
        }
        
    }
}

在这里插入图片描述
现在它就是一个可以四处拖动,且阴影有衰减的一个Cube了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值