
前言
在 .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 简介
1. 什么是 OpenTK?

OpenTK (Open Toolkit) 是一个专为 .NET 开发的开源框架,封装了 OpenGL (图形)、OpenGL ES (嵌入式图形)和 OpenAL (音频) 的功能。
它允许开发者直接使用底层 GPU 功能,实现高性能的 3D 渲染和多媒体处理。
2. OpenTK 的主要特点
- 灵活性高:直接控制 OpenGL 渲染管线,包括顶点、片段着色器等。
- 适用于高性能需求:例如游戏开发、3D 可视化。
- 跨平台支持:兼容主流桌面平台(Windows、Linux、macOS)。
3. OpenTK 的应用场景
- 构建游戏引擎或 3D 编辑器。
- 实现复杂的实时 3D 渲染效果。
- 使用 OpenGL 直接控制 GPU。
三、SkiaSharp 和 OpenTK 的关系

虽然 SkiaSharp 和 OpenTK 是两个独立的库,但它们可以协同工作。SkiaSharp 的某些后端渲染(如 SKGLControl
)依赖 OpenGL,这为它们的结合提供了可能性。以下是两者关系的几个关键点:
1. SkiaSharp 的 OpenGL 后端
SkiaSharp 提供了 OpenGL 支持,用于提高绘图效率:
- 在桌面端,SkiaSharp 使用 OpenGL 渲染 2D 图形(通过
SKGLControl
)。 - OpenTK 是 OpenGL 在 .NET 环境下的一种实现,因此可以作为 SkiaSharp 的底层支持。
2. 互补的特性
特性 | SkiaSharp | OpenTK |
---|---|---|
2D 图形绘制 | 高效、简单,功能强大 | 可实现,但代码复杂 |
3D 渲染支持 | 不支持 | 原生支持,灵活高效 |
底层控制能力 | 屏蔽底层实现,抽象度高 | 完全暴露 OpenGL API |
开发难度 | 易于上手 | 需要熟悉 OpenGL 概念 |
两者的特性互补,使得在一些场景中,开发者可以结合它们实现 2D 和 3D 图形的混合渲染。
四、SkiaSharp 和 OpenTK 的结合使用
1. 使用场景
- 2D 和 3D 混合渲染:使用 OpenTK 渲染 3D 图形,并用 SkiaSharp 绘制叠加的 2D 图形。
- 共享 OpenGL 上下文:OpenTK 的 OpenGL 上下文可以传递给 SkiaSharp,利用 SkiaSharp 的高层 API 进行 2D 绘图。
2. 示例:SkiaSharp + OpenTK 动态绘制波形
以下示例演示如何通过 OpenTK 创建 OpenGL 上下文,并使用 SkiaSharp 绘制动态波形。
1. 环境依赖
csharp
<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
csharp
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刷新
csharp
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 是两个互补的工具,灵活搭配使用可以大大提升开发效率和图形效果的表现力。