Sign in to follow this  
sensate

Need help porting Sean O'neils GPU gems 2 article to HLSL

Recommended Posts

I'm currently porting O'neils article from GPU Gems 2 to HLSL. I was wondering if anyone can spot anything in here that would cause the atmosphere to render as a thick white band when the camera is looking at the atmosphere from space? CONSTANTS FROM C Code:
_samples  = 3;
_Kr = 0.0025f;	
_Kr4PI = (_Kr*4.0f*3.141592),
_Km = (0.0015f),
_Km4PI = (_Km*4.0f*3.141592),
_eSun = (15.0f);
_g = (-0.95f);
_g2 = (_g*_g);
_exposure = (10.0f);
_innerRadius = (150);
_outerRadius = (153.75);
_scale  = 1.0f / (_outerRadius - _innerRadius);
_invScaleDepth  = 1.0 / 0.25;
_rayleighScaleDepth  = 0.25f;
_mieScaleDepth = 0.1f;

_wavelength.x = 0.650f;		// 650 nm for red
_wavelength.y = 0.570f;		// 570 nm for green
_wavelength.z = 0.475f;		// 475 nm for blue
_wavelength.w = 1.0f;

_wavelength4.x = powf (_wavelength.x, 4.0f);
_wavelength4.y = powf (_wavelength.y, 4.0f);
_wavelength4.z = powf (_wavelength.z, 4.0f);
_wavelength4.w = 1;

_invWavelength = 
D3DXVECTOR4 (
1.0f / _wavelength4.x, 
1.0f / _wavelength4.y, 
1.0f / _wavelength4.z, 1
0);
FX Code:
float4x4 WorldViewProj: WORLDVIEWPROJECTION 
<
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "none";
    string ToString = "WORLDVIEWPROJECTION";
> = 0.0;	

float Script : STANDARDSGLOBAL 
<
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "color";
    string ToString = "FlxAtmosphere";
> = 0.0;

float4  invWavelength 
<
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "none";
    string ToString = "INVWAVELENGTH";
> = 0;//

float4	cameraPosition : EYELOCATION 
<
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "none";
    string ToString = "EYELOCATION";
> = 0.0;	

float4  lightDirection : LIGHTDIRECTION
<
    string UIWidget = "none";
    string ScriptClass = "object";
    string ScriptOrder = "standard";
    string ScriptOutput = "none";
    string ToString = "LIGHTDIRECTION";
> = 0.0;


float 	cameraHeight;	    // The camera's current height
float 	cameraHeight2;	// fCameraHeight^2
float 	outerRadius;		// The outer (atmosphere) radius
float 	outerRadius2;	    // fOuterRadius^2
float 	innerRadius;		// The inner (planetary) radius
float 	innerRadius2;	    // fInnerRadius^2
float 	krESun;			// Kr * ESun
float 	kmESun;			// Km * ESun
float 	kr4PI;			// Kr * 4 * PI
float 	km4PI;			// Km * 4 * PI
float 	scale;			// 1 / (fOuterRadius - fInnerRadius)
float 	scaleDepth = 0.25f;	// The scale depth (i.e. the altitude at which the atmosphere's average density is found)
float 	scaleOverScaleDepth = 6;	// fScale / fScaleDepth
float 	g;
float 	g2;

struct vpconn
{
	float4 Position		: POSITION;
	float3 t0 			: TEXCOORD0;
	float3 c0 			: COLOR;				float3 c1 			: COLOR1;			
};

struct vsconn
{
    float4 rgbColor : COLOR0;  
    //float4 depthColor : COLOR1;
};

// The scale equation calculated by Vernier's Graphical Analysis
float expScale (float fCos)
{
	float x = 1.0 - fCos;
	return scaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));

}

// Calculates the Mie phase function
float getMiePhase(float fCos, float fCos2, float g, float g2)
{
	return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
}

// Calculates the Rayleigh phase function
float getRayleighPhase(float fCos2)
{
	return 1.0;
	//return 0.75 + (0.75*fCos2);
}

// Returns the near intersection point of a line and a sphere
float getNearIntersection(float3 v3Pos, float3 v3Ray, float fDistance2, float fRadius2)
{
	float B = 2.0 * dot(v3Pos, v3Ray);
	float C = fDistance2 - fRadius2;
	float fDet = max(0.0, B*B - 4.0 * C);
	return 0.5 * (-B - sqrt(fDet));
}

// Returns the far intersection point of a line and a sphere
float getFarIntersection(float3 v3Pos, float3 v3Ray, float fDistance2, float fRadius2)
{
	float B = 2.0 * dot(v3Pos, v3Ray);
	float C = fDistance2 - fRadius2;
	float fDet = max(0.0, B*B - 4.0 * C);
	return 0.5 * (-B + sqrt(fDet));
}

vpconn 
AtmosphereFromSpaceVS(float4 vPos : POSITION, 
    				  float3 vNormal : NORMAL,
   					  float2 vTexCoord0 : TEXCOORD0 )
{
	float3 ray = vPos.xyz - cameraPosition.xyz;

	float far = length (ray);
	ray /= far;

	float near = getNearIntersection (cameraPosition, ray, 
								      cameraHeight2, outerRadius2);
								 
	float3 start = cameraPosition + ray * near;
	far -= near;
	
	float startAngle = dot (ray, start) / outerRadius;
	float startDepth = exp (-1.0 / scaleDepth);
	float startOffset = startDepth * expScale (startAngle);
	
	float sampleLength = far / 3.0f;
	float scaledLength = sampleLength * scale;
	float sampleRay = ray * sampleLength;
	float samplePoint = start + sampleRay * 0.5f;
	
	float3 frontColor = float3 (0,0,0);
	
	for (int i = 0; i < 3; i++)
	{
		float height = length (samplePoint);
		float depth = exp (scaleOverScaleDepth * (innerRadius - height));
		float lightAngle = dot (lightDirection, samplePoint) / height;
		float cameraAngle = dot (ray, samplePoint) / height;
		float scatter = (startOffset + depth * (
			expScale (lightAngle) - expScale (cameraAngle)));
			
		float3 attenuate = exp (-scatter * (invWavelength.xyz * kr4PI + km4PI));
		
		frontColor += attenuate * (depth * scaledLength);
		samplePoint = samplePoint + sampleRay;
	}
	
	vpconn OUT;
	
	OUT.t0 = cameraPosition.xyz - vPos.xyz;
	OUT.Position = mul(vPos, WorldViewProj);
	OUT.c0.xyz = frontColor * (invWavelength.xyz * krESun);
	OUT.c1.xyz = frontColor * kmESun;


	return OUT;
}

vsconn
AtmosphereFromSpacePS(vpconn IN)
{
	vsconn OUT;

	float cos = dot (lightDirection, IN.t0) / length (IN.t0);
	float cos2 = cos * cos;

	float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + cos*cos) /
             pow(1.0 + g2 - 2.0*g*cos, 1.5);

	OUT.rgbColor.rgb = getRayleighPhase(cos2) * IN.c0 + fMiePhase *     
             IN.c1; //getRayleighPhase (cos2) * IN.c0 +
	OUT.rgbColor.a = OUT.rgbColor.b;

	
	return OUT;
}

technique AtmosphereFromSpace
{
    pass P0
    {         
    	//AlphaBlendEnable = true;
    	CullMode = CW;
        VertexShader = compile vs_3_0 AtmosphereFromSpaceVS();
        PixelShader  = compile ps_2_a AtmosphereFromSpacePS(); 
    }
}
EDIT: Please remember to use 'source' tags in future! [Edited by - jollyjeffers on August 12, 2007 9:08:41 AM]

Share this post


Link to post
Share on other sites
Sorry, can't help you right now, but... from looking at your code it seems like you are trying to do a shader to render a planet's atmosphere from space. Is that right??

Share this post


Link to post
Share on other sites
Static code analysis for this sort of thing is unlikely to be the right way to go about it. You need to do some runtime debugging via PIX.

Use the 'pixel history' feature to examine one of these white pixels and see what intermediary values were used. I'd take a guess at an FP exception (e.g. divide by zero) causing a +INF result which eventually gets clamped to white when written to the frame buffer.

If you can identify this you just back-track through the arithmetic to find out the special case - maybe a perpendicular view vector generates a 0.0 dot product result or something like that...

hth
Jack

Share this post


Link to post
Share on other sites
Thanks for taking the time!

It turns out that I'm an idiot... and that I had two variables that should have been float3's instead of floats...

On the flip side, I found that I detest working with PIX. Well actually, I detest working with DirectX in debug.

Thanks for pointing me to it though. (I knew of it, but I didn't realize that the debugging facilities had progressed to the point where shaders could be debugged... cool stuff.

Share this post


Link to post
Share on other sites
I tried to implement the code from the original poster using Rendermonkey (I'm in the midst of some refactoring so I didn't have the chance to try it in my engin) but all I'm getting is a white sphere. There's probably some mathematical instruction that's making all pixels go white. I think I fixed the float3/float issue from the OP (I believe those two variables were sampleRay and samplePoint) but there's still something amiss.

So I have some questions about all this

1) Does the original sphere need to have something "particular"? I see the code uses in the vertex shader signature texture Coordinates and Normals, but they don't seem to be used in the code (vNormal and vTexcoord). Or any sphere will do?
2) Is there a relationship between the camera position and the effect before something will show up? I mean, for a sphere of radius of 1 unit, one should be at at least x times the radius away..?
3) About the lightDirection vector.. does that mean the position of the light source (ie its position in object space) or the vector from its position to the sphere? (vObjectPosition - vLightPosition if I'm not mistaken)
4) FInally, what should I see when everything is working right? I suppose that an all-white sphere doesn't mean that it's working right. :D
5) Why won't it work :D

Thanks in advance!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this