[SOLVED] From JME to pure LWJGL (gems 2 chapter 16 - atmospheric scattering)

Started by wondersonic, October 04, 2010, 13:41:16

Previous topic - Next topic

wondersonic

Hi all,
I wish to implement this effect but I don't want to migrate to JME (if possible). The code attached to the post (JME 2+) works well on a 2.0 compatible video card (Intel).

Note that this is the JME implementation of http://http.developer.nvidia.com/GPUGems2/gpugems2_chapter16.html (with some bugs corrected).

I've tried a brute force conversion to pure opengl resulting in nothing looking good.

I've even coded an aspect to catch LWJGL method calls without success (although this gives good hints).

Are there some 3D + shader gurus wanting to help?

Regards,
WS
S.

wondersonic

Note that I'm currently working with LWJGL in ortho mode and that I'm just a noob regarding 3D with shaders (although I've some knowledge regarding basic 3D and that I understand the goal of Vertex and Fragment shaders)  ;D
S.

wondersonic

Here are the lwjgl traces for 5 frames of the example running with JME 2+.

Samples:
LWJGL (org.lwjgl.opengl.ContextCapabilities.initAllStubs(ContextCapabilities.java:3079) -> org.lwjgl.opengl.GLContext.getSupportedExtensions(GLContext.java:185)): String org.lwjgl.opengl.GL11.glGetString(int)( 7938 )
LWJGL (org.lwjgl.opengl.ContextCapabilities.initAllStubs(ContextCapabilities.java:3079) -> org.lwjgl.opengl.GLContext.getSupportedExtensions(GLContext.java:226)): String org.lwjgl.opengl.GL11.glGetString(int)( 7939 )
LWJGL (org.lwjgl.opengl.References.<init>(References.java:7) -> org.lwjgl.opengl.BaseReferences.<init>(BaseReferences.java:51)): void org.lwjgl.opengl.GL11.glGetInteger(int, IntBuffer)( 34921, [16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] )
LWJGL (org.lwjgl.opengl.References.<init>(References.java:7) -> org.lwjgl.opengl.BaseReferences.<init>(BaseReferences.java:59)): void org.lwjgl.opengl.GL11.glGetInteger(int, IntBuffer)( 34018, [8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] )
LWJGL (org.lwjgl.opengl.Display.makeCurrentAndSetSwapInterval(Display.java:866) -> org.lwjgl.opengl.Util.checkGLError(Util.java:52)): int org.lwjgl.opengl.GL11.glGetError()(  )
LWJGL (org.lwjgl.opengl.WindowsContextImplementation.setSwapInterval(WindowsContextImplementation.java:109) -> org.lwjgl.opengl.Util.checkGLError(Util.java:52)): int org.lwjgl.opengl.GL11.glGetError()(  )
LWJGL (org.lwjgl.opengl.Display.create(Display.java:844) -> org.lwjgl.opengl.Display.initContext(Display.java:872)): void org.lwjgl.opengl.GL11.glClear(int)( 16384 )
S.

wondersonic

S.


wondersonic

S.

wondersonic

And it is finished! (At least the effect I wish to have):

import org.lwjgl.BufferUtils;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.ARBFragmentProgram;
import org.lwjgl.opengl.ARBFragmentShader;
import org.lwjgl.opengl.ARBShaderObjects;
import org.lwjgl.opengl.ARBTextureCubeMap;
import org.lwjgl.opengl.ARBVertexProgram;
import org.lwjgl.opengl.ARBVertexShader;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.EXTFogCoord;
import org.lwjgl.opengl.EXTStencilTwoSide;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.PixelFormat;
import org.lwjgl.util.vector.Quaternion;
import org.lwjgl.util.vector.Vector3f;
import org.lwjgl.util.vector.Vector4f;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

/**
 * Inspired by Sean O'Neil code and snareoj2 (http://jmonkeyengine.org/groups/free-announcements/forum/topic/atmospheric-scattering).
 *
 * @author Wondersonic
 */
public class AtmosphericScattering {
    private static float SAMPLES = 2f;
    private static final float RAD_TO_DEG = 180f / (float) Math.PI;
    private static final float INNER_RADIUS = 100.0f;
    private static final float OUTER_RADIUS = 102.5f;

    static final ByteBuffer temp16 = ByteBuffer.allocateDirect(Float.SIZE * 16);
    static final ByteBuffer temp4 = ByteBuffer.allocateDirect(Float.SIZE * 4);
    static final ByteBuffer temp1 = ByteBuffer.allocateDirect(Integer.SIZE);

    static {
        temp16.order(ByteOrder.nativeOrder());
        temp4.order(ByteOrder.nativeOrder());
        temp1.order(ByteOrder.nativeOrder());
    }

    static final Vector3f lightPos = new Vector3f();
    static final Vector3f lightNormalized = new Vector3f();
    static float cameraHeight = INNER_RADIUS + 0.25f;
    static Quaternion q = new Quaternion();
    static float angle = 0f;

    public static void main(String[] args) {
        try {
            Display.setDisplayMode(new DisplayMode(640, 480));
            Display.create(new PixelFormat(32, 8, 24, 8, 0));

            GL11.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
            GL11.glMatrixMode(GL11.GL_PROJECTION);

            GL11.glLoadMatrix((FloatBuffer) temp16.asFloatBuffer().put(new float[]{1.8106601f, 0.0f, 0.0f, 0.0f, 0.0f, 2.4142134f, 0.0f, 0.0f, 0.0f, 0.0f, -1.002002f, -1.0f, 0.0f, 0.0f, -2.002002f, -0.0f}).flip());

            GL11.glViewport(0, 0, 640, 480);

            GL11.glMatrixMode(GL11.GL_MODELVIEW);

            GL11.glLoadMatrix((FloatBuffer) temp16.asFloatBuffer().put(new float[]{0.58123815f, -0.061288267f, -0.81142217f, 0.0f, -0.8137335f, -0.043777328f, -0.5795872f, 0.0f, 0.0f, 0.9971597f, -0.07531736f, 0.0f, 0.0f, -100.214554f, 7.5693946f, 1.0f}).flip());

            GL11.glDisable(GL11.GL_DEPTH_TEST);
            GL11.glDepthMask(true);

            boolean hasToInit = true;

            final GroundShader groundShader = new GroundShader(
                    "// GroundFromAtmosphereVert.glsl\n" +
                    "//\n" +
                    "//\n" +
                    "// Atmospheric scattering vertex shader\n" +
                    "//\n" +
                    "// Author: Sean O'Neil\n" +
                    "//\n" +
                    "// Copyright (c) 2004 Sean O'Neil\n" +
                    "//\n" +
                    "\n" +
                    "uniform vec3 v3CameraPos;\t\t// The camera's current position\n" +
                    "uniform vec3 v3LightPos;\t\t// The direction vector to the light source\n" +
                    "uniform vec3 v3InvWavelength;\t// 1 / pow(wavelength, 4) for the red, green, and blue channels\n" +
                    "uniform float fCameraHeight;\t// The camera's current height\n" +
                    "uniform float fInnerRadius;\t\t// The inner (planetary) radius\n" +
                    "uniform float fKrESun;\t\t\t// Kr * ESun\r\n" +
                    "uniform float fKmESun;\t\t\t// Km * ESun\r\n" +
                    "uniform float fKr4PI;\t\t\t// Kr * 4 * PI\r\n" +
                    "uniform float fKm4PI;\t\t\t// Km * 4 * PI\r\n" +
                    "uniform float fScale;\t\t\t// 1 / (fOuterRadius - fInnerRadius)\r\n" +
                    "uniform float fScaleDepth;\t\t// The scale depth (i.e. the altitude at which the atmosphere's average density is found)\n" +
                    "uniform float fScaleOverScaleDepth;\t// fScale / fScaleDepth\r\n" +
                    "\r\n" +
                    "uniform int nSamples;\r\n" +
                    "uniform float fSamples;\r\n" +
                    "\n" +
                    "float scale(float fCos)\n" +
                    "{\n" +
                    "\tfloat x = 1.0 - fCos;\n" +
                    "\treturn fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));\n" +
                    "}\n" +
                    "\n" +
                    "void main(void)\n" +
                    "{\n" +
                    "\t// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)\n" +
                    "\tvec3 v3Pos = gl_Vertex.xyz;\n" +
                    "\tvec3 v3Ray = v3Pos - v3CameraPos;\n" +
                    "\tfloat fFar = length(v3Ray);\n" +
                    "\tv3Ray /= fFar;\n" +
                    "\n" +
                    "\t// Calculate the ray's starting position, then calculate its scattering offset\n" +
                    "\tvec3 v3Start = v3CameraPos;\n" +
                    "\tfloat fDepth = exp((fInnerRadius - fCameraHeight) / fScaleDepth);\n" +
                    "\tfloat fCameraAngle = dot(-v3Ray, v3Pos) / length(v3Pos);\n" +
                    "\tfloat fLightAngle = dot(v3LightPos, v3Pos) / length(v3Pos);\n" +
                    "\tfloat fCameraScale = scale(fCameraAngle);\n" +
                    "\tfloat fLightScale = scale(fLightAngle);\n" +
                    "\tfloat fCameraOffset = fDepth*fCameraScale;\n" +
                    "\tfloat fTemp = (fLightScale + fCameraScale);\n" +
                    "\n" +
                    "\t// Initialize the scattering loop variables\n" +
                    "\tfloat fSampleLength = fFar / fSamples;\n" +
                    "\tfloat fScaledLength = fSampleLength * fScale;\n" +
                    "\tvec3 v3SampleRay = v3Ray * fSampleLength;\n" +
                    "\tvec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;\n" +
                    "\n" +
                    "\t// Now loop through the sample rays\n" +
                    "\tvec3 v3FrontColor = vec3(0.0, 0.0, 0.0);\n" +
                    "\tvec3 v3Attenuate;\n" +
                    "\tfor(int i=0; i<nSamples; i++)\n" +
                    "\t{\n" +
                    "\t\tfloat fHeight = length(v3SamplePoint);\n" +
                    "\t\tfloat fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));\n" +
                    "\t\tfloat fScatter = fDepth*fTemp - fCameraOffset;\n" +
                    "\t\tv3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));\n" +
                    "\t\tv3FrontColor += v3Attenuate * (fDepth * fScaledLength);\n" +
                    "\t\tv3SamplePoint += v3SampleRay;\n" +
                    "\t}\n" +
                    "\n" +
                    "\tgl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun + fKmESun);\n" +
                    "\n" +
                    "\t// Calculate the attenuation factor for the ground\n" +
                    "\tgl_FrontSecondaryColor.rgb = v3Attenuate;\n" +
                    "\n" +
                    "\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
                    "\tgl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;\n" +
                    "\tgl_TexCoord[1] = gl_TextureMatrix[1] * gl_MultiTexCoord1;\n" +
                    "}\n"
                    , "// GroundFromAtmosphereFrag.glsl\n" +
                      "//\n" +
                      "//\n" +
                      "// Atmospheric scattering fragment shader\n" +
                      "//\n" +
                      "// Author: Sean O'Neil\n" +
                      "//\n" +
                      "// Copyright (c) 2004 Sean O'Neil\n" +
                      "//\n" +
                      "\n" +
                      "//uniform sampler2D s2Tex1;\n" +
                      "//uniform sampler2D s2Tex2;\n" +
                      "\n" +
                      "\n" +
                      "void main (void)\n" +
                      "{\n" +
                      "\tgl_FragColor = gl_Color + 0.25 * gl_SecondaryColor;\n" +
                      "\t//gl_FragColor = gl_Color + texture2D(s2Tex1, gl_TexCoord[0].st) * texture2D(s2Tex2, gl_TexCoord[1].st) * gl_SecondaryColor;\n" +
                      "}\n");

            final SkyShader skyShader = new SkyShader(
                    "// SkyFromAtmosphereVert.glsl\n" +
                    "//\n" +
                    "//\n" +
                    "// Atmospheric scattering vertex shader\n" +
                    "//\n" +
                    "// Author: Sean O'Neil\n" +
                    "//\n" +
                    "// Copyright (c) 2004 Sean O'Neil\n" +
                    "//\n" +
                    "\n" +
                    "uniform vec3 v3CameraPos;\t\t// The camera's current position\n" +
                    "uniform vec3 v3LightPos;\t\t// The direction vector to the light source\n" +
                    "uniform vec3 v3InvWavelength;\t// 1 / pow(wavelength, 4) for the red, green, and blue channels\n" +
                    "uniform float fCameraHeight;\t// The camera's current height\n" +
                    "uniform float fInnerRadius;\t\t// The inner (planetary) radius\n" +
                    "uniform float fKrESun;\t\t\t// Kr * ESun\n" +
                    "uniform float fKmESun;\t\t\t// Km * ESun\n" +
                    "uniform float fKr4PI;\t\t\t// Kr * 4 * PI\n" +
                    "uniform float fKm4PI;\t\t\t// Km * 4 * PI\n" +
                    "uniform float fScale;\t\t\t// 1 / (fOuterRadius - fInnerRadius)\n" +
                    "uniform float fScaleDepth;\t\t// The scale depth (i.e. the altitude at which the atmosphere's average density is found)\n" +
                    "uniform float fScaleOverScaleDepth;\t// fScale / fScaleDepth\n" +
                    "\n" +
                    "uniform int nSamples;\n" +
                    "uniform float fSamples;\n" +
                    "\n" +
                    "varying vec3 v3Direction;\n" +
                    "varying vec3 newFrontColor;\n" +
                    "varying vec3 newFrontSecondaryColor;\n" +
                    "\n" +
                    "float scale(float fCos)\n" +
                    "{\n" +
                    "\tfloat x = 1.0 - fCos;\n" +
                    "\treturn fScaleDepth * exp(-0.00287 + x*(0.459 + x*(3.83 + x*(-6.80 + x*5.25))));\n" +
                    "}\n" +
                    "\n" +
                    "void main(void)\n" +
                    "{\n" +
                    "\t// Get the ray from the camera to the vertex, and its length (which is the far point of the ray passing through the atmosphere)\n" +
                    "\tvec3 v3Pos = gl_Vertex.xyz;\n" +
                    "\tvec3 v3Ray = v3Pos - v3CameraPos;\n" +
                    "\tfloat fFar = length(v3Ray);\n" +
                    "\t// ray gets normalized\n" +
                    "\tv3Ray /= fFar;\n" +
                    "\n" +
                    "\t// Calculate the ray's starting position, then calculate its scattering offset\n" +
                    "\tvec3 v3Start = v3CameraPos;\n" +
                    "\tfloat fHeight = length(v3Start);\n" +
                    "\tfloat fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fCameraHeight));\n" +
                    "\tfloat fStartAngle = dot(v3Ray, v3Start) / fHeight;\n" +
                    "\tfloat fStartOffset = fDepth*scale(fStartAngle);\n" +
                    "\n" +
                    "\t// Initialize the scattering loop variables\n" +
                    "\t//gl_FrontColor = vec4(0.0, 0.0, 0.0, 0.0);\n" +
                    "\tfloat fSampleLength = fFar / fSamples;\n" +
                    "\tfloat fScaledLength = fSampleLength * fScale;\n" +
                    "\tvec3 v3SampleRay = v3Ray * fSampleLength;\n" +
                    "\tvec3 v3SamplePoint = v3Start + v3SampleRay * 0.5;\n" +
                    "\n" +
                    "\t// Now loop through the sample rays\n" +
                    "\tvec3 v3FrontColor = vec3(0.0, 0.0, 0.0);\n" +
                    "\tfor(int i=0; i<nSamples; i++)\n" +
                    "\t{\n" +
                    "\t\tfloat fHeight = length(v3SamplePoint);\n" +
                    "\t\tfloat fDepth = exp(fScaleOverScaleDepth * (fInnerRadius - fHeight));\n" +
                    "\t\tfloat fLightAngle = dot(v3LightPos, v3SamplePoint) / fHeight;\n" +
                    "\t\tfloat fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight;\n" +
                    "\t\tfloat fScatter = (fStartOffset + fDepth*(scale(fLightAngle) - scale(fCameraAngle)));\n" +
                    "\t\tvec3 v3Attenuate = exp(-fScatter * (v3InvWavelength * fKr4PI + fKm4PI));\n" +
                    "\t\tv3FrontColor += v3Attenuate * (fDepth * fScaledLength);\n" +
                    "\t\tv3SamplePoint += v3SampleRay;\n" +
                    "\t}\n" +
                    "\n" +
                    "\t// Finally, scale the Mie and Rayleigh colors and set up the varying variables for the pixel shader\n" +
                    "\tgl_FrontSecondaryColor.rgb = v3FrontColor * fKmESun;\n" +
                    "\tgl_FrontColor.rgb = v3FrontColor * (v3InvWavelength * fKrESun);\n" +
                    "\tgl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;\n" +
                    "\tv3Direction = v3CameraPos - v3Pos;\n" +
                    "\tnewFrontSecondaryColor.xyz = v3FrontColor * fKmESun;\n" +
                    "\tnewFrontColor.xyz = v3FrontColor * (v3InvWavelength * fKrESun);\n" +
                    "}\n"
                    , "// SkyFromAtmosphereFrag.glsl\n" +
                      "//\n" +
                      "//\n" +
                      "// Atmospheric scattering fragment shader\n" +
                      "//\n" +
                      "// Author: Sean O'Neil\n" +
                      "//\n" +
                      "// Copyright (c) 2004 Sean O'Neil\n" +
                      "//\n" +
                      "\n" +
                      "uniform vec3 v3LightPos;\n" +
                      "uniform float g;\n" +
                      "uniform float g2;\n" +
                      "\n" +
                      //"uniform float fExposure;\n" +
                      "\n" +
                      "varying vec3 v3Direction;\n" +
                      "\n" +
                      "varying vec3 newFrontColor;\n" +
                      "varying vec3 newFrontSecondaryColor;\n" +
                      "\n" +
                      "void main (void)\n" +
                      "{\n" +
                      "\tfloat fCos = dot(v3LightPos, v3Direction) / length(v3Direction);\n" +
                      "\t// here should be the texture lookup for opticalDepthBuffer ? - snareoj\n" +
                      "\tfloat fRayleighPhase = 0.75 * (1.0 + fCos*fCos);\n" +
                      "\tfloat fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos*fCos) / pow(1.0 + g2 - 2.0*g*fCos, 1.5);\n" +
                      "\tgl_FragColor.rgb = fRayleighPhase * newFrontColor + fMiePhase * newFrontSecondaryColor;\n" +
                      "\tgl_FragColor.a = gl_FragColor.b;\n" +
                      "\n" +
                      "\t// simple \"HDR\" clamping\n" +
                      "\t//gl_FragColor = (gl_FragColor - exp(-fExposure * gl_FragColor));\n" +
                      "}\n");

            float r = 100f;
            Quaternion qSun = new Quaternion(10000f, 10000f, 10000f, 0);
            Vector4f qHelper = new Vector4f(0, 1, 0, angle);

            long start = System.currentTimeMillis();
            long time;
            long end = System.currentTimeMillis();

            final SphereInfo ground = SphereBuilder.build(50, 25, INNER_RADIUS);
            final SphereInfo sky = SphereBuilder.build(50, 25, OUTER_RADIUS);
            final SphereInfo sun = SphereBuilder.build(8, 8, 100);

...
S.

wondersonic

            while (!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE) && !Display.isCloseRequested()) {

                time = end - start;
                start = end;

                angle += 0.05f * ((float) time / 1000f);

                qHelper.w = angle;
                q.setFromAxisAngle(qHelper);

                Quaternion.mul(q, qSun, q);

                GL11.glDisable(GL11.GL_DEPTH_TEST);

                GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT);

                GL11.glEnable(GL11.GL_DEPTH_TEST);

                if (hasToInit) {
                    GL11.glDisable(GL11.GL_TEXTURE_2D);
                    GL11.glEnable(GL11.GL_DEPTH_TEST);

                    GL11.glDisable(GL11.GL_BLEND);
                    GL11.glDisable(GL11.GL_ALPHA_TEST);
                    GL11.glDisable(GL11.GL_FOG);
                    GL11.glEnable(GL11.GL_LIGHTING);

                    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_TWO_SIDE, GL11.GL_TRUE);
                    GL11.glLightModeli(GL11.GL_LIGHT_MODEL_LOCAL_VIEWER, GL11.GL_FALSE);
                    GL11.glLightModeli(GL12.GL_LIGHT_MODEL_COLOR_CONTROL, GL12.GL_SINGLE_COLOR);
                    GL11.glEnable(GL11.GL_LIGHT0);
                    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_AMBIENT, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0.5f, 0.5f, 0.5f, 1.0f}).flip());
                    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_DIFFUSE, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0.75f, 0.75f, 0.75f, 0.75f}).flip());
                    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_SPECULAR, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{1, 1, 1, 1}).flip());
                    GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_CONSTANT_ATTENUATION, 1.0f);
                    GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_LINEAR_ATTENUATION, 0.0f);
                    GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_QUADRATIC_ATTENUATION, 0.0f);
                    GL11.glLight(GL11.GL_LIGHT0, GL11.GL_POSITION, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{100, 100, 100, 1}).flip());
                    GL11.glLightf(GL11.GL_LIGHT0, GL11.GL_SPOT_CUTOFF, 180.0f);
                    GL11.glDisable(GL11.GL_LIGHT1);
                    GL11.glDisable(GL11.GL_LIGHT2);
                    GL11.glDisable(GL11.GL_LIGHT3);
                    GL11.glDisable(GL11.GL_LIGHT4);
                    GL11.glDisable(GL11.GL_LIGHT5);
                    GL11.glDisable(GL11.GL_LIGHT6);
                    GL11.glDisable(GL11.GL_LIGHT7);
                    GL11.glLightModel(GL11.GL_LIGHT_MODEL_AMBIENT, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0, 0, 0, 1}).flip());
                    GL11.glDisable(GL11.GL_COLOR_MATERIAL);
                    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_AMBIENT, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0.2f, 0.2f, 0.2f, 1.0f}).flip());
                    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_DIFFUSE, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0.8f, 0.8f, 0.8f, 1.0f}).flip());
                    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_EMISSION, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0f, 0f, 0f, 1.0f}).flip());
                    GL11.glMaterial(GL11.GL_FRONT, GL11.GL_SPECULAR, (FloatBuffer) temp4.asFloatBuffer().put(new float[]{0f, 0f, 0f, 1.0f}).flip());
                    GL11.glMaterialf(GL11.GL_FRONT, GL11.GL_SHININESS, 0.0f);
                    GL11.glShadeModel(GL11.GL_SMOOTH);
                    GL11.glHint(GL11.GL_PERSPECTIVE_CORRECTION_HINT, GL11.GL_NICEST);
                    GL11.glDisable(GL11.GL_TEXTURE_1D);
                    GL11.glDisable(ARBTextureCubeMap.GL_TEXTURE_CUBE_MAP_ARB);

                    GL11.glPolygonMode(GL11.GL_FRONT_AND_BACK, GL11.GL_FILL);

                    GL11.glEnable(GL11.GL_DEPTH_TEST);
                    GL11.glDepthFunc(GL11.GL_LEQUAL);
                    GL11.glDisable(GL11.GL_CULL_FACE);
                    GL11.glFrontFace(GL11.GL_CCW);
                    GL11.glDisable(ARBVertexProgram.GL_VERTEX_PROGRAM_ARB);
                    GL11.glDisable(ARBFragmentProgram.GL_FRAGMENT_PROGRAM_ARB);
                    GL11.glDisable(GL11.GL_STENCIL_TEST);
                    GL11.glDisable(EXTStencilTwoSide.GL_STENCIL_TEST_TWO_SIDE_EXT);

                    GL11.glColorMask(true, true, true, true);

                    hasToInit = false;
                }

                groundShader.enable();

                ground.rewind();
                GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
                GL11.glVertexPointer(3, 0, ground.vertices);

                GL11.glDisableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT);

                GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
                GL11.glNormalPointer(0, ground.normals);

                GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);

                GL11.glColor4f(1f, 1f, 1f, 1f);

                GL11.glDrawElements(4, ground.indices);

                // sky
                GL11.glEnable(GL11.GL_BLEND);
                GL11.glDisable(GL11.GL_LIGHTING);
                GL11.glEnable(GL11.GL_CULL_FACE);
                GL11.glFrontFace(GL11.GL_CW);

                skyShader.enable();

                sky.rewind();
                GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
                GL11.glVertexPointer(3, 0, sky.vertices);

                GL11.glDisableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT);

                GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
                GL11.glNormalPointer(0, sky.normals);

                GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);

                GL11.glColor4f(1f, 1f, 1f, 1f);

                GL11.glDrawElements(4, sky.indices);

                skyShader.disable();

                GL11.glDisable(GL11.GL_BLEND);
                GL11.glEnable(GL11.GL_LIGHTING);
                GL11.glDisable(GL11.GL_CULL_FACE);
                GL11.glFrontFace(GL11.GL_CCW);


                sun(sun);

                // flip buffers
                GL11.glFlush();

                end = System.currentTimeMillis();

                Display.update();
                Display.sync(60);
            }

            Display.destroy();
        }
        catch (Throwable t) {
            t.printStackTrace();
        }
    }

    private static void sun(SphereInfo sun) {
        // sun
        GL11.glPushMatrix();

        GL11.glTranslatef(q.x, q.y, q.z);
        GL11.glRotatef(angle * RAD_TO_DEG, 0.0f, 1f, 0.0f);

        lightPos.x = q.x;
        lightPos.y = q.y;
        lightPos.z = q.z;
        lightPos.normalise(lightNormalized);

        sun.rewind();
        GL11.glEnableClientState(GL11.GL_VERTEX_ARRAY);
        GL11.glVertexPointer(3, 0, sun.vertices);

        GL11.glDisableClientState(EXTFogCoord.GL_FOG_COORDINATE_ARRAY_EXT);

        GL11.glEnableClientState(GL11.GL_NORMAL_ARRAY);
        GL11.glNormalPointer(0, sun.normals);

        GL11.glDisableClientState(GL11.GL_COLOR_ARRAY);
        GL11.glColor4f(1f, 1f, 1f, 1f);

        GL11.glDrawElements(4, sun.indices);

        GL11.glPopMatrix();
    }

    static class SkyShader extends Shader {
        int v3CameraPosPointer;
        int v3LightPosPointer;
        int v3InvWavelengthPointer;
        int fCameraHeightPointer;
        int fInnerRadiusPointer;
        int fKrESunPointer;
        int fKmESunPointer;
        int fKr4PIPointer;
        int fKm4PIPointer;
        int fScalePointer;
        int fScaleDepthPointer;
        int fScaleOverScaleDepthPointer;
        int nSamplesPointer;
        int fSamplesPointer;
        int gPointer;
        int g2Pointer;

        public SkyShader(String vertexShaderSource, String fragmentShadersource) {
            super(vertexShaderSource, fragmentShadersource);
        }

        @Override
        protected void initializeUniforms() {
            v3CameraPosPointer = initUniform("v3CameraPos");
            v3LightPosPointer = initUniform("v3LightPos");
            v3InvWavelengthPointer = initUniform("v3InvWavelength");
            fCameraHeightPointer = initUniform("fCameraHeight");
            fInnerRadiusPointer = initUniform("fInnerRadius");
            fKrESunPointer = initUniform("fKrESun");
            fKmESunPointer = initUniform("fKmESun");
            fKr4PIPointer = initUniform("fKr4PI");
            fKm4PIPointer = initUniform("fKm4PI");
            fScalePointer = initUniform("fScale");
            fScaleDepthPointer = initUniform("fScaleDepth");
            fScaleOverScaleDepthPointer = initUniform("fScaleOverScaleDepth");
            nSamplesPointer = initUniform("nSamples");
            fSamplesPointer = initUniform("fSamples");
            gPointer = initUniform("g");
            g2Pointer = initUniform("g2");
        }

        @Override
        public void bindUniforms() {
            ARBShaderObjects.glUniform1fARB(fSamplesPointer, SAMPLES);
            ARBShaderObjects.glUniform1fARB(gPointer, -0.99f);
            ARBShaderObjects.glUniform1iARB(nSamplesPointer, (int) SAMPLES);
            ARBShaderObjects.glUniform1fARB(fCameraHeightPointer, cameraHeight);
            ARBShaderObjects.glUniform1fARB(fKrESunPointer, 0.049999997f);
            ARBShaderObjects.glUniform1fARB(fScalePointer, 0.4f);
            ARBShaderObjects.glUniform1fARB(fKr4PIPointer, 0.03141593f);
            ARBShaderObjects.glUniform1fARB(fScaleOverScaleDepthPointer, 1.6f);
            ARBShaderObjects.glUniform3fARB(v3CameraPosPointer, 0.0f, 0.0f, cameraHeight);
            ARBShaderObjects.glUniform3fARB(v3InvWavelengthPointer, 5.6020455f, 9.473285f, 19.643803f);
            ARBShaderObjects.glUniform1fARB(fScaleDepthPointer, 0.25f);
            ARBShaderObjects.glUniform1fARB(g2Pointer, 0.98010004f);
            ARBShaderObjects.glUniform3fARB(v3LightPosPointer, lightNormalized.x, lightNormalized.y, lightNormalized.z);
            ARBShaderObjects.glUniform1fARB(fKm4PIPointer, 0.012566372f);
            ARBShaderObjects.glUniform1fARB(fKmESunPointer, 0.020000001f);
            ARBShaderObjects.glUniform1fARB(fInnerRadiusPointer, INNER_RADIUS);
        }

    }

    static class GroundShader extends Shader {
        int v3CameraPosPointer;
        int v3LightPosPointer;
        int v3InvWavelengthPointer;
        int fCameraHeightPointer;
        int fInnerRadiusPointer;
        int fKrESunPointer;
        int fKmESunPointer;
        int fKr4PIPointer;
        int fKm4PIPointer;
        int fScalePointer;
        int fScaleDepthPointer;
        int fScaleOverScaleDepthPointer;
        int nSamplesPointer;
        int fSamplesPointer;

        public GroundShader(String vertexShaderSource, String fragmentShadersource) {
            super(vertexShaderSource, fragmentShadersource);
        }

        @Override
        protected void initializeUniforms() {
            v3CameraPosPointer = initUniform("v3CameraPos");
            v3LightPosPointer = initUniform("v3LightPos");
            v3InvWavelengthPointer = initUniform("v3InvWavelength");
            fCameraHeightPointer = initUniform("fCameraHeight");
            fInnerRadiusPointer = initUniform("fInnerRadius");
            fKrESunPointer = initUniform("fKrESun");
            fKmESunPointer = initUniform("fKmESun");
            fKr4PIPointer = initUniform("fKr4PI");
            fKm4PIPointer = initUniform("fKm4PI");
            fScalePointer = initUniform("fScale");
            fScaleDepthPointer = initUniform("fScaleDepth");
            fScaleOverScaleDepthPointer = initUniform("fScaleOverScaleDepth");
            nSamplesPointer = initUniform("nSamples");
            fSamplesPointer = initUniform("fSamples");
        }

        @Override
        public void bindUniforms() {
            ARBShaderObjects.glUniform1fARB(fSamplesPointer, SAMPLES);
            ARBShaderObjects.glUniform1iARB(nSamplesPointer, (int) SAMPLES);
            ARBShaderObjects.glUniform1fARB(fCameraHeightPointer, cameraHeight);
            ARBShaderObjects.glUniform1fARB(fKrESunPointer, 0.049999997f);
            ARBShaderObjects.glUniform1fARB(fScalePointer, 0.4f);
            ARBShaderObjects.glUniform1fARB(fKr4PIPointer, 0.03141593f);
            ARBShaderObjects.glUniform1fARB(fScaleOverScaleDepthPointer, 1.6f);
            ARBShaderObjects.glUniform3fARB(v3CameraPosPointer, 0.0f, 0.0f, cameraHeight);
            ARBShaderObjects.glUniform3fARB(v3InvWavelengthPointer, 5.6020455f, 9.473285f, 19.643803f);
            ARBShaderObjects.glUniform1fARB(fScaleDepthPointer, 0.25f);
            ARBShaderObjects.glUniform3fARB(v3LightPosPointer, lightNormalized.x, lightNormalized.y, lightNormalized.z);
            ARBShaderObjects.glUniform1fARB(fKm4PIPointer, 0.012566372f);
            ARBShaderObjects.glUniform1fARB(fKmESunPointer, 0.020000001f);
            ARBShaderObjects.glUniform1fARB(fInnerRadiusPointer, INNER_RADIUS);
        }
    }

    static abstract class Shader {
        final int vshID;
        final int fshID;
        final int programID;

        public Shader(final String vertexShaderSource, final String fragmentShadersource) {
            final IntBuffer temp1 = ByteBuffer.allocateDirect(Integer.SIZE).order(ByteOrder.nativeOrder()).asIntBuffer();

            // Initialize the shader program.
            this.programID = ARBShaderObjects.glCreateProgramObjectARB();

            // Initialize the vertex shader.
            this.vshID = ARBShaderObjects.glCreateShaderObjectARB(ARBVertexShader.GL_VERTEX_SHADER_ARB);
            ARBShaderObjects.glShaderSourceARB(vshID, vertexShaderSource);
            ARBShaderObjects.glCompileShaderARB(vshID);
            if (ARBShaderObjects.glGetObjectParameteriARB(vshID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE) {
                temp1.flip();
                ARBShaderObjects.glGetObjectParameterARB(this.vshID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, temp1);
                checkProgramError(temp1.get(0), this.vshID);
                throw new IllegalStateException("A compilation error occurred in a vertex shader.");
            }
            ARBShaderObjects.glAttachObjectARB(this.programID, this.vshID);

            // Initialize the fragment shader.
            this.fshID = ARBShaderObjects.glCreateShaderObjectARB(ARBFragmentShader.GL_FRAGMENT_SHADER_ARB);
            ARBShaderObjects.glShaderSourceARB(fshID, fragmentShadersource);
            ARBShaderObjects.glCompileShaderARB(fshID);
            if (ARBShaderObjects.glGetObjectParameteriARB(fshID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB) == GL11.GL_FALSE) {
                temp1.flip();
                ARBShaderObjects.glGetObjectParameterARB(this.fshID, ARBShaderObjects.GL_OBJECT_COMPILE_STATUS_ARB, temp1);
                checkProgramError(temp1.get(0), this.fshID);
                throw new IllegalStateException("A compilation error occurred in a fragment shader.");
            }

            ARBShaderObjects.glAttachObjectARB(programID, vshID);
            ARBShaderObjects.glAttachObjectARB(programID, fshID);

            ARBShaderObjects.glLinkProgramARB(programID);

            if (ARBShaderObjects.glGetObjectParameteriARB(programID, ARBShaderObjects.GL_OBJECT_LINK_STATUS_ARB) == GL11.GL_FALSE) {
                throw new IllegalStateException("A linking error occurred in a shader program.");
            }

            initializeUniforms();
        }

        protected abstract void initializeUniforms();

        protected int initUniform(final String field) {
            return getUniformLocation(programID, field);
        }

        private static int getUniformLocation(int ID, String name) {
            final int location = ARBShaderObjects.glGetUniformLocationARB(ID, name);

            if (location == -1) {
                throw new IllegalArgumentException("The uniform \"" + name + "\" does not exist in the Shader Program.");
            }

            return location;
        }

        protected static void checkProgramError(int compileStatus, int id) {
            if (compileStatus == 0) {
                IntBuffer iVal = BufferUtils.createIntBuffer(1);
                ARBShaderObjects.glGetObjectParameterARB(id, ARBShaderObjects.GL_OBJECT_INFO_LOG_LENGTH_ARB, iVal);
                int length = iVal.get();
                String out = null;

                if (length > 0) {
                    ByteBuffer infoLog = BufferUtils.createByteBuffer(length);

                    iVal.flip();
                    ARBShaderObjects.glGetInfoLogARB(id, iVal, infoLog);

                    byte[] infoBytes = new byte[length];
                    infoLog.get(infoBytes);
                    out = new String(infoBytes);
                }

                System.err.println(out);
            }
        }

        public void enable() {
            // enable shader!
            ARBShaderObjects.glUseProgramObjectARB(programID);

            bindUniforms();
        }

        public abstract void bindUniforms();

        public void disable() {
            ARBShaderObjects.glUseProgramObjectARB(0);
        }

        public void cleanup() {
            ARBShaderObjects.glDetachObjectARB(programID, vshID);
            ARBShaderObjects.glDetachObjectARB(programID, fshID);

            ARBShaderObjects.glDeleteObjectARB(vshID);
            ARBShaderObjects.glDeleteObjectARB(fshID);

            ARBShaderObjects.glDeleteObjectARB(programID);
        }

        public int getProgramID() {
            return programID;
        }
    }
...
S.

wondersonic

    static class SphereInfo {
        public FloatBuffer vertices;
        public FloatBuffer normals;
        public IntBuffer indices;

        public SphereInfo() {
        }

        public void rewind() {
            vertices.rewind();
            normals.rewind();
            indices.rewind();
        }
    }

    static class SphereBuilder {
        public static SphereInfo build(int zSamples, int radialSamples, float radius) {
            final SphereInfo si = new SphereInfo();

            final Vector3f center = new Vector3f();

            final Vector3f tempVa = new Vector3f();

            final Vector3f tempVb = new Vector3f();

            final Vector3f tempVc = new Vector3f();

            final float[] data = new float[3];

            // allocate vertices
            final int vertexCount = (zSamples - 2) * (radialSamples + 1) + 2;
            si.vertices = ByteBuffer.allocateDirect(vertexCount * Float.SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();

            // allocate normals if requested
            si.normals = ByteBuffer.allocateDirect(vertexCount * Float.SIZE).order(ByteOrder.nativeOrder()).asFloatBuffer();

            // generate geometry
            float fInvRS = 1.0f / radialSamples;
            float fZFactor = 2.0f / (zSamples - 1);

            // Generate points on the unit circle to be used in computing the mesh
            // points on a sphere slice.
            float[] afSin = new float[(radialSamples + 1)];
            float[] afCos = new float[(radialSamples + 1)];
            for (int iR = 0; iR < radialSamples; iR++) {
                float fAngle = (float) (Math.PI * 2d) * fInvRS * iR;
                afCos[iR] = (float) Math.cos(fAngle);
                afSin[iR] = (float) Math.sin(fAngle);
            }
            afSin[radialSamples] = afSin[0];
            afCos[radialSamples] = afCos[0];

            // generate the sphere itself
            int i = 0;
            for (int iZ = 1; iZ < (zSamples - 1); iZ++) {
                float fAFraction = (float) (Math.PI / 2d) * (-1.0f + fZFactor * iZ); // in (-pi/2, pi/2)
                float fZFraction;
                fZFraction = (float) Math.sin(fAFraction); // in (-1,1)

                float fZ = radius * fZFraction;

                // compute center of slice
                Vector3f kSliceCenter = tempVb.set(center);
                kSliceCenter.z += fZ;

                // compute radius of slice
                float fSliceRadius = (float) Math.sqrt(Math.abs(radius * radius - fZ * fZ));

                // compute slice vertices with duplication at end point
                Vector3f kNormal = new Vector3f();
                int iSave = i;
                for (int iR = 0; iR < radialSamples; iR++) {
                    float fRadialFraction = iR * fInvRS; // in [0,1)
                    Vector3f kRadial = new Vector3f(afCos[iR], afSin[iR], 0);
                    kRadial.scale(fSliceRadius);
                    si.vertices.put(kSliceCenter.x + kRadial.x).put(
                            kSliceCenter.y + kRadial.y).put(
                            kSliceCenter.z + kRadial.z);

                    tempVa.set(si.vertices.get(i * 3), si.vertices.get(i * 3 + 1), si.vertices.get(i * 3 + 2));
                    Vector3f.sub(tempVa, center, kNormal);
                    kNormal.normalise(kNormal);
                    si.normals.put(kNormal.x).put(kNormal.y).put(kNormal.z);

                    i++;
                }

                si.vertices.position(iSave * 3);
                si.vertices.get(data);
                si.vertices.position(i * 3);
                si.vertices.put(data);

                si.normals.position(iSave * 3);
                si.normals.get(data);
                si.normals.position(i * 3);
                si.normals.put(data);

                i++;
            }

            // south pole
            si.vertices.position(i * 3);
            si.vertices.put(center.x).put(center.y).put(center.z - radius);

            si.normals.position(i * 3);
            si.normals.put(0).put(0).put(-1); // allow for inner

            // north pole
            si.vertices.put(center.x).put(center.y).put(center.z + radius);

            si.normals.put(0).put(0).put(1);

            // allocate connectivity
            final int triangleCount = 2 * (zSamples - 2) * radialSamples;
            si.indices = ByteBuffer.allocateDirect(triangleCount * Integer.SIZE).order(ByteOrder.nativeOrder()).asIntBuffer();

            // generate connectivity
            int index = 0;
            for (int iZ = 0, iZStart = 0; iZ < (zSamples - 3); iZ++) {
                int i0 = iZStart;
                int i1 = i0 + 1;
                iZStart += (radialSamples + 1);
                int i2 = iZStart;
                int i3 = i2 + 1;
                for (int j = 0; j < radialSamples; j++, index += 6) {
                    si.indices.put(i0++);
                    si.indices.put(i1);
                    si.indices.put(i2);
                    si.indices.put(i1++);
                    si.indices.put(i3++);
                    si.indices.put(i2++);
                }
            }

            // south pole triangles
            for (int j = 0; j < radialSamples; j++, index += 3) {
                si.indices.put(j);
                si.indices.put(vertexCount - 2);
                si.indices.put(j + 1);
            }

            // north pole triangles
            int iOffset = (zSamples - 3) * (radialSamples + 1);
            for (int j = 0; j < radialSamples; j++, index += 3) {
                si.indices.put(j + iOffset);
                si.indices.put(j + 1 + iOffset);
                si.indices.put(vertexCount - 1);
            }

            si.rewind();

            return si;
        }
    }
}
S.