[LWJGL3] No visible vertices

Started by n4n0lix, April 26, 2015, 20:23:25

Previous topic - Next topic

n4n0lix

Hi,

I've been playing around with LWJGL 3 (I know it haven't been officialy released yet) and tried to display a simple triangle via a VAO/VBO and Shader. Unfortunately I can't make the vertices to be drawn.

For the sake of simplicity I used the "Getting-Started"-Example as the base of my example. My code starts below the main method. Any help appreciated.

package de.orangestar;
 
import org.lwjgl.Sys;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
 
import de.orangestar.engine.values.Color4f;
import de.orangestar.engine.values.Vector2f;
import de.orangestar.engine.values.Vector3f;
import de.orangestar.engine.values.Vertex;
 
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;
import java.util.Arrays;
 
import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
 
public class HelloWorld {
 
    // We need to strongly reference callback instances.
    private GLFWErrorCallback errorCallback;
    private GLFWKeyCallback   keyCallback;
 
    // The window handle
    private long window;
 
    public void run() {
        System.out.println("Hello LWJGL " + Sys.getVersion() + "!");
 
        try {
            init();
            loop();
 
            // Release window and window callbacks
            glfwDestroyWindow(window);
            keyCallback.release();
        } finally {
            // Terminate GLFW and release the GLFWerrorfun
            glfwTerminate();
            errorCallback.release();
        }
    }
 
    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        glfwSetErrorCallback(errorCallback = errorCallbackPrint(System.err));
 
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( glfwInit() != GL11.GL_TRUE )
            throw new IllegalStateException("Unable to initialize GLFW");
 
        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
 
        int WIDTH = 300;
        int HEIGHT = 300;
 
        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");
 
        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                    glfwSetWindowShouldClose(window, GL_TRUE); // We will detect this in our rendering loop
            }
        });
 
        // Get the resolution of the primary monitor
        ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
            window,
            (GLFWvidmode.width(vidmode) - WIDTH) / 2,
            (GLFWvidmode.height(vidmode) - HEIGHT) / 2
        );
 
        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);
 
        // Make the window visible
        glfwShowWindow(window);
    }
 
    private void loop() {
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the ContextCapabilities instance and makes the OpenGL
        // bindings available for use.
        GLContext.createFromCurrent();
 
        // Set the clear color
        glClearColor(0.0f, 0.2f, 0.0f, 0.0f);
 
        createShader();
        createBuffer();
        fillBuffer();
 
        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
        while ( glfwWindowShouldClose(window) == GL_FALSE ) {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
 
            display();
           
            glfwSwapBuffers(window); // swap the color buffers
 
            // Poll for window events. The key callback above will only be
            // invoked during this call.
            glfwPollEvents();
        }
    }
 
    public static void main(String[] args) {
        new HelloWorld().run();
    }
   
    private void display() {
        GL20.glUseProgram(shader);
        GL30.glBindVertexArray( vao );
       
        GL11.glDrawArrays( GL11.GL_TRIANGLES, 0, 3 );
 
        GL30.glBindVertexArray( 0 );
        GL20.glUseProgram(0);
    }
   
    private void createBuffer() {
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
       
            vbo = GL15.glGenBuffers();
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vbo);
            GL15.glBufferData( GL15.GL_ARRAY_BUFFER, 1024 * Vertex.ByteSize, null, GL15.GL_STREAM_DRAW );
               
            GL20.glEnableVertexAttribArray(0);
            GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 7 * Float.BYTES, 0);
   
            GL20.glEnableVertexAttribArray(1);
            GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 7 * Float.BYTES, 3 * Float.BYTES);
                       
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0);
           
        GL30.glBindVertexArray(0);  
    }
   
    private void fillBuffer() {
        Vertex[] vertices = new Vertex[] {
                new Vertex(new Vector3f(-1.0f, -1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
                new Vertex(new Vector3f( 1.0f, -1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
                new Vertex(new Vector3f( 0.0f,  1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
        };
       
        // 1# Create buffer
        FloatBuffer buffer = ByteBuffer.allocateDirect(3 * 7 * Float.BYTES).asFloatBuffer();
        buffer.position(0);
        for(Vertex vertex : vertices) {
            buffer.put(vertex.position.x);
            buffer.put(vertex.position.y);
            buffer.put(vertex.position.z);
            buffer.put(vertex.color.r);
            buffer.put(vertex.color.g);
            buffer.put(vertex.color.b);
            buffer.put(vertex.color.a);
        }
       
        // 2# Write data
        GL30.glBindVertexArray(vao);
       
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vbo);
            GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, buffer);
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0);
           
        GL30.glBindVertexArray(0);
    }
   
    private void createShader() {
        String dummyVertexShaderSrc =
                "#version 330 core"
                + "\n" + "layout(location = 0) in vec3 vs_position;"
                + "\n" + "layout(location = 1) in vec4 vs_color;"
                + "\n" + ""
                + "\n" + "out vec4 fs_color;"
                + "\n" + ""
                + "\n" + "void main() {"
                + "\n" + "    gl_Position = vec4(vs_position, 1.0);"
                + "\n" + "    fs_color = vs_color;"
                + "\n" + "}"
                ;
       
        String dummyFragmentShaderSrc = "#version 330 core"
                + "\n" + "in vec4 fs_color;"
                + "\n" + ""
                + "\n" + "out vec4 out_color;"
                + "\n" + ""
                + "\n" + "void main() {"
                + "\n" + "    out_color = vec4(1.0, 0.0, 0.0, 1.0);"
                + "\n" + "}";
       
        System.out.println("Vertex-Shader: \n" + dummyVertexShaderSrc + "\n");
        System.out.println("Fragment-Shader: \n" + dummyFragmentShaderSrc + "\n");
       
        // 1# Read/Compile VertexShader
        int idVertexShader = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        GL20.glShaderSource(idVertexShader, dummyVertexShaderSrc);
        GL20.glCompileShader(idVertexShader);
 
        if (GL20.glGetShaderi(idVertexShader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile vertex shader: " + GL20.glGetShaderInfoLog(idVertexShader));
            System.exit(-1);
        }
       
        // 2# Read/Compile FragmentShader
        int idFragmentShader = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        GL20.glShaderSource(idFragmentShader, dummyFragmentShaderSrc);
        GL20.glCompileShader(idFragmentShader);
 
        if (GL20.glGetShaderi(idFragmentShader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile fragment shader: " + GL20.glGetShaderInfoLog(idFragmentShader));
            System.exit(-1);
        }
       
        // 3# Create Shader-Program
        shader = GL20.glCreateProgram();
        GL20.glAttachShader(shader, idVertexShader);
        GL20.glAttachShader(shader, idFragmentShader);
       
        GL20.glBindAttribLocation(shader, 0, "vs_position");
        GL20.glBindAttribLocation(shader, 1, "vs_color");
 
        GL20.glLinkProgram(shader);
        if (GL20.glGetProgrami(shader, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.out.println("Shader linking failed: " + GL20.glGetProgramInfoLog(shader));
            System.exit(-1);
        }
 
        GL20.glValidateProgram(shader);
        GL20.glDeleteShader(idVertexShader);
        GL20.glDeleteShader(idFragmentShader);
    }
 
    private int vao;
    private int vbo;
    private int shader;
}

Kai

Common problem with Java ByteBuffer's. Just put a buffer.flip() after line 177 of your code listing.

The thing is, that Java ByteBuffer's have a read/write position/index, that is its position().
This position will be interpreted as the "write" pointer when doing relative put operations and it will be interpreted as "read" pointer when operations try to read from that ByteBuffer, such as the glBufferData. And LWJGL also respects this read/write pointer when invoking glBufferData or glBufferSubData.

n4n0lix

Ah thanks yes, I forgot about that. But still no visible vertices. I removed all external classes, so feel free to copy+paste the code and try to run it on your machine.

package de.orangestar;
  
import org.lwjgl.Sys;
import org.lwjgl.glfw.*;
import org.lwjgl.opengl.*;
    
import java.nio.ByteBuffer;
import java.nio.FloatBuffer;

import static org.lwjgl.glfw.Callbacks.*;
import static org.lwjgl.glfw.GLFW.*;
import static org.lwjgl.opengl.GL11.*;
import static org.lwjgl.system.MemoryUtil.*;
  
public class HelloWorld {
  
    private static class Vector3f {
        public static int ByteSize = 3 * Float.BYTES;
        
        public float x,y,z;
        
        public Vector3f(float x, float y, float z) {
            this.x = x; this.y = y; this.z = z;
        }
    }
    
    private static class Color4f {
        public static int ByteSize = 4 * Float.BYTES;
        
        public float r,g,b,a;
        
        public Color4f(float r, float g, float b, float a ) {
            this.r = r; this.g = g; this.b = b; this.a = a;
        }
    }
    
    private static class Vertex {
        public static int ByteSize = Vector3f.ByteSize + Color4f.ByteSize;
        
        public Vector3f position;
        public Color4f  color;
        
        public Vertex(Vector3f position, Color4f  color) {
            this.position = position;
            this.color = color;
        }
    }
    
    
    // We need to strongly reference callback instances.
    private GLFWErrorCallback errorCallback;
    private GLFWKeyCallback   keyCallback;
  
    // The window handle
    private long window;
  
    public void run() {
        System.out.println("Hello LWJGL " + Sys.getVersion() + "!");
  
        try {
            init();
            loop();
  
            // Release window and window callbacks
            glfwDestroyWindow(window);
            keyCallback.release();
        } finally {
            // Terminate GLFW and release the GLFWerrorfun
            glfwTerminate();
            errorCallback.release();
        }
    }
  
    private void init() {
        // Setup an error callback. The default implementation
        // will print the error message in System.err.
        glfwSetErrorCallback(errorCallback = errorCallbackPrint(System.err));
  
        // Initialize GLFW. Most GLFW functions will not work before doing this.
        if ( glfwInit() != GL11.GL_TRUE )
            throw new IllegalStateException("Unable to initialize GLFW");
  
        // Configure our window
        glfwDefaultWindowHints(); // optional, the current window hints are already the default
        glfwWindowHint(GLFW_VISIBLE, GL_FALSE); // the window will stay hidden after creation
        glfwWindowHint(GLFW_RESIZABLE, GL_TRUE); // the window will be resizable
  
        int WIDTH = 300;
        int HEIGHT = 300;
  
        // Create the window
        window = glfwCreateWindow(WIDTH, HEIGHT, "Hello World!", NULL, NULL);
        if ( window == NULL )
            throw new RuntimeException("Failed to create the GLFW window");
  
        // Setup a key callback. It will be called every time a key is pressed, repeated or released.
        glfwSetKeyCallback(window, keyCallback = new GLFWKeyCallback() {
            @Override
            public void invoke(long window, int key, int scancode, int action, int mods) {
                if ( key == GLFW_KEY_ESCAPE && action == GLFW_RELEASE )
                    glfwSetWindowShouldClose(window, GL_TRUE); // We will detect this in our rendering loop
            }
        });
  
        // Get the resolution of the primary monitor
        ByteBuffer vidmode = glfwGetVideoMode(glfwGetPrimaryMonitor());
        // Center our window
        glfwSetWindowPos(
            window,
            (GLFWvidmode.width(vidmode) - WIDTH) / 2,
            (GLFWvidmode.height(vidmode) - HEIGHT) / 2
        );
  
        // Make the OpenGL context current
        glfwMakeContextCurrent(window);
        // Enable v-sync
        glfwSwapInterval(1);
  
        // Make the window visible
        glfwShowWindow(window);
    }
  
    private void loop() {
        // This line is critical for LWJGL's interoperation with GLFW's
        // OpenGL context, or any context that is managed externally.
        // LWJGL detects the context that is current in the current thread,
        // creates the ContextCapabilities instance and makes the OpenGL
        // bindings available for use.
        GLContext.createFromCurrent();
  
        // Set the clear color
        glClearColor(0.0f, 0.2f, 0.0f, 0.0f);
  
        createShader();
        createBuffer();
        fillBuffer();
  
        // Run the rendering loop until the user has attempted to close
        // the window or has pressed the ESCAPE key.
        while ( glfwWindowShouldClose(window) == GL_FALSE ) {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // clear the framebuffer
  
            display();
            
            glfwSwapBuffers(window); // swap the color buffers
  
            // Poll for window events. The key callback above will only be
            // invoked during this call.
            glfwPollEvents();
        }
    }
  
    public static void main(String[] args) {
        new HelloWorld().run();
    }
    
    private void display() {
        GL20.glUseProgram(shader);
        GL30.glBindVertexArray( vao );
        
        GL11.glDrawArrays( GL11.GL_TRIANGLES, 0, 3 );
  
        GL30.glBindVertexArray( 0 );
        GL20.glUseProgram(0);
    }
    
    private void createBuffer() {
        vao = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vao);
        
            vbo = GL15.glGenBuffers();
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vbo);
            GL15.glBufferData( GL15.GL_ARRAY_BUFFER, 1024 * Vertex.ByteSize, null, GL15.GL_STREAM_DRAW );
                
            GL20.glEnableVertexAttribArray(0);
            GL20.glVertexAttribPointer(0, 3, GL11.GL_FLOAT, false, 7 * Float.BYTES, 0);
    
            GL20.glEnableVertexAttribArray(1);
            GL20.glVertexAttribPointer(1, 4, GL11.GL_FLOAT, false, 7 * Float.BYTES, 3 * Float.BYTES);
                        
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0);
            
        GL30.glBindVertexArray(0);  
    }
    
    private void fillBuffer() {
        Vertex[] vertices = new Vertex[] {
                new Vertex(new Vector3f(-1.0f, -1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
                new Vertex(new Vector3f( 1.0f, -1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
                new Vertex(new Vector3f( 0.0f,  1.0f, 0.0f), new Color4f(0.5f, 0.5f, 0.5f, 0.5f)),
        };
        
        // 1# Create buffer
        FloatBuffer buffer = ByteBuffer.allocateDirect(3 * Vertex.ByteSize).asFloatBuffer();
        buffer.position(0);
        for(Vertex vertex : vertices) {
            buffer.put(vertex.position.x);
            buffer.put(vertex.position.y);
            buffer.put(vertex.position.z);
            buffer.put(vertex.color.r);
            buffer.put(vertex.color.g);
            buffer.put(vertex.color.b);
            buffer.put(vertex.color.a);
        }
        buffer.flip();
        
        // 2# Write data
        GL30.glBindVertexArray(vao);
        
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, vbo);
            GL15.glBufferSubData(GL15.GL_ARRAY_BUFFER, 0, buffer);
            GL15.glBindBuffer( GL15.GL_ARRAY_BUFFER, 0);
            
        GL30.glBindVertexArray(0);
    }
    
    private void createShader() {
        String dummyVertexShaderSrc =
                "#version 330 core"
                + "\n" + "layout(location = 0) in vec3 vs_position;"
                + "\n" + "layout(location = 1) in vec4 vs_color;"
                + "\n" + ""
                + "\n" + "out vec4 fs_color;"
                + "\n" + ""
                + "\n" + "void main() {"
                + "\n" + "    gl_Position = vec4(vs_position, 1.0);"
                + "\n" + "    fs_color = vs_color;"
                + "\n" + "}"
                ;
        
        String dummyFragmentShaderSrc = 
                "#version 330 core"
                + "\n" + "in vec4 fs_color;"
                + "\n" + ""
                + "\n" + "out vec4 out_color;"
                + "\n" + ""
                + "\n" + "void main() {"
                + "\n" + "    out_color = vec4(1.0, 0.0, 0.0, 1.0);"
                + "\n" + "}";
        
        System.out.println("Vertex-Shader: \n" + dummyVertexShaderSrc + "\n");
        System.out.println("Fragment-Shader: \n" + dummyFragmentShaderSrc + "\n");
        
        // 1# Read/Compile VertexShader
        int idVertexShader = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        GL20.glShaderSource(idVertexShader, dummyVertexShaderSrc);
        GL20.glCompileShader(idVertexShader);
  
        if (GL20.glGetShaderi(idVertexShader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile vertex shader: " + GL20.glGetShaderInfoLog(idVertexShader));
            System.exit(-1);
        }
        
        // 2# Read/Compile FragmentShader
        int idFragmentShader = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        GL20.glShaderSource(idFragmentShader, dummyFragmentShaderSrc);
        GL20.glCompileShader(idFragmentShader);
  
        if (GL20.glGetShaderi(idFragmentShader, GL20.GL_COMPILE_STATUS) == GL11.GL_FALSE) {
            System.err.println("Could not compile fragment shader: " + GL20.glGetShaderInfoLog(idFragmentShader));
            System.exit(-1);
        }
        
        // 3# Create Shader-Program
        shader = GL20.glCreateProgram();
        GL20.glAttachShader(shader, idVertexShader);
        GL20.glAttachShader(shader, idFragmentShader);
        
        GL20.glBindAttribLocation(shader, 0, "vs_position");
        GL20.glBindAttribLocation(shader, 1, "vs_color");
  
        GL20.glLinkProgram(shader);
        if (GL20.glGetProgrami(shader, GL20.GL_LINK_STATUS) == GL11.GL_FALSE) {
            System.out.println("Shader linking failed: " + GL20.glGetProgramInfoLog(shader));
            System.exit(-1);
        }
  
        GL20.glValidateProgram(shader);
        GL20.glDeleteShader(idVertexShader);
        GL20.glDeleteShader(idFragmentShader);
    }
  
    private int vao;
    private int vbo;
    private int shader;
}

Kai

Ahh... the reason is ByteBuffer.allocateDirect allocates a buffer that is BIG_ENDIAN (i.e. "network byte order"), which x86 IS NOT! :)
For this you should always use BufferUtils.createByteBuffer instead, which uses the machine's endianness, so that writing of floats will work.
So change line 194 of your second code listing to this and it works:
FloatBuffer buffer = BufferUtils.createByteBuffer(3 * Vertex.ByteSize).asFloatBuffer();

n4n0lix

Wow, I never would have thought of that, but indeed, that was the problem. Thanks alot!

abcdef

Quote
For this you should always use BufferUtils.createByteBuffer instead, which uses the machine's endianness,

You don't need BufferUtils for this, BufferUtils is for lazy people. You just need to add

.order(ByteOrder.nativeOrder());


to your byte buffer initialisation.

SHC

Just a bit of pointer, but you should not be creating the shaders and the buffers in the loop. You should move them to the init method instead.