Hot questions for Using Lightweight Java Game Library in skybox

Question:

I am working on an OpenGL game in Java with LWJGL (ThinMatrix's tutorials at the moment) and I just added my skybox. As you can see from the picture, however, it is clipping through the trees and covering everything behind a certain point.

Here is my rendering code for the skybox:

public void render(Camera camera, float r, float g, float b) {
    shader.start();
    shader.loadViewMatrix(camera);
    shader.loadFogColor(r, g, b);
    GL30.glBindVertexArray(cube.getVaoID());
    GL20.glEnableVertexAttribArray(0);
    bindTextures();
    GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, cube.getVertexCount());
    GL30.glBindVertexArray(0);
    shader.stop();
}

private void bindTextures() {
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texture);
    GL13.glActiveTexture(GL13.GL_TEXTURE1);
    GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, nightTexture);
    shader.loadBlendFactor(getBlendFactor());
}

also if it is needed, here is my code for my master renderer:

public void render(List<Light> lights, Camera camera){
    prepare();
    shader.start();
    shader.loadSkyColor(RED, GREEN, BLUE);
    shader.loadLights(lights);
    shader.loadViewMatrix(camera);
    renderer.render(entities);
    shader.stop();
    terrainShader.start();
    terrainShader.loadSkyColor(RED, GREEN, BLUE);
    terrainShader.loadLight(lights);
    terrainShader.loadViewMatrix(camera);
    terrainRenderer.render(terrains);
    terrainShader.stop();
    skyboxRenderer.render(camera, RED, GREEN, BLUE);
    terrains.clear();
    entities.clear();
}


Answer:

There are two things you can do

If you draw your skybox first, you can disable your depth test glDisable(GL_DEPTH_TEST) or your depth write glDepthMask(false). This will prevent that your skybox draws depth values, and the skybox will never be in front of anything that will be drawn later.

If you draw your skybox last, you can make it literally infinitely big by using vertex coordinates with a w-coordinate as 0. A vertex (x y z 0) means it is a vertex infinitely far in the direction of the vector (x y z). To prevent clipping, you have to enable depth clamping glEnable(GL_DEPTH_CLAMP) this will prevent OpenGl to clip away your skybox faces, and you are sure that the skybox is always at the maximum distance and will never hide anything you have drawn earlier.

the advantage of the second method is within the depth test. Because you already have a depth values written for your scene, the OpenGL pipeline can skip the calculation of the skybox pixels that are already covered by your scene. But the fragment shader for skyboxes is usually very trivial, so it shouldn't make that much of a difference.

Question:

I have a problem with skybox textures. If you twist the camera, it creates a feeling that one texture overlays another, as in the screenshot:

Skybox show code:

private void drawSkybox(int texId){

    glColor4f(1,1,1,1);
    glDepthMask(false);
    glEnable(GL_TEXTURE_CUBE_MAP);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, texId);
    glBindVertexArray(vao[0]);
    glBindBuffer (GL_ARRAY_BUFFER, vbo[0]);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);
    glBindBuffer (GL_ARRAY_BUFFER, 0);
    glDepthMask(true);
    glDisable(GL_TEXTURE_CUBE_MAP);
}

My opengl paramters:

glEnable(GL_ALPHA_TEST);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_TEXTURE_2D);
glEnable(GL_BLEND);
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glEnable(GL_NORMALIZE);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glShadeModel(GL_SMOOTH);
glColorMask (true, true, true, true);
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE);

And my call drawSkybox:

glViewport(0, 0, WIDTH, HEIGHT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-max, max, -1, 1, 10, -10);
glRotated(cameraX, 1f, 0f, 0);
glRotated(cameraY, 0f, 1f, 0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
drawSkybox(texId);

How i can fix the problem?

I understand that the problem is glDepthMask(false); but how can it be replaced?

If I just remove glDepthMask(false); and replace it with an example with glDepthFunc(GL_LEQUAL); and glDepthFunc(GL_LESS); then the skybox will overlap all other objects and only it will be visible –


Answer:

Do not change the depth mask or the depth test when you draw the skybox. Keep the depth test function GL_LESS.

private void drawSkybox(int texId){

    glColor4f(1,1,1,1);
    // glDepthMask(false); <---- DELETE

    // [...]
}

But clear the depth buffer again, after drawing the skybox. Draw the skybox first, then the skybox covers the entire screen. Then clear the depth buffer, thus all objects which are draw after, will cover the skybox:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// [...]

drawSkybox(texId);
glClear(GL_DEPTH_BUFFER_BIT);

// render other objects
// [...]

Question:

I'm following ThinMatrix's guide on Youtube and trying to create a skybox for my engine. However, my skybox comes out black. I've tried all the different suggestions and common mistakes that people suggested in the comments, I can not for the life of me figure out what is wrong with it.

I'm not getting any error either :/

Bellow are the relevant methods in the relevant classes. I can provide more code if needed.

Loader.java

 public int loadCubeMap(String[] cubeMapTextures) { 
        int textureID = GL11.glGenTextures(); 

        GL13.glActiveTexture(GL13.GL_TEXTURE0); 
        GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, textureID); 

        for (int i = 0; i < cubeMapTextures.length; i++) { 
          TextureData data = decodeTexture("res/skybox/" + cubeMapTextures[i] + ".png"); 

          GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 
              0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getBuffer()); 
        } 

        GL11.glTexParameteri(GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); 
        GL11.glTexParameteri(GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); 
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); 
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); 

        textures.add(textureID); 
        return textureID; 
      } 

      private TextureData decodeTexture(String fileName) { 
        int width = 0; 
        int height = 0; 
        ByteBuffer buffer = null; 

        try { 
          FileInputStream in = new FileInputStream(fileName); 
          PNGDecoder decoder = new PNGDecoder(in); 

          width = decoder.getWidth(); 
          height = decoder.getHeight(); 
          buffer = ByteBuffer.allocateDirect(4 * width * height); 
          decoder.decode(buffer, width * 4, Format.RGBA); 

          buffer.flip(); 
          in.close(); 
        } catch (Exception e) { 
          e.printStackTrace(); 
          System.err.println("Failed loading:  " + fileName); 
          System.exit(-1); 
        } 

        return new TextureData(height, width, buffer); 
      } 

SkyboxRenderer.java

private static final float SIZE = 500f;
private static final float[] VERTICES = {        
    -SIZE,  SIZE, -SIZE,
    -SIZE, -SIZE, -SIZE,
    SIZE, -SIZE, -SIZE,
     SIZE, -SIZE, -SIZE,
     SIZE,  SIZE, -SIZE,
    -SIZE,  SIZE, -SIZE,

    -SIZE, -SIZE,  SIZE,
    -SIZE, -SIZE, -SIZE,
    -SIZE,  SIZE, -SIZE,
    -SIZE,  SIZE, -SIZE,
    -SIZE,  SIZE,  SIZE,
    -SIZE, -SIZE,  SIZE,

     SIZE, -SIZE, -SIZE,
     SIZE, -SIZE,  SIZE,
     SIZE,  SIZE,  SIZE,
     SIZE,  SIZE,  SIZE,
     SIZE,  SIZE, -SIZE,
     SIZE, -SIZE, -SIZE,

    -SIZE, -SIZE,  SIZE,
    -SIZE,  SIZE,  SIZE,
     SIZE,  SIZE,  SIZE,
     SIZE,  SIZE,  SIZE,
     SIZE, -SIZE,  SIZE,
    -SIZE, -SIZE,  SIZE,

    -SIZE,  SIZE, -SIZE,
     SIZE,  SIZE, -SIZE,
     SIZE,  SIZE,  SIZE,
     SIZE,  SIZE,  SIZE,
    -SIZE,  SIZE,  SIZE,
    -SIZE,  SIZE, -SIZE,

    -SIZE, -SIZE, -SIZE,
    -SIZE, -SIZE,  SIZE,
     SIZE, -SIZE, -SIZE,
     SIZE, -SIZE, -SIZE,
    -SIZE, -SIZE,  SIZE,
     SIZE, -SIZE,  SIZE
};

private static String[] textureFiles = {"stormydays_rt", "stormydays_lf", "stormydays_up", "stormydays_dn", "stormydays_bk", "stormydays_ft"};

private RawModel cube;
private int textureID;
private SkyboxShader shader;

public SkyBoxRenderer(Loader loader, Matrix4f projectionMatrix) {
    cube = loader.loadToVAO(VERTICES, 3);
    textureID = loader.loadCubeMap(textureFiles);
    shader = new SkyboxShader();
    shader.start();
    shader.loadProjectionMatrix(projectionMatrix);
    shader.stop();
}

public void render(Camera camera) {
    shader.start();
    shader.loadViewMatrix(camera);

    GL30.glBindVertexArray(cube.getVaoID());
    GL20.glEnableVertexAttribArray(0);
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, textureID);
    GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, cube.getVertexCount());
    GL20.glDisableVertexAttribArray(0);
    GL30.glBindVertexArray(0);

    shader.stop();
}

SkyboxShader.java

 private static final String VERTEX_FILE = "src/shaders/skyboxVertexShader.txt"; 
  private static final String FRAGMENT_FILE = "src/shaders/skyboxFragmentShader.txt"; 

  private int location_projectionMatrix; 
  private int location_viewMatrix; 

  public SkyboxShader() { 
      super(VERTEX_FILE, FRAGMENT_FILE); 
  } 

  public void loadProjectionMatrix(Matrix4f matrix){ 
      super.loadMatrix(location_projectionMatrix, matrix); 
  } 

  public void loadViewMatrix(Camera camera){ 
      Matrix4f matrix = Maths.createViewMatrix(camera); 
      super.loadMatrix(location_viewMatrix, matrix); 
  } 

  @Override 
  protected void getAllUniformLocations() { 
      location_projectionMatrix = super.getUniformLocation("projectionMatrix"); 
      location_viewMatrix = super.getUniformLocation("viewMatrix"); 
  } 

  @Override 
  protected void bindAttributes() { 
      super.bindAttribute(0, "position"); 
  } 

And here is my GLSL code skyboxVertexShader

#version 400

in vec3 position;
out vec3 textureCoords;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;

void main(void){

    gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); 
    textureCoords = position;

}

skyboxFragmentShader

#version 400

in vec3 textureCoords;
out vec4 out_Color;

uniform samplerCube cubeMap;

void main(void){
    out_Color = texture(cubeMap, textureCoords);
}

Answer:

This probably happens because the texture isn't complete. To complete your cube map texture you have to set GL_TEXTURE_WRAP_R with glTexParameteri because a cube is 3D. Furthermore you are using GL_PROXY_TEXTURE_CUBE_MAP instead of GL_TEXTURE_CUBE_MAP, this does't work because proxy textures aren't a valid parameter.

So just replace:

GL11.glTexParameteri(GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR); 
GL11.glTexParameteri(GL13.GL_PROXY_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR); 
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE); 
GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE); 

With:

glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

If this doesn't help, confirm that all textures have the same size.

PS: Use import static GL##.*;, then you can write methodName() instead of GL##.methodName() (replace ## with numbers)

Question:

I am new at openGL

I created this skybox on LWJGL but It's all black

SkyboxRenderer Class :

private static String[] TEXTURE_FILES = {"right","left","bottom","back","front"};
private RawModel cube;
private int texture;
private SkyboxShader shader;

public SkyboxRenderer(Loader loader, Matrix4f projectionMatirx) {
    cube = loader.loadToVAO(VERTICES, 3);
    texture = loader.loadCubeMap(TEXTURE_FILES);
    shader = new SkyboxShader();
    shader.start();
    shader.loadProjectionMatrix(projectionMatirx);
    shader.stop();
}

public void render(Camera camera){
    shader.start();
    shader.loadViewMatrix(camera);
    GL30.glBindVertexArray(cube.getVaoID());
    GL20.glEnableVertexAttribArray(0);
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texture);
    GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, cube.getVertexCount());
    GL20.glDisableVertexAttribArray(0);
    GL30.glBindVertexArray(0);
    shader.stop();
}

Loader loadCubeMap function :

public int loadCubeMap(String[] textureFiles){
    int texID = GL11.glGenTextures();
    GL13.glActiveTexture(GL13.GL_TEXTURE0);
    GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, texID);

    for(int i = 0; i < textureFiles.length;i++){
        TextureData data = decodeTextureFile("res/" + textureFiles[i] + ".png");
        GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0, GL11.GL_RGBA, 
                GL11.GL_UNSIGNED_BYTE, data.getBuffer());

    }
    GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
    GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_S, GL12.GL_CLAMP_TO_EDGE);
    GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_WRAP_T, GL12.GL_CLAMP_TO_EDGE);
    textures.add(texID);
    return texID;
}

private TextureData decodeTextureFile(String fileName) {
    int width = 0;
    int height = 0;
    ByteBuffer buffer = null;
    try {
        FileInputStream in = new FileInputStream(fileName);
        PNGDecoder decoder = new PNGDecoder(in);
        width = decoder.getWidth();
        height = decoder.getHeight();
        buffer = ByteBuffer.allocateDirect(4 * width * height);
        decoder.decode(buffer, width * 4, Format.RGBA);
        buffer.flip();
        in.close();
    } catch (Exception e) {
        e.printStackTrace();
        System.err.println("Tried to load texture " + fileName + ", didn't work");
        System.exit(-1);
    }
    return new TextureData(buffer, width, height);
}

Textures exist, but skybox is fullyblack can someone help me! How can i fix it

I need to add more details for posting because there is too much code...

SkyboxShader :

public class SkyboxShader extends ShaderProgram{

private static final String VERTEX_FILE = "src/com/redcatengine/skybox/skyboxVertexShader.txt";
private static final String FRAGMENT_FILE = "src/com/redcatengine/skybox/skyboxFragmentShader.txt";

private int location_projectionMatrix;
private int location_viewMatrix;

public SkyboxShader() {
    super(VERTEX_FILE, FRAGMENT_FILE);
}

public void loadProjectionMatrix(Matrix4f matrix){
    super.loadMatrix(location_projectionMatrix, matrix);
}

public void loadViewMatrix(Camera camera){
    Matrix4f matrix = Maths.createViewMatrix(camera);
    matrix.m30 = 0;
    matrix.m31 = 0;
    matrix.m32 = 0;
    super.loadMatrix(location_viewMatrix, matrix);
}

@Override
protected void getAllUniformLocations() {
    location_projectionMatrix = super.getUniformLocation("projectionMatrix");
    location_viewMatrix = super.getUniformLocation("viewMatrix");
}

@Override
protected void bindAttributes() {
    super.bindAttribute(0, "position");
}

}

public abstract class ShaderProgram {

private int programID;
private int vertexShaderID;
private int fragmentShaderID;

private static FloatBuffer matrixBuffer = BufferUtils.createFloatBuffer(16);
public ShaderProgram(String vertexFile, String fragmentFile) {
    vertexShaderID = loadShader(vertexFile, GL20.GL_VERTEX_SHADER);
    fragmentShaderID = loadShader(fragmentFile, GL20.GL_FRAGMENT_SHADER);
    programID = GL20.glCreateProgram();
    GL20.glAttachShader(programID, vertexShaderID);
    GL20.glAttachShader(programID, fragmentShaderID);
    bindAttributes();
    GL20.glLinkProgram(programID);
    GL20.glValidateProgram(programID);
    getAllUniformLocations();
}

protected abstract void getAllUniformLocations();

protected int getUniformLocation(String uniformName){
    return GL20.glGetUniformLocation(programID, uniformName); 
}

public void start(){
    GL20.glUseProgram(programID);
}

public void stop(){
    GL20.glUseProgram(0);
}

public void cleanUp(){
    stop();
    GL20.glDetachShader(programID, vertexShaderID);
    GL20.glDetachShader(programID, fragmentShaderID);
    GL20.glDeleteShader(vertexShaderID);
    GL20.glDeleteShader(fragmentShaderID);
    GL20.glDeleteProgram(programID);
}

protected abstract void bindAttributes();

protected void bindAttribute(int attribute, String variableName){
    GL20.glBindAttribLocation(programID, attribute, variableName);
}

protected void loadInt(int location, int value){
    GL20.glUniform1i(location, value);
}


protected void loadFloat(int location, float value){
    GL20.glUniform1f(location, value);
}

protected void loadVector(int location, Vector3f value){
    GL20.glUniform3f(location, value.x, value.y, value.z);
}

protected void load2DVector(int location, Vector2f value){
    GL20.glUniform2f(location, value.x, value.y);
}

protected void loadBoolean(int location, boolean value){
    float toLoad = 0;
    if(value)toLoad = 1;else toLoad = 0;
    GL20.glUniform1f(location, toLoad);
}

protected void loadMatrix(int location, Matrix4f matrix){
    matrix.store(matrixBuffer);
    matrixBuffer.flip();
    GL20.glUniformMatrix4(location, false, matrixBuffer);
}

private static int loadShader(String file, int type){
    StringBuilder shaderSource = new StringBuilder();
    try{
        BufferedReader reader = new BufferedReader(new FileReader(file));
        String line;
        while((line = reader.readLine()) != null){
            shaderSource.append(line).append("\n");
        }
        reader.close();
    }catch(IOException e){
        System.err.println("Could not read shader file!");
        e.printStackTrace();
        System.exit(-1);
    }
    int shaderID = GL20.glCreateShader(type);
    GL20.glShaderSource(shaderID, shaderSource);
    GL20.glCompileShader(shaderID);
    if(GL20.glGetShaderi(shaderID, GL20.GL_COMPILE_STATUS)==GL11.GL_FALSE){
        System.out.println(GL20.glGetShaderInfoLog(shaderID, 500));
        System.out.println("Could not compile shader.");
        System.exit(-1);    
    }
    return shaderID;
}

}

skyboxFragmentShader :

#version 400

in vec3 textureCoords;
out vec4 out_Color;

uniform samplerCube cubeMap;

void main(void){
    out_Color = texture(cubeMap, textureCoords);
}

skyboxVertexShader

#version 400

in vec3 position;
out vec3 textureCoords;

uniform mat4 projectionMatrix;
uniform mat4 viewMatrix;

void main(void){

    gl_Position = projectionMatrix * viewMatrix * vec4(position, 1.0); 
    textureCoords = position;

}`

Answer:

Your cube map texture is not cube complete:

Your loader code iterates over all files in the array it is called with:

for(int i = 0; i < textureFiles.length;i++){
    // [...]
    GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, [...])
}

However, your input array contains only 5 entires:

String[] TEXTURE_FILES = {"right","left","bottom","back","front"};

You provide only 5 faces for the cube, and forgot the "top" face.

According to the GL spec (quotes are from section 8.17 of the OpenGL 4.5 core profile specification),

A cube map texture is mipmap complete if each of the six texture images, considered individually, is mipmap complete. Additionally, a cube map texture is cube complete if the following conditions all hold true:

  • The level_base texture images of each of the six cube map faces have identical, positive, and square dimensions.

  • The levelbase images were each specified with the same internal format.

It further goes on define texture completeness:

Using the preceding definitions, a texture is complete unless any of the following conditions hold true:

  • [...]
  • The texture is a cube map texture, and is not cube complete.
  • [...]

So your cube map texture is not complete.

Section 11.1.3.5 states:

If a sampler is used in a shader and the sampler’s associated texture is not complete, as defined in section 8.17, (0; 0; 0; 1) will be returned for a non-shadow sampler and 0 for a shadow sampler.

So indeed, your cube map should appear completely black.

Question:

I'm loading a cube map skybox texture (3D coordinates rather than 2D) and the skybox turns out to be black. I don't receive any errors and the skybox is rendering in at the right locations and all, but something is still not right.

I have a custom class CubeMap which just takes in a String[] for the file names of the textures and loads them with the following code:

public class CubeMap {

    private int cubeMapID;

    public CubeMap (String[] textureFiles) {
        this.cubeMapID = GL11.glGenTextures();
        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, cubeMapID);

        for (int i = 0; i < textureFiles.length; i++) {
            Image data = Loader.decodeTextureFile("res/textures/" + textureFiles[i]);
            GL11.glTexImage2D(GL13.GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL11.GL_RGBA, data.getWidth(), data.getHeight(), 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, data.getByteBuffer());
        }

        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
        GL11.glTexParameteri(GL13.GL_TEXTURE_CUBE_MAP, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
    }

    public int getCubeMapID() {
        return cubeMapID;
    }

    public void destroy () {
        GL11.glDeleteTextures(cubeMapID);
    }
}

The decodeTextureFile() method in Loader:

public static Image decodeTextureFile (String fileName) {

    int width = 0, height = 0;
    ByteBuffer buffer = null;

    try {

        FileInputStream in = new FileInputStream(fileName);
        PNGDecoder decoder = new PNGDecoder(in);

        width = decoder.getWidth();
        height = decoder.getHeight();
        buffer = ByteBuffer.allocateDirect(4 * width * height);

        decoder.decode(buffer, width * 4, Format.RGBA);

        buffer.flip();
        in.close();

    } catch (IOException e) {
        System.out.println("Error : Decoding in loader failed; IO - Error");
        System.exit(-1);
    }

    return new Image (width, height, buffer);
}

Additionally, I have a SkyBox class which creates a CubeMap and stores the vertices of a preset array into a vertexArray and a vertexBuffer (this part of the code works fine so I don't see the need to add the Model code)

public class SkyBox extends Model {

    private static final float SIZE = 500f;

    private static final float[] VERTICES = {
            -SIZE,  SIZE, -SIZE,
            -SIZE, -SIZE, -SIZE,
            SIZE, -SIZE, -SIZE,
             SIZE, -SIZE, -SIZE,
             SIZE,  SIZE, -SIZE,
            -SIZE,  SIZE, -SIZE,

            -SIZE, -SIZE,  SIZE,
            -SIZE, -SIZE, -SIZE,
            -SIZE,  SIZE, -SIZE,
            -SIZE,  SIZE, -SIZE,
            -SIZE,  SIZE,  SIZE,
            -SIZE, -SIZE,  SIZE,

             SIZE, -SIZE, -SIZE,
             SIZE, -SIZE,  SIZE,
             SIZE,  SIZE,  SIZE,
             SIZE,  SIZE,  SIZE,
             SIZE,  SIZE, -SIZE,
             SIZE, -SIZE, -SIZE,

            -SIZE, -SIZE,  SIZE,
            -SIZE,  SIZE,  SIZE,
             SIZE,  SIZE,  SIZE,
             SIZE,  SIZE,  SIZE,
             SIZE, -SIZE,  SIZE,
            -SIZE, -SIZE,  SIZE,

            -SIZE,  SIZE, -SIZE,
             SIZE,  SIZE, -SIZE,
             SIZE,  SIZE,  SIZE,
             SIZE,  SIZE,  SIZE,
            -SIZE,  SIZE,  SIZE,
            -SIZE,  SIZE, -SIZE,

            -SIZE, -SIZE, -SIZE,
            -SIZE, -SIZE,  SIZE,
             SIZE, -SIZE, -SIZE,
             SIZE, -SIZE, -SIZE,
            -SIZE, -SIZE,  SIZE,
             SIZE, -SIZE,  SIZE
    };

    private CubeMap cubeMap;
    private int vertexArrayID;
    private int vertexCount;

    public SkyBox (String[] textures) {
        cubeMap = new CubeMap(textures);
        vertexArrayID = super.createVertexArray();
        super.storeData(0, 3, VERTICES);

        vertexCount = VERTICES.length / 3;

        GL30.glBindVertexArray(0);
    }

    public CubeMap getCubeMap() {
        return cubeMap;
    }

    public int getVertexArrayID() {
        return vertexArrayID;
    }

    public int getVertexCount() {
        return vertexCount;
    }

    public void destroy () {
        cubeMap.destroy();
    }
}

I have a SkyboxShader class of which I'm fairly certain that it functions properly. The actual rendering of the SkyBox happens in the SkyboxRenderer class:

public class SkyboxRenderer {

    private SkyboxShader shader;

    public SkyboxRenderer (SkyboxShader shader) {
        this.shader = shader;
    }

    public void render (SkyBox skyBox) {
        shader.bind();
        shader.useMatrices();
        GL30.glBindVertexArray(skyBox.getVertexArrayID());
        GL20.glEnableVertexAttribArray(0);

        GL13.glActiveTexture(GL13.GL_TEXTURE0);
        GL11.glBindTexture(GL13.GL_TEXTURE_CUBE_MAP, skyBox.getCubeMap().getCubeMapID());

        GL11.glDrawArrays(GL11.GL_TRIANGLES, 0, skyBox.getVertexCount());

        GL20.glDisableVertexAttribArray(0);
        GL30.glBindVertexArray(0);
        shader.unbind();
    }
}

If anyone knows how to fix this problem it would be greatly appreciated. Any additional code will be posted if requested. Thanks in advance!


Edits

Edit: The Image class is custom and just stores a ByteBuffer and width and height ints. Getters are provided but no additional functionality.

Edit: I've even tried refactoring all of my code to no prevail.

Edit: I think the error lies in the cube map, not the textures or other code, as I can get it to kind-of work when I just use 6 different quads to render the box.

Edit: I solved the problem. I forgot to call the method .create() on type shader in the renderer.


Answer:

CLOSED: I forgot to add the method call shader.create(); in the SkyboxRenderer.init() function so no shader programs were linked.