0

I've been hitting my head against the wall for two days on this. I'm trying to distill the simplest possible OpenGL Core ~2.0-3.2 drawing sequence so that I can build code off of it and really understand the API. The problem I'm running into is that the tutorials never seem to come with a helpful tag for what version of OpenGL they're using, and it's purely by luck I happened across documentation on how to even request a particular version from my context.

I'm certain that I have 3.2 core enabled now, as immediate mode drawing throws errors (that's a good thing! I want to leave immediate mode behind!), and I've tried to strip out anything fancy like coordinate transforms or triangle winding that might screw up my display. The problem is, I can't get anything to appear on-screen.

In prior iterations of this program, I did manage to get a white triangle on-screen sometimes, using random coordinates, but it seems to me like the vertices aren't getting set properly, and strange bit combinations produce strange results. Sign did not matter in where the triangles appeared - therefore my theory is that either the vertex information is not being transferred properly to the vertex shader, or the shader is mangling it. The problem is, I'm checking all the results and logs I can find, and the shader compiles and links beautifully.

I will provide links and code below, but in addition to just getting the triangle on-screen I'm wondering, can I get the shader program to spit text and/or diagnostic values out to its shaderInfoLog? That would simplify the debugging process immensely.

The various tutorials I'm consulting are...

http://arcsynthesis.org/gltut/Basics/Tutorial%2001.html http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Introduction https://en.wikipedia.org/wiki/Vertex_Buffer_Object http://www.opengl.org/wiki/Tutorial2:_VAOs,_VBOs,_Vertex_and_Fragment_Shaders_(C_/_SDL) http://antongerdelan.net/opengl/hellotriangle.html http://lwjgl.org/wiki/index.php?title=The_Quad_with_DrawArrays http://lwjgl.org/wiki/index.php?title=Using_Vertex_Buffer_Objects_(VBO) http://www.opengl.org/wiki/Vertex_Rendering

http://www.opengl.org/wiki/Layout_Qualifier_(GLSL) (not present in provided code, but something I tried was #version 420 with layout qualifiers 0 (in_Position) and 1 (in_Color))

Code (LWJGL + Groovy)

package com.thoughtcomplex.gwdg.core

import org.lwjgl.input.Keyboard
import org.lwjgl.opengl.ContextAttribs
import org.lwjgl.opengl.Display
import org.lwjgl.opengl.GL11
import org.lwjgl.opengl.GL15
import org.lwjgl.opengl.GL20
import org.lwjgl.opengl.GL30
import org.lwjgl.opengl.PixelFormat
import org.lwjgl.util.glu.GLU

import java.nio.ByteBuffer
import java.nio.FloatBuffer

/**
 * Created by Falkreon on 5/21/2014.
 */
class GwDG {

    static final String vertexShader = """
        #version 150
        in vec2 in_Position;
        in vec3 in_Color;

        smooth out vec3 ex_Color;
        void main(void) {
            gl_Position = vec4(in_Position,0.0,1.0);
            ex_Color = in_Color;
        }

    """;

    static final String fragmentShader = """
        #version 150

        smooth in vec3 ex_Color;
        out vec4 fragColor;

        void main(void) {
            //fragColor = vec4(ex_Color, 1.0);
            fragColor = vec4(1.0, 1.0, 1.0, 1.0);
        }
    """;

    static int vaoHandle = -1;
    static int vboHandle = -1;
    protected static int colorHandle = -1;
    static int vertexShaderHandle = -1;
    static int fragmentShaderHandle = -1;
    static int shaderProgram = -1;

    protected static FloatBuffer vboBuffer = ByteBuffer.allocateDirect(6*4).asFloatBuffer();
    protected static FloatBuffer colorBuffer = ByteBuffer.allocateDirect(9*4).asFloatBuffer();


    public static void main(String[] args) {
        //Quick and dirty hack to get something on the screen; this *works* for immediate mode drawing
        System.setProperty("org.lwjgl.librarypath", "C:\\Users\\Falkreon\\IdeaProjects\\GwDG\\native\\windows");

        Display.setTitle("Test");
        ContextAttribs attribs = new ContextAttribs();
        attribs.profileCompatibility = false;
        attribs.profileCore = true;
        attribs.majorVersion = 3;
        attribs.minorVersion = 2;
        Display.create( new PixelFormat().withDepthBits(24).withSamples(4).withSRGB(true), attribs );

        //Kill any possible winding error
        GL11.glDisable(GL11.GL_CULL_FACE);

        vaoHandle = GL30.glGenVertexArrays();
        GL30.glBindVertexArray(vaoHandle);

        reportErrors("VERTEX_ARRAY");

        vboHandle = GL15.glGenBuffers();
        colorHandle = GL15.glGenBuffers();

        vertexShaderHandle = GL20.glCreateShader(GL20.GL_VERTEX_SHADER);
        fragmentShaderHandle = GL20.glCreateShader(GL20.GL_FRAGMENT_SHADER);
        reportErrors("CREATE_SHADER");

        GL20.glShaderSource( vertexShaderHandle, vertexShader );
        GL20.glShaderSource( fragmentShaderHandle, fragmentShader );

        GL20.glCompileShader( vertexShaderHandle );
        String vertexResult = GL20.glGetShaderInfoLog( vertexShaderHandle, 700 );
        if (!vertexResult.isEmpty()) System.out.println("Vertex result: "+vertexResult);

        GL20.glCompileShader( fragmentShaderHandle );
        String fragmentResult = GL20.glGetShaderInfoLog( fragmentShaderHandle, 700 );
        if (!fragmentResult.isEmpty()) System.out.println("Fragment result: "+fragmentResult);

        shaderProgram = GL20.glCreateProgram();
        reportErrors("CREATE_PROGRAM");
        GL20.glAttachShader( shaderProgram, vertexShaderHandle );
        GL20.glAttachShader( shaderProgram, fragmentShaderHandle );
        GL20.glLinkProgram(shaderProgram);
        int result = GL20.glGetProgrami( shaderProgram, GL20.GL_LINK_STATUS );
        if (result!=1) System.out.println("LINK STATUS: "+result);
        reportErrors("SHADER_LINK");

        //Attribs
        int vertexParamID = GL20.glGetAttribLocation(shaderProgram, "in_Position");
        int colorParamID = GL20.glGetAttribLocation(shaderProgram, "in_Color");

        while (!Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {

            //Intentional flicker so I can see if something I did freezes or lags the program
            GL11.glClearColor(Math.random()/6 as Float, Math.random()/8 as Float, (Math.random()/8)+0.4 as Float, 1.0f);
            GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT );

            float[] coords = [
                 0.0f,  0.8f,
                -0.8f, -0.8f,
                 0.8f, -0.8f
            ];

            vboBuffer.clear();
            coords.each {
                vboBuffer.put it;
            }
            vboBuffer.flip();

            float[] colors = [
                1.0f, 0.0f, 0.0f,
                0.0f, 1.0f, 0.0f,
                0.0f, 0.0f, 1.0f
            ];

            colorBuffer.clear();
            colors.each {
                colorBuffer.put it;
            }
            colorBuffer.flip();

            //System.out.println(dump(vboBuffer));
            reportErrors("SETUP_TRIANGLE_DATA");

            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboHandle);
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, vboBuffer, GL15.GL_STATIC_DRAW);
            reportErrors("BIND_VBO_AND_FILL_DATA");

            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colorHandle);
            GL15.glBufferData(GL15.GL_ARRAY_BUFFER, colorBuffer, GL15.GL_STATIC_DRAW);
            reportErrors("BIND_COLOR_BUFFER_AND_FILL_DATA");

            GL20.glEnableVertexAttribArray( vertexParamID );
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboHandle);
            GL20.glVertexAttribPointer(
                    vertexParamID, 2, GL11.GL_FLOAT, false, 0, 0
            );
            GL20.glEnableVertexAttribArray( colorParamID );
            GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, colorHandle);
            GL20.glVertexAttribPointer(
                    colorParamID, 3, GL11.GL_FLOAT, false, 0, 0
            );

            reportErrors("VERTEX_ATTRIB_POINTERS");

            GL20.glUseProgram( shaderProgram );
            GL11.glDrawArrays( GL11.GL_TRIANGLES, 0, 3 );

            reportErrors("POST_RENDER");

            Display.update(true);
            Thread.sleep(12);
            Keyboard.poll();
        }
        Display.destroy();
    }


    private static String dump(FloatBuffer f) {
        String result = "[ ";
        f.position(0);
        //f.rewind();
        for(it in 0..<f.limit()) {
            result+= f.get(it);
            if (it!=f.limit()-1) result+=", ";
        }
        result +=" ]";
        f.position(0);
        result;
    }

    private static void reportErrors(String prefix) {
        int err = GL11.glGetError();
        if (err!=0) System.out.println("["+prefix + "]: "+GLU.gluErrorString(err)+" ("+err+")");
    }
}

Not that it matters, but the card is an ATI Radeon HD 8550G (part of an A8 APU) with support for GL4.

I'll update with more information at request, I just don't know what else might be helpful in diagnosing this.

Edit: I've updated the code above to reflect changes suggested by Reto Koradi. I've also got a variant of the code running with an alternate vertex declaration:

float[] coords = [
    0.0f,  0.8f,
    -0.8f, -0.8f,
    Math.random(), Math.random(),
    //0.8f, -0.8f,
];

This does actually produce something rasterized on the screen, but it is not at all what I would expect. Rather than simply relocating the bottom-right (top-right?) point, it flips between nothing, completely white, and the following two shapes:

Strange Shape 1 Strange shape 2

If I replace the second or third vertex, this happens. If I replace the first vertex, nothing appears on-screen. So, to check my assumptions about which vertex is actually appearing in the center of the window, I tried the following:

static final String vertexShader = """
    #version 150
    in vec2 in_Position;
    in vec3 in_Color;

    smooth out vec3 ex_Color;
    void main(void) {
        gl_Position = vec4(in_Position,0.0,1.0);
        ex_Color = vec3(1.0, 1.0, 1.0);
        if (gl_VertexID==0) ex_Color = vec3(1.0, 0.0, 0.0);
        if (gl_VertexID==1) ex_Color = vec3(0.0, 1.0, 0.0);
        if (gl_VertexID==2) ex_Color = vec3(0.0, 0.0, 1.0);
        //ex_Color = in_Color;
    }

""";

static final String fragmentShader = """
    #version 150

    smooth in vec3 ex_Color;
    out vec4 fragColor;

    void main(void) {
        fragColor = vec4(ex_Color, 1.0);
        //fragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
""";

Simple, right? The vertex in the middle should probably be the first, "red" vertex, since it's the non-optional vertex without which I can't seem to draw anything on the screen. That is not actually the case. The half-screen blocks are always red as expected, but the left-facing triangle shape is always the color of whatever vertex I replace - replacing the second vertex makes it green, replacing the third vertex makes it blue. It definitely seems like both "-0.8, -0.8" and "0.8, -0.8" are so far off-screen that the triangle sections visible are effectively an infinitely thin line. But I don't think this is due to a transform - this behaves more like an alignment problem, with its arbitrary threshold around 0.9 that sends coordinates shooting off into the farlands. Like perhaps the significand of a value in the vertex buffer is winding up in the exponent of in_Position values.

Just to keep drilling down, I increased the amount of hardcoded GLSL to ignore the buffers completely -

static final String vertexShader = """
    #version 150
    in vec2 in_Position;
    in vec3 in_Color;

    smooth out vec3 ex_Color;
    void main(void) {
        gl_Position = vec4(in_Position,0.0,1.0);
        ex_Color = vec3(1.0, 1.0, 1.0);
        if (gl_VertexID==0) {
            ex_Color = vec3(1.0, 0.0, 0.0);
            gl_Position = vec4(0.0, 0.8, 0.0, 1.0);
        }
        if (gl_VertexID==1) {
            ex_Color = vec3(0.0, 1.0, 0.0);
            gl_Position = vec4(-0.8, -0.8, 0.0, 1.0);
        }
        if (gl_VertexID==2) {
            ex_Color = vec3(0.0, 0.0, 1.0);
            gl_Position = vec4(0.8, -0.8, 0.0, 1.0);
        }
        //ex_Color = in_Color;
    }

""";

This produces the desired result, a nice big triangle with a different color on each vertex. Obviously I want to get this same triangle out of the vertex buffers but it's a really good start - with two vertices I can tell at least what direction the final vertex is shooting off in. In the case of the first vertex, it's definitely down.

I also figured out how to enable debug mode in the profile, and it's spitting color buffer errors at me. Good! That's a start. Now why isn't it throwing massive amounts of VBO errors?

Falkreon
  • 569
  • 5
  • 13

2 Answers2

1

Your code is not compatible with the core profile. Your reportErrors() should actually fire. In the core profile, you have to use vertex array objects (VAO) for your vertex setup. You will have to generate a VAO with glGenVertexArrays(), and bind it with `glBindVertexArray(), before setting up your vertex state.

The use of gl_FragColor in the fragment shader is also deprecated. You need to declare your own out variable in the core profile, for example:

out vec4 FragColor;
...
    FragColor = ...
Reto Koradi
  • 49,246
  • 7
  • 76
  • 116
  • Inserting: int vaoHandle = GL30.glGenVertexArrays(); GL30.glBindVertexArray(vaoHandle); after disabling backface culling but before the VBO and color buffer are defined does not change the output. Do I need to specify something beyond the vertexAttribPointers? Since I've gotten output out of the fragment shader, I'm confident that while deprecated it's still working; still, it's good to know, I switched the function over to an "out vec4 fragColor;" with no change in the output. I'll update the source above in a bit. – Falkreon May 29 '14 at 06:57
0

The answer finally came in a flash of inspiration from http://lwjgl.org/forum/index.php?topic=5171.0

It's fairly wrong, so let me explain what finally went right. My dev environment is java on an intel chip. All of java runs in big-endian. I was mystified that the exponent of my floats seemed to be winding up in the significand, but seeing this post it finally struck me- the easiest way that happens if endianness is flipped! FloatBuffers are still going to be big-endian. The likelihood that OpenGL runs in anything besides native byte order is pretty much zero. Either none of the lwjgl resources I consulted mentioned this quirk, or I missed them.

The correct initializer for the ByteBuffers in this program is:

protected static FloatBuffer vboBuffer = ByteBuffer.allocateDirect(6*4).order( ByteOrder.nativeOrder(  ) ).asFloatBuffer();
protected static FloatBuffer colorBuffer = ByteBuffer.allocateDirect(9*4).order( ByteOrder.nativeOrder(  ) ).asFloatBuffer();

The important part being the ByteOrder.nativeOrder()

Falkreon
  • 569
  • 5
  • 13
  • Interesting. I've done some OpenGL programming in Java, and never had to worry about byte order of buffers. That has been on Android, though. The devices are little endian as well, but maybe it handles it differently? I wonder if it would make a difference if you used `putFloat()` on the `ByteBuffer`, instead of using `asFloatBuffer()`, and then `put` on the `FloatBuffer` you get from that. Also, at least on Android, you don't need direct buffers for specifying VBOs. You can simply take your array of floats, e.g. `coords`, and then pass `FloatBuffer.wrap(coords)` to `glBufferData()`. – Reto Koradi May 31 '14 at 05:33
  • Strange! The tutorials on OpenGL ES I'm seeing explicitly ask for native byte order as well. Specifically, http://developer.android.com/training/graphics/opengl/shapes.html `// initialize vertex byte buffer for shape coordinates ByteBuffer bb = ByteBuffer.allocateDirect( // (number of coordinate values * 4 bytes per float) triangleCoords.length * 4); // use the device hardware's native byte order bb.order(ByteOrder.nativeOrder());` – Falkreon Jun 01 '14 at 11:54