I’m trying to port a cloud shader from the excellent Game Programming Gems series. Specifically, Game Programming Gems 5 “Realistic Cloud Rendering on Modern GPUs”. The idea is basically to raymarch a heightmap field representing your clouds, accumulating density and then approximating light scattering with that density value.
I’ve pored over my code and it looks exactly like the code presented in the book, but I can’t for the life of me seem to get it working quite right.
Shader "Custom/CloudShader" {
Properties {
_Color ("Light Color", Color) = (1,1,1,1)
_MainTex ("Base (RGB)", 2D) = "white" {}
_SunDir ("Sun Direction", Vector) = (0,1,0,0)
}
SubShader {
Pass {
Blend SrcAlpha OneMinusSrcAlpha
CGPROGRAM
#pragma target 3.0
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _Color;
float4 _SunDir;
struct v2f {
float4 pos : SV_POSITION;
float4 tex : TEXCOORD0;
};
v2f vert (appdata_base v)
{
v2f o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
o.tex = v.texcoord;
return o;
}
half4 frag (v2f i) : COLOR
{
float4 tex = tex2D( _MainTex, i.tex.xy + float2( _Time.y * 0.01, 0 ) );
float cloudCover = 0.55;
float cloudSharp = 0.95;
float Density = 0;
tex = max( tex - cloudCover, 0 );
//_SunDir = normalize( _SunDir );
_SunDir.z *= 128;
float3 EndTracePos = float3( i.tex.xy, -tex.r * 0.25 );
float3 TraceDir = EndTracePos - _SunDir.xyz;
TraceDir = normalize( TraceDir );
float3 CurTracePos = _SunDir + TraceDir * 1.25;
TraceDir *= 2.0;
tex = pow(tex * 20, cloudSharp );
for( int i = 0; i < 64; i++ )
{
CurTracePos += TraceDir;
float4 tex2 = tex2D( _MainTex, CurTracePos.xy + float2( _Time.y * 0.01, 0 ) ) * 128;
Density += 0.1 * step( CurTracePos.z*2, tex2.r*2 );
}
//Density = clamp( Density, 0, 1.5 );
float Light = 1 / exp( Density * 0.2 ) * 1.75;
//float Light = Density;
return half4 (Light * _Color.r, Light * _Color.g, Light * _Color.b, tex.r);
}
ENDCG
}
}
}
It’s very frustrating. I feel like it’s very close, but not quite there. Maybe would help to know exactly what range of values “SunDir” is supposed to have. I unfortunately got my GPG used, and it doesn’t come with CD. The GPG website additionally appears to be what is scientifically referred to as “completely screwed”, so that’s no help for either getting the CD, or checking the errata.Anybody else done a similar shader and could help?
EDIT: For reference, here’s the original code snippet they provided:
[/FONT]
[FONT=Georgia]float Density = 0.0f;[/FONT]
[FONT=Georgia]float3 EndTracePos = float3(uv, -tex.r);[/FONT]
[FONT=Georgia]float3 TraceDir = EndTracePos - SunPos;[/FONT]
[FONT=Georgia]TraceDir = normalize(TraceDir);[/FONT]
[FONT=Georgia]float3 CurTracePos = SunPos + TraceDir * 1.25f;[/FONT]
[FONT=Georgia]TraceDir *= 2.0f;[/FONT]
[FONT=Georgia]for( int i = 0; i < 64; i++)[/FONT]
[FONT=Georgia]{[/FONT]
[FONT=Georgia] CurTracePos += TraceDir;[/FONT]
[FONT=Georgia] float4 tex2 = tex2D(DensityFieldTexture, CurTracePos.xy) * 255.0f;[/FONT]
[FONT=Georgia] Density += 0.1f * step(CurTracePos.z *2, tex2.r*2);[/FONT]
[FONT=Georgia]}[/FONT]
[FONT=Georgia]float Light = 1.0f / exp(Scattering * 0.4f); // I replaced "Scattering" with "Density" in mine, I assume they're the same thing and this is a typo...???[/FONT]
[FONT=Georgia]
EDIT 2:
I now multiply SunDir z position by 256, which seems to work better.
Now feeding the value 0.5, 0.5, 1 seems to look right for some views - but in motion it looks pretty bad, the lighting acts like there’s some kind of parallax effect and often does not match the actual outline of the clouds.