Atmosphere Shader (From Space)

I've put together an atmospheric shader from various sources and temporarily hardcoded as much as possible except the camera position. The only problem is it doesn't work in jME. I've tested the shaders in precompiled C apps - so I suspect I'm missing something during the set up of the sphere.



I was wondering if Mr Coder could run his eyes over it and provide any help?  :slight_smile:



Vertex Shader


uniform vec3 v3CameraPos;      // The camera's current position

//const vec3 v3CameraPos = vec3(0.0, 0.0, 25.0);
const float fInnerRadius = 10.0;
const float fInnerRadius2 = fInnerRadius * fInnerRadius;
const float fOuterRadius = 10.25;
const float fOuterRadius2 = fOuterRadius * fOuterRadius;
const int nSamples = 2;
const float fSamples = 2.0;
const vec3 v3LightPos = vec3(1000.0, 1000.0, 1000.0) / length(vec3(1000.0, 1000.0, 1000.0));
const float fCameraHeight = length(v3CameraPos);
const float fCameraHeight2 = (v3CameraPos.x * v3CameraPos.x) + (v3CameraPos.y * v3CameraPos.y) + (v3CameraPos.z * v3CameraPos.z);
const vec3 v3Wavelength = pow(vec3(0.650, 0.570, 0.475), vec3(4.0, 4.0, 4.0));
const float fKrESun = 0.0025 * 15.0;
const float fKmESun = 0.0015 * 15.0;
const float fKr4PI = 0.0025 * 4.0 * 3.142;
const float fKm4PI = 0.0015 * 4.0 * 3.142;
const float fScale = 1.0 / (fOuterRadius - fInnerRadius);
const float fScaleDepth = 0.25;
const float fScaleOverScaleDepth = (1.0 / (fOuterRadius - fInnerRadius)) / fScaleDepth;

varying vec3 v3Direction;

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

void main(void)
{
   vec3 v3InvWavelength = v3Wavelength;

   v3InvWavelength.x = 1.0/v3InvWavelength.x;
   v3InvWavelength.y = 1.0/v3InvWavelength.y;
   v3InvWavelength.z = 1.0/v3InvWavelength.z;
   
   /*
   v3InvWavelength.x = 5.6020;
   v3InvWavelength.y = 9.4732;
   v3InvWavelength.z = 19.6438;
   */
   
   // Get the ray from the camera to the vertex and its length (which is the far point of the ray passing through the atmosphere)
   vec3 v3Pos = gl_Vertex.xyz;
   vec3 v3Ray = v3Pos - v3CameraPos;
   float fFar = length(v3Ray);
   v3Ray /= fFar;

   // Calculate the closest intersection of the ray with the outer atmosphere (which is the near point of the ray passing through the atmosphere)
   float B = 2.0 * dot(v3CameraPos, v3Ray);
   float C = fCameraHeight2 - fOuterRadius2;
   float fDet = max(0.0, B*B - 4.0 * C);
   float fNear = 0.5 * (-B - sqrt(fDet));

   // Calculate the ray's starting position, then calculate its scattering offset
   vec3 v3Start = v3CameraPos + v3Ray * fNear;
   fFar -= fNear;
   float fStartAngle = dot(v3Ray, v3Start) / fOuterRadius;
   float fStartDepth = exp(-1.0 / fScaleDepth);
   float fStartOffset = fStartDepth*scale(fStartAngle);

   // Initialize the scattering loop variables
   //gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);
   float fSampleLength = fFar / fSamples;
   float fScaledLength = fSampleLength * fScale;
   vec3 v3SampleRay = v3Ray * fSampleLength;
   vec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;

   // Now loop through the sample rays
   vec3 v3FrontColor = vec3(0.0, 0.0, 0.0);
   for(int i=0; i<nSamples; i++)
   {
      float fHeight = length(v3SamplePoint);
      float fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));
      float fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;
      float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;
      float fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));
      vec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));
      v3FrontColor += v3Attenuate * (fDepth * fScaledLength);
      v3SamplePoint += v3SampleRay;
   }

   // Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader
   gl_FrontSecondaryColor.rgb = v3FrontColor * fKmESun;
   gl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun);
   gl_Position = ftransform();
   v3Direction = v3CameraPos - v3Pos;
}



Fragment Shader


varying vec3 v3Direction;

const float g = -0.95;
const float g2 = 0.9025;
const vec3 v3LightPos = vec3(1000.0, 1000.0, 1000.0) / length(vec3(1000.0, 1000.0, 1000.0));

void main (void)
{
   float fCos = dot(v3LightPos, v3Direction) / length(v3Direction);
   float fRayleighPhase = 0.75 * (1.0 + fCos*fCos);
   float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);
   gl_FragColor = fRayleighPhase * gl_Color + fMiePhase * gl_SecondaryColor;
}



Test Class


package com.captiveimagination.gb;

import com.jme.app.SimpleGame;
import com.jme.bounding.BoundingSphere;
import com.jme.scene.shape.Sphere;
import com.jme.scene.state.GLSLShaderObjectsState;
import com.jme.system.DisplaySystem;
import java.util.logging.Level;
import java.util.logging.Logger;

public class TestAtmo extends SimpleGame {

    private GLSLShaderObjectsState shader;

    public static void main(String[] args) {
       TestAtmo app = new TestAtmo();
        app.setDialogBehaviour(ALWAYS_SHOW_PROPS_DIALOG);
        app.start();
    }

    @Override
   protected void simpleInitGame() {
       Sphere sphere = new Sphere("Sphere", 50, 50, 10);

      shader = DisplaySystem.getDisplaySystem().getRenderer().createGLSLShaderObjectsState();

      if(!shader.isSupported()) {
           Logger.getLogger("GB").log(Level.WARNING, "Shader not supported");
      } else {
         shader.load(TestAtmo.class.getClassLoader().getResource("data/glsl/sun.vert"), TestAtmo.class.getClassLoader().getResource("data/glsl/sun.frag"));
         shader.setUniform("v3CameraPos", DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().x, DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().y, DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().z);
         shader.apply();

         sphere.setRenderState(shader);
      }

      sphere.setModelBound(new BoundingSphere());
      sphere.updateModelBound();
      sphere.updateRenderState();

      rootNode.attachChild(sphere);
    }

    @Override
   protected void simpleUpdate() {
      if(shader.isSupported()) {
         shader.setUniform("v3CameraPos", DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().x, DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().y, DisplaySystem.getDisplaySystem().getRenderer().getCamera().getLocation().z);
         shader.apply();
      }
   }
}

It seems overexposed - all white. Maybe you are using HDR with your C tests and not using it in jME.


Now I look at it, yes. Thanks for pointing that out. Does jME support HDR rendering?

How did Mr Coder do it?

You can create an RGB16 or floating-point colorbuffer with LWJGL… jME doesn't seem to support it.

although not optimal you can scale the values to dynamic range, preferably with a tone mapping algo.

Ok thanks.



I think i'll leave it until its supported. I guess all this stuff including 3D textures will be shipping with jME 2.0 - all the stuff for implementing impressive graphics.