Search code examples
c#openglopentk

Why is my code not rendering a very simple rectangle?


I am very new to both C# and OpenGL. So I apologize in advance for any stupid mistakes.

I am trying to render a simple rectangle using OpenTK. I have followed a bunch of tutorials and started getting a grasp of what exactly needed to be done. In fact, I was able to render it at one point

For some reason, after I tried to fiddle a bit with the vertex shader code (trying to convert normal coordinates to NDC), everything just stopped working. I did not even touch my game window code (at least I have no recollection). So this is really frustrating me because I cannot seem to find out what went wrong. This is what appears now:

Note: The background color is different because I changed that.

My guess is that it's either the shader code or it's some binding issues. I'm not really sure. If anyone could help me out, it would be very appreciated!

Here are the files:

Game.cs

using System;
using OpenTK;
using OpenTK.Graphics.OpenGL4;
using OpenTK.Windowing.Common;
using OpenTK.Windowing.Desktop;
using OpenTK.Windowing.GraphicsLibraryFramework;
using OpenTK.Mathematics;

namespace Test
{
    public class Game : GameWindow
    {

        private int vertexBufferHandle;
        private int colorBufferHandle;
        private int indexBufferHandle;
        private int vertexArrayHandle;

        private Shader shader;

        private readonly float[] vertices = new float[]
        {
            -0.7f, -0.5f, 0.0f,
            -0.7f,  0.5f, 0.0f,
             0.7f, -0.5f, 0.0f,
             0.7f,  0.5f, 0.0f
        };

        private readonly float[] colors = new float[]
        {
            1.0f, 0.0f, 0.0f, 1.0f,
            0.0f, 1.0f, 0.0f, 1.0f,
            0.0f, 0.0f, 1.0f, 1.0f,
            1.0f, 0.0f, 1.0f, 1.0f
        };

        private readonly uint[] indices = new uint[]
        {
            0, 1, 2,
            2, 1, 3
        };

        public Game(int width, int height, string title = "Game") : base(GameWindowSettings.Default, new NativeWindowSettings()
        {
            Title = title,
            Size = new Vector2i(width, height),
            WindowBorder = WindowBorder.Fixed,
            StartVisible = false,
            StartFocused = true,
            API = ContextAPI.OpenGL,
            Profile = ContextProfile.Core,
            APIVersion = new Version(4, 1)
        })
        {
            this.CenterWindow();
        }

        protected override void OnLoad()
        {
            this.IsVisible = true;

            GL.ClearColor(Color4.AliceBlue);

            this.vertexBufferHandle = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, this.vertexBufferHandle);
            GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsageHint.StaticDraw);
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

            this.colorBufferHandle = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ArrayBuffer, this.colorBufferHandle);
            GL.BufferData(BufferTarget.ArrayBuffer, colors.Length * sizeof(float), colors, BufferUsageHint.StaticDraw);
            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);

            this.indexBufferHandle = GL.GenBuffer();
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, this.indexBufferHandle);
            GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(float), indices, BufferUsageHint.StaticDraw);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

            vertexArrayHandle = GL.GenVertexArray();
            GL.BindVertexArray(vertexArrayHandle);

            GL.BindBuffer(BufferTarget.ArrayBuffer, this.vertexBufferHandle);
            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);

            GL.BindBuffer(BufferTarget.ArrayBuffer, this.colorBufferHandle);
            GL.VertexAttribPointer(1, 4, VertexAttribPointerType.Float, false, 4 * sizeof(float), 0);
            GL.EnableVertexAttribArray(1);

            GL.BindBuffer(BufferTarget.ElementArrayBuffer, this.indexBufferHandle);

            GL.BindVertexArray(0);

            shader = new Shader("Shader/shader.vert", "Shader/shader.frag");

            base.OnLoad();

        }

        protected override void OnRenderFrame(FrameEventArgs args)
        {
            GL.Clear(ClearBufferMask.ColorBufferBit);

            shader.Use();
            GL.BindVertexArray(vertexArrayHandle);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, indexBufferHandle);
            GL.DrawElements(PrimitiveType.Triangles, 6, DrawElementsType.UnsignedInt, 0);
            // GL.BindVertexArray(0);

            Context.SwapBuffers();
            base.OnRenderFrame(args);
        }

        protected override void OnUpdateFrame(FrameEventArgs args)
        {
            base.OnUpdateFrame(args);
        }

        protected override void OnUnload()
        {
            
            GL.BindVertexArray(0);
            GL.DeleteVertexArray(vertexArrayHandle);

            GL.BindBuffer(BufferTarget.ArrayBuffer, 0);
            GL.BindBuffer(BufferTarget.ElementArrayBuffer, 0);

            GL.DeleteBuffer(this.vertexBufferHandle);
            GL.DeleteBuffer(this.colorBufferHandle);

            GL.UseProgram(0);
            GL.DeleteProgram(shader.ProgramHandle);

            base.OnUnload();
        }
    }
}

Program.cs

using System;

namespace Test
{
    class Rectangle
    {
        static void Main(string[] args)
        {
            using(Game game = new Game(width: 400, height: 400))
            {
                game.Run();
            };
        }
    }
}

Shader.cs

using System;
using OpenTK.Graphics.OpenGL4;

namespace Test
{
    public class Shader
    {

        public int ProgramHandle;

        public Shader(string vert, string frag)
        {
            string vertexShaderCode = File.ReadAllText("../../../" + vert);
            string fragmentShaderCode = File.ReadAllText("../../../" + frag);

            int vertexShaderHandle = GL.CreateShader(ShaderType.VertexShader);
            GL.ShaderSource(vertexShaderHandle, vertexShaderCode);

            int fragmentShaderHandle = GL.CreateShader(ShaderType.FragmentShader);
            GL.ShaderSource(fragmentShaderHandle, fragmentShaderCode);

            Console.Write(GL.GetShaderInfoLog(vertexShaderHandle));
            Console.Write(GL.GetShaderInfoLog(fragmentShaderHandle));

            GL.CompileShader(vertexShaderHandle);
            GL.CompileShader(fragmentShaderHandle);

            this.ProgramHandle = GL.CreateProgram();
            GL.AttachShader(this.ProgramHandle, vertexShaderHandle);
            GL.AttachShader(this.ProgramHandle, fragmentShaderHandle);

            GL.LinkProgram(this.ProgramHandle);

            GL.DetachShader(this.ProgramHandle, vertexShaderHandle);
            GL.DetachShader(this.ProgramHandle, fragmentShaderHandle);

            GL.DeleteShader(vertexShaderHandle);
            GL.DeleteShader(fragmentShaderHandle);
        }

        public void Use()
        {
            GL.UseProgram(ProgramHandle);
        }
    }
}

(I don't get any errors from the GL.GetShaderInfoLog)

shader.vert

#version 410

layout(location = 0) in vec3 aPosition;
layout(location = 1) in vec4 aColor;
out vec4 vColor;

void main(void){
    vColor = aColor;
    gl_Position = vec4(aPosition, 1f);
}

shader.frag

#version 410

in vec4 vColor;
out vec4 outputColor;

void main()
{
    outputColor = vColor;
}

Solution

  • It is sneaky, but 1f is not a valid floating point constant in GLSL.

    The official syntax is:

    GLSL floating point constant

    1f is a digit-sequence with a floating-suffix, but that is not one of the options. There either needs to be an exponent-part, or a dot to turn the digit-sequence into a fractional-constant.

    I don't get any errors from the GL.GetShaderInfoLog

    You should get one if you call this after compiling the shaders.