Atmospheric Scattering

Started by
3 comments, last by Shabadoo 17 years ago
I've been toying with this for most of today and I just can't get my sky to look blue [dead] I am basically trying to implement the Preetham method and have ported over his shaders to HLSL but I must have made a mistake some place. All my input values are identical to those in the Preetham demo (I removed all those slider bar things and just hard coded the sun position to noon in the Preetham demo) but my sky still comes out devoid of any blue. Screenshot of my sky Here is the effect file I wrote based on Preetham's shader
float4x4 worldViewProj : WORLDVIEWPROJ;
float4x4 world : WORLD;
float4x4 worldView : WORLDVIEW;

float3 eyePos;
float3 sunDirection;

float3 betaRayleighMie;
float3 reflectance;
float3 HGg;
 
float3 betaDashRay;
float3 betaDashMie;
float3 oneOverBetaRM;
 
float4 sunColourIntensity;
float2 termMultipliers;
 
float4 constants = { 1.0f, 1.4426950f, 0.5f, 0.0f };
 
struct VSInput
{
	float4 position : POSITION;
	float3 normal : NORMAL;
	float2 texCoords : TEXCOORD0;
};
 
struct VSOutput
{
	float4 position : POSITION0;
	float4 diffuse : COLOR0;
};
 
struct PSOutput
{
	float4 colour : COLOR0;
};
 
void VS( in VSInput IN, out VSOutput OUT )
{
	OUT.position = mul(IN.position, worldViewProj);
 
	float4 worldPos = mul(IN.position, world);
	float3 wDir = normalize(worldPos.xyz - eyePos.xyz);
 
	float cosTheta = dot(sunDirection, wDir);
	float thetaRay = (cosTheta * cosTheta) + 1.0f;
 
	float distance = mul(IN.position, worldView).z;
 
	float3 extinction = -betaRayleighMie * distance * constants.y;
	extinction = exp(extinction);
 
	float3 totalExtinction = extinction * reflectance;
 
	float thetaMie = HGg.x / pow((HGg.y - HGg.z * cosTheta), (3.0f / 2.0f));
 
	float3 inscattering = (betaDashRay * thetaRay) + (betaDashMie * thetaMie) * (1.0f - totalExtinction) * oneOverBetaRM;
 
	inscattering *= termMultipliers.y;
	inscattering *= sunColourIntensity.xyz;
	inscattering *= sunColourIntensity.w;
 
	OUT.diffuse = float4(inscattering, 1.0f);
}
 
void PS( in VSOutput IN, out PSOutput OUT )
{
	OUT.colour = IN.diffuse;
}
 
technique Sky
{
	pass p0
	{
		vertexshader = compile vs_1_1 VS();
		pixelshader = compile ps_2_0 PS();
	}
}
and here is Preetham's shader
; Scattering Vertex shader
; (c) 2002 Nathaniel Hoffman, Kenneth J. Mitchell and Arcot J. Preetham
;
; 

vs.1.0

#include "TerrainVShaderConstants.h"

; 
; V - View direction
; L - Sun direction
; Theta - Scattering angle
; s - Distance
; E - Total extinction (including reflectance).

; Transformation.
m4x4 oPos, VPOSITION, c[CV_WORLD_VIEW_PROJECTION]	; Transform position to viewport

; Texture Coordinates.
; 0 - Normal/Horizon map
; 1 - Terrain texture
; 2 - Cloud texture
mad oT0.xy, VPOSITION.xz, c[CV_BASE_TEX_PROJECTION].xy, c[CV_BASE_TEX_PROJECTION].zw
mad oT1.xy, VPOSITION.xz, c[CV_BASE_TEX_PROJECTION].xy, c[CV_BASE_TEX_PROJECTION].zw
mad oT2.xy, VPOSITION.xz, c[CV_CLOUD_TEX_PROJECTION_0].xy, c[CV_CLOUD_TEX_PROJECTION_0].zw


; Calculate V
m4x4 r1, VPOSITION, c[CV_WORLD]	        ; Transform position to world space
sub r1, c[CV_EYE_POSITION], r1	        ; V = eye - position
dp3 r1.w, r1, r1                        ; Normalize V.
rsq r1.w, r1.w							
mul r1, r1, r1.w
//mov oT3.xy, -r1.xy                     ; For sky, if applying texture.


; Angle (theta) between sun direction (L) and view direction (V).
dp3 r0.x, r1, c[CV_SUN_DIRECTION]       ; r0.x = [cos(theta)] = V.L
mad r0.y, r0.x, r0.x, c[CV_CONSTANTS].x	; r0.y = [1+cos^2(theta)] = Phase1(theta)


; Distance (s)
m4x4 r1, v0, c[CV_WORLD_VIEW]           ; r1.z = s
mov r0.z, r1.z							; store in r0.z for future use.

; Terms used in the scattering equation.
; r0 = [cos(theta), 1+cos^2(theta), s] 

; Extinction term E

mul r1, c[CV_BETA_1_PLUS_2], -r0.z       ; -(beta_1+beta_2) * s
mul r1, r1, c[CV_CONSTANTS].y           ; -(beta_1+beta_2) * s * log_2 e
exp r1.x, r1.x					
exp r1.y, r1.y					
exp r1.z, r1.z                          ; r1 = e^(-(beta_1 + beta_2) * s) = E1


; Apply Reflectance to E to get total net effective E
mul r3, r1, c[CV_TERRAIN_REFLECTANCE]   ;r3 = E (Total extinction) 


; Phase2(theta) = (1-g^2)/(1+g-2g*cos(theta))^(3/2)
; theta is 180 - actual theta (this corrects for sign)
; c[CV_HG] = [1-g^2, 1+g, 2g]
mad r4.x, c[CV_HG].z, r0.x, c[CV_HG].y; 


rsq r4.x, r4.x						
mul r4.y, r4.x, r4.x			
mul r4.x, r4.y, r4.x;
mul r0.w, r4.x, c[CV_HG].x              ; r0.w = Phase2(theta)


; Inscattering (I) = (Beta'_1 * Phase_1(theta) + Beta'_2 * Phase_2(theta)) * 
;        [1-exp(-Beta_1*s).exp(-Beta_2*s)] / (Beta_1 + Beta_2)

mul r4, c[CV_BETA_DASH_1], r0.y 
mul r5, c[CV_BETA_DASH_2], r0.w  
sub r6, c[CV_CONSTANTS].x, r1
mov r7, c[CV_BETA_1_PLUS_2]

add r4, r4, r5
mul r4, r4, r6
mul r4, r4, c[CV_ONE_OVER_BETA_1_PLUS_2]	; r4 = I (inscattering)


; Apply Inscattering contribution factors.

mul r4, r4, c[CV_TERM_MULTIPLIERS].y


; Scale with Sun color & intesity.
mul r4, r4, c[CV_SUN_COLOR]		
mul r4, r4, c[CV_SUN_COLOR].w	

mul r3, r3, c[CV_SUN_COLOR]		
mul r3, r3, c[CV_SUN_COLOR].w	

; Outputs.
mov oD0, r3                             ; Extinction
mov oD1, r4                             ; Inscattering
I am not calculating the final extinction factor in my effect as I am just working with the skydome and not yet the terrain so if anyone who is better at reading asm shaders than I am (which will not be hard cause I suck at reading them [lol]) can help me find the mistake I would be overjoyed [smile] Many Thanks, ViLiO
Richard 'ViLiO' Thomasv.net | Twitter | YouTube
Advertisement
I actually got this sorted by myself some time last week so I figure I may as well post a follow up in case the graphics coders of the future hit the same problems I did [wink]

The shader contains techniques for both Sky and Terrain.
float4x4 worldViewProj : WORLDVIEWPROJ;float4x4 world : WORLD;float4x4 worldView : WORLDVIEW;float3 eyePos;float3 sunDirection;float3 betaRayleighMie;float3 reflectance;float3 HGg; float3 betaDashRay;float3 betaDashMie;float3 oneOverBetaRM; float4 sunColourIntensity;float2 termMultipliers; float4 constants = { 1.0f, 1.4426950f, 0.5f, 0.0f };texture2D diffuseMap;sampler2D DiffuseMap = sampler_state{    Texture = <diffuseMap>;    AddressU = WRAP;    AddressV = WRAP;    MinFilter = ANISOTROPIC;    MagFilter = ANISOTROPIC;    MipFilter = LINEAR;}; struct VSInput{	float4 position : POSITION;	float3 normal : NORMAL;	float2 texCoords : TEXCOORD0;}; struct VSOutput{	float4 position : POSITION0;	float4 inscattering : COLOR0;	float4 extinction: COLOR1;	float2 texCoords: TEXCOORD0;	float3 normal: TEXCOORD1;}; struct PSOutput{	float4 colour : COLOR0;}; void VS( in VSInput IN, out VSOutput OUT ){	OUT.position = mul(IN.position, worldViewProj);		float4 worldPos = mul(IN.position, world);	float3 viewDir = normalize(eyePos.xyz - worldPos.xyz); 	float cosTheta = dot(viewDir, sunDirection);	float thetaRay = (cosTheta * cosTheta) + 1.0f;		float distance = mul(IN.position, worldView).z;		float3 extinction = exp(betaRayleighMie * distance * constants.y);		float3 totalExtinction = extinction * reflectance;		float thetaMie = HGg.x / pow((HGg.y + HGg.z * cosTheta), 1.5f);		float3 inscattering = ((betaDashRay * thetaRay) + (betaDashMie * thetaMie)) * (1.0f - extinction) * oneOverBetaRM;		inscattering *= termMultipliers.y;	inscattering *= sunColourIntensity.xyz;	inscattering *= sunColourIntensity.w;		totalExtinction *= sunColourIntensity;	totalExtinction *= sunColourIntensity.w;	 	OUT.inscattering = float4(inscattering, 1.0f); 	OUT.extinction = float4(totalExtinction, 1.0f); 	 	OUT.texCoords = IN.texCoords; 	OUT.normal = IN.normal;}void SkyPS( in VSOutput IN, out PSOutput OUT){	OUT.colour = IN.inscattering;}void TerrainPS( in VSOutput IN, out PSOutput OUT ){	float nDotL = max(dot(sunDirection, IN.normal), 0.0f);	float4 colour = tex2D(DiffuseMap, IN.texCoords) * nDotL;		colour *= IN.extinction;	OUT.colour = (colour + IN.inscattering);}technique Sky{	pass p0	{		vertexshader = compile vs_1_1 VS();		pixelshader = compile ps_2_0 SkyPS();	}}technique Terrain{	pass p0	{		vertexshader = compile vs_1_1 VS();		pixelshader = compile ps_2_0 TerrainPS();	}}

The results are quite pleasing and, even though the sun is absolutely massive, I quite like the overly-bloomed effect it produces.

I posted some pics of my terrain renderer on my website here

If anyone can spot anything silly I am doing in the effect, let me know [smile]

Regards,
ViLiO
Richard 'ViLiO' Thomasv.net | Twitter | YouTube
Hey, I don't know if you've looked at these, but there are several valuable posts on atmospheric scattering here. Including this one.
Here's another discussion, which IIRC touched the sun size issue too.

Hi all, I have also been playing around with atmospheric rendering and I think I have figured out the HUGE sun problem.

I have noticed that in most sample code the in-scattering is calculated the same way for both aerial perspective and sky colour. From what I have read of Preetham's papers (including the gdmag article) the in-scattering terms are calculated differently, as follows... (apologies in advance for the ascii equations)

In-scattering for aerial perspective:
Lin(S,theta) =  1               --- * Esun * Bsc(theta) * (1 - e^(-BexS))               Bex


In-scattering for sky colour (i.e. THE sky colour):
Edit - replaced ascii with image


The difference in the result of using the sky colour equation instead of the aerial perspective equation can be seen in the following screenshots:

Sky using aerial perspective:


Sky using sky colour:


Now all I need to do is figure out why my sky is black no matter where the sun is...

Cheers,
Brett

[Edited by - Shabadoo on April 9, 2007 3:51:32 PM]

This topic is closed to new replies.

Advertisement