atmospheric scattering (again)

Started by
11 comments, last by Lutz 18 years, 8 months ago
Hi, I'm trying to implement the scattering terms by hoffman/preetham in Rendermonkey with GLSL. But my output is...well it's just white and has holes! I have a rendermonkey zip file if someone wants to see it in action... The following is the vertex shader i use. I know this can be optimized a lot but i just want to see it working...then I will optimize it :)
uniform vec3 sunDir; // should be normalized
const vec3 bethaR = vec3(0.00000695, 0.0000118, 0.0000244);
const vec3 bethaM = vec3(0.0000004, 0.0000006, 0.0000024);
uniform float g;
uniform vec4 sunColor;
varying vec3 L_in;
varying vec3 F_ex;
varying vec2 vTexCoord0;
uniform vec3 reflectance;
uniform vec4 vViewPosition;

void main(void)
{
   vTexCoord0 = gl_MultiTexCoord0.xy;
   vec3 gConst = vec3(pow(1.0-g,2.0), 1.0+pow(g,2.0), 2.0*g);
   vec3 ecPosition = gl_Vertex.xyz;
   float s = length(ecPosition-vViewPosition.xyz);
   
   F_ex = exp((bethaR + bethaM)* (-s));
   
   vec3 ecDirection = normalize(ecPosition);
   float cos0 = dot(sunDir, ecDirection);
   //just for prototyping -------------------->
   float bethaRay = 3.0/(16.0 * 3.1415926535897932384626433832795);
   float bethaMie = 1.0/(4.0 * 3.1415926535897932384626433832795);
   //just for prototyping <--------------------
   
   
   float phiR = bethaRay * (1.0 + pow(cos0,2.0));
   float phiM = bethaMie * gConst.x / pow(gConst.y - gConst.z * cos0, 1.5 );
   
   L_in = ( phiR + phiM )/( bethaR + bethaM ) * sunColor.xyz *
          ( 1.0 - F_ex );
   
   vec3 totalF_ex = F_ex * sunColor.xyz * reflectance;
   F_ex = totalF_ex * sunColor.w;
   L_in *= sunColor.w; 
   
   gl_Position = ftransform();
}
in the fragment shader I just add F_ex and L_in together as output color for the skydome! I think that I've missed something in the paper. But I don't know what. Is there anyone can help me or give some suggestions? -------- Bebud :) [EDIT by mittens] Threw your snippet in some source tags. [Edited by - mittens on July 27, 2005 8:22:29 AM]
Advertisement
Some parts I find strange...

1) Why do you multiply L_in by (1-F_ex)?
Try computing how long the sunlight travels through the atmosphere (say "sSun"). sSun should depend on daytime (bigger in the evening). Then, multiply L_In with exp(-sSun*(betaM+betaR))

2) Where did you get the constants for betaM and betaR from?
As far as I know, Mie scattering is wavelength independent, so the three numbers should be equal (but I might be well wrong!)

3) Try multiplying L_In by some constant (e.g. 20 as in my code).

4) Don't add L_In and F_Ex for the skydome. Just use L_In. F_Ex tells you how much light of the object you see is filtered out on the way to your eye. But for a skydome there is no object (resp. there is black space), so nothing is filtered out.

5) 30 digits of PI... zezeze...

6) I don't understand what you are doing here:
vec3 totalF_ex = F_ex * sunColor.xyz * reflectance;
F_ex = totalF_ex * sunColor.w;
L_in *= sunColor.w;
Actually, L_In should be multiplied by the sun color, not F_ex. F_ex is independent of sun color and shouldn't be changed.
What's reflectance, anyway?


Here is a part of my shaders that I used in my project.
It works not only near the ground, but also in space.
Maybe it helps you to get the details.

struct atmosphereShaderResults{  float3 LIn, FEx, sunColorEx;  float  lenRayViewerVertex;};atmosphereShaderResults AtmosphereShader(float3 vertexObject, float3 rayViewerVertex, float3 lightObjectNormalized, float3 atmConstants, float3 betaSum, float3 constBetaR, float3 fBetaM, float3 sunColor, float3 clampVal){  atmosphereShaderResults OUT;  // constBetaR = 3.0f/(16*3.1415927f) * betaR  // fBetaM = (1-gHG)*(1-gHG) / (4*PI) * betaM  // betaSum = betaR+betaM  // atmConstants = (gHG, 1+gHG*gHG, radius2)  // Compute how far light needs to travel through the atmosphere from the vertex to us  // Compute l such that vertexObject + l * (viewerObject-vertexObject) = sphere around 0 with radius sqrt(atmConstants.z)  OUT.lenRayViewerVertex = length(rayViewerVertex);  float2 lEye = clamp(IntersectionLineSphere(vertexObject, -rayViewerVertex, atmConstants.z), clampVal.x, clampVal.y);  // mid = point next to the planet center along eye ray  // The distance to the viewer needs to be clipped!  float3 mid = vertexObject - rayViewerVertex * clamp((lEye.x+lEye.y)*0.5f, 1-clampVal.z/OUT.lenRayViewerVertex, 1);  float3 density = exp(-2 * (length(mid)-1) / (sqrt(atmConstants.z)-1));  // Compute sun color  // The attenuation length is computed from the intersection of  // mid + l * lightObjectNormalized = sphere around 0 with radius sqrt(atmConstants.z)  float2 lSun = IntersectionLineSphere(mid, lightObjectNormalized, atmConstants.z);  float  distSunInAtm = clamp(lSun.y,0,1);  OUT.sunColorEx = sunColor * exp(-betaSum * density * distSunInAtm);  // Compute attenuation and color of inscattered light  float  cosTheta   = dot(lightObjectNormalized, rayViewerVertex) / OUT.lenRayViewerVertex;  OUT.FEx           = exp(-betaSum * density * OUT.lenRayViewerVertex * (lEye.y-lEye.x));  float  betaDiv    = atmConstants.y-2.0f*atmConstants.x*cosTheta;  float3 betaRTheta = constBetaR * (1.0f+cosTheta*cosTheta);  float3 betaMTheta = fBetaM / (betaDiv*sqrt(betaDiv));  OUT.LIn           = 20.0f * (betaRTheta+betaMTheta) / betaSum * OUT.sunColorEx;  return OUT;}


[Edited by - mittens on July 28, 2005 10:55:05 AM]
Hey Lutz,

thank you very much. I had to reread the papers to get some things more clear. Also I found a very good paper in gpu gems 2(gg2)! But as I don't need to have a planet atmosphere I simplified the algos described in gg2.
The results look are ok. now I'm coding a cloud shader...

you can have a look at two screen-shots:
[image]http://bebud.be.funpic.de/temp/sonne-rendermonkey.jpg[/image]
[image]http://bebud.be.funpic.de/temp/sonne-rendermonkey2.jpg[/image]

...hmmm...don' know how to include pics and couldn't find it out quickly so use the links :)


I must say, sombody should write a new paper, summing up all the papers/articles written about atmospheric scattering so far.. because it's a real mess! Everybody's coming up with their own formulas, constants, parameters, etc.. and i'm confused to hell.

First of all, i will say i mostly base myself of the paper "Real Time Rendering Of Atmospheric Scattering Effects for Flight Simulators", by Ralf Stockholm Nielsen, since it's one of the most complete/accurate i've read.

Lutz:

Quote:
Why do you multiply L_in by (1-F_ex)?


That's what every paper i've read so far is doing. The question is actually, why aren't you doing it ? :)

For me, (1-F_ex) makes sense. Think as the F_ex factor as a kind of RGB blending factor between extinction and in-scattering..

Quote:
As far as I know, Mie scattering is wavelength independent, so the three numbers should be equal (but I might be well wrong!)


Aie. Mie scattering is wavelength dependant as far as i understand it. The formula is the following one:

BetaM = 0.434 * c * PI * ( 4 * PI * PI ) * K / ( Lambda * Lambda )withc = ( 0.6544 * T - 0.6510 ) * 10e-16and T is the turbidity factorand K is almost equal to 0.67


The main thing to notice is that it's dependant on one over Lambda, ie. wavelength dependant.

By the way, your vertex shader looks very complicated. I know you're handling atmosphere seen from space, but it involves two line/sphere intersections. Is performance good ? How many vertices are you applying this vertex shader on ?

I also find it very interesting that for L_in you are using "s" as the distance vertex-atmosphere intersection in the sun's direction. It was my understanding that "s" is actually the distance between the vertex and the camera...

Y.
Quote:Original post by Ysaneya
I must say, sombody should write a new paper, summing up all the papers/articles written about atmospheric scattering so far.. because it's a real mess! Everybody's coming up with their own formulas, constants, parameters, etc.. and i'm confused to hell.


Atmosphere scattering is very difficult in its full generality. On the other hand, there are many good approximations that seem to suffice for computer graphics. It's hard to decide which degree of generality to use. Some authors treat scattering more general, some more specialized. That's the source of confusion in my eyes.

Quote:
Lutz:

Quote:
Why do you multiply L_in by (1-F_ex)?


That's what every paper i've read so far is doing. The question is actually, why aren't you doing it ? :)

For me, (1-F_ex) makes sense. Think as the F_ex factor as a kind of RGB blending factor between extinction and in-scattering.


It's true that the factor comes out when you assume constant atmosphere (i.e. when you stand on the ground). At the time of writing I was thinking he tries to do planet rendering related stuff where the atmosphere (more precisely, light inscatter along the ray) is not constant.

Quote:
Quote:
As far as I know, Mie scattering is wavelength independent, so the three numbers should be equal (but I might be well wrong!)


Aie. Mie scattering is wavelength dependant as far as i understand it. The formula is the following one:

BetaM = 0.434 * c * PI * ( 4 * PI * PI ) * K / ( Lambda * Lambda )withc = ( 0.6544 * T - 0.6510 ) * 10e-16and T is the turbidity factorand K is almost equal to 0.67


The main thing to notice is that it's dependant on one over Lambda, ie. wavelength dependant.


Yes, of course it depends on wavelength somehow. But the question is: Does it matter?
A dependence on 1/Lambda (1/(Lambda*Lambda)??) is actually much weaker than Rayleigh scattering which depends on 1/Lambda^4.

Anyways, I did a quick search on Google:
This page says
Quote:Mie scattering is not strongly wavelength dependent and produces the almost white glare around the sun when a lot of particulate material is present in the air. It also gives us the the white light from mist and fog."


So what the guys say is: If the dependence of Mie scattering on the wavelength would matter, fog wouldn't be white. Also, the glare around the sun wouldn't be white.

Quote:
By the way, your vertex shader looks very complicated. I know you're handling atmosphere seen from space, but it involves two line/sphere intersections. Is performance good ? How many vertices are you applying this vertex shader on ?


Good point. The vertex shader is applied to every surface vertex and some skydome vertices. At the moment, I use quite dense surface in terms of vertex density. For planets with atmosphere, I'm (probably) vertex limited at 8M triangles/second (Athlon 3000+, Radeon 9800), which is still quite good in my eyes. Planets without atmosphere have about twice that throughput. I sacrificed performance for eye candy.

Recently, I made some experiments with atmosphere lookup tables (with Sean O'Neill). However, I didn't quite succeed to store everything in a 2D table the way I wanted. In future, I plan to reduce the vertex density of my planets and increase the normalmap resolution. That should look better and be faster.

Quote:
I also find it very interesting that for L_in you are using "s" as the distance vertex-atmosphere intersection in the sun's direction. It was my understanding that "s" is actually the distance between the vertex and the camera...
Y.


Light from the sun is attenuated twice: Once from the sun to the point where it is inscattered into the ray of sight (this actually produces the red light at dusk/dawn) and another time when travelling from the point of inscattering to the eye. I only seem to have considered the first term, but don't ask me why. I guess, "distSunInAtm" should be somewhat bigger in my vertex program.

Very nice discussion, I like it!

Lutz
This is the discussion I've been waiting for! I would also like to implement atmospheric light scattering in my engine. As Ysaneya knows, I've devised a postprocessing trick which achieves a similiar effect.. but it's not the same thing.

Since the math is somewhat obscure to me what I would like to know how to use this kind of shader. Lutz, are you applying it directly to the planet mesh (I mean, the "sphere"?) And how do you compute all those parameters that you are passing to the shader? And what do you do with the results? It seems that is is returning more than just the resulting color (I assume that that shader is a pixel shader).

Yeah I know I may seem like a total noob but I have been unable to find some clear explanation.. Someone should write a tutorial "Atmospheric Light Scattering for dummies!" :)

Thanks in advance!
--Avengers UTD Chronicles - My game development blog
Quote:Recently, I made some experiments with atmosphere lookup tables (with Sean O'Neill). However, I didn't quite succeed to store everything in a 2D table the way I wanted.


This must be the old Paper from sean o'neill. He improved his scattering terms and don't use lookups any more. You should have a look at it. I think this would solve all your problems. You can find the article in "gpu gems 2". He also uses HDR to improve the lighting...

It's a very nice a also complete work :)

Bebud
Lutz, i've implemented your model (but on the CPU - i'm just testing different approaches at the moment), and it looks pretty good. A screenshot at dawn:



Quote:
So what the guys say is: If the dependence of Mie scattering on the wavelength would matter, fog wouldn't be white. Also, the glare around the sun wouldn't be white.


I've re-read some papers and it looks like you're right. Mie scattering is dependant on the wavelength, but also the particle size. There's a strong dependancy for small particles, but it disapears for larger particles. Since most models assume an average particle size, you get in average a low wavelength dependancy.

Quote:
I guess, "distSunInAtm" should be somewhat bigger in my vertex program.


Although i have implemented your shader on the CPU, there are still points of details that i do not understand in your code.

Let's start in order: you are using a constant called "clampVal". What does this vector contain ? Initially i thought it was (0, 1, -, -), but at some point you are doing "clamp(lSun.y, 0, 1)" instead of "clamp(lSun.y, clampVal.x, clampVal.y)". In consequence, it cannot be as simple as that. In addition, you are using a mind-boggling "clampVal.z" at some other point in the program.

Density calculation: minor detail, but you are calculating "sqrt(atmConstants.z)". You could probably optimize this by putting the value directly in "atmConstants.w" (or another constant).

I haven't fully understood your density calculation:
"float3 density = exp(-2 * (length(mid)-1) / (sqrt(atmConstants.z)-1));"

a. What is that "-2" constant and why did you choose it ?
b. Why are you subtracting 1 to length(mid) and the atmosphere's radius ?
c. That density model seems to start at 0 at the planet center and to reach 1 at the atmosphere's radius (i'm talking before the exponentiation). But in reality, shouldn't you get a value of 0 at the planet's radius ? My point being, for a point located on the ground, you should get a density of 0..
d. Why is the density stored in a "float3" ? Shouldn't it be a scalar value ?

Let's continue: the midpoint calculation. You are getting the atmosphere's two intersection parameters in lEye.x and lEye.y. Then you take the half distance. Then you clamp it to.. "1-clampVal.z/OUT.lenRayViewerVertex". I skipped this part in my test code.. what is it supposed to do ? :)

Sorry, but i'm not done yet. Comes the most obscure part for me: the calculation of the distance the sun ray travels in the atmosphere, from the midpoint. You are doing this:
"float2 lSun = IntersectionLineSphere(mid, lightObjectNormalized, atmConstants.z);
float distSunInAtm = clamp(lSun.y,0,1);"


Unless i'm mistaken, "lightObjectNormalized" is the normalized sun direction in object space. Which means your IntersectionLineSphere function will return as parameters.. the actual distance in whatever units you are using, of the two intersection points of the sun ray and the atmosphere. Because you are starting at the midpoint, you can safely ignore lSun.x (it will be in the back of the ray) (stop me if i'm mistaking). But lSun.y is an actual distance, not a parameter in 0-1, isn't it ? Then the clamping to 0-1 in the next line is completely wrong. If i'm mistaken, could you post the code to your IntersectionLineSphere function ?

A last question: from what i'm seeing with my results, everything looks good except i do not get a kind of white haze at the horizon, whatever my position on the planet is. Do you have the same problem ?

In the next days, i'll try to fine-tune all these parameters, and to offset as much work as possible on the GPU. I hope i can somewhat optimize your shader, using some lookup tables..

Y.
Quote:
1) Why do you multiply L_in by (1-F_ex)?


Lutz: a basic xplanation photographic exposure and cramming HDI onto 8-bit channels:
http://freespace.virgin.net/hugo.elias/graphics/x_posure.htm


hope that helps

Quote:Original post by AvengerDr
Since the math is somewhat obscure to me what I would like to know how to use this kind of shader. Lutz, are you applying it directly to the planet mesh (I mean, the "sphere"?) And how do you compute all those parameters that you are passing to the shader? And what do you do with the results? It seems that is is returning more than just the resulting color (I assume that that shader is a pixel shader).


The atmosphere program is called by the vertex shader.
This shader is applied to every vertex of the mesh.
And also to some of the skydome and to the clouds.
The fragment program only gets L_In and F_ex.

Currently, I'm using the constants
betaR[0] = 1/550e3f;
betaR[1] = 1/210e3f;
betaR[2] = 1/60e3f;
and
betaM[0] = 1/800e3f;
betaM[1] = 1/800e3f;
betaM[2] = 1/800e3f;

I'll post the complete shader. This might bring some more clarity, I hope.
Basically, in the fragment program at the bottom I compute
fragment color = (color of object, i.e. the ground) * F_ex + L_In*(1-F_ex)
(Yes... there is a (1-F_ex). I overlooked it first...)

struct app2vert {  float4 VPos    : POSITION;      // model space vertex position   float4 VWeight : BLENDWEIGHT;   // weights for skinning (u,1-u)  float4 Norm    : NORMAL;        // model space vertex normal  float4 PCol    : COLOR0;        // primary color (diffuse)  float4 SCol    : COLOR1;        // seconary color (specular)  float4 PSize   : PSIZE0;        // point size  float4 Tex0    : TEXCOORD0;     // texture coordinate set 0  float4 Tex1    : TEXCOORD1;     // texture coordinate set 1  float4 Tex2    : TEXCOORD2;     // texture coordinate set 2  float4 Tex3    : TEXCOORD3;     // texture coordinate set 3  float4 Tex4    : TEXCOORD4;     // texture coordinate set 4  float4 Tex5    : TEXCOORD5;     // texture coordinate set 5  float4 Tex6    : TEXCOORD6;     // texture coordinate set 6  float4 Tex7    : TEXCOORD7;     // texture coordinate set 7};struct vert2frag{  float4 HPos    : POSITION;      // homogeneous clip space postion  float4 PCol    : COLOR0;        // primary (diffuse) color  float4 SCol    : COLOR1;        // secondary (specular) color  float4 Tex0    : TEXCOORD0;     // texture coordinate set 0  float4 Tex1    : TEXCOORD1;     // texture coordinate set 1  float4 Tex2    : TEXCOORD2;     // texture coordinate set 2  float4 Tex3    : TEXCOORD3;     // texture coordinate set 3  float4 Tex4    : TEXCOORD4;     // texture coordinate set 4  float4 Tex5    : TEXCOORD5;     // texture coordinate set 5  float4 Tex6    : TEXCOORD6;     // texture coordinate set 6  float4 Tex7    : TEXCOORD7;     // texture coordinate set 7};struct atmosphereShaderResults{  float3 LIn, FEx, sunColorEx;  float  lenRayViewerVertex;};#define ambientLight float4(0.1,0.1,0.25,0)#define globalBlend 0.8#define cloudDiffuseShadow 0.5#define cloudSpecularShadow 0.8#define planetSpecularExponent 16#define planetSpecularLuminance 0.4/**********************************************************************************************//****************************************   HELPER ROUTINES   *********************************//**********************************************************************************************/float2 IntersectionLineSphere(float3 p, float3 r, float rad2){  // Compute intersections of line p+l*r with sphere around 0 with radius sqrt(rad2)  // Return the three stretch factors (l1,l2,(l1+l2)/2) with l1<=l2  float  d = dot(r,r);  float  pSQR = - dot(r, p) / d;  float  qSQR = (dot(p,p) - rad2) / d;  d = pSQR*pSQR-qSQR;  if (d<0) d=0; else d=sqrt(d);  return float2(pSQR-d,pSQR+d);}/**********************************************************************************************/atmosphereShaderResults AtmosphereShader(float3 vertexObject, float3 rayViewerVertex, float3 lightObjectNormalized, float3 atmConstants,                                         float3 betaSum, float3 constBetaR, float3 fBetaM, float3 sunColor, float3 clampVal){  atmosphereShaderResults OUT;  // constBetaR = 3.0f/(16*3.1415927f) * betaR  // fBetaM = (1-gHG)*(1-gHG) / (4*PI) * betaM  // betaSum = betaR+betaM  // atmConstants = (gHG, 1+gHG*gHG, radius2)  // Compute how far light needs to travel through the atmosphere from the vertex to us  // Compute l such that vertexObject + l * (viewerObject-vertexObject) = sphere around 0 with radius sqrt(atmConstants.z)  OUT.lenRayViewerVertex = length(rayViewerVertex);  float2 lEye = clamp(IntersectionLineSphere(vertexObject, -rayViewerVertex, atmConstants.z), clampVal.x, clampVal.y);  // mid = point next to the planet center along eye ray  // The distance to the viewer needs to be clipped!  float3 mid = vertexObject - rayViewerVertex * clamp((lEye.x+lEye.y)*0.5f, 1-clampVal.z/OUT.lenRayViewerVertex, 1);  float3 density = exp(-2 * (length(mid)-1) / (sqrt(atmConstants.z)-1));  // Compute sun color  // The attenuation length is computed from the intersection of  // mid + l * lightObjectNormalized = sphere around 0 with radius sqrt(atmConstants.z)  float2 lSun = IntersectionLineSphere(mid, lightObjectNormalized, atmConstants.z);  float  distSunInAtm = clamp(lSun.y,0,1);  OUT.sunColorEx = sunColor * exp(-betaSum * density * distSunInAtm);  // Compute attenuation and color of inscattered light  float  cosTheta   = dot(lightObjectNormalized, rayViewerVertex) / OUT.lenRayViewerVertex;  OUT.FEx           = exp(-betaSum * density * OUT.lenRayViewerVertex * (lEye.y-lEye.x));  float  betaDiv    = atmConstants.y-2.0f*atmConstants.x*cosTheta;  float3 betaRTheta = constBetaR * (1.0f+cosTheta*cosTheta);  float3 betaMTheta = fBetaM / (betaDiv*sqrt(betaDiv));  OUT.LIn            = 20.0f * (betaRTheta+betaMTheta) / betaSum * OUT.sunColorEx;  return OUT;}/**********************************************************************************************/float ComputeFineCoarseBlendWeight(float nVert, float2 tCoords, float4 stBorders, float2 alphaMinMax){  float2 stCoords = nVert*tCoords.yx;  // yx since the texture coordinates are (t,s) and not (s,t)        float alpha= saturate(1-( stCoords.x-stBorders.x)*0.1);   // Left  alpha = max(alpha, saturate(1-( stCoords.y-stBorders.y)*0.1));  // Bottom  alpha = max(alpha, saturate(1-(stBorders.z- stCoords.x)*0.1));  // Right  alpha = max(alpha, saturate(1-(stBorders.w- stCoords.y)*0.1));  // Top  alpha = clamp(alpha, alphaMinMax.x, alphaMinMax.y);  return alpha;}/**********************************************************************************************/float3 ComputeLightInTangentSpace(float3 lightObjectNormalized, float3 p){  float3 binormal = float3(0,1e-6,1);   float3 tangent = normalize(cross(binormal, p));  binormal = cross(p, tangent);  return float3(dot(lightObjectNormalized, p),                dot(lightObjectNormalized, tangent),                dot(lightObjectNormalized, binormal));}/**********************************************************************************************//*******************************   PLANET GLOBAL + ATMOSPHERE   *******************************//**********************************************************************************************/vert2frag mainPlanetGlobalAtmVertex(app2vert IN,            // Light and viewer in object coordinates            uniform float3 lightObjectNormalized,  uniform float3 viewerObject, uniform float3 vertexTransformBias,            // Atmosphere scattering            uniform float3 constBetaR, uniform float3 fBetaM, uniform float3 betaSum,             uniform float3 atmConstants, uniform float3 sunColor,                                                         // Transform matrix            uniform float4x4 objectToWindowMatrix){  vert2frag OUT;  IN.VPos.xyz *= IN.Tex0.x;  // Compute the homogeneous vertex position  OUT.HPos = mul(objectToWindowMatrix, float4(IN.VPos.xyz-vertexTransformBias,1));  // Position on sphere * height  // Convert light to tangent space  float3 lightTangentNormalized = ComputeLightInTangentSpace(lightObjectNormalized, IN.VPos.xyz);  // Compute atmosphere stuff  float3 rayViewerVertex = IN.VPos.xyz - viewerObject;  atmosphereShaderResults atmRes =     AtmosphereShader(IN.VPos.xyz, rayViewerVertex, lightObjectNormalized, atmConstants,                     betaSum, constBetaR, fBetaM, sunColor, float3(0,1,1e5));  // Copy texture coordinates  OUT.Tex0     = IN.VPos;                        // Global texture coordinates for cube map  OUT.Tex2.xyz = atmRes.sunColorEx;              // Sun color (needed for specular lighting)  OUT.Tex4.xyz = reflect(rayViewerVertex, IN.VPos.xyz);  // Reflected vector  OUT.Tex5.xyz = lightObjectNormalized;          // Light vector  OUT.Tex6.xyz = lightTangentNormalized;          // Light vector  OUT.Tex7.xyz = atmRes.FEx;                    // Extinction factor  OUT.SCol.xyz = atmRes.LIn * (1-atmRes.FEx);        // Inscatter color  return OUT;}/**********************************************************************************************/float4 mainPlanetGlobalAtmFragment(vert2frag IN,                     uniform samplerCUBE globalMap : TEXUNIT0,                      uniform samplerCUBE normalMap : TEXUNIT1) : COLOR{    float3 lightObjectNormalized  = IN.Tex5.xyz;  float3 lightTangentNormalized = IN.Tex6.xyz;  float4 globalTex = texCUBE(globalMap, IN.Tex0.xyz);  float4 normalVec = texCUBE(normalMap, IN.Tex0.xyz).xywz;  normalVec.yz = 2*normalVec.yz-1;  normalVec.x  = sqrt(1 - normalVec.y*normalVec.y - normalVec.z*normalVec.z);    float4 diffuseLight = ambientLight + saturate(dot(normalVec.xyz, lightTangentNormalized));  float specularLight = pow(saturate(dot(normalize(IN.Tex4.xyz), lightObjectNormalized)), 16);    return (globalTex*diffuseLight + IN.Tex2*specularLight*normalVec.w) * IN.Tex7 + IN.SCol;}



Quote:Original post by Ysaneya
Lutz, i've implemented your model (but on the CPU - i'm just testing different approaches at the moment), and it looks pretty good. A screenshot at dawn:


Looks good as far as I can see.

Quote:
Although i have implemented your shader on the CPU, there are still points of details that i do not understand in your code.

Let's start in order: you are using a constant called "clampVal". What does this vector contain ? Initially i thought it was (0, 1, -, -), but at some point you are doing "clamp(lSun.y, 0, 1)" instead of "clamp(lSun.y, clampVal.x, clampVal.y)". In consequence, it cannot be as simple as that. In addition, you are using a mind-boggling "clampVal.z" at some other point in the program.


The problem with my shader is that the sky at dawn/dusk is too dark. This is, as far as I understand, because the rays through the atmosphere are too long, so the midpoint integration rule is very inexact then. So I tried to clamp those ray length. This clampVal stuff is a hack to achieve this. You can safely ignore this. Mostly, clamlVal is something like (0,1,very big number).

Quote:
Density calculation: minor detail, but you are calculating "sqrt(atmConstants.z)". You could probably optimize this by putting the value directly in "atmConstants.w" (or another constant).


Yes, thanks. It hasn't been optimized yet, it's more or less experimental stuff.

Quote:
I haven't fully understood your density calculation:
"float3 density = exp(-2 * (length(mid)-1) / (sqrt(atmConstants.z)-1));"

a. What is that "-2" constant and why did you choose it ?
b. Why are you subtracting 1 to length(mid) and the atmosphere's radius ?
c. That density model seems to start at 0 at the planet center and to reach 1 at the atmosphere's radius (i'm talking before the exponentiation). But in reality, shouldn't you get a value of 0 at the planet's radius ? My point being, for a point located on the ground, you should get a density of 0..
d. Why is the density stored in a "float3" ? Shouldn't it be a scalar value ?


a. That's a "looking-good-constant"
b. and c. My local coordinate system is centered in the planet and the planet radius is 1. Thus, points on the surface have unit length. sqrt(atmConstants.z) is the radius of the atmosphere in local coordinates (that is, about (6378000m + 60000m) / 6378000m). On the surface, you get
density = exp(-2 * 0) = 1. On the "boundary" of the atmosphere you get
density = exp(-2) ~ 0.15 or something. That means, the density drops to about 15% within 60km. This is not the physical number. I think, the actual atmosphere drops more, so -2 should be bigger.
This whole bussiness makes the boundary of the atmosphere look more fuzzy and realistic.
d. Probably a bug, thanks.

Quote:
Let's continue: the midpoint calculation. You are getting the atmosphere's two intersection parameters in lEye.x and lEye.y. Then you take the half distance. Then you clamp it to.. "1-clampVal.z/OUT.lenRayViewerVertex". I skipped this part in my test code.. what is it supposed to do ? :)


As I mentioned above, that was meant to produce a more realistic result when rays through the atmosphere are long. I think, clampVal.z was something like the horizon distance, but I forgot the details. It's not so important.

Quote:
Sorry, but i'm not done yet. Comes the most obscure part for me: the calculation of the distance the sun ray travels in the atmosphere, from the midpoint. You are doing this:
"float2 lSun = IntersectionLineSphere(mid, lightObjectNormalized, atmConstants.z);
float distSunInAtm = clamp(lSun.y,0,1);"


Unless i'm mistaken, "lightObjectNormalized" is the normalized sun direction in object space. Which means your IntersectionLineSphere function will return as parameters.. the actual distance in whatever units you are using, of the two intersection points of the sun ray and the atmosphere. Because you are starting at the midpoint, you can safely ignore lSun.x (it will be in the back of the ray) (stop me if i'm mistaking). But lSun.y is an actual distance, not a parameter in 0-1, isn't it ? Then the clamping to 0-1 in the next line is completely wrong. If i'm mistaken, could you post the code to your IntersectionLineSphere function ?


The intersection code determines t such that (starting point) + t * (ending point-starting point) intersects the sphere. Thus, "1" means the intersection occurred in the sun.

Quote:
A last question: from what i'm seeing with my results, everything looks good except i do not get a kind of white haze at the horizon, whatever my position on the planet is. Do you have the same problem ?


I guess that had to do with the missing (1-F_ex), right?

Quote:Original post by pentaphobe
Lutz: a basic xplanation photographic exposure and cramming HDI onto 8-bit channels:
http://freespace.virgin.net/hugo.elias/graphics/x_posure.htm


Very nice explanation of HDRI. Although it's not directly related to the question of (1-F_Ex), I think I'll try to include this scaling some time.

Lutz

This topic is closed to new replies.

Advertisement