Atmospheric light scattering shader... 4 use with ATMOsphere

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.

Atmospheric light scattering shader... 4 use with ATMOsphere

Postby devsh » Sun May 09, 2010 7:57 pm

Dear Irrlichters,

I have swapped engines due to irrlicht's poor parallel performance, things were being held back by the CPU etc etc. causing a drop in FPS which was unnacceptable. Therefore I didnt produce any code for irrlicht (I have but a quick hack over ATMOsphere, unsuitable for redistribution because of code cleaness and performance).

This is per-pixel, and not per vertex unlike nvidia's and Ogre3D's SkyX. Therefore better :)
so you can take 3 samples for it to look good (I prefer 8)

Image
The thing is first rendered to a texture, then texture wrapped on a hemi-sphere, this is LatLong wrapping but I will work on getting the thing in Peirce Quincuncial Projection so area is preserved (there is oversampling at the top and undersampling on the rim of the hemi-sphere)

So you will have to C++ this code for yourself

However I have not left you on your own, in the shader comments there are recomended values for uniform values.

I would also build on top of "ATMOsphere by pazystamo" to get the sun vector... normalize(AbsolutecameraPos-AbsolutesunPos)

Of course you will need to get rid of ATMOsphere's billboard sun...

Shaders:
std.vert
Code: Select all
varying vec3 normal;

void main()
{
        gl_Position = gl_ModelViewProjectionMatrix *gl_Vertex;
   gl_FrontColor = gl_Color;

   gl_TexCoord[0] = gl_MultiTexCoord0;
        normal = gl_Normal;
}


Scatter.frag
this shader tricks a screenQuad into "thinking" its a sphere and performs scattering
Code: Select all
// Could not be defines because they get altered

float Mie = 0.0005;
float Ray = 0.0025;

// time' contains seconds since the program was linked.

uniform float g,gSQ,altitudeMetres,atmoAltInKm,sunIntensity,Exposure;
// -0.921, g^2, x<250, 120, 50, 1.6 == reccomended values
uniform int nSamples; // 8<x<24
uniform vec3 WaveLen, sun; // (x,y,z), (0.57,0.54,0.44)
uniform sampler2D stars;

#define innerR 9.82751
#define outerR 10.2963
#define IOscale 0.46879
float scaledDepth;

float F(float cosD) {
   return 0.75*cosD*cosD+0.75;
}

float FMie(float cosD) {
   return ((3.0*(1.0-gSQ))/(2.0*(2.0+gSQ)))*((1.0+cosD*cosD)/pow(1.0+gSQ-2.0*g*cosD,1.5));
}

float scale(float cosd)
{
   float x = 1.0 - cosd;
   return scaledDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));
}

void main()
{
   // == Dome from UV reconstr
   vec3 norm;
   norm.y = cos(gl_TexCoord[0].y*1.570796);
   norm.x = cos(6.283184*gl_TexCoord[0].x);
   norm.z = sin(6.283184*gl_TexCoord[0].x);
   norm.xz *= sqrt(1.0-norm.y*norm.y);

   // == Begin Scattering
   vec3 sunDir = normalize(sun);
   float sunToStar = 1.0+min(0.1+sunDir.y/max(abs(sunDir.x),max(abs(sunDir.y),abs(sunDir.z))),0.0);
   float Ho = 1.0-pow(max(sunDir.y,0.0),0.5)*0.8;
   Mie += pow(1.0-abs(sunDir.y),3.0)*0.006;
   Ray += pow(1.0-abs(sunDir.y),1.25)*0.003;
    scaledDepth = IOscale*Ho;
   float IOscaleSQHo = pow(IOscale,2.0)*Ho;


   float cameraH = (IOscale/atmoAltInKm)*altitudeMetres/1000.0;
   float fDepth = exp(-cameraH/IOscaleSQHo);
   vec3 vRay = norm;
   vRay.y -= cameraH;
   float fFar = length(vRay);
   vRay /= fFar;
   vec3 vStart = vec3(0.0,cameraH,0.0);
   float fStartOffset = fDepth * scale(vRay.y);
   
   float fSampleLen = fFar/float(nSamples);
   float fScaledLen = fSampleLen/IOscale;
   vec3 vSampleRay = vRay*fSampleLen;
   vec3 vSamplePoint = vStart+vSampleRay*0.5;

   vec3 invPwrRayWave = 1.0/pow(WaveLen,4.0)*Ray;
   vec3 invPwrMieWave = 1.0/pow(WaveLen,0.84)*Mie;
   vec3 loopWave = invPwrMieWave*12.56637+invPwrRayWave*12.56637;

   // ray tracing :)
   vec3 color;
   for (int i = 0; i < nSamples; i++)
   {
      float fHeight = length(vSamplePoint);
      fDepth = exp(-fHeight/IOscaleSQHo);
      
      float fLightAngle = 0.45+0.55*dot(sunDir, vSamplePoint) / fHeight;
      float fCameraAngle = 0.45+0.55*dot(vRay, vSamplePoint) / fHeight;
      
      float fScatter = fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle));
      vec3 vAttenuate = exp(-fScatter*loopWave );
      
      // Accumulate color
      color.xyz += vAttenuate*(fDepth * fScaledLen);
      
      // Next sample point
      vSamplePoint += vSampleRay;
   }

    vec3 RayleighColor = color*invPwrRayWave;
    vec3 MieColor      = color*invPwrMieWave;

   float cosd = dot(normalize(vec3(0.0,cameraH,0.0)-norm),sunDir);
   vec4 final = vec4(1.0-exp(-(Exposure)*(FMie(cosd)*MieColor+F(cosd)*RayleighColor)*sunIntensity*(1.0+pow(max(sunDir.y*1.25,0.0),4.0))),1.0)*pow(sunToStar,6.0);

   //Stolen from SkyX
   float nightmult = saturate(1 - max(final.x, max(final.y, final.z))*64);
   final.xyz += nightmult *(vec3(0.02, 0.02, 0.04)*(2.0-0.75*saturate(-sunDir.y))*pow(1.0-norm.y,3.0) + texture2D(stars,normalize(vec3(norm.x,norm.y*0.85+0.15,norm.z)).xz).xyz*(0.35 + saturate(-sunDir.y*0.45)));

   gl_FragColor = final;
}


LookUp.frag
Code: Select all
// simple fragment shader

// 'time' contains seconds since the program was linked.
uniform sampler2D tex;

varying vec3 normal;

void main()
{
   vec3 norm = normalize(normal);
   vec2 normXZ = normalize(norm.xz);
   float rX = (norm.z<0.0 ? (6.283183-acos(normXZ.x)):acos(normXZ.x))/6.283183;
   float rY = acos(norm.y)/1.570796;
   vec2 TC =  vec2(rX,rY);
   gl_FragColor = texture2D(tex,clamp(TC,0.005,0.996));
   if (norm.y<0.0) gl_FragColor = 0.0;
}


Ok here is the performance friendly implementation:

1) make a ScreenQuad (like xEffects)
2) make a shader material for that quad using the Scatter.frag and std.vert shader
3) make two Render Targets (if you are a cheapscape 512x512 will do, but I advise >=1024X1024)
4) have a function called updateTexture() which is invoked each frame
now In this function you need to update the first renderTarget, render
into it using the quad with the Scatter shader
HOWEVER THERE IS AN IMPORTANT BUT:
If you try to update a 1024*1024 texture in one go your FPS is guaranteed to drop by loads. So here is a simple trick, split the screen into n^2 cells (i.e. 64). Then each updateTexture() function call, you set the view port to a particular cell and render that fraction of the screenQuad, then move along one cell. If you had split the texture into 64 cells and had 60 FPS then the texture would be updated within 1.5 seconds. Now when you completed with updating all the cells (64 out of 64 done) you copy the texture into the second Render Target we made earlier, then start the process again. You needed that second RTT to only see the finished result, and not see the half the cells upgraded and half not. Because the sun is moving slowly (if I remember right ATMOsphere updates the sun position every 15 virtual mins) you will not notice any lags.
5) every frame draw a skydome with the LookUp.frag shader&&material !!IT MUST HAVE BILINEAR FILTERING!! (trilinear and anisotropic are even better)

If you implement according to the above, even a shitty-just-support-shader-model-2.0 5800 geforce mobile laptop chipset should get 60 fps

P.S. I am really sorry that it sounds like a speech, and that i presented irrlicht in a bad light
Portfolio (WIP) and Development Blog:
http://indirectlightandmagic.tumblr.com/

Do you want to hire a GLSL graphics programmer cheaply???
Try me!
http://indirectlightandmagic.tumblr.com/contact
User avatar
devsh
Competition winner
 
Posts: 1304
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Postby devsh » Mon May 10, 2010 3:54 pm

Of course you can reuse the sky texture for different uses.

cheap object reflections (would be useful for the VeeDub guy)
Image

non uniform ambient
Image

Cheap water rendering
Image
Last edited by devsh on Mon May 10, 2010 7:22 pm, edited 3 times in total.
Portfolio (WIP) and Development Blog:
http://indirectlightandmagic.tumblr.com/

Do you want to hire a GLSL graphics programmer cheaply???
Try me!
http://indirectlightandmagic.tumblr.com/contact
User avatar
devsh
Competition winner
 
Posts: 1304
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Postby ACE247 » Mon May 10, 2010 5:48 pm

So you switched Engines. :(
What happened to "I'm on a mission to make irrlicht next gen... and I dont have time to play with D3D" :?:
But nice shader :!: I will see how I can use this. :)
Last edited by ACE247 on Mon May 10, 2010 6:01 pm, edited 1 time in total.
H8L6L5M4G3H5M7N8S7N9O1R8J1P5M7N9O4P2Q5R6T7U4M3N8X6S5T8W (If you want the secret, why not 'TRY' decrypting it? )
This Door's lock doesn't need a key, the Lock is the key to open the Lock and you don't know how to turn the key...
Link: My Blog :)
User avatar
ACE247
 
Posts: 701
Joined: Tue Mar 16, 2010 12:31 am
Location: Namibia

Postby Bate » Mon May 10, 2010 6:01 pm

That looks awesome, too bad it's not HLSL.
Never take advice from someone who likes to give advice, so take my advice and don't take it.
User avatar
Bate
 
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Postby ACE247 » Mon May 10, 2010 6:05 pm

Yeah I'd love to have this in HLSL.
Unfortunately i dont have a clue about GL so anyone that would convert this will make me very happy. :D
H8L6L5M4G3H5M7N8S7N9O1R8J1P5M7N9O4P2Q5R6T7U4M3N8X6S5T8W (If you want the secret, why not 'TRY' decrypting it? )
This Door's lock doesn't need a key, the Lock is the key to open the Lock and you don't know how to turn the key...
Link: My Blog :)
User avatar
ACE247
 
Posts: 701
Joined: Tue Mar 16, 2010 12:31 am
Location: Namibia

Postby devsh » Mon May 10, 2010 6:12 pm

converting to HLSL is too easy

get a tutorial and find out the equivalents for the gl_ variables
Portfolio (WIP) and Development Blog:
http://indirectlightandmagic.tumblr.com/

Do you want to hire a GLSL graphics programmer cheaply???
Try me!
http://indirectlightandmagic.tumblr.com/contact
User avatar
devsh
Competition winner
 
Posts: 1304
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Postby ACE247 » Mon May 10, 2010 6:17 pm

Will try, It'll probably do me good anyhow. Any special links for good GL Tut's welcome.
PS: were did you get that titanic model? It looks awesome!
H8L6L5M4G3H5M7N8S7N9O1R8J1P5M7N9O4P2Q5R6T7U4M3N8X6S5T8W (If you want the secret, why not 'TRY' decrypting it? )
This Door's lock doesn't need a key, the Lock is the key to open the Lock and you don't know how to turn the key...
Link: My Blog :)
User avatar
ACE247
 
Posts: 701
Joined: Tue Mar 16, 2010 12:31 am
Location: Namibia

Postby slavik262 » Mon May 10, 2010 6:27 pm

Out of curiosity, what engine have you switched to?

Also, can we see your water rendering shader? That's one of the prettiest water surfaces I've ever seen.
User avatar
slavik262
 
Posts: 753
Joined: Sun Nov 22, 2009 9:25 pm
Location: Wisconsin, USA

Postby shadowslair » Mon May 10, 2010 6:38 pm

Lol! Devsh, can you please post image links/thumbnails only? And looks like SF doesn`t resize the avatars automatically, but it would be very nice of you to crop

your avatar a little bit. Yeah, we spend many hours staring at the screen, but luckily we`re still able to see things. BTW, I have seen these images- Virion had

already posted a link at: http://irrlicht.sourceforge.net/phpBB2/ ... hp?t=38262 ( http://www-evasion.imag.fr/Membres/Eric.Bruneton/ )
:)
Last edited by shadowslair on Mon May 10, 2010 6:40 pm, edited 2 times in total.
Image
"Although we walk on the ground and step in the mud... our dreams and endeavors reach the immense skies..."
User avatar
shadowslair
 
Posts: 758
Joined: Mon Mar 31, 2008 3:32 pm
Location: Bulgaria

Postby Bate » Mon May 10, 2010 6:39 pm

slavik262 wrote:Out of curiosity, what engine have you switched to?

Also, can we see your water rendering shader? That's one of the prettiest water surfaces I've ever seen.


Hehe, indeed. I was afraid of asking myself :)
Never take advice from someone who likes to give advice, so take my advice and don't take it.
User avatar
Bate
 
Posts: 364
Joined: Sun Nov 01, 2009 11:39 pm
Location: Germany

Postby ACE247 » Mon May 10, 2010 6:46 pm

Seems like devsh just copied those pic's. Image
http://www-evasion.imag.fr/Membres/Eric.Bruneton/images/small/ocean.png
But obviously they were just for demonstration. 8)
H8L6L5M4G3H5M7N8S7N9O1R8J1P5M7N9O4P2Q5R6T7U4M3N8X6S5T8W (If you want the secret, why not 'TRY' decrypting it? )
This Door's lock doesn't need a key, the Lock is the key to open the Lock and you don't know how to turn the key...
Link: My Blog :)
User avatar
ACE247
 
Posts: 701
Joined: Tue Mar 16, 2010 12:31 am
Location: Namibia

Postby devsh » Mon May 10, 2010 7:02 pm

yeh they are the only ones I did not render myself, due to to sheer effort required to implement Bidirectional Reflectance Distribution Function, this is what makes the surface look so nice. But you can see it is only that and the reflection of the skydome*fresnel term which give the surface its colour.

Now it is time for volumetric clouds, http://www.gamedev.net/columns/hardcore/cloudrendering/ from that I will progress onto the better looking method by the guy who did the water
Portfolio (WIP) and Development Blog:
http://indirectlightandmagic.tumblr.com/

Do you want to hire a GLSL graphics programmer cheaply???
Try me!
http://indirectlightandmagic.tumblr.com/contact
User avatar
devsh
Competition winner
 
Posts: 1304
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Postby Grz- » Tue Aug 10, 2010 11:40 am

I finally got it working with a few tweaks but it fail to apply the texture correctly on the skydome and the sun is missing (with tweaks i can have either the sun or the 'gradient' but not both at the same time), only a small portion of the skydome is covered and the star texture is not shown too... if someone could help out...

Here is what i have so far:

Image

shader parameters:

Code: Select all
            irr::f32 TexVar = -0.991f;
            services->setPixelShaderConstant("g", &TexVar, 1);

            TexVar = -0.982f;
            services->setPixelShaderConstant("gSQ", &TexVar, 1);

            TexVar = 10.0f;
            services->setPixelShaderConstant("altitudeMetres", &TexVar, 1);

            TexVar = 120.0f;
            services->setPixelShaderConstant("atmoAltInKm", &TexVar, 1);

            TexVar = 50.0f;
            services->setPixelShaderConstant("sunIntensity", &TexVar, 1);

            TexVar = 2.0f;
            services->setPixelShaderConstant("Exposure", &TexVar, 1);

            irr::u32 TexVar2 = 8;
            services->setPixelShaderConstant("nSamples", (irr::f32*)(&TexVar2), 1);

core::vector3df pos = core::vector3df(core::vector3df(1/pow(0.650f, 4.0f),1/pow(0.570f, 4.0f),1/pow(0.475f, 4.0f)));

services->setPixelShaderConstant ( "WaveLen" , reinterpret_cast<f32*>( &pos ) , 3 );

core::vector3df np = core::vector3df(0.57,0.54,0.44);
services->setPixelShaderConstant ( "sun" , reinterpret_cast<f32*>( &np ) , 3 );


         irr::u32 TexVar3 = 1;
         services->setPixelShaderConstant("stars", (irr::f32*)(&TexVar3), 1);
            TexVar3 = 0;
         services->setPixelShaderConstant("tex", (irr::f32*)(&TexVar3), 1);


Applied using this code:

Code: Select all
    s32 matScattering = loadShaders("data/shaders/std.vert", "data/shaders/scatter.frag");
    s32 matLookUp = loadShaders("data/shaders/std.vert", "data/shaders/lookup.frag");

    CScreenQuad *screenQuad = new CScreenQuad(smgr->getRootSceneNode(),smgr,10);
     screenQuad->getMaterial(0).MaterialType = (video::E_MATERIAL_TYPE)matScattering ;

   driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
   driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, true);

    video::ITexture* rt = driver->addRenderTargetTexture(core::dimension2d<u32>(1024,1024), "RTT1", video::ECF_A8R8G8B8);

    driver->beginScene(true, true, 0);
    driver->setRenderTarget(rt, true, true, video::SColor(255,255,255,255));
    screenQuad->render();
    driver->setRenderTarget(0, true, true, 0);
    driver->endScene();

    scene::ISceneNode *skyDome = smgr->addSkyDomeSceneNode(rt,32,16,1.0f,2.0f);
    skyDome->setMaterialFlag ( video::EMF_LIGHTING , false );
    skyDome->setMaterialFlag(video::EMF_BILINEAR_FILTER, true);
    skyDome->setMaterialFlag(video::EMF_ANISOTROPIC_FILTER, true);
    skyDome->setMaterialType ( (video::E_MATERIAL_TYPE)matLookUp );
    skyDome->setMaterialTexture(0, rt);
    skyDome->setMaterialTexture(1, driver->getTexture("data/stars.jpg"));
Grz-
 
Posts: 60
Joined: Wed Dec 26, 2007 1:06 pm

Postby Grz- » Sat Aug 14, 2010 5:42 am

i finally got the sun working (gSQ = 0.858) and the projection somewhat work but i needed to add this line in the lookup.frag in order to get a correct alignment of the horizon with my terrain:

Code: Select all
float rY = acos(norm.y+1.0)/1.570796;


The only problem now is that it seem i scaled down the texture a bit with that line, i guess there is another way to get the correct alignment, if someone can help...

A screenshot of the shader (somewhat) working:

Image
Grz-
 
Posts: 60
Joined: Wed Dec 26, 2007 1:06 pm

Postby devsh » Fri Sep 03, 2010 10:01 am

whoah soz dude, I always take 6 weeks off in the summer and not do any game devving, anyway what is your problem?
Portfolio (WIP) and Development Blog:
http://indirectlightandmagic.tumblr.com/

Do you want to hire a GLSL graphics programmer cheaply???
Try me!
http://indirectlightandmagic.tumblr.com/contact
User avatar
devsh
Competition winner
 
Posts: 1304
Joined: Tue Dec 09, 2008 6:00 pm
Location: UK

Next

Return to Code Snippets

Who is online

Users browsing this forum: No registered users and 1 guest