Search code examples
c#winformsopenglgraphicsopentk

Showing OpenTk.GLControl in winforms


I've been experimenting with showing OpenGl graphics in C# winforms, and I stumbled upon a very useful NuGet package called OpenTK. I followed the introductory tutorial on OpenTK's Learn tab, and was able to render some simple shapes. The tutorial was super helpful, but it is predicated on showing the OpengGL graphics in a GameWindow (separate from the main Form). I found that it is possible to show OpenGL graphics directly in the main Form by using an OpenTK.GLControl control. To make with work, I installed the additional NuGet package OpenTK.GLControl. I added the control in the Form1.Designer.cs file and defined its necessary methods in Form1.cs. However, I'm not able to see the GLControl.

Am I missing a crucial step in my code, or do I have an issue with the packages/dll files?

Form1.Designer.cs:

    private void InitializeComponent()
    {
        /* Generic Form1 code */

        this.gLControl.Location = new System.Drawing.Point(-2, 0);
        this.gLControl.Name = "gLControl";
        this.gLControl.Size = new System.Drawing.Size(500, 300);
        this.gLControl.TabIndex = 0;
        this.gLControl.VSync = false;
        this.gLControl.Load += new System.EventHandler(this.GLControl_Load);
        this.gLControl.Resize += new System.EventHandler(this.GLControl_Resize);
        this.gLControl.Paint += new System.Windows.Forms.PaintEventHandler(this.GLControl_Paint);
    }

    private OpenTK.GLControl gLControl;

Form1.cs:

    private bool _loaded;
    private Shader shader;

    public Form1()
    {
        InitializeComponent();

    }

    private void GLControl_Load(object sender, EventArgs e)
    {
        GL.ClearColor(0.3f, 0.2f, 0.3f, 1.0f);
        GL.Enable(EnableCap.DepthTest);
        GL.Viewport(0, 0, gLControl.Width, gLControl.Height);
        shader = new Shader("shader.vert", "shader.frag");
        shader.Use();
        _loaded = true;
        gLControl.Invalidate();
    }

    private void GLControl_Resize(object sender, EventArgs e)
    {
        if (!_loaded)
            return;
        GL.Viewport(0, 0, gLControl.Width, gLControl.Height);
        gLControl.Invalidate();
    }

    private void GLControl_Paint(object sender, PaintEventArgs e)
    {
        GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
        shader.Use();
        gLControl.SwapBuffers();
    }

Shader.cs:

class Shader
{
    int Handle;

    public Shader(string vertexPath, string fragmentPath)
    {
        int VertexShader;
        int FragmentShader;

        string VertexShaderSource;

        using (StreamReader reader = new StreamReader(vertexPath, Encoding.UTF8))
        {
            VertexShaderSource = reader.ReadToEnd();
        }

        string FragmentShaderSource;

        using (StreamReader reader = new StreamReader(fragmentPath, Encoding.UTF8))
        {
            FragmentShaderSource = reader.ReadToEnd();
        }

        VertexShader = GL.CreateShader(ShaderType.VertexShader);
        GL.ShaderSource(VertexShader, VertexShaderSource);

        FragmentShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(FragmentShader, FragmentShaderSource);

        GL.CompileShader(VertexShader);

        string infoLogVert = GL.GetShaderInfoLog(VertexShader);
        if (infoLogVert != System.String.Empty)
            Console.WriteLine(infoLogVert);

        GL.CompileShader(FragmentShader);

        string infoLogFrag = GL.GetShaderInfoLog(FragmentShader);

        if (infoLogFrag != System.String.Empty)
            Console.WriteLine(infoLogFrag);

        Handle = GL.CreateProgram();

        GL.AttachShader(Handle, VertexShader);
        GL.AttachShader(Handle, FragmentShader);

        GL.LinkProgram(Handle);

        GL.DetachShader(Handle, VertexShader);
        GL.DetachShader(Handle, FragmentShader);
        GL.DeleteShader(FragmentShader);
        GL.DeleteShader(VertexShader);
    }

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

    private bool disposedValue = false;

    protected virtual void Dispose(bool disposing)
    {
        if (!disposedValue)
        {
            GL.DeleteProgram(Handle);

            disposedValue = true;
        }
    }

    ~Shader()
    {
        GL.DeleteProgram(Handle);
    }


    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

shader.vert:

#version 330 core
layout (location = 0) in vec3 aPosition;

void main()
{
    gl_Position = vec4(aPosition, 1.0);
}

shader.frag:

#version 330 core
out vec4 FragColor;

void main()
{
    FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

Solution

  • You've missed to add the control to the form:

    this.Controls.Add(this.gLControl);