最近正好有时间,需要研究增强一下项目中的阴影效果,顺带看到了这种实现,记一下笔记。
最近真是加班加太多,人都加傻了,回家睡了一觉突然醒悟,我为什么要写一个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了。
本文介绍了一种在Unity 3D中实现平面阴影和衰减效果的方法,通过修改shader代码,避免使用C#脚本,直接在shader中完成阴影的计算和渲染。作者强调了劳逸结合的重要性,并提供了完整的shader代码示例,包括阴影的计算公式和渲染过程。

被折叠的 条评论
为什么被折叠?



