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}"); } } }
关键修改说明
- 替换过时API:彻底移除核心模式不支持的
GL.Enable(Texture2D)、GL.Begin()/GL.End() - 引入着色器:使用GLSL 330核心版本着色器,处理顶点变换和纹理采样逻辑
- 顶点缓冲管理:用VAO/VBO存储四边形顶点数据,替代立即模式的逐顶点提交
- 坐标适配:通过正交投影矩阵翻转Y轴,让OpenGL坐标系统与WinForms像素坐标对齐
- 纹理处理优化:保留原有纹理加载逻辑,改为通过着色器采样器激活纹理
内容的提问来源于stack exchange,提问作者NightmareZX
相关产品推荐
相关产品推荐

