Jump to content
  • Advertisement
Sign in to follow this  
ViLiO

Atmospheric Scattering

This topic is 4213 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!