コースティクス
環境
Unity2021.2.18f1
概要
テクスチャを使用したコースティクスです。
参考リンクのものを実装しました。ルミナンスマスクは実装していません。

以下のサイトのテクスチャを使用させていただいています。
Water Caustics Effect (Small) | OpenGameArt.org
Shader "Custom/Caustics"
{
Properties
{
_CausticsTexture("Caustics Texture", 2D) = "white" {}
_CausticsSpeed("CausticsSpeed", float) = 0.05
_CausticsScale("CausticsScale", float) = 3
_CausticsStrength("CausticsStrength", float) = 1
_CausticsSplit("CausticsSplit", float) = 0.002
_CausticsFadeRadius("CausticsFadeRadius", float) = 0.3
_CausticsFadeStrength("CausticsFadeStrength", float) = 0.6
}
SubShader
{
Tags
{
"RenderType" = "Transparent"
"Queue" = "Transparent"
//"LightMode" = "ForwardBase"
}
Pass
{
ZWrite Off
ZTest Always
Cull Front
Lighting Off
Cull Back
Blend SrcAlpha One//OneMinusSrcAlpha
CGPROGRAM
//#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
//#include "AutoLight.cginc"
//#include "Lighting.cginc"
half4 _Color;
sampler2D _CameraDepthTexture;
float _CausticsSpeed;
float _CausticsScale;
float _CausticsStrength;
float _CausticsSplit;
float _CausticsFadeRadius;
float _CausticsFadeStrength;
struct VSInput
{
float4 vertex : POSITION;
float3 texcoord : TEXCOORD0;
};
struct VSOut
{
float4 positionCS :SV_POSITION;
float4 positionSS : TEXCOORD0;
float3 rayVS : TEXCOORD1;
float radius : TEXCOORD3;
};
half4x4 _MainLightDirection;
sampler2D _CausticsTexture;
VSOut vert(VSInput v)
{
VSOut o;
o.positionCS = UnityObjectToClipPos(v.vertex);
o.positionSS = ComputeScreenPos(o.positionCS);
o.rayVS = UnityObjectToViewPos(v.vertex).xyz;
o.rayVS.z *= -1;
o.radius = length(UNITY_MATRIX_M._m01_m11_m21) * 0.5;
return o;
}
half2 Panner(half2 uv, half speed, half tiling)
{
return (half2(1, 0) * _Time.y * speed) + (uv * tiling);
}
half3 SampleCaustics(half2 uv, half split)
{
half2 uv1 = uv + half2(split, split);
half2 uv2 = uv + half2(split, -split);
half2 uv3 = uv + half2(-split, -split);
half r = tex2D(_CausticsTexture, uv1).r;
half g = tex2D(_CausticsTexture, uv2).r;
half b = tex2D(_CausticsTexture, uv3).r;
return half3(r, g, b);
}
fixed4 frag(VSOut i) : Color
{
const float far = _ProjectionParams.z;
i.rayVS = i.rayVS * (far / i.rayVS.z);
float depth;
float3 normalVS;
float2 positionSS = i.positionSS.xy / i.positionSS.w;
depth = SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, positionSS);
depth = Linear01Depth(depth);
float4 positionVS = float4(i.rayVS * depth, 1);
float3 positionWS = mul(unity_CameraToWorld, positionVS).xyz;
/*
half4 color = half4(frac(positionWS.xyz), 1.0);
#if UNITY_REVERSED_Z
if (depth < 0.0001) return half4(0,0,0,1);
#else
if (depth > 0.9999) return half4(0,0,0,1);
#endif
*/
// calculate position in object-space coordinates
float3 positionOS = mul(unity_WorldToObject, float4(positionWS, 1.0)).xyz;
/*
// create bounding box mask
float boundingBoxMask = all(step(positionOS, 0.5) * (1 - step(positionOS, -0.5)));
color.rgb = boundingBoxMask;
*/
half2 uv = mul(positionWS, _MainLightDirection).xy;
half2 uv1 = Panner(uv, 0.75 * _CausticsSpeed, 1 / _CausticsScale);
half2 uv2 = Panner(uv, 1 * _CausticsSpeed, -1 / _CausticsScale);
half3 tex1 = SampleCaustics(uv1, _CausticsSplit);
half3 tex2 = SampleCaustics(uv2, _CausticsSplit);
half edgeFadeMask = 1 - saturate((distance(positionOS, 0) - _CausticsFadeRadius) / (1 - _CausticsFadeStrength));
half3 caustics = min(tex1, tex2) * _CausticsStrength * edgeFadeMask;
return fixed4(caustics, 1);
}
ENDCG
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class Caustics : MonoBehaviour
{
private Material _causticsMaterial = null;
private void OnEnable()
{
var renderer = GetComponent<Renderer>();
_causticsMaterial = renderer.sharedMaterial;
}
private void Update()
{
var sunMatrix = RenderSettings.sun.transform.localToWorldMatrix;
_causticsMaterial.SetMatrix("_MainLightDirection", sunMatrix);
}
}
感想
色収差の部分のテクスチャフェッチは重いので、パラメータの値が決まったら、あらかじめテクスチャのUVにチャンネルごとにオフセットを入れておくと良いと思いました。