You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenTK GLControl绘制Bitmap失败技术求助

问题原因与修复方案

你遇到的两个错误都是因为OpenTK 4.x搭配的GLControl默认使用OpenGL 3.2+核心模式,而你写的代码用了核心模式已废弃的旧API:

  • GL.Enable(EnableCap.Texture2D)报错:核心模式移除了全局纹理开关,纹理激活改由着色器采样器控制
  • GL.Begin()/GL.End()报错:核心模式彻底废弃了立即渲染模式,必须用着色器+顶点缓冲(VBO)/顶点数组(VAO)来绘制

完整修正代码

using OpenTK;
using OpenTK.Graphics.OpenGL;
using OpenTK.WinForms;
using OpenTK.Mathematics;
using System.Drawing.Imaging;

using DrwImage = System.Drawing.Image;
using GLControl = OpenTK.WinForms.GLControl;

public class OpenGLRenderer
{
    public GLControl _MainGLControl;
    private int _shaderProgram;
    private int _vao;
    private int _vbo;
    private int _textureUniformLocation;
    private Matrix4 _projectionMatrix;

    public OpenGLRenderer(Form contextFrom, Size size, Point location, Color backGroundColour)
    {
        _MainGLControl = new GLControl(new GraphicsMode(32, 24, 8, 4))
        {
            Parent = contextFrom,
            Location = location,
            Size = size,
            BackColor = backGroundColour
        };
        _MainGLControl.Paint += OnPaint;
        contextFrom.Load += (sender, e) => InitializeOpenGL();
        _MainGLControl.Resize += OnResize;
    }

    public OpenGLRenderer(GLControl gLControl)
    {
        _MainGLControl = gLControl;
        _MainGLControl.Paint += OnPaint;
        _MainGLControl.Resize += OnResize;
        InitializeOpenGL();
    }

    private void InitializeOpenGL()
    {
        _MainGLControl.MakeCurrent();

        // 创建并编译着色器程序
        _shaderProgram = CreateShaderProgram();
        GL.UseProgram(_shaderProgram);

        // 获取纹理采样器的Uniform位置
        _textureUniformLocation = GL.GetUniformLocation(_shaderProgram, "texture0");

        // 设置适配WinForms的正交投影矩阵(翻转Y轴)
        UpdateProjectionMatrix();

        // 初始化四边形的顶点缓冲与数组对象
        InitializeQuadBuffers();

        GL.ClearColor(_MainGLControl.BackColor);
        GL.Enable(EnableCap.Blend);
        GL.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
    }

    private void OnResize(object? sender, EventArgs e)
    {
        if (_MainGLControl == null) return;
        _MainGLControl.MakeCurrent();
        GL.Viewport(0, 0, _MainGLControl.Width, _MainGLControl.Height);
        UpdateProjectionMatrix();
    }

    private void UpdateProjectionMatrix()
    {
        // 正交投影:让OpenGL坐标和WinForms像素坐标对齐(Y轴翻转)
        _projectionMatrix = Matrix4.CreateOrthographicOffCenter(0, _MainGLControl.Width, _MainGLControl.Height, 0, -1, 1);
        int projLocation = GL.GetUniformLocation(_shaderProgram, "projection");
        GL.UniformMatrix4(projLocation, false, ref _projectionMatrix);
    }

    private int CreateShaderProgram()
    {
        // 顶点着色器:处理顶点位置和纹理坐标传递
        string vertexShaderSource = @"
            #version 330 core
            layout(location = 0) in vec2 aPosition;
            layout(location = 1) in vec2 aTexCoord;
            uniform mat4 projection;
            uniform mat4 model;
            out vec2 texCoord;
            void main()
            {
                gl_Position = projection * model * vec4(aPosition, 0.0, 1.0);
                texCoord = aTexCoord;
            }";

        // 片段着色器:采样并输出纹理颜色
        string fragmentShaderSource = @"
            #version 330 core
            in vec2 texCoord;
            uniform sampler2D texture0;
            out vec4 fragColor;
            void main()
            {
                fragColor = texture(texture0, texCoord);
            }";

        // 编译顶点着色器
        int vertexShader = GL.CreateShader(ShaderType.VertexShader);
        GL.ShaderSource(vertexShader, vertexShaderSource);
        GL.CompileShader(vertexShader);
        CheckShaderCompileError(vertexShader);

        // 编译片段着色器
        int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
        GL.ShaderSource(fragmentShader, fragmentShaderSource);
        GL.CompileShader(fragmentShader);
        CheckShaderCompileError(fragmentShader);

        // 链接着色器程序
        int program = GL.CreateProgram();
        GL.AttachShader(program, vertexShader);
        GL.AttachShader(program, fragmentShader);
        GL.LinkProgram(program);
        CheckProgramLinkError(program);

        // 删除临时着色器对象
        GL.DeleteShader(vertexShader);
        GL.DeleteShader(fragmentShader);

        return program;
    }

    private void InitializeQuadBuffers()
    {
        // 四边形数据:每个顶点包含(x,y)位置和(u,v)纹理坐标
        float[] quadData = new float[]
        {
            0, 0,  0, 1,
            1, 0,  1, 1,
            1, 1,  1, 0,
            0, 1,  0, 0
        };

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

        _vbo = GL.GenBuffer();
        GL.BindBuffer(BufferTarget.ArrayBuffer, _vbo);
        GL.BufferData(BufferTarget.ArrayBuffer, quadData.Length * sizeof(float), quadData, BufferUsageHint.StaticDraw);

        // 启用顶点位置属性
        GL.VertexAttribPointer(0, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 0);
        GL.EnableVertexAttribArray(0);

        // 启用纹理坐标属性
        GL.VertexAttribPointer(1, 2, VertexAttribPointerType.Float, false, 4 * sizeof(float), 2 * sizeof(float));
        GL.EnableVertexAttribArray(1);

        GL.BindVertexArray(0);
    }

    public void DrawImage(DrwImage image, float x, float y)
    {
        _MainGLControl.MakeCurrent();
        GL.UseProgram(_shaderProgram);

        int textureId = ImageToTexture(image);

        GL.ClearColor(Color.Brown);
        GL.Clear(ClearBufferMask.ColorBufferBit);

        // 绑定纹理到采样器
        GL.Uniform1(_textureUniformLocation, 0);
        GL.ActiveTexture(TextureUnit.Texture0);
        GL.BindTexture(TextureTarget.Texture2D, textureId);

        // 计算模型矩阵:实现图像的平移和缩放
        Matrix4 modelMatrix = Matrix4.CreateTranslation(x, y, 0) * Matrix4.CreateScale(image.Width, image.Height, 1);
        int modelLocation = GL.GetUniformLocation(_shaderProgram, "model");
        GL.UniformMatrix4(modelLocation, false, ref modelMatrix);

        // 绘制四边形
        GL.BindVertexArray(_vao);
        GL.DrawArrays(PrimitiveType.TriangleFan, 0, 4);

        _MainGLControl.SwapBuffers();

        // 清理资源
        GL.BindTexture(TextureTarget.Texture2D, 0);
        GL.DeleteTexture(textureId);
    }

    private void OnPaint(object? sender, PaintEventArgs e)
    {
        _MainGLControl.MakeCurrent();
        GL.Clear(ClearBufferMask.ColorBufferBit);
        _MainGLControl.SwapBuffers();
    }

    private int ImageToTexture(DrwImage image)
    {
        Bitmap bmp = new Bitmap(image);
        int textureId = GL.GenTexture();

        GL.BindTexture(TextureTarget.Texture2D, textureId);

        BitmapData data = bmp.LockBits(new System.Drawing.Rectangle(0, 0, bmp.Width, bmp.Height),
            ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, data.Width, data.Height, 0,
            OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.Byte, data.Scan0);

        bmp.UnlockBits(data);

        // 设置纹理过滤与包裹模式
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.ClampToEdge);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.ClampToEdge);

        return textureId;
    }

    private void CheckShaderCompileError(int shader)
    {
        GL.GetShader(shader, ShaderParameter.CompileStatus, out int status);
        if (status == 0)
        {
            string error = GL.GetShaderInfoLog(shader);
            throw new Exception($"着色器编译失败: {error}");
        }
    }

    private void CheckProgramLinkError(int program)
    {
        GL.GetProgram(program, GetProgramParameterName.LinkStatus, out int status);
        if (status == 0)
        {
            string error = GL.GetProgramInfoLog(program);
            throw new Exception($"着色器程序链接失败: {error}");
        }
    }
}

关键修改说明

  1. 替换过时API:彻底移除核心模式不支持的GL.Enable(Texture2D)、GL.Begin()/GL.End()
  2. 引入着色器:使用GLSL 330核心版本着色器,处理顶点变换和纹理采样逻辑
  3. 顶点缓冲管理:用VAO/VBO存储四边形顶点数据,替代立即模式的逐顶点提交
  4. 坐标适配:通过正交投影矩阵翻转Y轴,让OpenGL坐标系统与WinForms像素坐标对齐
  5. 纹理处理优化:保留原有纹理加载逻辑,改为通过着色器采样器激活纹理

内容的提问来源于stack exchange,提问作者NightmareZX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 01:04:53