OpenTK为SkiaSharp在.NET 环境下提供OpenGL支持,使其进行高效的2D渲染

news/2024/12/11 21:23:44/

在这里插入图片描述

前言

在 .NET 环境下,OpenTK 为 SkiaSharp 提供了 OpenGL 支持,使得 SkiaSharp 能够利用 OpenGL 进行高效的 2D 渲染。这种结合能够充分发挥 GPU 的加速能力,从而提升渲染性能,尤其是在需要进行复杂图形处理或频繁更新的应用中,如游戏开发、图形编辑器或实时数据可视化等场景。本文将详细介绍两者的关系、使用场景,以及如何通过示例代码将它们集成到一个项目中。

一、SkiaSharp 简介

1. 什么是 SkiaSharp?

在这里插入图片描述
SkiaSharp 是 Google Skia 图形引擎的 .NET 封装,提供了一套跨平台、高效的 2D 图形绘制 API。
它的主要特点包括:

  • 强大的 2D 绘图功能:绘制文本、矢量图形、图像等。
  • 跨平台支持:兼容 Windows、Linux、macOS、iOS 和 Android。
  • 抽象底层细节:支持多种后端(OpenGL、Direct3D、Metal、Vulkan 等),对开发者透明。
  • 易于使用:以简单的 C# API 实现复杂的图形绘制。

2. SkiaSharp 的应用场景

  • 绘制跨平台的 2D 界面(如图表、控件)。
  • 实现自定义图形效果(如矢量动画)。
  • 构建高性能的 UI 组件。

OpenTK__19">二、OpenTK 简介

OpenTK_20">1. 什么是 OpenTK

在这里插入图片描述

OpenTK(Open Toolkit) 是一个专为 .NET 开发的开源框架,封装了 OpenGL(图形)、OpenGL ES(嵌入式图形)和 OpenAL(音频) 的功能。
它允许开发者直接使用底层 GPU 功能,实现高性能的 3D 渲染和多媒体处理。

OpenTK__25">2. OpenTK 的主要特点

  • 灵活性高:直接控制 OpenGL 渲染管线,包括顶点、片段着色器等。
  • 适用于高性能需求:例如游戏开发、3D 可视化。
  • 跨平台支持:兼容主流桌面平台(Windows、Linux、macOS)。

OpenTK__29">3. OpenTK 的应用场景

  • 构建游戏引擎或 3D 编辑器。
  • 实现复杂的实时 3D 渲染效果。
  • 使用 OpenGL 直接控制 GPU。

OpenTK__34">三、SkiaSharp 和 OpenTK 的关系

在这里插入图片描述
虽然 SkiaSharp 和 OpenTK 是两个独立的库,但它们可以协同工作。SkiaSharp 的某些后端渲染(如 SKGLControl )依赖 OpenGL,这为它们的结合提供了可能性。以下是两者关系的几个关键点:

1. SkiaSharp 的 OpenGL 后端

SkiaSharp 提供了 OpenGL 支持,用于提高绘图效率:

  • 在桌面端,SkiaSharp 使用 OpenGL 渲染 2D 图形(通过 SKGLControl)。
  • OpenTK 是 OpenGL 在 .NET 环境下的一种实现,因此可以作为 SkiaSharp 的底层支持。

2. 互补的特性

特性SkiaSharpOpenTK
2D 图形绘制高效、简单,功能强大可实现,但代码复杂
3D 渲染支持不支持原生支持,灵活高效
底层控制能力屏蔽底层实现,抽象度高完全暴露 OpenGL API
开发难度易于上手需要熟悉 OpenGL 概念

两者的特性互补,使得在一些场景中,开发者可以结合它们实现 2D 和 3D 图形的混合渲染。

OpenTK__54">四、SkiaSharp 和 OpenTK 的结合使用

1. 使用场景

  • 2D 和 3D 混合渲染:使用 OpenTK 渲染 3D 图形,并用 SkiaSharp 绘制叠加的 2D 图形。
  • 共享 OpenGL 上下文OpenTK 的 OpenGL 上下文可以传递给 SkiaSharp,利用 SkiaSharp 的高层 API 进行 2D 绘图。

OpenTK__59">2. 示例:SkiaSharp + OpenTK 动态绘制波形

以下示例演示如何通过 OpenTK 创建 OpenGL 上下文,并使用 SkiaSharp 绘制动态波形。

1. 环境依赖
<Project Sdk="Microsoft.NET.Sdk"><PropertyGroup><OutputType>WinExe</OutputType><TargetFramework>net8.0-windows10.0.19041.0</TargetFramework><Nullable>disable</Nullable><UseWindowsForms>true</UseWindowsForms><ImplicitUsings>enable</ImplicitUsings></PropertyGroup><ItemGroup>                                          <PackageReference Include="SkiaSharp" Version="3.118.0-preview.1.2" /><PackageReference Include="SkiaSharp.Views.WindowsForms" Version="3.118.0-preview.1.2" /></ItemGroup>
</Project>

在这里插入图片描述

2. 编写代码

构建SkiaOpenGLControl,继承GLControl

using OpenTK.GLControl;
using OpenTK.Graphics.OpenGL;
using SkiaSharp;public class SkiaOpenGLControl : GLControl
{private GRContext _grContext;                      // SkiaSharp 上下文private GRBackendRenderTarget _renderTarget;       // Skia 渲染目标private SKSurface _skSurface;                      // Skia 表面private bool _initialized = false;                 // 控件是否已初始化private readonly object _lock = new object();      // 锁对象,确保线程安全public SkiaOpenGLControl(): base(GLControlSettings.Default){Initialize();}private void Initialize(){if (_initialized) return;MakeCurrent(); // 绑定 OpenGL 上下文var glInterface = GRGlInterface.Create();_grContext = GRContext.CreateGl(glInterface);CreateSkiaSurface();_initialized = true;}private void CreateSkiaSurface(){_skSurface?.Dispose();_renderTarget?.Dispose();int width = Width;int height = Height;GL.BindFramebuffer(FramebufferTarget.Framebuffer, 0);var framebufferInfo = new GRGlFramebufferInfo(0, (uint)All.Rgba8);_renderTarget = new GRBackendRenderTarget(width, height, 0, 8, framebufferInfo);_skSurface = SKSurface.Create(_grContext, _renderTarget, GRSurfaceOrigin.BottomLeft, SKColorType.Rgba8888);}public void RenderOnUIThread(Action<SKCanvas> drawAction){lock (_lock){MakeCurrent();var canvas = _skSurface.Canvas;canvas.Clear(SKColors.White);drawAction(canvas);canvas.Flush();SwapBuffers();}}protected override void OnResize(EventArgs e){base.OnResize(e);if (_initialized){MakeCurrent();CreateSkiaSurface();}}protected override void Dispose(bool disposing){if (disposing){_skSurface?.Dispose();_renderTarget?.Dispose();_grContext?.Dispose();}base.Dispose(disposing);}
}

构建绘图数据队列+定时生成绘图数据,触发UI刷新

using SkiaSharp;
namespace GLControlExample
{public partial class Form2 : Form{private readonly SkiaOpenGLControl _skiaControl;private Thread _drawThread;private bool _isRunning;private readonly Queue<List<SKPoint>> _waveDataQueue = new(); // 绘图数据队列private readonly object _queueLock = new();                  // 队列锁private float _time;public Form2(){InitializeComponent();_skiaControl = new SkiaOpenGLControl { Dock = DockStyle.Fill };Controls.Add(_skiaControl);}private void Form2_Load(object sender, EventArgs e){_isRunning = true;_drawThread = new Thread(GenerateWaveData){IsBackground = true};_drawThread.Start();// UI 定时绘图var timer = new System.Windows.Forms.Timer { Interval = 16 };timer.Tick += (s, args) =>{List<SKPoint> waveData;lock (_queueLock){if (_waveDataQueue.Count == 0) return;waveData = _waveDataQueue.Dequeue();}// 在 UI 线程中绘图_skiaControl.RenderOnUIThread(canvas =>{DrawWave(canvas, waveData);});};timer.Start();}private void GenerateWaveData(){while (_isRunning){var width = _skiaControl.Width;var height = _skiaControl.Height;var points = new List<SKPoint>();for (int i = 0; i < width; i += 5){float x = i;float y = (float)(height / 2 + Math.Sin((_time + i) * 0.05) * 50);points.Add(new SKPoint(x, y));}lock (_queueLock){_waveDataQueue.Enqueue(points);if (_waveDataQueue.Count > 10) _waveDataQueue.Dequeue(); // 控制队列长度}_time += 0.5f;Thread.Sleep(16); // 控制生成频率}}private void DrawWave(SKCanvas canvas, List<SKPoint> points){using var paint = new SKPaint{Color = SKColors.Blue,StrokeWidth = 2,IsAntialias = true};canvas.DrawPoints(SKPointMode.Polygon, points.ToArray(), paint);}protected override void OnFormClosing(FormClosingEventArgs e){_isRunning = false;_drawThread.Join();base.OnFormClosing(e);}}
}
3. 运行程序

在这里插入图片描述

五、总结

1. 适用场景

  • SkiaSharp 适合需要高效 2D 绘图的应用场景,例如绘制图表、界面或文本。
  • OpenTK 则是实现高性能 3D 渲染和复杂图形处理的理想工具。

2. 结合优势

通过结合 SkiaSharp 和 OpenTK,开发者可以在 OpenGL 提供的灵活环境中,轻松实现 2D 和 3D 图形的混合渲染。

3. 实践建议

  • 如果项目主要是 2D 绘图,优先使用 SkiaSharp。
  • 如果需要 3D 渲染或底层控制,考虑直接使用 OpenTK

SkiaSharp 和 OpenTK 是两个互补的工具,灵活搭配使用可以大大提升开发效率和图形效果的表现力。


http://www.ppmy.cn/news/1554320.html

相关文章

MR30分布式 I/O 模块助力 CNC 设备产能飞跃

背景分析 在现代制造业中&#xff0c;CNC 设备扮演着极为关键的角色。然而&#xff0c;CNC 设备在运行过程中也存在着诸多痛点。传统的 CNC 设备往往在控制与通信方面存在局限&#xff0c;其内部的 I/O 系统大多采用集中式架构。这种架构下&#xff0c;一旦需要处理大量的输入输…

MVC基础——市场管理系统(三)Clean Architecture

文章目录 项目地址五、Clean Architecture5.1 user cage driven5.1.1创建CoreBusiness 5.2 创建UseCases5.2.1 创建CategoriesUseCases1. 创建VeiwCategoriesUseCase获取所有Cagegory 5.2.2. 实现ICategoryRepository接口3. 实现获取所有Category的方法4. 实现获取一个Cagegory…

k8s-容器运行时接口分析

1、为了什么需要 CRI &#xff1f; 在 k8s v1.5 之前&#xff0c;Docker 作为第一代的容器运行时&#xff0c; kubelet 通过内嵌其中的 DockerShim 操作 Docker API 来操作容器。在 Kubernetes 1.5 中引入了 CRI&#xff0c;可以解耦了kubelet与容器运行时&#xff0c;该插件接…

qiankun学习记录

什么是微前端 微前端是指存在于浏览器中的微服务&#xff0c;其借鉴了微服务的架构理念&#xff0c;将微服务的概念扩展到了前端。 如果对微服务的概念比较陌生的话&#xff0c;可以简单的理解为微前端就是将一个大型的前端应用拆分成多个模块&#xff0c;每个微前端模块可以…

Linux: glibc: 频繁调用new/delete会不会导致内存的碎片

最近同事问了一个问题:频繁调用new/delete会不会导致内存的碎片。 下面是我想到的一些回答, glibc的内存处理机制,是在释放的时候会自动将小块内存整合成大块内存,为接下来满足大块的需求的可能。而且程序也不是一直占着内存不释放(如果是一直不释放,要考虑是不是内存泄漏…

【Linux】08 -- 重定向命令及管道命令

重定向命令及管道命令 Shell命令或应用程序在执行时&#xff0c;往往需要从输入设备接收一些输入数据&#xff0c;并将处理结果输出到输出设备上。在Linux系统中&#xff0c;这些输入/输出设备都被作为文件来对待。对应输入/输出设备的文件称为I/O文件。 Linux系统定义了3个标准…

Ansible常用操作-ansible模块

1.Ansible介绍 1.1 Ansible简介 &#xff08;1&#xff09;Ansible不需要安装客户端&#xff0c;通过sshd去通信&#xff08;无密钥登陆&#xff09;。 &#xff08;2&#xff09;Ansible无服务器端&#xff0c;使用时直接运行命令。 &#xff08;3&#xff09;Ansible基于…

Linux下进程替换exec系列接口

文章目录 Linux下进程替换1. c库exec函数族一、exec函数族简介二、exec函数族函数原型及参数说明三、exec函数族的工作机制四、注意事项五、示例代码 2. 系统调用execve接口一、execve接口与C库exec函数族的关系二、函数原型三、参数说明四、工作原理五、返回值六、注意事项七、…