图形开发基础之在WinForms中使用OpenTK.GLControl进行图形绘制

在这里插入图片描述

前言

GLControl 是 OpenTK 库中一个重要的控件,专门用于在 Windows Forms 应用程序中集成 OpenGL 图形渲染。通过 GLControl,可以轻松地将 OpenGL 的高性能图形绘制功能嵌入到传统的桌面应用程序中。

1. GLControl 的核心功能

  • OpenGL 渲染上下文: 提供一个 OpenGL 上下文,用于调用 OpenGL 的绘图函数。
  • 与 WinForms 集成: 能嵌入到 WinForms 界面中,与其他控件如按钮、文本框一起使用。
  • 双缓冲支持: 默认启用双缓冲以减少画面撕裂。
  • 硬件加速支持: 自动利用 GPU 的并行计算能力以实现高效渲染。

2. GLControl 的典型使用场景

  1. 实时图形渲染: 游戏开发、3D 数据可视化。
  2. 科学计算可视化: 例如绘制复杂函数曲面、模拟物理系统等。
  3. CAD/建模工具: 提供交互式的 3D 建模功能。
  4. 教学演示: 展示 OpenGL 图形渲染的基本原理和实现方法。

3. GLControl 的主要属性和方法

主要属性

属性描述
Context获取 OpenGL 渲染上下文。
GraphicsMode指定 OpenGL 渲染模式(颜色深度、模板缓冲、抗锯齿等)。
IsIdle指示当前控件是否处于空闲状态,可以用于控制渲染循环。
MakeCurrent()将当前 OpenGL 上下文切换到此控件。
SwapBuffers()交换前缓冲区和后缓冲区,用于实现双缓冲渲染。

主要事件

事件描述
Load在控件加载时触发,用于初始化 OpenGL 配置。
Resize在控件大小调整时触发,用于重新设置视口尺寸。
Paint在控件需要重新绘制时触发,调用 OpenGL 的绘图逻辑。

在这里插入图片描述

4. 使用 GLControl 的完整示例代码

以下代码展示了如何在 Windows Forms 中使用 GLControl 实现鼠标控制旋转的三角锥(四面体)。

环境准备和引用库

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>WinExe</OutputType>
    <TargetFramework>net8.0-windows</TargetFramework>
    <Nullable>enable</Nullable>
    <UseWindowsForms>true</UseWindowsForms>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>
	<ItemGroup>
		<PackageReference Include="OpenTK" Version="5.0.0-pre.13" />
		<PackageReference Include="OpenTK.Core" Version="5.0.0-pre.13" />
		<PackageReference Include="OpenTK.Mathematics" Version="5.0.0-pre.13" />
		<PackageReference Include="OpenTK.GLControl" Version="4.0.1" />
		<PackageReference Include="OpenTK.Windowing.Common" Version="5.0.0-pre.13" />
		<PackageReference Include="OpenTK.Windowing.Desktop" Version="5.0.0-pre.13" />
	</ItemGroup>
</Project>

主窗体代码

using OpenTK.GLControl;
using OpenTK.Graphics.OpenGL;
using OpenTK.Mathematics;

namespace GLControlExample
{
    public partial class Form1 : Form
    {
        private GLControl glControl;
        private int vao, vbo, shaderProgram;
        private Matrix4 model, view, projection;
        private float rotationX = 0.0f, rotationY = 0.0f; // 旋转角度
        private bool isDragging = false;
        private Point lastMousePosition;

        public Form1()
        {
            InitializeComponent();
            // 创建 GLControl
            glControl = new GLControl
            {
                Dock = DockStyle.Fill
            };
            Controls.Add(glControl);
            // 绑定事件
            glControl.Load += GlControl_Load;
            glControl.Paint += GlControl_Paint;
            glControl.Resize += GlControl_Resize;
            glControl.MouseDown += GlControl_MouseDown;
            glControl.MouseUp += GlControl_MouseUp;
            glControl.MouseMove += GlControl_MouseMove;
        }

        private void GlControl_Load(object sender, EventArgs e)
        {
            // 设置清屏颜色
            GL.ClearColor(0.2f, 0.3f, 0.3f, 1.0f);

            // 初始化 VAO 和 VBO
            vao = GL.GenVertexArray();
            vbo = GL.GenBuffer();

            GL.BindVertexArray(vao);

            float[] vertices = {
                // 顶点位置       // 颜色
                 0.0f,  0.5f,  0.0f,  1.0f, 0.0f, 0.0f, // 顶点1
                -0.5f, -0.5f,  0.5f,  0.0f, 1.0f, 0.0f, // 顶点2
                 0.5f, -0.5f,  0.5f,  0.0f, 0.0f, 1.0f, // 顶点3
                 0.0f, -0.5f, -0.5f,  1.0f, 1.0f, 0.0f  // 顶点4
            };

            int[] indices = {
                0, 1, 2, // 正面
                0, 2, 3, // 右面
                0, 3, 1, // 左面
                1, 3, 2  // 底面
            };

            int ebo = GL.GenBuffer();

            GL.BindBuffer(BufferTarget.ArrayBuffer, vbo);
            GL.BufferData(BufferTarget.ArrayBuffer, vertices.Length * sizeof(float), vertices, BufferUsage.StaticDraw);

            GL.BindBuffer(BufferTarget.ElementArrayBuffer, ebo);
            GL.BufferData(BufferTarget.ElementArrayBuffer, indices.Length * sizeof(int), indices, BufferUsage.StaticDraw);

            GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 0);
            GL.EnableVertexAttribArray(0);
            GL.VertexAttribPointer(1, 3, VertexAttribPointerType.Float, false, 6 * sizeof(float), 3 * sizeof(float));
            GL.EnableVertexAttribArray(1);

            // 创建并编译着色器
            string vertexShaderSource = @"
                #version 330 core
                layout (location = 0) in vec3 aPosition;
                layout (location = 1) in vec3 aColor;

                out vec3 vertexColor;

                uniform mat4 model;
                uniform mat4 view;
                uniform mat4 projection;

                void main()
                {
                    gl_Position = projection * view * model * vec4(aPosition, 1.0);
                    vertexColor = aColor;
                }
            ";

            string fragmentShaderSource = @"
                #version 330 core
                in vec3 vertexColor;
                out vec4 FragColor;

                void main()
                {
                    FragColor = vec4(vertexColor, 1.0);
                }
            ";

            int vertexShader = CompileShader(ShaderType.VertexShader, vertexShaderSource);
            int fragmentShader = CompileShader(ShaderType.FragmentShader, fragmentShaderSource);

            shaderProgram = GL.CreateProgram();
            GL.AttachShader(shaderProgram, vertexShader);
            GL.AttachShader(shaderProgram, fragmentShader);
            GL.LinkProgram(shaderProgram);

            // 删除着色器
            GL.DeleteShader(vertexShader);
            GL.DeleteShader(fragmentShader);

            // 初始化矩阵
            view = Matrix4.LookAt(new Vector3(0.0f, 0.0f, 2.0f), Vector3.Zero, Vector3.UnitY);
            projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45.0f), glControl.Width / (float)glControl.Height, 0.1f, 100.0f);

            GL.BindVertexArray(0);
        }

        private void GlControl_Resize(object sender, EventArgs e)
        {
            GL.Viewport(0, 0, glControl.Width, glControl.Height);
            projection = Matrix4.CreatePerspectiveFieldOfView(MathHelper.DegreesToRadians(45.0f), glControl.Width / (float)glControl.Height, 0.1f, 100.0f);
        }

        private void GlControl_Paint(object sender, PaintEventArgs e)
        {
            // 清屏
            GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);

            // 绘制三角锥
            GL.UseProgram(shaderProgram);

            model = Matrix4.CreateRotationX(MathHelper.DegreesToRadians(rotationX)) *
                    Matrix4.CreateRotationY(MathHelper.DegreesToRadians(rotationY));

            GL.UniformMatrix4f(GL.GetUniformLocation(shaderProgram, "model"),1, false, ref model);
            GL.UniformMatrix4f(GL.GetUniformLocation(shaderProgram, "view"), 1, false, ref view);
            GL.UniformMatrix4f(GL.GetUniformLocation(shaderProgram, "projection"), 1, false, ref projection);

            GL.BindVertexArray(vao);
            GL.DrawElements(PrimitiveType.Triangles, 12, DrawElementsType.UnsignedInt, 0);

            glControl.SwapBuffers();
        }

        private void GlControl_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isDragging = true;
                lastMousePosition = e.Location;
            }
        }

        private void GlControl_MouseUp(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                isDragging = false;
            }
        }

        private void GlControl_MouseMove(object sender, MouseEventArgs e)
        {
            if (isDragging)
            {
                int deltaX = e.X - lastMousePosition.X;
                int deltaY = e.Y - lastMousePosition.Y;

                rotationX += deltaY * 0.5f;
                rotationY += deltaX * 0.5f;

                lastMousePosition = e.Location;

                glControl.Invalidate();
            }
        }

        private int CompileShader(ShaderType type, string source)
        {
            int shader = GL.CreateShader(type);
            GL.ShaderSource(shader, source);
            GL.CompileShader(shader);

            GL.GetShaderi(shader, ShaderParameterName.CompileStatus, out int status);
            if (status == 0)
            {
                GL.GetShaderInfoLog(shader, out string infoLog);
                throw new Exception($"Error compiling shader ({type}): {infoLog}");
            }

            return shader;
        }
    }
}

启动程序

using System;
using System.Windows.Forms;

namespace GLControlExample
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new Form1());
        }
    }
}

在这里插入图片描述

5. 性能优势

  • 硬件加速: GLControl 能直接利用 GPU 的并行计算能力,大幅提升复杂场景的渲染效率。
  • 现代 OpenGL 特性: 支持着色器编程、帧缓冲、深度测试等现代图形技术。
  • 与 UI 的无缝集成: 在嵌入 WinForms 界面的同时,保持强大的图形渲染能力。

结语

通过本文,可以了解如何使用 OpenTK.GLControl 进行图形绘制,并掌握GLControl 基本用法,通过硬件加速是实现高效图形渲染。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/930597.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Facebook广告文案流量秘诀

Facebook 广告文案是制作有效 Facebook 广告的关键方面。它侧重于伴随广告视觉元素的文本内容。今天我们的博客将深入探讨成功的 Facebook 广告文案的秘密&#xff01; 一、广告文案怎么写&#xff1f; 正文&#xff1a;这是帖子的正文&#xff0c;出现在您姓名的正下方。它可…

java面向对象实验——扫雷+24点

扫雷 窗口绘制&#xff1a; GameWin package com.sxt;import javax.swing.*;public class GameWin extends JFrame {void launch(){this.setVisible(true);this.setSize(500, 500);this.setLocationRelativeTo(null);this.setTitle("SWE23070扫雷游戏");this.setD…

Ubuntu24安装 python3-mysql.connector

正确命令 sudo apt install python3-mysql.connector说明 网络上已有的文章Python版本和Ubuntu版本旧&#xff0c;命令不生效。

【西门子PLC.博途】——在S71200里写时间设置和读取功能块

之前我们在这篇文章中介绍过如何读取PLC的系统时间。我们来看看在西门子1200里面有什么区别。同时也欢迎关注gzh。 我们在S71200的帮助文档中搜索时间后找到这个数据类型 在博途中他是一个结构体&#xff0c;具体为 然后我们再看看它带的读取和写入时间块 读取时间&#xff1…

如何搭建智慧工厂?IOT+AI:赋能未来制造业灯塔工厂建设

在当今数字化和智能化的浪潮中&#xff0c;传统制造业正经历着前所未有的变革。智慧工厂作为智能制造的核心内容&#xff0c;正逐步成为未来制造业的发展趋势。本文将深入探讨智慧工厂的搭建过程&#xff0c;以及IoT&#xff08;物联网&#xff09;和AI&#xff08;人工智能&am…

内存图及其画法

所有的文件都存在硬盘上&#xff0c;首次使用的时候才会进入内存 进程&#xff1a;有自己的Main方法&#xff0c;并且依赖自己Main运行起来的程序。独占一块内存区域&#xff0c;互不干扰。内存中有一个一个的进程。 操作系统只认识c语言。操作系统调度驱动管理硬件&#xff0…

Linux下,用ufw实现端口关闭、流量控制(二)

本文是 网安小白的端口关闭实践 的续篇。 海量报文&#xff0c;一手掌握&#xff0c;你值得拥有&#xff0c;让我们开始吧&#xff5e; ufw 与 iptables的关系 理论介绍&#xff1a; ufw&#xff08;Uncomplicated Firewall&#xff09;是一个基于iptables的前端工具&#xf…

Python使用Selenium自动实现表单填写之蛇年纪念币蛇钞预约(附源码,源码有注释解析,已测试可用

Python实现纪念币预约自动填写表单 声明:本文只做技术交流,不可用代码为商业用途,文末有源码下载,已测试可用。 Part 1 配置文件改写(源码 有详细的注释说明 读取配置文件,自己组数据库,录入信息 配置文件 Part 2 主函数 每一期的xpath路径都不一样 所以需要提前去网站…

内存管理面试常问

为什么要有虚拟内存&#xff1f; 虚拟内存 如果你是电⼦相关专业的&#xff0c;肯定在⼤学⾥捣⿎过单⽚机。 单⽚机是没有操作系统的&#xff0c;所以每次写完代码&#xff0c;都需要借助⼯具把程序烧录进去&#xff0c;这样程序才能跑起来。 另外&#xff0c; 单⽚机的 CPU …

插入排序⁻⁻⁻⁻直接插入排序希尔排序

引言 所谓的排序&#xff0c;就是使一串记录按照其中的某个或某些关键字的大小&#xff0c;递增或递减的排列起来的操作。 常见的排序算法有&#xff1a; 今天我们主要学习插入排序的直接插入排序和希尔排序。 直接插入排序 什么是直接插入排序&#xff1f; 直接插入排序其…

基于Springboot + Vue开发的飞驰驾校预约学习平台(项目源码 + lw)

一、功能介绍 飞驰驾校预约学习平台包含管理员、教练、用户三个角色以及前后台系统。 主要功能 前台系统功能 首页展示、理论考试、教练信息、教练预约、学习资料、学习视频观看、用户留言、公告信息展示、个人中心信息管理 后台系统功能 管理员或用户登录成功后&#xff0c…

【vivado】时序报告--best时序和worst时序

利用vivado进行开发时&#xff0c;生成best时序报告和worst时序报告。 best时序报告 slow选择min_max&#xff0c;fast选择none。 worst时序报告 fast选择min_max&#xff0c;slow选择none。

深度学习GPU显卡4060ti与4060有什么区别?又与游戏显卡有什么区别?

深度学习GPU显卡4060 Ti与4060的区别 &#xff1a; 性能差异 &#xff1a; 4060 Ti : 4060 Ti通常比4060更强大&#xff0c;具有更多的CUDA核心和更高的显存带宽&#xff0c;因此在计算密集型任务&#xff08;如深度学习训练和推理&#xff09;中表现更好。其显卡核心频率、CUD…

李飞飞:Agent AI 多模态交互的前沿探索

发布于:2024 年 11 月 27 日 星期三 北京 #RAG #李飞飞 #Agent #多模态 #大模型 Agent AI在多模态交互方面展现出巨大潜力,通过整合各类技术,在游戏、机器人、医疗等领域广泛应用。如游戏中优化NPC行为,机器人领域实现多模态操作等。然而,其面临数据隐私、偏见、可解释性…

leetcode 3001. 捕获黑皇后需要的最少移动次数 中等

现有一个下标从 1 开始的 8 x 8 棋盘&#xff0c;上面有 3 枚棋子。 给你 6 个整数 a 、b 、c 、d 、e 和 f &#xff0c;其中&#xff1a; (a, b) 表示白色车的位置。(c, d) 表示白色象的位置。(e, f) 表示黑皇后的位置。 假定你只能移动白色棋子&#xff0c;返回捕获黑皇后…

bash命令缓存导致命令执行失败的问题

1、问题背景 为了修复老版本 vsftpd 的安全漏洞&#xff0c;需要把生产环境上 vsftpd 版本升级到 vsftpd-3.0.5&#xff0c;因为直接使用 rpm 包的方式进行升级还涉及到下层依赖包的升级(生产环境上的依赖包版本不能随意变更&#xff0c;可能会影响其他上层应用)&#xff0c;所…

Docker部署的gitlab升级的详细步骤(升级到17.6.1版本)

文章目录 一、Gitlab提示升级信息二、老版本的docker运行gitlab命令三、备份老版本Gitlab数据四、确定升级路线五、升级(共分3个版本升级)5.1 升级第一步(17.1.2 > 17.3.7)5.2 升级第二步(17.3.7 > 17.5.3)5.3 升级第三步(17.5.3 > 17.6.1) 六、web端访问gitlab服务 一…

Spring03——基于xml的Spring应用

Spring开发中主要对Bean的配置 Bean的常用配置一览如下&#xff1a; Xml配置方式功能描述<bean id"" class"">Bean的id和全限定名配置<bean name"">通过name设置Bean的别名&#xff0c;通过别名也能直接获取到Bean实例<bean sc…

源码可运行-PHP注册登录源码,PHP实现登陆后才能访问页面

最近有一个项目需要实现会员注册和页面登陆后才能访问&#xff0c;所以简单的HTML是无法实现的&#xff0c;就必须通过PHP、html和Mysql来实现&#xff0c;先给大家看一下登录和注册页的效果图。&#xff08;注册完成后会自动跳转到登录窗口&#xff0c;即使A用户登陆后分享了网…

使用Goland对6.5840项目进行go build出现异常

使用Goland对6.5840项目进行go build出现异常 Lab地址: https://pdos.csail.mit.edu/6.824/labs/lab-mr.html项目地址: git://g.csail.mit.edu/6.5840-golabs-2024 6.5840运行环境: mac系统 goland git clone git://g.csail.mit.edu/6.5840-golabs-2024 6.5840 cd 6.5840/src…