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?
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();
}
}
}