[SOLVED] First steps with LWJGL and OpenGL - why is nothing rendered?

Started by stuck1a, March 23, 2024, 18:33:23

Previous topic - Next topic

stuck1a

Hello everyone,

I'm actually a web developer, so I haven't had much to do with OpenGL yet.
But I want to get into game development and because I already know Java, it was obvious for me to make my first attempts with LWJGL.

I have implemented the basic structure for my game project and am now trying to render a triangle with the help of thistutorial. No exception is thrown during execution, but nothing is rendered either. The tutorial mentions that you have to bind the VertexBufferObjects to a VertexArrayObject, but unfortunately the tutorial does not explain how this is done. The linked repository differs greatly from the tutorial, so I cannot derive this from it.
Since I'm stuck at this point for two days now, I hope someone here can help me out.


I've shortened the code as good as possible. In my render loop, I simply call drawTriangle() once.
The renderer object is created once on game initialization. As you can see, it will compile and bind the shaders.

Code: Class Renderer
public class Renderer {
  protected ShaderProgram shaders;
  ArrayList<VertexBufferObject> registeredVboObjects = new ArrayList<>();
  ArrayList<VertexArrayObject> registeredVaoObjects = new ArrayList<>();

  public Renderer() {
    if (shaders == null) {
      try {
        shaders = new ShaderProgram();
        shaders.initializeBaseShaders();
        shaders.bind();
      } catch (Exception e) {
        System.err.println("Could not initialize shaders.\n" + e.getMessage());
        return;
      }
    }
  }

  private void drawTriangle() {
    VertexArrayObject vaoTriangle1 = new VertexArrayObject();
    vaoTriangle1.initialize();

    VertexBufferObject vboTriangle1 = new VertexBufferObject(
      new float[]{
        0.5f, 1.0f, 0.0f, 1f, 0f, 0f,
        0.0f, 0.0f, 0.0f, 0f, 1f, 0f,
        1.0f, 0.0f, 0.0f, 0f, 0f, 1f
      }
    );
    vboTriangle1.initialize();
    
    // TODO: Bind the VBO to the VAO ???
    
    // Register all objects related to the triangle
    registeredVboObjects.add(vboTriangle1);
    registeredVaoObjects.add(vaoTriangle1);
    
    // Draw the Triangle
    vboTriangle1.render();
  }
}



Code: Class ShaderProgram
public class ShaderProgram {
  private final int programId;
  private int vertexShaderId;
  private int fragmentShaderId;
  
  public ShaderProgram() throws Exception {
    programId = glCreateProgram();
    if (programId == 0) {
      throw new Exception("Could not create Shader");
    }
  }
  
  public int getProgramId() {
    return programId;
  }
  
  public int getVertexShaderId() {
    return vertexShaderId;
  }
  
  public int getFragmentShaderId() {
    return fragmentShaderId;
  }
  
  public void bind() {
    glUseProgram(programId);
  }
  
  public void unbind() {
    glUseProgram(0);
  }
  
  public void link() throws Exception {
    glLinkProgram(programId);
    if (glGetProgrami(programId, GL_LINK_STATUS) == 0) {
      throw new Exception("Error linking Shader code: " + glGetProgramInfoLog(programId, 1024));
    }
    if (vertexShaderId != 0) glDetachShader(programId, vertexShaderId);
    if (fragmentShaderId != 0) glDetachShader(programId, fragmentShaderId);
  }
  
  public void initializeBaseShaders() throws Exception {
    String shadersDir = Util.getShadersDir();
    String vertexShaderCode = Util.readFullFile(shadersDir + "vertex.glsl");
    vertexShaderId = compileShader(vertexShaderCode, GL_VERTEX_SHADER);
    String fragmentShaderCode = Util.readFullFile(shadersDir + "fragment.glsl");
    fragmentShaderId = compileShader(fragmentShaderCode, GL_FRAGMENT_SHADER);
    link();
  }
  
  public int compileShader(String shaderCode, int shaderType) throws Exception {
    int shaderId = glCreateShader(shaderType);
    if (shaderId == 0) throw new RuntimeException("Error creating shader. Type: " + shaderType);
    glShaderSource(shaderId, shaderCode);
    glCompileShader(shaderId);
    if (glGetShaderi(shaderId, GL_COMPILE_STATUS) == 0) {
      throw new RuntimeException("Error compiling Shader code: " + glGetShaderInfoLog(shaderId, 1024));
    }
    glAttachShader(programId, shaderId);
    return shaderId;
  }
}



Code: Class VertexArrayObject
public class VertexArrayObject {
  private final int vaoId;
  
  public VertexArrayObject() {
    vaoId = glGenVertexArrays();
  }
  
  public void initialize() {
    bind();
  }
  
  public int getID() {
    return vaoId;
  }
  
  public void delete() {
    glDeleteVertexArrays(vaoId);
  }
  
  private void bind() {
    glBindVertexArray(vaoId);
  }
}



Code: Class VertexBufferObject
public class VertexBufferObject {
  private int vboId;
  MemoryStack stack;
  FloatBuffer vertices;
  
  public VertexBufferObject(float[] vertexData) {
    stack = MemoryStack.stackPush();
    vertices = stack.mallocFloat(vertexData.length);
    for (float val : vertexData) {
      vertices.put(val);
    }
    vertices.flip();
  }
  
  public void initialize() {
    vboId = glGenBuffers();
    bind(GL_ARRAY_BUFFER);
    uploadData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
    MemoryStack.stackPop();
    specifyVertexAttributes();
  }
  
  public void render() {
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 3);
  }
  
  public void delete() {
    glDeleteBuffers(vboId);
  }
  
  public int getID() {
    return vboId;
  }
  
  private void bind(int target) {
    glBindBuffer(target, vboId);
  }
  
  private void uploadData(int target, FloatBuffer data, int usage) {
    glBufferData(target, data, usage);
  }
  
  private void specifyVertexAttributes() {
    int shaderProgram = Deadzone.getApplication().getRenderer().getShaders().getProgramId();
    // position attribute
    int positionAttribute = glGetAttribLocation(shaderProgram, "position");
    glEnableVertexAttribArray(positionAttribute);
    glVertexAttribPointer(positionAttribute, 3, GL_FLOAT, false, 6 * Float.BYTES, 0);
    // color attribute
    int colorAttribute = glGetAttribLocation(shaderProgram, "color");
    glEnableVertexAttribArray(colorAttribute);
    glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, false, 6 * Float.BYTES, 3 * Float.BYTES);
  }
}




Those are my shaders:

Code: vertex.glsl
#version 150 core

in vec2 position;
in vec3 color;

out vec3 vertexColor;

void main() {
  vertexColor = color;
  gl_Position = vec3(position, 1.0);
}



Code: fragment.glsl
#version 150 core

in vec3 vertexColor;

out vec4 outColor;

void main() {
  outColor = vec4(vertexColor, 1.0);
}



Perhaps somone here can help me and explain what is missing or what I am doing wrong?
The complete project repository can be viewed here.

Many thanks in advance and best regards,
stuck1a

stuck1a

In the meantime, I implemented a few more validations and combined all the code into a single class for the sake of simplicity.
Compiling and linking of the shaders works, but for some reason, the JVM crashes with exit code -1073741819 (0xC0000005) everytime it comes to  glDrawArrays(), but I don't know why. As you can see in the code, both, the buffer and the shader is set.

This is the complete sequence:


Code: Class "Deadzone"
public class Deadzone {
  public static void main(String[] args) throws InterruptedException {
    TestForOpenGL testing = new TestForOpenGL();
    try {
      testing.executeTest();
    } catch (java.lang.Exception e) {
      System.err.println(e.getMessage());
    }
  }
}


Code: Class "TestForOpenGL"
import org.lwjgl.glfw.GLFWErrorCallback;
import org.lwjgl.glfw.GLFWVidMode;
import org.lwjgl.opengl.GL;
import org.lwjgl.system.MemoryStack;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.util.Objects;

import static java.lang.Thread.sleep;
import static org.lwjgl.glfw.Callbacks.glfwFreeCallbacks;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.opengl.GL20.*;
import static org.lwjgl.opengl.GL30.*;
import static org.lwjgl.system.MemoryStack.stackPush;
import static org.lwjgl.system.MemoryUtil.NULL;

public class TestForOpenGL {
  private long windowId;
  
  private int shaderProgramId = 0;
  private int vertexShaderId = 0;
  private int fragmentShaderId = 0;
  
  private int vaoId = 0;
  private int vboId = 0;
  
  
  public void executeTest() throws Exception {
    // Initialize LWJGL
    GLFWErrorCallback errorCallback = GLFWErrorCallback.createPrint(System.err);
    glfwSetErrorCallback(errorCallback);
    if ( !glfwInit() ) throw new IllegalStateException("Unable to initialize GLFW");
    
    // Create render context (the application window)
    glfwDefaultWindowHints();
    glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
    glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GLFW_TRUE);
    windowId = glfwCreateWindow(800, 600, "Render test", NULL, NULL);
    if ( windowId == NULL ) {
      glfwTerminate();
      throw new RuntimeException("Failed to create the GLFW window");
    }
    try ( MemoryStack stack = stackPush() ) {
      IntBuffer pWidth = stack.mallocInt(1);
      IntBuffer pHeight = stack.mallocInt(1);
      glfwGetWindowSize(windowId, pWidth, pHeight);
      GLFWVidMode vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
      assert vidmode != null;
      glfwSetWindowPos(windowId, (vidmode.width() - pWidth.get(0)) / 2, (vidmode.height() - pHeight.get(0)) / 2);
    }
    glfwMakeContextCurrent(windowId);
    glfwSwapInterval(1);
    glfwShowWindow(windowId);
    GL.createCapabilities();
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    
    // Render loop
    while ( !glfwWindowShouldClose(windowId) ) {
      glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
      glfwSwapBuffers(windowId);
      glfwPollEvents();
      render();
      sleep(1000L / 30L);
    }

    // Shutdown
    glfwFreeCallbacks(windowId);
    glfwDestroyWindow(windowId);
    glfwTerminate();
    Objects.requireNonNull(glfwSetErrorCallback(null)).free();
  }
  
  
  private void render() throws Exception {
    /* Set up the shader program, if not done yet */
    if (shaderProgramId == 0) {
      try {
        shaderProgramId = glCreateProgram();
        if (shaderProgramId == 0) throw new Exception("Could not create shader program");
        
        // Create, compile and attach vertex shader
        String vertexShaderCode =
          "#version 150 core\n" +
          "in vec2 position;\n" +
          "in vec3 color;\n" +
          "out vec3 vertexColor;\n" +
          "void main() { vertexColor = color; gl_Position = vec4(position, 0.0, 1.0); }";
        vertexShaderId = glCreateShader(GL_VERTEX_SHADER);
        if (vertexShaderId == 0) throw new RuntimeException("Failed to create vertex shader!");
        glShaderSource(vertexShaderId, vertexShaderCode);
        glCompileShader(vertexShaderId);
        if (glGetShaderi(vertexShaderId, GL_COMPILE_STATUS) == 0) throw new RuntimeException("Error compiling vertex shader!\n" + glGetShaderInfoLog(vertexShaderId, 1024));
        glAttachShader(shaderProgramId, vertexShaderId);
        
        // Create, compile and attach fragment shader
        String fragmentShaderCode =
          "#version 150 core\n\n" +
          "in vec3 vertexColor;\n\n" +
          "out vec4 outColor;\n\n" +
          "void main() { outColor = vec4(vertexColor, 1.0);}";
        fragmentShaderId = glCreateShader(GL_FRAGMENT_SHADER);
        if (fragmentShaderId == 0) throw new RuntimeException("Failed to create fragment shader!");
        glShaderSource(fragmentShaderId, fragmentShaderCode);
        glCompileShader(fragmentShaderId);
        if (glGetShaderi(fragmentShaderId, GL_COMPILE_STATUS) == 0) throw new RuntimeException("Error compiling fragment shader!\n" + glGetShaderInfoLog(fragmentShaderId, 1024));
        glAttachShader(shaderProgramId, fragmentShaderId);
      } catch (Exception e) {
        System.err.println("Could not initialize shaders.\n" + e.getMessage());
      }
    }
    
    // Link shader program and detach shaders from RAM
    glLinkProgram(shaderProgramId);
    if (glGetProgrami(shaderProgramId, GL_LINK_STATUS) == 0) throw new Exception("Error linking shader");
    glDetachShader(shaderProgramId, vertexShaderId);
    glDetachShader(shaderProgramId, fragmentShaderId);
  
    // Validate shaders
    glValidateProgram(shaderProgramId);
    if (glGetProgrami(shaderProgramId, GL_VALIDATE_STATUS) == 0) {
      System.err.println("Failed to validate shaders");
    }
    
    // Prepare and use shader program
    glBindFragDataLocation(shaderProgramId, 0, "outColor");
    glUseProgram(shaderProgramId);
    
    /* VAO */
    vaoId = glGenVertexArrays();
    glBindVertexArray(vaoId);
    
    /* VBO */
    float[] triangleInput = {
      // x     y   R   G   B
      0.5f, 0.9f, 1f, 0f, 0f,  // Point A
      0.1f, 0.1f, 1f, 0f, 0f,  // Point B
      0.9f, 0.1f, 1f, 0f, 0f   // Point C
    };
    MemoryStack stack = MemoryStack.stackPush();
    FloatBuffer vertices = stack.mallocFloat(triangleInput.length);
    for (float val : triangleInput) {
      vertices.put(val);
    }
    vertices.flip();
    
    // Bind VBO
    vboId = glGenBuffers();
    glBindBuffer(GL_ARRAY_BUFFER, vboId);
    
    // Upload vertices to the GPU
    glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW);
    MemoryStack.stackPop();
  
    // Specify vertex attributes
    int positionAttribute = glGetAttribLocation(shaderProgramId, "position");
    glEnableVertexAttribArray(positionAttribute);
    glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, false, 0, 0);
    int colorAttribute = glGetAttribLocation(shaderProgramId, "color");
    glEnableVertexAttribArray(colorAttribute);
    glVertexAttribPointer(colorAttribute, 3, GL_FLOAT, false, 0, 2 * Float.BYTES);
    
    /* Draw the triangle */
    glClear(GL_COLOR_BUFFER_BIT);
    glDrawArrays(GL_TRIANGLES, 0, 3);
  
    /* Free resources */
    glDeleteBuffers(vboId);
    glDeleteVertexArrays(vaoId);
    if (vertexShaderId != 0) glDeleteShader(vertexShaderId);
    if (fragmentShaderId != 0) glDeleteShader(fragmentShaderId);
    glUseProgram(0);
    if (shaderProgramId != 0) glDeleteProgram(shaderProgramId);
  }
  
}


Here you can see the Test Class with syntax highlighting:
https://github.com/stuck1a/deadzone/blob/main/src/deadzone/TestForOpenGL.java

stuck1a

Heureka, I finally found the issue myself  :)

The problem was, that I've called glfwSwapBuffers() before the renderer (instead of after) within the renderloop.

For all those who have similar problems and end up here via google, I'd like to point out the following excellent tutorial. which uses OpenGL under C++, but which can be easily transferred to Java/LWJGL and is the only tutorial I know that shows the complete render process in detail.

Best regards,
stuck1a