样例 1:
样例 2:
参考了网上一个大神的体积光shader, 增加了光线的线性衰减和地面接受光源。
Light :
Shader "Hidden/VertexVolumetricLight" { Properties { } SubShader { Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "IgnoreProjector"="true" } LOD 100 Pass { zwrite off blend srcalpha one colormask rgb CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fog #pragma multi_compile __ USE_COOKIE #include "UnityCG.cginc" #include "Lighting.cginc" #include "AutoLight.cginc" #define RAY_STEP 8 uniform float4x4 _VolumetricLightProjector; uniform float4 _WorldLightPos; uniform float4 _VolumetricProjectionParams; #ifdef USE_COOKIE uniform sampler2D _LightCookie; #endif struct appdata { float4 vertex : POSITION; float3 color : COLOR; }; struct v2f { UNITY_FOG_COORDS(0) float4 vertex : SV_POSITION; float3 viewPos : TEXCOORD1; float3 viewCamPos : TEXCOORD2; float3 vcol : COLOR; }; float LinearLightEyeDepth(float z) { return 1.0 / (_VolumetricProjectionParams.y * z + _VolumetricProjectionParams.z); } v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); UNITY_TRANSFER_FOG(o, o.vertex); o.viewCamPos = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos.xyz, 1)).xyz; o.viewCamPos.z *= -1; o.viewPos = v.vertex.xyz; o.viewPos.z *= -1; o.vcol = v.color; return o; } float4 frag (v2f i) : SV_Target { float delta = 1.0 / RAY_STEP; float4 col = 0; float4 beginPjPos = mul(_VolumetricLightProjector, float4(i.viewPos, 1)); beginPjPos /= beginPjPos.w; float4 pjCamPos = mul(_VolumetricLightProjector, float4(i.viewCamPos, 1)); pjCamPos /= pjCamPos.w; float3 pjViewDir = normalize(beginPjPos.xyz - pjCamPos.xyz); pjViewDir -= 2 * pjViewDir * step(0, i.viewCamPos.z) * _WorldLightPos.w; for (float k = 0; k < RAY_STEP; k++) { float4 curpos = beginPjPos; float3 vdir = pjViewDir.xyz*k*delta; curpos.xyz += vdir; float boardFac = step(-1, curpos.x)*step(-1, curpos.y)*step(-1, curpos.z)*step(curpos.x, 1)*step(curpos.y, 1)*step(curpos.z, 1); curpos = ComputeScreenPos(curpos); half2 pjuv = curpos.xy / curpos.w; #if UNITY_UV_STARTS_AT_TOP pjuv.y = 1 - pjuv.y; #endif #ifdef USE_COOKIE fixed4 cookie = tex2D(_LightCookie, pjuv); fixed3 cookiecol = cookie.rgb * cookie.a; #else half2 toCent = pjuv - half2(0.5, 0.5); half l = 1 - saturate((length(toCent) - 0.3) / (0.5 - 0.3)); fixed3 cookiecol = fixed3(l, l, l); #endif col.rgb += cookiecol * i.vcol.rgb * delta * boardFac; } col.a = 1; UNITY_APPLY_FOG(i.fogCoord, col); // 线性衰减 float z = LinearLightEyeDepth(-beginPjPos.z); col.rgb *= 1 - z * _VolumetricProjectionParams.w; return col; } ENDCG } } }
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'PositionFog()' with transforming position into clip space. // Upgrade NOTE: replaced 'V2F_POS_FOG' with 'float4 pos : SV_POSITION' Shader "VLight/VolumetricCaster" { Properties { _Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "Queue" = "Geometry" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #define VLNR_MAX 2 struct v2f { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; float3 worldPos : TEXCOORD1; }; uniform float4 _MainTex_ST; uniform sampler2D _MainTex; half4 _Color; uniform int _VLightCount; uniform float4x4 _VLightWorldToLocal[VLNR_MAX]; uniform float4x4 _VLightProjector[VLNR_MAX]; uniform float4 _VLightPos[VLNR_MAX]; uniform sampler2D _VLightTex; float3 CalculateVLight(int index, float3 worldPos) { // 世界坐标转模型坐标!!! float3 pos = mul(_VLightWorldToLocal[index], float4(worldPos, 1.0)).xyz; pos.z *= -1; float4 pjPos = mul(_VLightProjector[index], float4(pos, 1)); pjPos /= pjPos.w; float boardFac = step(-1, pjPos.x) * step(-1, pjPos.y) * step(-1, pjPos.z) * step(pjPos.x, 1) * step(pjPos.y, 1) * step(pjPos.z, 1); pjPos = ComputeScreenPos(pjPos); half2 pjuv = pjPos.xy / pjPos.w; #if UNITY_UV_STARTS_AT_TOP pjuv.y = 1 - pjuv.y; #endif fixed4 cookie = tex2D(_VLightTex, pjuv); return cookie.rgb * cookie.a * 1.0 / _VLightCount * boardFac; } v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; return o; } float4 frag(v2f input) : SV_Target { half4 texcol = tex2D(_MainTex, input.uv); float3 col = 0; for (int i = 0; i < _VLightCount; i++) { float3 worldPos = input.worldPos; col += CalculateVLight(i, worldPos); } return texcol * _Color + float4(col, 1.0); } ENDCG } } }