[solved] Atmospheric shader problem

You are an experienced programmer and have a problem with the engine, shaders, or advanced effects? Here you'll get answers.
No questions about C++ programming or topics which are answered in the tutorials!
Post Reply
Puck6633
Posts: 27
Joined: Wed Aug 10, 2005 8:35 am

[solved] Atmospheric shader problem

Post by Puck6633 »

I'm trying to implement the atmospheric shader from GPU Gems 2 (http://http.developer.nvidia.com/GPUGem ... ter16.html) and I'm getting, well... Unexpected results:

Image

Anyone know what I'm doing wrong here? The C++ is here:

http://codepad.org/w2QhmYJZ

And the shaders are here:

http://codepad.org/dfKuCcg1
Last edited by Puck6633 on Wed Oct 17, 2012 3:36 pm, edited 1 time in total.
Granyte
Posts: 849
Joined: Tue Jan 25, 2011 11:07 pm
Contact:

Re: Atmospheric shader problem

Post by Granyte »

WIch part of the code are you trying to get to work? the sky from space or the atmosphere from space? is this a picture from your inner sphere(ground)? or from youyr outer sphere (atmosphere)?


Because atmospheric scatering is a effect that comes in 4 shader and GPU gem is a rather poor source
Puck6633
Posts: 27
Joined: Wed Aug 10, 2005 8:35 am

Re: Atmospheric shader problem

Post by Puck6633 »

Either one would be fine. The sphere in the picture is the outer (atmosphere) sphere. The inner sphere just has an earth texture applied for now, no shader.
Granyte
Posts: 849
Joined: Tue Jan 25, 2011 11:07 pm
Contact:

Re: Atmospheric shader problem

Post by Granyte »

alright you need to invert the culling of the outer sphere so that the polygon are rendered inside out.

Also from what i can tell you seem to be missing several function after your loop to mix the colors properly
Puck6633
Posts: 27
Joined: Wed Aug 10, 2005 8:35 am

Re: Atmospheric shader problem

Post by Puck6633 »

Okay, I'm still trying to resolve this problem, but I've made progress. I had made several mistakes in the way the parameters were intended to be used:
  • innerRadius and outerRadius were reverse such that innerRadius > outerRadius, duh.
  • v3LightPos is misleadingly named, as it's supposed to be a normalized vector to the light source, not the light position
  • cameraHeight was being calculated wrong, and replacing that bit with a call to getDistanceFrom() helped
Making the above changes resulted in the ground shader suddenly working as expected, but surprisingly with the exact same parameters the sky shader is still non-functional. See here:

Image

The artifacts from the sky sphere jump around seemingly at random, varying with the camera position (everything else is static). Any help would be very much appreciated. I know that GPU Gems is not an ideal source of information, but there's not a lot out there on atmospheric scattering on the GPU.

My code is as follows:

Code: Select all

 
#include <unistd.h>
#include <iostream>
#include <vector>
 
#include <irrlicht.h>
 
using namespace std;
using namespace irr;
using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;
 
IrrlichtDevice *device = 0;
ILightSceneNode* light = 0;
 
class AtmosphereCallBack : public IShaderConstantSetCallBack {
    public:
        virtual void OnSetConstants(video::IMaterialRendererServices* services, s32 userData) {
            vector3df pos = device->getSceneManager()->getActiveCamera()->getAbsolutePosition();
            float camerapos[3] = {pos.X, pos.Y, pos.Z};
            //float camerapos[3] = {1, 0, -100};
            vector3df pos2 = light->getAbsolutePosition();
            pos2.normalize();
            float lightpos[3] = {pos2.X, pos2.Y, pos2.Z};
            //float lightpos[3] = {0, 0, -1};
      services->setVertexShaderConstant("v3CameraPos", &camerapos[0], 3);
      services->setVertexShaderConstant("v3LightPos", &lightpos[0], 3);
      float invWavelength[3] = {1/powf(0.65, 4), 1/powf(0.57, 4), 1/powf(0.475, 4)};
      services->setVertexShaderConstant("v3InvWavelength", &invWavelength[0], 3);
      f32 cameraHeight2 = camerapos[0]*camerapos[0]+camerapos[1]*camerapos[1]+camerapos[2]*camerapos[2];
      f32 cameraHeight = sqrt(cameraHeight2);
      services->setVertexShaderConstant("fCameraHeight", &cameraHeight, 1);
      services->setVertexShaderConstant("fCameraHeight2", &cameraHeight2, 1);
      f32 innerRadius = 10.0;
      f32 innerRadius2 = innerRadius*innerRadius;
      f32 outerRadius = 10.25;
      f32 outerRadius2 = outerRadius*outerRadius;
      services->setVertexShaderConstant("fOuterRadius", &outerRadius, 1);
      services->setVertexShaderConstant("fOuterRadius2", &outerRadius2, 1);
      services->setVertexShaderConstant("fInnerRadius", &innerRadius, 1);
      services->setVertexShaderConstant("fInnerRadius2", &innerRadius2, 1);
      f32 KrESun = 0.0025*50.0;
      f32 KmESun = 0.0010*50.0;
      services->setVertexShaderConstant("fKrESun", &KrESun, 1);
      services->setVertexShaderConstant("fKmESun", &KmESun, 1);
      f32 Kr4PI = 0.0025*4.0f*PI;
      f32 Km4PI = 0.0010*4.0f*PI;
      services->setVertexShaderConstant("fKr4PI", &Kr4PI, 1);
      services->setVertexShaderConstant("fKm4PI", &Km4PI, 1);
      f32 scale = 1/(outerRadius-innerRadius);
      f32 scaleDepth = 0.25;
      f32 scaleOverScaleDepth = scale/scaleDepth;
      services->setVertexShaderConstant("fScale", &scale, 1);
      services->setVertexShaderConstant("fScaleDepth", &scaleDepth, 1);
      services->setVertexShaderConstant("fScaleOverScaleDepth", &scaleOverScaleDepth, 1);
 
      f32 g = -0.990f;
      f32 g2 = g*g;
      services->setPixelShaderConstant("v3LightPos", &lightpos[0], 3);
      services->setPixelShaderConstant("g", &g, 1);
      services->setPixelShaderConstant("g2", &g2, 1);
        }
};
 
class MyEventReceiver : public IEventReceiver {
    IrrlichtDevice* Device;
 
    public:
        MyEventReceiver(IrrlichtDevice* device) : Device(device) {
        }
 
        bool OnEvent(const SEvent& event) {
            if (event.EventType == irr::EET_KEY_INPUT_EVENT && !event.KeyInput.PressedDown) {
                switch (event.KeyInput.Key) {
                    default:
                    break;
                }
            }
 
            return false;
        }
};
 
int main(int argc, char** argv) {
    device = createDevice(EDT_OPENGL, dimension2d<u32>(1024, 768), 32, false, true, false, 0);
    IVideoDriver* driver = device->getVideoDriver();
    ISceneManager* smgr = device->getSceneManager();
    IGUIEnvironment* guienv = device->getGUIEnvironment();
    IGPUProgrammingServices* gpu = driver->getGPUProgrammingServices();
 
    device->setWindowCaption(L"GLSL Test");
 
    driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, false);
    smgr->addSkyBoxSceneNode(driver->getTexture("irrlicht2_up.jpg"), driver->getTexture("irrlicht2_dn.jpg"), driver->getTexture("irrlicht2_lf.jpg"), driver->getTexture("irrlicht2_rt.jpg"), driver->getTexture("irrlicht2_ft.jpg"), driver->getTexture("irrlicht2_bk.jpg"));
    driver->setTextureCreationFlag(ETCF_CREATE_MIP_MAPS, true);
 
    ICameraSceneNode* cam = smgr->addCameraSceneNodeFPS();
    cam->setPosition(vector3df(0, 0, -100));
    cam->setFarValue(1000);
    ((ISceneNodeAnimatorCameraFPS*)*cam->getAnimators().begin())->setMoveSpeed(0.01f);
    device->getCursorControl()->setVisible(false);
 
    light = smgr->addLightSceneNode(smgr->getRootSceneNode(), vector3df(1000, 0, 0), SColorf(1.0, 1.0, 1.0), 10000);
 
    ISceneNode* surf = smgr->addSphereSceneNode(10, 40);
    surf->setMaterialTexture(0, driver->getTexture("earth.jpg"));
    /*ISceneNodeAnimator* anim = smgr->createRotationAnimator(vector3df(0, 0.1f, 0));
    surf->addAnimator(anim);
    anim->drop();*/
    ISceneNode* atmo = smgr->addSphereSceneNode(10.25, 200);
 
 
    //Shaders
    AtmosphereCallBack* gc = new AtmosphereCallBack();
    surf->setMaterialType((E_MATERIAL_TYPE)gpu->addHighLevelShaderMaterialFromFiles("GroundFromSpaceVert.glsl", "main", EVST_VS_2_0, "GroundFromSpaceFrag.glsl", "main", EPST_PS_2_0, gc, EMT_SOLID));
    gc->drop();
 
    AtmosphereCallBack* ac = new AtmosphereCallBack();
    atmo->setMaterialType((E_MATERIAL_TYPE)gpu->addHighLevelShaderMaterialFromFiles("SkyFromSpaceVert.glsl", "main", EVST_VS_2_0, "SkyFromSpaceFrag.glsl", "main", EPST_PS_2_0, ac, EMT_TRANSPARENT_ADD_COLOR));
    ac->drop();
 
    while(device->run()) {
        driver->beginScene(true, true, SColor(0, 0, 0, 0));
 
        smgr->drawAll();
        guienv->drawAll();
 
        driver->endScene();
        usleep(1000);
    }
 
    device->drop();
    return 0;
}
 
 
As a side note you can find a Win32/GCC binary below that demonstrates the problem in real time.

http://www.mediafire.com/?rur3jy5ar05y1js
Last edited by Puck6633 on Tue Oct 16, 2012 11:16 pm, edited 2 times in total.
Granyte
Posts: 849
Joined: Tue Jan 25, 2011 11:07 pm
Contact:

Re: Atmospheric shader problem

Post by Granyte »

Your executable is not working because the lib you are using is missing.


also i can tell for sure that the innerradius is suposed to be smaller then outerradius

Code: Select all

float fCameraHeight = v3CameraPos.getDistanceFrom(vector3df(0,0,0));
                //services->setPixelShaderConstant("fCameraHeight", reinterpret_cast<f32*>(&fCameraHeight), 1);
                float fCameraHeight2 = fCameraHeight*fCameraHeight;  // fCameraHeight^2
                services->setPixelShaderConstant("fCameraHeightSqr", reinterpret_cast<f32*>(&fCameraHeight2), 1);
                float fInnerRadius = mybody->MyData.Radius;    // The inner (planetary) radius
                services->setPixelShaderConstant("fInnerRadius", reinterpret_cast<f32*>(&fInnerRadius), 1);
                float fInnerRadius2 = fInnerRadius*fInnerRadius;  // fInnerRadius^2
                //services->setPixelShaderConstant("fInnerRadius2", reinterpret_cast<f32*>(&fInnerRadius2), 1);
                float fOuterRadius = fInnerRadius*1.025f;    // The outer (atmosphere) radius
                services->setPixelShaderConstant("fOuterRadius", reinterpret_cast<f32*>(&fOuterRadius), 1);
                float fOuterRadius2 = fOuterRadius*fOuterRadius;  // fOuterRadius^2
this is how i set this constant in my code

also note when you finish assembling your colors
This is what you do and to be honest i don't undertand how it works

Code: Select all

gl_FrontSecondaryColor.rgb = v3FrontColor * fKmESun;
    gl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun);
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    v3Direction = v3CameraPos - v3Pos;
 
because my version make use of

Code: Select all

float fCos = dot(fvLightPos, fvCameraPos - fvPos) / length(fvCameraPos - fvPos);
   float fCos2 = fCos*fCos;
   float4 color = getRayleighPhase(fCos2) * c0 + getMiePhase(fCos, fCos2, fG, fG2) * c1;
   color.a = color.b;
 
  
  Output.RGBColor = (1.0f-exp(-fExposure *color));//

An here are the get reyleight and get mie functions

Code: Select all

// 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 0.75 * (2.0 + 0.5 * fCos2);
   //return 0.75 + 0.75*fCos2;
}
 
Puck6633
Posts: 27
Joined: Wed Aug 10, 2005 8:35 am

Re: Atmospheric shader problem

Post by Puck6633 »

Thank you! I've updated the previous post with a link that includes libIrrlicht.dll, so anyone who's interested can have a look. I did verify in my latest code that outerRadius is greater than innerRadius, and I'm not sure exactly what the shader is doing either, but it's taken directly from O'Neil's sources, so I'm assuming it's something I'm doing wrong in setting up the uniforms. I've also tried reducing the fragment shader to a simple vertex color fill and the result is approximately the same, so the problem lies more in the vertex shader.

I'll keep playing with it, but any advice would still be appreciated!
Granyte
Posts: 849
Joined: Tue Jan 25, 2011 11:07 pm
Contact:

Re: Atmospheric shader problem

Post by Granyte »

try this

Code: Select all

    Atmosphere->setMaterialFlag(EMF_BLEND_OPERATION,true);
    Atmosphere->setMaterialFlag(EMF_BACK_FACE_CULLING,false);
    Atmosphere->setMaterialFlag(EMF_FRONT_FACE_CULLING,true);

i think that your code is actualy working but we are looking at the wrong side of the sphere

and you should try to make the calculation int he frag shader instead of in the vert shader otherwise you will never geta smooth result with the 256 poly sphere of irrlicht
Puck6633
Posts: 27
Joined: Wed Aug 10, 2005 8:35 am

Re: Atmospheric shader problem

Post by Puck6633 »

Sure enough, you were right! I toggled back/front face culling and it works fine now. Funny that I don't remember the article mentioning anything about that.. Thank you so much!
Post Reply