Search code examples
c#openglglslvertex-array-object

Can't seem to get a VAO to render... Can someone help me see what I did wrong?


So an interesting thing about this problem is, I tried to use RenderDoc but RenderDoc crashed when I tried to load a frame I captured, this happened every time I tried, so it would seem graphics debugging tools wont help me in this situation...

I am using C#, and a nice set of OpenGL and GLFW bindings called CSGL (https://github.com/ThatOneCheetah/CSGL)

Here is my render code (my game uses procedural solid colored rectangles as a graphics base, the class below is meant to batch all of my draw calls.)

using System;
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

using static CSGL.OpenGL;

namespace BoxBlight
{
    class BoxRenderer
    {
        public const int MAX_RECTANGLES = 500;
        static Rectangle[] rectangles = new Rectangle[MAX_RECTANGLES];
        static int index = 0;

        static uint VAO = 250;
        static uint VBO = 250;
        static uint CBO = 250;

        static bool err = false;

        const string vertShader = @"
#version 150
in  vec3 in_Position;
in  vec4 in_Color;
out vec4 ex_Color;

void main(void) {
    gl_Position = vec4(in_Position.x, in_Position.y, in_Position.z, 1.0);
    ex_Color = in_Color;
}
";

        const string fragShader = @"
#version 150
precision highp float;

in  vec4 ex_Color;
out vec4 gl_FragColor;

void main(void) {
    gl_FragColor = ex_Color;
}
";

        static uint shaderProg = 250;
        static uint vs = 0;
        static uint fs = 0;

        public static void draw(Rectangle rectangle, int offsetX, int offsetY)
        {
            Rectangle r2 = new Rectangle(rectangle);
            r2.x += offsetX;
            r2.y += offsetY;
            rectangles[index++] = r2;
        }

        public static void init()
        {

            if (VAO == 250)
            {
                glGenVertexArrays(1, ref VAO);
            }

            if (VBO == 250)
            {
                glGenBuffers(1, ref VBO);
            }

            if (CBO == 250)
            {
                glGenBuffers(1, ref CBO);
            }

            if (shaderProg == 250)
            {
                shaderProg = glCreateProgram();

                uint vertShd = glCreateShader(GL_VERTEX_SHADER);

                IntPtr str = Marshal.AllocHGlobal(vertShader.Length);
                Marshal.Copy(Encoding.ASCII.GetBytes(vertShader), 0, str, vertShader.Length);

                int length = vertShader.Length;

                glShaderSource(vertShd, 1, ref str, ref length);
                glCompileShader(vertShd);

                Marshal.FreeHGlobal(str);

                int isCompiled = 0;

                glGetShaderiv(vertShd, GL_COMPILE_STATUS, ref isCompiled);

                if (isCompiled == 0)
                {
                    int loglen = 0;
                    glGetShaderiv(vertShd, GL_INFO_LOG_LENGTH, ref loglen);

                    IntPtr log = Marshal.AllocHGlobal(loglen);

                    glGetShaderInfoLog(vertShd, loglen, ref loglen, log);

                    char[] chrs = new char[loglen];
                    string logs = "";
                    Marshal.Copy(log, chrs, 0, loglen);
                    logs.Concat(chrs.AsEnumerable());
                    Marshal.FreeHGlobal(log);
                    Console.Error.WriteLine(logs);

                    err = true;
                    return;
                }

                uint fragShd = glCreateShader(GL_FRAGMENT_SHADER);

                str = Marshal.AllocHGlobal(fragShader.Length);
                Marshal.Copy(Encoding.ASCII.GetBytes(fragShader), 0, str, fragShader.Length);

                length = fragShader.Length;

                glShaderSource(fragShd, 1, ref str, ref length);
                glCompileShader(fragShd);

                Marshal.FreeHGlobal(str);

                isCompiled = 0;

                glGetShaderiv(fragShd, GL_COMPILE_STATUS, ref isCompiled);

                if (isCompiled == 0)
                {
                    int loglen = 0;
                    glGetShaderiv(fragShd, GL_INFO_LOG_LENGTH, ref loglen);

                    IntPtr log = Marshal.AllocHGlobal(loglen);

                    glGetShaderInfoLog(fragShd, loglen, ref loglen, log);

                    char[] chrs = new char[loglen];
                    string logs = "";
                    Marshal.Copy(log, chrs, 0, loglen);
                    logs.Concat(chrs.AsEnumerable());
                    Marshal.FreeHGlobal(log);
                    Console.Error.WriteLine(logs);

                    err = true;
                    return;
                }

                glAttachShader(shaderProg, vertShd);
                glAttachShader(shaderProg, fragShd);

                glLinkProgram(shaderProg);

                int isLinked = 0;

                glGetProgramiv(shaderProg, GL_LINK_STATUS, ref isLinked);
                if (isLinked == 0)
                {
                    int loglen = 0;
                    glGetProgramiv(shaderProg, GL_INFO_LOG_LENGTH, ref loglen);

                    IntPtr log = Marshal.AllocHGlobal(loglen);

                    glGetProgramInfoLog(shaderProg, loglen, ref loglen, log);

                    char[] chrs = new char[loglen];
                    string logs = "";
                    Marshal.Copy(log, chrs, 0, loglen);
                    logs.Concat(chrs.AsEnumerable());
                    Marshal.FreeHGlobal(log);
                    Console.Error.WriteLine(logs);

                    err = true;
                    return;
                }

                vs = vertShd;
                fs = fragShd;
            }
        }

        public static void flush(int xoffset, int yoffset, Rectangle camera)
        {
            if (err)
            {
                index = 0;
                return;
            }

            double[] verts = new double[index * 18];
            double[] colrs = new double[index * 24];

            int indecies = 0;

            for(int i = 0; i < index; i++)
            {
                Rectangle rect = rectangles[i];

                rect.x += xoffset;
                rect.y += yoffset;

                if(camera.Intersects(rect))
                {
                    double x1, y1, x2, y2;
                    x1 = (double)rect.x / camera.w * 2 - 1;
                    x2 = ((double)rect.x + rect.w) / camera.w * 2 - 1;
                    y1 = (1 - (double)rect.y / camera.h) * 2 - 1;
                    y2 = (1 - ((double)rect.y + rect.h) / camera.h) * 2 - 1;

                    verts[i * 18 + 0 ] = x1;
                    verts[i * 18 + 1 ] = y1;
                    verts[i * 18 + 2 ] = 1;

                    verts[i * 18 + 3 ] = x2;
                    verts[i * 18 + 4 ] = y1;
                    verts[i * 18 + 5 ] = 1;

                    verts[i * 18 + 6 ] = x1;
                    verts[i * 18 + 7 ] = y2;
                    verts[i * 18 + 8 ] = 1;

                    verts[i * 18 + 9 ] = x2;
                    verts[i * 18 + 10] = y1;
                    verts[i * 18 + 11] = 1;

                    verts[i * 18 + 12] = x1;
                    verts[i * 18 + 13] = y2;
                    verts[i * 18 + 14] = 1;

                    verts[i * 18 + 15] = x1;
                    verts[i * 18 + 16] = y2;
                    verts[i * 18 + 17] = 1;

                    for (int j = 0; j < 6; j++)
                    {
                        colrs[i * 24 + j * 4 + 0] = (double)rect.c.R / 255;
                        colrs[i * 24 + j * 4 + 1] = (double)rect.c.G / 255;
                        colrs[i * 24 + j * 4 + 2] = (double)rect.c.B / 255;
                        colrs[i * 24 + j * 4 + 3] = (double)rect.c.A / 255;
                    }

                    indecies += 6;
                }
            }

            glBindVertexArray(VAO);

            IntPtr data0 = Marshal.AllocHGlobal(verts.Length * sizeof(double));
            Marshal.Copy(verts, 0, data0, verts.Length);

            glBindBuffer(GL_VERTEX_ARRAY, VBO);
            glBufferData(GL_VERTEX_ARRAY, verts.Length * sizeof(double), data0, GL_DYNAMIC_DRAW);

            glEnableVertexAttribArray(VBO);
            glVertexAttribPointer(VBO, 3, GL_DOUBLE, GL_FALSE, 0, IntPtr.Zero);

            IntPtr data1 = Marshal.AllocHGlobal(colrs.Length * sizeof(double));
            Marshal.Copy(colrs, 0, data1, colrs.Length);

            glBindBuffer(GL_VERTEX_ARRAY, CBO);
            glBufferData(GL_VERTEX_ARRAY, colrs.Length * sizeof(double), data1, GL_DYNAMIC_DRAW);

            glEnableVertexAttribArray(CBO);
            glVertexAttribPointer(CBO, 4, GL_DOUBLE, GL_FALSE, 0, IntPtr.Zero);

            glUseProgram(shaderProg);
            glBindAttribLocation(shaderProg, VBO, "in_Position");
            glBindAttribLocation(shaderProg, CBO, "in_Color");

            glDrawArrays(GL_TRIANGLES, 0, indecies);
            glFlush();

            Marshal.FreeHGlobal(data0);
            Marshal.FreeHGlobal(data1);

            glUseProgram(0);
            glDisableVertexAttribArray(VBO);
            glDisableVertexAttribArray(CBO);

            index = 0;
            rectangles = new Rectangle[MAX_RECTANGLES];
        }

        public static void clean()
        {
            glDetachShader(shaderProg, vs);
            glDetachShader(shaderProg, fs);

            glDeleteProgram(shaderProg);

            glDeleteShader(vs);
            glDeleteShader(fs);

            glDeleteBuffers(1, ref VBO);
            glDeleteBuffers(1, ref CBO);

            glDeleteVertexArrays(1, ref VAO);

            rectangles = new Rectangle[0];
            index = 0;
            err = true;
        }
    }
}

Here is my main game class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static CSGL.CSGL;
using static CSGL.OpenGL;
using static CSGL.Glfw3;
using System.Drawing;

namespace BoxBlight
{
    class Program
    {
        public static IntPtr window = IntPtr.Zero;
        public static bool running = true;
        public const long FPS = 60;

        public static long CurrentTimeMS { get => DateTime.Now.Ticks / TimeSpan.TicksPerMillisecond; }

        static void Main(string[] args)
        {
            csglLoadGlfw();

            if(glfwInit() != GLFW_TRUE)
            {
                Console.Error.WriteLine("Failed to initialize GLFW!");
            }

            glfwDefaultWindowHints();
            window = glfwCreateWindow(1024, 768, "Box Blight (v1.0.0)", IntPtr.Zero, IntPtr.Zero);

            if(window == IntPtr.Zero)
            {
                Console.Error.WriteLine("Failed to create window!");
            }

            glfwMakeContextCurrent(window);

            csglLoadGL();
            glInitNames();

            long lastTime = CurrentTimeMS;

            glViewport(0, 0, 1024, 768);

            BoxRenderer.init();

            while(running)
            {
                glOrtho(0, 1024, 768, 0, 0.1, 3);

                BoxRenderer.draw(new Rectangle(0, 0, 1024, 768, Color.HotPink), 0, 0);
                BoxRenderer.draw(new Rectangle(16, 16, 16, 16, Color.White), 0, 0);

                BoxRenderer.flush(0, 0, new Rectangle(0, 0, 1024, 768, Color.Black));

                glfwSwapBuffers(window);
                glfwPollEvents();

                while (lastTime + FPS / 1000 > CurrentTimeMS) ;
            }

            BoxRenderer.clean();

            glfwTerminate();
        }
    }
}

Solution

  • the parameters to glVertexAttribPointer respectively glEnableVertexAttribArray are the attribute indices rather than the buffer objects. The buffer object has to be the current buffer object (has to be bound) when the array of generic vertex attribute data is generated. The attribute index can be get by glGetAttribLocation from the linked program object. The target for the vertex array buffer is GL_ARRAY_BUFFER rather than GL_VERTEX_ARRAY (GL_VERTEX_ARRAY is a specifies a capability state for the legacy OpenGL fixed function attributes and is not a valid buffer target).

    int vertex_index = glGetAttribLocation(shaderProg, "in_Position");
    int color_index  = glGetAttribLocation(shaderProg, "in_Color");
    
    glBindVertexArray(VAO);
    
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, verts.Length * sizeof(double), data0, GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(vertex_index);
    glVertexAttribPointer(vertex_index, 3, GL_DOUBLE, GL_FALSE, 0, IntPtr.Zero);
    
    glBindBuffer(GL_ARRAY_BUFFER, CBO);
    glBufferData(GL_ARRAY_BUFFER, colrs.Length * sizeof(double), data1, GL_DYNAMIC_DRAW);
    
    glEnableVertexAttribArray(color_index);
    glVertexAttribPointer(color_index, 4, GL_DOUBLE, GL_FALSE, 0, IntPtr.Zero);
    

    Note it is possible to set attribute indices by glBindAttribLocation, but this has to be done before the program is linked. The attribute index is a program resource and can't be changed after the program was linked. Furthermore, the attribute index is not the buffer object. The buffer and an attribute index are associate when glVertexAttribPointer is called and is stored in the state vector of the Vertex Array Object