Sky box issue

Hey I’ve been working on a Skybox for my java game (OpenGL/LWJGL) what my issues is that:

One the texture is not “stretched” so to say across the quad face (how would I go about that? also the texture is rendered upside down)

Two I can’t get it to translate/rotate properly (the matrix makes it follow the camera which I want. But translate simple moves the skybox away from me, and rotate, well I’m doing something wrong with the pitch,yaw, and roll)

Notes:
Camera on load up is facing -Z, -X is left, and +Y is up
“multipier” can be ignored

What I need it to do:
Follow Camera: Check
Rotate when camera looks around: nope (glRotate works in a sense, saddly works weird cause i’m sure not using params right)
Texture to be nicely spread across the quad faces: nope

What am I doing wrong?

package terrain;

import static org.lwjgl.opengl.GL11.GL_RGBA;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Vector;

import loaders.ImageLoader;

import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL12;
import org.lwjgl.opengl.GL13;
import org.lwjgl.opengl.GL14;
import org.lwjgl.opengl.GL21;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;

import static org.lwjgl.opengl.GL11.*;

import camera.EulerCamera;

public class SkyBox {

        private Vector<Texture> textures;
        private EulerCamera cam;
        float boxSize = 5f;
        float texSize = 1f;
        float multipier = 2f;
        
        public SkyBox(EulerCamera cam) 
        {
                this.cam = cam;
        }
        
        public void draw() {
            	glPushMatrix();               
                glPushAttrib(GL_ENABLE_BIT);
                
                glLoadIdentity();                
                GL13.glActiveTexture(GL13.GL_TEXTURE0);
                glEnable(GL_TEXTURE_2D);
                
              //load textures
                textures = new Vector<Texture>();
                try 
                {                   	
                    textures.add(ImageLoader.loadTexture("Sky/front", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/left", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/back", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/right", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/top", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/bottom", "PNG"));
                }
                catch (Exception e)
                {
                	System.out.println(e.getStackTrace().toString());
                	System.out.println(e.getMessage());
                	System.out.println(e.getCause());
                	System.out.println(e.getLocalizedMessage());
                }
                
                glTranslatef(cam.roll(), cam.pitch(), cam.yaw());
//                glRotatef(25, cam.roll(), cam.pitch(), cam.yaw());
           
                // Just in case set all vertices to white.
            glColor4f(1,1,1,1);

            // Render the front quad
            textures.get(0).bind();            
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
                    glVertex3f(  -boxSize * multipier , boxSize * multipier , -boxSize );
                glTexCoord2f(1, 0); 
                    glVertex3f( boxSize * multipier , boxSize * multipier , -boxSize );
                glTexCoord2f(1, 1); 
                    glVertex3f( boxSize * multipier,  -boxSize * multipier, -boxSize );
                glTexCoord2f(0, 1); 
                    glVertex3f(  -boxSize * multipier,  -boxSize * multipier, -boxSize );
            glEnd();

            // Render the left quad
            textures.get(1).bind();
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
            		glVertex3f(  -boxSize , boxSize * multipier, boxSize * multipier);
            	glTexCoord2f(1, 0); 
            		glVertex3f( -boxSize , boxSize * multipier, -boxSize * multipier);
            	glTexCoord2f(1, 1); 
            		glVertex3f( -boxSize ,  -boxSize * multipier, -boxSize * multipier);
            	glTexCoord2f(0, 1); 
            		glVertex3f(  -boxSize ,  -boxSize * multipier, boxSize * multipier);
            glEnd();
            
            // Render the back quad
            textures.get(2).bind();
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
            		glVertex3f(  boxSize * multipier, boxSize * multipier, boxSize );
            	glTexCoord2f(1, 0); 
            		glVertex3f( -boxSize * multipier, boxSize * multipier, boxSize );
            	glTexCoord2f(1, 1); 
            		glVertex3f( -boxSize * multipier,  -boxSize * multipier, boxSize );
            	glTexCoord2f(0, 1); 
            		glVertex3f(  boxSize * multipier,  -boxSize * multipier, boxSize );
            glEnd();
            
            // Render the right quad
            textures.get(3).bind();
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
            		glVertex3f(  boxSize , boxSize * multipier, -boxSize * multipier);
            	glTexCoord2f(1, 0); 
            		glVertex3f( boxSize , boxSize * multipier, boxSize * multipier);
            	glTexCoord2f(1, 1); 
            		glVertex3f( boxSize ,  -boxSize * multipier, boxSize * multipier);
            	glTexCoord2f(0, 1); 
            		glVertex3f(  boxSize ,  -boxSize * multipier, -boxSize * multipier);
            glEnd();
            
            // Render the top quad
            textures.get(4).bind();
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
            		glVertex3f(  -boxSize * multipier, boxSize , boxSize * multipier);
            	glTexCoord2f(1, 0); 
            		glVertex3f( boxSize * multipier, boxSize , boxSize * multipier);
            	glTexCoord2f(1, 1); 
            		glVertex3f( boxSize * multipier,  boxSize , -boxSize * multipier);
            	glTexCoord2f(0, 1); 
            		glVertex3f(  -boxSize * multipier,  boxSize , -boxSize * multipier);
            glEnd();
            
            // Render the bottom quad
            textures.get(5).bind();
            clampToEdge();
            glBegin(GL_QUADS);
            	glTexCoord2f(0, 0); 
            		glVertex3f(  -boxSize * multipier, -boxSize , -boxSize * multipier);
            	glTexCoord2f(1, 0); 
            		glVertex3f( boxSize * multipier, -boxSize , -boxSize * multipier);
            	glTexCoord2f(1, 1); 
            		glVertex3f( boxSize * multipier,  -boxSize , boxSize * multipier);
            	glTexCoord2f(0, 1); 
            		glVertex3f(  -boxSize * multipier,  -boxSize , boxSize * multipier);
            glEnd();
                     
            // Restore enable bits and matrix
            glPopAttrib();            
            glPopMatrix();

        }

        //clamp textures, that edges get don't create a line in between
        private void clampToEdge() {
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        }
}

First, a skybox should be a unit cube (i.e. all vertex coordinates should be 1 or -1). The size doesn’t matter because it should be drawn first with depth tests and depth writes disabled.

Second, a skybox shouldn’t be translated. The viewpoint should always be at the exact centre of the cube.

It’s hard to say exactly how you should implement the latter because you don’t provide the source for the EulerCamera class. But the general idea is to perform the rotation component but not the translation. If you can get a matrix in a suitable form for passing to glLoadMatrix() or glMultMatrix() (i.e. an inverse matrix), then you can just set the translation components (the first three elements in the right-hand column) to zero. Or you may be able to make a copy of the camera then set the copy’s position to (0,0,0).

The size doesn’t matter because it should be drawn first with depth tests and depth writes disabled.

No, the skybox should be drawn after all opaque objects have been rendered, so that depth tests can cull as much of it out as possible.

Then you need to be careful to ensure that the skybox isn’t too small (or it will occlude objects) nor too large (or it will get clipped to the far plane).

OTOH, objects being occluded by the skybox may be preferable to clipping by the far plane, as the skybox rotates with the rest of the scene, so you won’t get objects visible at the edges of the screen disappearing when you turn towards them.

From an efficiency perspective, I’m curious whether early-depth optimisation wins over using the skybox to clear the depth buffer. I suppose that it depends upon how much of the skybox is visible.


                glTranslatef(cam.roll(), cam.pitch(), cam.yaw());
//                glRotatef(25, cam.roll(), cam.pitch(), cam.yaw());

Now that I’ve had a proper look, you probably want something like:


                glRotatef(-cam.roll(),  0, 0, 1);
                glRotatef(-cam.pitch(), 1, 0, 0);
                glRotatef(-cam.yaw(),   0, 1, 0);

although the exact order depends upon how you define these angles with respect to each other.

Also:

Life will probably be easier in the long run if your world has +X=east, +Y=north, +Z=up. Typically this means a glRotatef(-90, 1, 0, 0) call before everything else.

Then you need to be careful to ensure that the skybox isn’t too small (or it will occlude objects) nor too large (or it will get clipped to the far plane).

Or you could just render the skybox with a projection to infinity, thus ensuring that only things clipped by the far plane will clip against the skybox. So nothing clips against it.

From an efficiency perspective, I’m curious whether early-depth optimisation wins over using the skybox to clear the depth buffer. I suppose that it depends upon how much of the skybox is visible.

Buffer clears on hardware made in the last decade or so are a function of memory caches. They don’t actually modify the memory; when the ROPS go to perform a read/modify/write to a cleared framebuffer, if that cache line hasn’t been read since the last clear, it automatically gets the clear value. Same goes for depth testing; if the value hasn’t been tested since the last clear, it gets the clear value.

Clearing the depth buffer is free. Drawing the skybox is not free. Free is better :wink:

Thanks GClements for this code:

             glRotatef(-cam.roll() + 180,  0, 0, -1);
                glRotatef(-cam.pitch(), 1, 0, 0);
                glRotatef(-cam.yaw(),   0,-1, 0);

I’ve slightly altered it so the would rotate correctly (looking up brings the top into view instead of bottom) so that fixed my rotation issue, but saddly I still don’t know how to get that image to stretch all the way across the boxes face.

Also Thank you Alfonse for the explaination and the link “projection to infinity” I shall read up on this, however this is my first game and I really don’t mind too much on how things are rendered at the moment (well I do care, but I just want things to start to work so I can come back later and upgrade it. More or less a learning experience) Once again thank you.

And thank you too GClements since the rotation code is exactly what I needed, I wasn’t sure if I could only use one glRotate call or as many as I wanted (as you pointed out with 3 calls)
any ideas on the images? [ATTACH=CONFIG]480[/ATTACH]

Edit:
I managed to flip everything the right way (it was all rotated 180 or rolled in this case) fixed a few other lines of code and it looks like it should… kinda, the image is still just a random spot on the quad.

That would be unnecessarily complicated for a skybox or skydome. To push anything to the far plane, simply use DepthRange(1, 1). Remember to return to normal DepthRange(0, 1) after you have rendered the sky.

Tried the glDepthRange calls (starts with 1,1 then another call at the end uses 0,1) and it simply does not draw the skybox at all. Maybe I’m using the calls in the wrong place?

Anyways anyone have any idea how I messed up the texture placement? because it is dead set on drawing only one tiny image then clamping it to the edges, which is driving me nuts. (I want the clamp to edge else it just tiles the texture, instead of just stretching it as I want.)

I am just guessing here, but I would suspect the transformation that you use for the sky object is not quite right.

Try rendering the sky with some simple transformations. First try with identity transform. This should give a “working” but fixed, non-rotating sky. The next step would be to try some rotation only transform.

[QUOTE=tksuoran;1253574]I am just guessing here, but I would suspect the transformation that you use for the sky object is not quite right.

Try rendering the sky with some simple transformations. First try with identity transform. This should give a “working” but fixed, non-rotating sky. The next step would be to try some rotation only transform.[/QUOTE]

Transformation? this is the code that starts the draw method

glPushMatrix();               
                glPushAttrib(GL_ENABLE_BIT);
                
                glLoadIdentity();   
                GL13.glActiveTexture(GL13.GL_TEXTURE0);
                glEnable(GL_TEXTURE_2D);
//                glDepthRange(0, 1);
                
              //load textures
                textures = new Vector<Texture>();
                try 
                {                   	
                    textures.add(ImageLoader.loadTexture("Sky/front", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/left", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/back", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/right", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/top", "PNG"));
                    textures.add(ImageLoader.loadTexture("Sky/bottom", "PNG"));
                }
                catch (Exception e)
                {
                	System.out.println(e.getStackTrace().toString());
                	System.out.println(e.getMessage());
                	System.out.println(e.getCause());
                	System.out.println(e.getLocalizedMessage());
                }                
                
                glRotatef(-cam.roll() + 180,  0, 0, -1);
                glRotatef(-cam.pitch(), 1, 0, 0);
                glRotatef(-cam.yaw(),   0, 1, 0);
//                glScalef(15,15, 15);
           
                // Just in case set all vertices to white.
            glColor4f(1,1,1,1);

then farther down it has 6 glbegin(GL_QUADS) and such. Also Identity Transform? how do I do that? I’m using LWJGL (which is openGL) for Java. (Also have Slick and Slick utils too)

Your transformation looks plausible for a sky object: There is LoadIndentity, followed by some rotations (and commented out scale). What I don’t see is a call to MatrixMode, so it would be possible that your matrix mode is something wrong (not modelview).

You should not be loading textures in the drawing code. Textures should be loaded before any drawing takes place.

There is not much texture setup code visible. You need to bind the texture and setup texture environment.

You should learn modern way to program OpenGL (shaders etc.) instead of the old fixed function way.

[QUOTE=tksuoran;1253632]Your transformation looks plausible for a sky object: There is LoadIndentity, followed by some rotations (and commented out scale). What I don’t see is a call to MatrixMode, so it would be possible that your matrix mode is something wrong (not modelview).

You should not be loading textures in the drawing code. Textures should be loaded before any drawing takes place.

There is not much texture setup code visible. You need to bind the texture and setup texture environment.

You should learn modern way to program OpenGL (shaders etc.) instead of the old fixed function way.[/QUOTE]

private static void render() 
    {
            //glPolygonMode(GL_FRONT, GL_POINT);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        glLoadIdentity();
        
        cam.applyTranslations();
        bound.setX((int)cam.x()-CHUNK_SIZE/2);
        bound.setY((int)cam.z()-CHUNK_SIZE/2);
        glLight(GL_LIGHT0, GL_POSITION, BufferTools.asFlippedFloatBuffer(cam.x(), cam.y(), cam.z(), 1));
        glUseProgram(terrainShader);
        sky.draw();
        terrain.draw(bound);
        glUseProgram(0);
        glMatrixMode(GL_PROJECTION);
        glLoadMatrix(orthographicProjectionMatrix);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glLoadIdentity();
        glDisable(GL_LIGHTING);
        glClear(GL_DEPTH_BUFFER_BIT);
        if(showDevInfo)
        {
                font.drawString(10, 10, "Position:
X: " + 
                		formatter.format(cam.x()) +
                        "
Y: " + 
                		formatter.format(cam.y()) +
                        "
Z: " + 
                		formatter.format(cam.z()) +
                		"
Pitch" +
                		formatter.format(cam.pitch()) +
                        "
Yaw: " + 
                		formatter.format(cam.yaw()) +
                        "
Roll: " + 
                		formatter.format(cam.roll()));
        }
        
        hud.draw();
        glEnable(GL_LIGHTING);
        glPopMatrix();
        glMatrixMode(GL_PROJECTION);
        glLoadMatrix(perspectiveProjectionMatrix);
        glMatrixMode(GL_MODELVIEW);            
    }

I do have shaders setup and run, as I was trying to get multitexturing to work for the terrain, saddly it either just merged my two textures then painted them, instead of what I wanted it to do (slopes are rock, flatter areas are grass)

I’m still very new to OpenGL and there are no good JAVA tutorials out there (lots of C++) But I moved the texture loading into the constructor, instead of loading the textures to infinity through the draw method.

And setup the texture enviroment? like

 private void clampToEdge() {
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        }

the clampToEdge method?

I’ve been a programmer for a few years now, and a 3d Modeler/animator. But I’ve never actually worked with graphical/3d rendering (other then really simple 2d Sprites)
Watched some tutorials on rendering modes and that VBOs is by far the closest to the best one you can use (display list are fast, but outdated, and cannot be altered during use)

[ATTACH=CONFIG]483[/ATTACH]

this is the current screen shot. Have an issue with the HUD since it doesn’t respect drawing two images on each other even if they have Alpha (but that’s another issue)