GLWpfControl探索(一):在WPF中用OpenGL 3.3绘制三角形


专栏目标

  用 GLWpfControl 实现一个可行的 WPF + OpenGL (3.3+) 的 3D Demo。

Part 1: About GLWpfControl

  相信了解过 C# + WPF 的读者一定非常喜欢其简洁直观的语法、统一的命名、美观的界面、以及“前后端”的高解耦思想。我是个非常业余的 C# or WPF coder,只是随用随查。同时,作为一个半吊子图形学爱好者,上过几门图形学课程,也小玩过 OpenGL(当然是配了 glut 和 GLFW 的),我也对这方面有些兴趣。

  大三大四期间玩了一些新引擎,例如 Taichi,自己也试图基于 Python 写个简单的引擎,不过最终由于效率、优化和界面美观的问题未能坚持。所以我突发奇想,如果将 OpenGL 与 WPF 结合一下,不就有界面美观、语法简单、优化极好的开发流程了么?

  当然,早有前人实现了一切。Silk.NETOpenTK 都是相当成熟的解决方案。前者有 dotnet 基金会官方背书,后者是相当活跃的社区开源项目。本专栏的 GLWpfControl 就是属于 OpenTK 针对 WPF 优化的一个“子方案”,官方称之为:A native WPF control for OpenTK 4.3.0+

  不过 OpenTK / GLWpfControl 的官方 Example 居然用的是 OpenGL 2.1 的旧语法,加之本项目热度相对较低,OpenTK 的文档也一直没跟上,所以探索起来还是颇为费劲的。不管怎么说了,我借此专栏抛砖引玉一下,希望更多人能玩一玩图形学和 WPF 喽。

  GLWpfControl 的 Github 页面:https://github.com/opentk/GLWpfControl

  GLWpfControl的安装:Visual Studio 直接 NuGet (OpenTKOpenTK.GLWpfControl)。

在这里插入图片描述
  说了这么多,GLWpfControl 的优势在哪里?——原生支持。
  Silk.NET 和 OpenTK 更倾向于生成独立的窗口(类似于用 C# 接口去调用OpenGL),当然也不是不能结合(用 WinFormsHost 套娃一层),但是终归有些麻烦。GLWpfControl 的优势就在于,配置了更简明的控件:

    <Grid>
        ...
        <glWpfControl:GLWpfControl x:Name="OpenTkControl" Render="OpenTkControl_OnRender"/>
        ...
    </Grid>

  引用一波 OpenTK 官方的评价结束 Part 1:

This offers a way more clean solution than embedding GLControl and totally solves the airspace problem. As controls can be layered, nested and structured over your 3D view. This package is intended to supercede the legacy GLControl completely, and we strongly encourage upgrading to this native WPF control instead.


Part 2: WPF 简单框架的搭建

  这部分代码相对简单(是我之前项目写的代码),所以我只简略地讲一讲大致设计思路。

在这里插入图片描述  色调就是 copy 的 vs2022,左侧白色部分是为后续加入快捷按钮留空位(比如视角回正、各种视图等),右侧黑色部分就是重头戏——GLWpfControl 了,这是本专栏的主要画布。
  在 XAML 中,GLWpfControl 就只有 Part 1代码里的区区两行。

<Grid Grid.Column="1">
    <glWpfControl:GLWpfControl x:Name="TriangleDisplay" Render="Triangle_OnRender" Margin="4, 4, 4, 4"/>
</Grid>

Part 3: OpenGL 三角形迁移至 GLWpfControl

参考:你好,三角形 - LearnOpenGL CN

  OpenTK的 OpenGL “接口”和原生的 OpenGL 接口几乎是1:1还原的,所以修改起来很方便,只是我之前一直用 glut 、GLFW、nupengl 这种集成函数库,所以多少有些不适应,搞了一下午加上一晚上才搞成。

  关键点就是:把 OpenGL 的 gl~ 函数API替换成 GL.~,不过关键还是对 OpenGL 3.3+ 的语法熟练程度。当然,GLWpfControl项目的官方文档暂无,Example也相当少,所以摸索起来稍有费劲。

  glWpfControl组件的响应:

private void Triangle_OnRender(TimeSpan delta)
{
    Triangle.Render();
}

  Render函数:

public static void Render()
{
    GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
    float[] vertices = {
        -0.5f, -0.5f, 0.0f,
         0.5f, -0.5f, 0.0f,
         0.0f,  0.5f, 0.0f
    };

    int VAO = GL.GenVertexArray();
    int VBO = GL.GenBuffer();
	
	// VAO Binding
    GL.BindVertexArray(VAO);
	
	// VBO Binding & vertices to GPU Buffer
    GL.BindBuffer(BufferTarget.ArrayBuffer, VBO);
    GL.BufferData(BufferTarget.ArrayBuffer, sizeof(float) * 9, vertices, BufferUsageHint.StaticDraw);
	
	// VBO Binding & vertices to GPU Buffer
    GL.VertexAttribPointer(0, 3, VertexAttribPointerType.Float, false, 3 * sizeof(float), 0);
    GL.EnableVertexAttribArray(0);
	
	// Vertex Shader
    string vertexShaderSource = "#version 330 core\nlayout (location = 0) in vec3 aPos;\n\nvoid main()\n{\n    gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n}";
    int vertexShader = GL.CreateShader(ShaderType.VertexShader);
    GL.ShaderSource(vertexShader, vertexShaderSource);

	// Fragment Shader
    string fragmentShaderSource = "#version 330 core\nout vec4 FragColor;\n\nvoid main()\n{\n    FragColor = vec4(1.0f, 0.8f, 0.6f, 1.0f);\n} ";
    int fragmentShader = GL.CreateShader(ShaderType.FragmentShader);
    GL.ShaderSource(fragmentShader, fragmentShaderSource);
	
	// Attach Shader to Program
    int shaderProgram = GL.CreateProgram();
    GL.AttachShader(shaderProgram, vertexShader);
    GL.AttachShader(shaderProgram, fragmentShader);
    GL.LinkProgram(shaderProgram);
    GL.UseProgram(shaderProgram);
    
    // Draw Triangle
    GL.DrawArrays(PrimitiveType.Triangles, 0, 3);
}

  最终效果如下:

在这里插入图片描述

  其实如果了解 OpenGL 的读者肯定会笑着说:“这完全就是把 OpenGL 套了层壳,加了个 C# 名儿嘛!连着色器都是强行搞成string嵌入的!”(当然其是支持 .vert 和 .frag 文件的!!!)

  确实是这样。

  不过我还是非常喜欢这个开源项目,一是微软代码层面的统一确实让我coding起来非常快乐(WPF 和 C#都是如此),二是嵌入OpenGL 的 WPF 真的非常炫酷。

  希望大家喜欢本文啦!

  再推荐一波,GLWpfControl 的 Github 页面:https://github.com/opentk/GLWpfControl

  最后,本文代码:https://github.com/Sicheng-Wei/RealScaleSolar/tree/triangle

要在一个五边形内绘制多个五边形,可以使用WPF的Path元素和多个PathGeometry对象。以下是一个示例代码,其中包含一个五边形和四个嵌套的五边形: ```xml <Path Stroke="Black" StrokeThickness="2"> <Path.Data> <PathGeometry> <PathFigure StartPoint="100,0"> <LineSegment Point="200,70"/> <LineSegment Point="170,200"/> <LineSegment Point="30,200"/> <LineSegment Point="0,70"/> <LineSegment Point="100,0"/> </PathFigure> <PathGeometry Transform="TranslateTransform 25 25"> <PathFigure StartPoint="50,0"> <LineSegment Point="100,35"/> <LineSegment Point="85,100"/> <LineSegment Point="15,100"/> <LineSegment Point="0,35"/> <LineSegment Point="50,0"/> </PathFigure> </PathGeometry> <PathGeometry Transform="TranslateTransform 25 25"> <PathFigure StartPoint="50,0"> <LineSegment Point="100,35"/> <LineSegment Point="85,100"/> <LineSegment Point="15,100"/> <LineSegment Point="0,35"/> <LineSegment Point="50,0"/> </PathFigure> </PathGeometry> <PathGeometry Transform="TranslateTransform 50 50"> <PathFigure StartPoint="50,0"> <LineSegment Point="100,35"/> <LineSegment Point="85,100"/> <LineSegment Point="15,100"/> <LineSegment Point="0,35"/> <LineSegment Point="50,0"/> </PathFigure> </PathGeometry> <PathGeometry Transform="TranslateTransform 50 50"> <PathFigure StartPoint="50,0"> <LineSegment Point="100,35"/> <LineSegment Point="85,100"/> <LineSegment Point="15,100"/> <LineSegment Point="0,35"/> <LineSegment Point="50,0"/> </PathFigure> </PathGeometry> </PathGeometry> </Path.Data> </Path> ``` 在这个示例中,五边形使用PathGeometry绘制,并包含四个PathGeometry对象,每个对象都使用Transform属性将其平移一定距离。这将在五边形内绘制四个嵌套的五边形。您可以根据需要添加更多的PathGeometry对象以绘制更多的嵌套五边形。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值