[JOGL] Displaying a skybox

Hello everyone,
This is my very first time in hear so sorry in advance if I forgot anything ^^

I’m currently working on a 3D game in java and I’ve chosen to work with the library JOGL. The reason of this new topic is that I’m currently stuck on the problem of displaying a skybox :

  • I managed to display a cube which is not textured,
  • The cube is displaying in the background, so the depth test is correctly disabled for rendering the skybox,
  • But the cube is always black :frowning:

My fragment shader looks like this :

#version 400

in vec3 textureCoordinates;

out vec4 outColor;

uniform samplerCube textureCubemap;
//uniform sampler2D textureCubemap; -> used when I wanna test my shader with a classic 2D texture

void main (void) {
    // The following line demonstrates that the coordinates are correctly used for sampling the texture
    //outColor = vec4(textureCoordinates, 1);

    // The following line permits to test the shader with a classic 2D texture
    //outColor = texture(textureCubemap, textureCoordinates.xy);

    // The following line is intended to work with a texture cubemap (but always displays a black cube)
    outColor = vec4(texture(textureCubemap, textureCoordinates).xyz, 1);
}

In Java, here’s my code for rendering the cubemap with a 3D texture (so this is the code that is not working) :

private void bufferData(GL3 gl) {
    // Setting the texture handler
    if (!isBinded) {
        int[] ids = new int[1];
        gl.glGenTextures(1, ids, 0);
        textureCubeMapHandler = ids[0];
        isBinded = true;
    }

    // Activating and buferring the texture
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_CUBE_MAP, textureCubeMapHandler);
    gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MAG_FILTER, GL.GL_NEAREST);
    gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_MIN_FILTER, GL.GL_NEAREST);
    gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP_TO_EDGE);
    gl.glTexParameteri(GL.GL_TEXTURE_CUBE_MAP, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP_TO_EDGE);

    // Buffering the X faces textures
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, positiveX.getInternalFormat(), positiveX.getWidth(),
            positiveX.getHeight(), 0, positiveX.getPixelFormat(), positiveX.getPixelType(),
            positiveX.getBuffer());
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, negativeX.getInternalFormat(), negativeX.getWidth(),
            negativeX.getHeight(), 0, negativeX.getPixelFormat(), negativeX.getPixelType(),
            negativeX.getBuffer());

    // Buffering the Y faces textures
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, positiveY.getInternalFormat(), positiveY.getWidth(),
            positiveY.getHeight(), 0, positiveY.getPixelFormat(), positiveY.getPixelType(),
            positiveY.getBuffer());
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, negativeY.getInternalFormat(), negativeY.getWidth(),
            negativeY.getHeight(), 0, negativeY.getPixelFormat(), negativeY.getPixelType(),
            negativeY.getBuffer());

    // Buffering the Z faces textures
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, positiveZ.getInternalFormat(), positiveZ.getWidth(),
            positiveZ.getHeight(), 0, positiveZ.getPixelFormat(), positiveZ.getPixelType(),
            positiveZ.getBuffer());
    gl.glTexImage2D(GL.GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, negativeZ.getInternalFormat(), negativeZ.getWidth(),
            negativeZ.getHeight(), 0, negativeZ.getPixelFormat(), negativeZ.getPixelType(),
            negativeZ.getBuffer());

    gl.glUniform1i(FxManager.textureCubemapHandler, 0);
}

And for the comparison, here’s the code I use to display the cubemap with a 2D texture (which is the code that is working) :

private void buffer2dData(GL3 gl) {
    // Setting the texture handler
    if (!isBinded) {
        int[] ids = new int[1];
        gl.glGenTextures(1, ids, 0);
        textureCubeMapHandler = ids[0];
        isBinded = true;
    }

    // Activating and buffering the texture
    gl.glActiveTexture(GL.GL_TEXTURE0);
    gl.glBindTexture(GL.GL_TEXTURE_2D, textureCubeMapHandler);
    gl.glTexImage2D(GL.GL_TEXTURE_2D, 0, texture2d.getInternalFormat(), texture2d.getWidth(),
            texture2d.getHeight(), 0, texture2d.getPixelFormat(), texture2d.getPixelType(),
            texture2d.getBuffer());
    gl.glGenerateMipmap(GL.GL_TEXTURE_2D);
    gl.glUniform1i(FxManager.textureCubemapHandler, 0);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR);
    gl.glTexParameteri(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR);
}

With only those lines of code, can anyone manage to see what may be wrong in my code for rendering with a 3D texture ? I was thinking about some parameterization missing (GL.GL_TEXTURE_WRAP_R doesn’t exist and I don’t know why 'cos I saw this int in a lot of examples on the net) but maybe it could come from another problem.

Don’t hesitate to ask more information if needed and thanks in advance for any help.

Hi Chennorris,

in the fragment you can write

outColor = texture(textureCubemap, textureCoordinates);

in your bufferData() you are quering if binded and if not, you generate the texture.

Texture binding and texture name generation poorly match.

You should generate the texture name a priori, once, at begin and then re-use it until you need and then delete.

During the runtime, bind the name before rendering.

Prefer IntBuffers over int arrays.

        IntBuffer cubeMapName = GLBuffers.newDirectIntBuffer(1);
        gl.glGenTextures(1, bufferName);

In the init, when you match a sampler to a texture index, be sure you got the program bound and use variable, if possible, to proper index you samples in order to be less error prone as possible and make your code readable


interface Sampler {
    int CUBEMAP = 0;
}

gl.glUseProgram(program);
gl.glUniform1i(FxManager.textureCubemapHandler, Sample.CUBEMAP);
gl.glUseProgram(0);

And in the rendering

    gl.glActiveTexture(GL_TEXTURE0 + Sampler.CUBEMAP);
    gl.glBindTexture(GL_TEXTURE_2D, cubeMapName);

However, be aware that a cubeMap is a special texture to render landscapes and similar (more info here), if you want to texture a box, you can render:

  • one face per time binding the corresponding texture (6 textures)
  • the whole box in one step binding one big texture containing all the faces, like here

Hi elect,

Thanks for this first response, it starts to help me a lot.

Understood for the change in my fragment shader (my mistake : I suppose the alpha component was still forced to 1 after debugging tries, I think I just forgot to put it back like you said).

[quote]Texture binding and texture name generation poorly match.
[/quote]
I think it’s not really clear in my head (I think I lack some experience in this domain) but can you confirm me the difference between texture binding and texture name generation :

  • Texture binding (made with gl.glBindTexture) permits to snap an integer on a type of texture (GL_TEXTURE_CUBE_MAP for example),
  • Texture name generation is the generation of an integer (the part where I should use IntBuffers instead of int array).

Is that right ? In that case, maybe I should rename isBinded into isNamed in my code…

Before calling this part of code, here’s the code that permits me to select the right shader :

public static void useShader(GL3 gl, String shader) {
    if (shaders.containsKey(shader)) {
        // Getting the desired shader
        int shaderProgram = shaders.get(shader).getShaderProgram();
        gl.glUseProgram(shaderProgram);

        // Retrieving the shader vertices' attributes locations
        positionHandler = shaders.get(shader).getPositionHandler();
        normalHandler = shaders.get(shader).getNormalHandler();
        colorHandler = shaders.get(shader).getColorHandler();
        uvHandler = shaders.get(shader).getUvHandler();

        // Retrieving the texture uniform location
        defaultTextureHandler = shaders.get(shader).getTextureDefaultHandler();
        textureCubemapHandler = shaders.get(shader).getTextureCubemapHandler();

        // Setting the shader's matrices
        int projectionHandler = shaders.get(shader).getProjectionHandler();
        int viewHandler = shaders.get(shader).getViewHandler();
        if (projectionHandler != -1 && viewHandler != -1) {
            gl.glUniformMatrix4fv(projectionHandler, 1, false, CameraManager.getProjectionMatrix(), 0);
            gl.glUniformMatrix4fv(viewHandler, 1, false, CameraManager.getViewMatrix(), 0);
        }
    }
    else {
        gl.glUseProgram(0);
    }
}

where shaders is a map like following :

private static Map<String, Shader> shaders;

My Shader class contains my handlers (positionHandler, colorHandler, …) and initializes them with the following method :

public boolean getLocations(GL3 gl) {
    // Detecting the attributes' locations
    positionHandler = gl.glGetAttribLocation(shaderProgram, "position");
    normalHandler = gl.glGetAttribLocation(shaderProgram, "normal");
    colorHandler = gl.glGetAttribLocation(shaderProgram, "color");
    uvHandler = gl.glGetAttribLocation(shaderProgram, "uvCoordinates");

    // Detecting the uniforms' locations
    projectionHandler = gl.glGetUniformLocation(shaderProgram, "projection");
    viewHandler = gl.glGetUniformLocation(shaderProgram, "view");

    // Detecting the textures' locations
    textureDefaultHandler = gl.glGetUniformLocation(shaderProgram, "defaultTexture");
    textureCubemapHandler = gl.glGetUniformLocation(shaderProgram, "textureCubemap");

    return true;
}

I just found something very very interesting…

As the handler for my cubemap was correct (≠ -1), I suggested to myself that the problem may come from the data I was buffering and decided to log into the console the buffers and here is what I discovered :

PositiveX data = java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
NegativeX data = java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
PositiveY data = java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
NegativeY data = java.nio.DirectByteBuffer[pos=0 lim=3145728 cap=3145728]
PositiveZ data = java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]
NegativeZ data = java.nio.DirectByteBuffer[pos=0 lim=4194304 cap=4194304]

The negative Y texture size is different from the other textures. I just had a look at my texture and discovered that the negative Y was not in 1024×1024 but in 1024×873. My mistake :cranky:
I changed the size of my texture with Gimp to set it to 1024×1024 but the file size still seems wrong (cubemap still black). But by copying another texture (negativeX) and renaming it to negativeY, the cubemap is properly displayed 8)

So I have now to understand why my negativeY texture doesn’t work (maybe I can find some compression options inside Gimp, I’ll see). Anyway, I’ll the post the solution when I find it.


EDIT :

Just had a look at the file details between the working textures and the negativeY and I discovered that my negativeY texture was 24 color-depthed while other ones are 32 color-depthed (sorry for my english in case « color-depthed » doesn’t exist ^^). In Gimp, I added an alpha channel and saved again my texture and now, everything works fine (every buffer now has the same size and the skybox is properly displayed).

END OF EDIT :


I also tried to replace my int arrays by IntBuffers but glBindTexture works with an int for the second parameter… Does that mean that I have to flip my IntBuffer everytime I need to call glBindTexture ?

[quote=“Chennorris,post:3,topic:58289”]
Then you should invest some time into understanding how opengl (texture) works first.
You can start from here, at the chapter “Texture Binding” at the middle of the page.
From the very same guy, another gold mine is this one.

To sum up, in the init, you should do something like this:


textureName = GLBuffers.newDirectIntBuffers(1)
glGenTextures(1, textureName)
glBindTexture(GL_TEXTURE_2D, textureName.get(0))
glTexImage2D
glTexParameteri
...

and then in the rendering something like this


glActiveTexture(GL_TEXTURE0 + Sampler.DIFFUSE)
glBindTexture(GL_TEXTURE_2D, textureName.get(0))
render
...

To be precise, glGenTextures() only reserve names, the corresponding memory space will be only allocated later, for example at glTexImage2D()

Why are you retreving all those attributes if you are not gonna use them?

Moreover, you are calling attributes location and texture names both as handler, don’t do that, this may give you more trouble understanding what is what and introduce subtle bugs…

How many shaders are you using?

Discaimer: I am a freak of minimalist code :stuck_out_tongue:

I’d move this under the shader constructor, remove the attribute locations part and use an unique structure shader-independent to manage them.

This means modifying your vertex shaders as follows


#include semantic.glsl

layout(location = POSITION) in vec3 position;
layout(location = TEXT_COORD) in vec2 texCoord;

and then, along with your shaders, you’d have a semantic.glsl

// Vertex attributes
#define POSITION    0
#define NORMAL      1
#define TEXT_COORD  2
#define COLOR       3

// Uniform

// Interfaces

// Fragment outputs

Then you will have a corresponding Semantic.java file on the client (java) side

interface Semantic {
   
    Attr {

        int POSITION = 0;
        int NORMAL = 1;
        int TEXT_COORD = 2;
        int COLOR = 3;
    }
    ...
}

In this way you bring down a lot of boilerplate code, no more retrieving/binding vertex attributes.

Technically this would’t be a problem as far as you load the other faces as rgb, in glTexImage2D() you need to tell OpenGL which formats it has to expect reading the buffer (format and type) you provide and how you want it to save it inside (internalFormat). Read those link I gave you, they are really excellent :wink:

glBindTexture(GL_TEXTURE_2D, textureName.get(0))

no need to deal with flip. Jogl doesn’t touch the position of the buffer you provide, but it relies on that.

if you have a IntBuffer with 2 texture names (integers) and you do

textureName.position(1)
glDeleteTextures(1, textureName)

this means the second texture will be deleted. And textureName position will still be 1 after that.

This also means that


textureName.position(1)
glDeleteTextures(2, textureName)

will produce unhappy results… :smiley:

Hello elect,

Thanks a lot for your links. I read them once and it begins to be a bit clearer for me now. Although I’ll have to read them again to really catch all the concepts, I now understand that the logic is the following :

  • Name the texture (ie generate an id),
  • Change OpenGL state by binding a texture type (TEXTURE2D, TEXTURE_CUBE_MAP, …) with a texture id,
  • Put the texture into memory (buffering the data with glTexImage* and parameterizing its behavior with glTexParameter*),
  • Unbind the texture,
  • Activate and bind the texture each time I want to render.

I still have some difficulties to understand when to call the glUniform* method in some cases : I thought it was needed every time I buffer data (as I use it when buffering my vertices information with glEnableVertexAttribArray and glVertexAttribPointer). But when I remove the calls to glUniform* when buffering my texture data, everything still works. I’ll have to dig again to really catch how texture data is buffered into graphical memory. I suppose it’s closely linked to the texture activation (I’ll have a closer too at the notation GL_TEXTURE0 + i instead of GL_TEXTUREi, by the way).

[quote=“elect,post:5,topic:58289”]
The method retrieving the attributes is a method called for every shader that’s why it tries to retrieve every attribute that may be found in all my shaders (all attributes aren’t in all shaders, of course). For now, I’m using only 2 shaders (a standard one and another one for the sky) but in the future, there’s gonna be other ones (one for rendering the water, one for heat haze, maybe other ones for particles, … I don’t know exactly yet).

I wasn’t aware of the layout(location = POSITION) syntax but this seems to be very nice indeed. Beside that, I’ll start by using constants for my attributes / uniforms names as you suggested : it’ll be clearer.

Thanks also for the IntBuffer syntax : I replaced all my int arrays with it and it works like a charm :slight_smile:

Yep ;), I myself had to read them multiple times

Bravo, you got it

Let’s clear one thing,

glUniform*

is useful to upload data to shader uniforms.

But in the texture stuff, it was borrowed (only the glUniform1i) to bind the texture image unit (diagram in the first link) to a program object sampler. And you have to do this after your program has been linked.

It has nothing to do with vertices attributes.

The notation

GL_TEXTURE0 + i

is useful only when you want to bind your texture to render, not in the init, when you upload it.

If you are using so few shaders (and I guess you are always using them) I’d declare them as variables. Normally each step has its own class, so you don’t get to name them accordingly, they can be simply program inside each of them

I saw you are using glsl 400 in the shader, check if you have ARB_explicit_uniform_location

System.out.println(gl.isExtensionAvailable("GL_ARB_explicit_uniform_location"));

because if yes, you can also assign location to uniform all within the shader as you just discovered with the vertices attributes

You are welcome :smiley:

In theory you should also take care to deallocate them, since they are off-heap. In general they got freed automatically, but I have a small util to make it manually (for oracle and openjdk vm)

Ps: if you really want to make your opengl concepts, consider following some tutorial, like this one (discaimer: I wrote mostly of that port). The first link I gave you, comes right from there…