Golang之OpenGL(一)

使用OpenGL实现窗口中绘制三角形(纯色|彩色)、正方形(变色)

  • 一、简单实现窗口绘制三角形
  • 二、绘制的多颜色三角形(基于 ‘ 简单实现窗口绘制三角形 ’ )
    • 1、在顶点着色器和片段着色器中添加了颜色的输入和输出处理。
    • 2、makeVao 函数中启用了顶点位置和颜色的属性,并分别设置了它们的属性指针。
    • 3、draw 函数中修改了绘制的顶点数量计算,因为现在每个顶点包含位置和颜色共 6 个值。
  • 三、绘制不停变换颜色三角形/正方形(基于 ‘ 简单实现窗口绘制三角形 ’ )
    • 1、如果需要展示三角形那么 triangle 就不动,如若正方形就需要自己调整对应的xyz坐标
    • 2、片段着色器中使用时间来计算颜色的变化。
    • 3、在draw函数中,获取当前时间并传递给片段着色器的uniform变量。同时,确保在初始化OpenGL时启用垂直同步,以控制渲染速率。

本文只是跟随此 链接 文章简单实现并了解OpenGL,如若你有兴趣可去实现本文衍生的【生命游戏】

在 OpenGL 中,大多数复杂的形状最终都是通过三角形来绘制和构建的,这是因为三角形是最简单且不可再分的多边形,具有良好的稳定性和通用性, 几乎所有的图形都可以通过组合多个三角形来近似表示。
/
例如矩形可以用两个三角形组成,圆形可以通过多个小三角形来逼近。
/
OpenGL 也支持其他基本图元,如点(GL_POINTS)、线(GL_LINES、GL_LINE_STRIP、GL_LINE_LOOP)等。但对于构建复杂的形状,三角形仍然是最常用和基础的元素。所以,虽然不是所有形状都严格地只能用三角形绘制,但在实际应用中,三角形是非常重要和常用的基础构建块。

一、简单实现窗口绘制三角形

在这里插入图片描述

// @Author cory 2024/8/1 15:59:00
package openGLimport ("fmt""github.com/go-gl/gl/v4.1-core/gl""github.com/go-gl/glfw/v3.2/glfw""log""runtime""strings"
)const (//定义画布参数width  = 600height = 600
)// @Title OneMain 2024/8/1 16:05:00
// @Description 主循环
// @Auth Cory
func OneMain() {//1、运行时包指示给 LockOSThread(),这确保我们将始终在同一操作系统线程中执行,这对于 GLFW 很重要,因为 GLFW 必须始终从初始化它的同一线程中调用runtime.LockOSThread()//2、调用 initGlfw 来获取窗口引用,并延迟终止。然后在for循环中使用窗口引用,在for循环中,我们说只要窗口应该保持打开状态,就做一些事情。window := initGlfw()defer glfw.Terminate()program := initOpenGL()  //初始化OpenGL并返回一个初始化的程序。vao := makeVao(triangle) //根据定义的三角形来返回顶点数组对象的指针//3、在当前循环中可以做很多事情for !window.ShouldClose() {// TODO// 检查链接状态var status int32gl.GetProgramiv(program, gl.LINK_STATUS, &status)if status == gl.FALSE {var logLength int32gl.GetProgramiv(program, gl.INFO_LOG_LENGTH, &logLength)log := strings.Repeat("\x00", int(logLength+1))gl.GetProgramInfoLog(program, logLength, nil, gl.Str(log))fmt.Println("链接程序错误:", log)continue}draw(vao, window, program)}
}// @Title initGlfw 2024/8/1 16:04:00
// @Description 创建窗口
// @Auth Cory
// @Return error ---> "返回窗口"
func initGlfw() *glfw.Window {//1、初始化 GLFW 包err := glfw.Init()if err != nil {panic(err)}//2、定义一些全局 GLFW 属性glfw.WindowHint(glfw.Resizable, glfw.False)                 //指定用户是否可以调整窗口的大小。glfw.WindowHint(glfw.ContextVersionMajor, 4)                //指定创建的上下文必须与之兼容的客户端API版本。OR 2glfw.WindowHint(glfw.ContextVersionMinor, 1)                //指定创建的上下文必须与之兼容的客户端API版本。glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) //指定要为哪个OpenGL配置文件创建上下文。硬约束。glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)    //指定OpenGL上下文是否应该是向前兼容的。硬约束。//3、创建一个 glfw (就是我们要进行未来绘画的地方,只需告诉它我们想要的宽度和高度,以及标题,然后调用 window.MakeContextCurrent,将窗口绑定到我们当前的线程。最后返回窗口)window, err := glfw.CreateWindow(width, height, "铁憨憨_自学GL", nil, nil)if err != nil {panic(err)}window.MakeContextCurrent()return window
}// compileShader 此函数的用途是以字符串及其类型的形式接收着色器源代码,并返回指向生成的编译着色器的指针。如果它编译失败,我们将返回一个包含详细信息的错误。
func compileShader(source string, shaderType uint32) (uint32, error) {shader := gl.CreateShader(shaderType)csources, free := gl.Strs(source)gl.ShaderSource(shader, 1, csources, nil)free()gl.CompileShader(shader)var status int32gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)if status == gl.FALSE {var logLength int32gl.GetShaderiv(shader, gl.INFO_LOG_LENGTH, &logLength)log := strings.Repeat("\x00", int(logLength+1))gl.GetShaderInfoLog(shader, logLength, nil, gl.Str(log))return 0, fmt.Errorf("failed to compile %v: %v", source, log)}return shader, nil
}/*
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
============================================================================================================================================================================================================
*/const (vertexShaderSource = `#version 410in vec3 vp;void main() {gl_Position = vec4(vp, 1.0);}` + "\x00"  // GLSL 源代码  顶点着色器fragmentShaderSource = `#version 410out vec4 frag_colour;void main() {frag_colour = vec4(241.0/255.0, 148.0/255.0, 138.0/255.0, 1.0);}` + "\x00"  // GLSL 源代码  片段着色器
)// 三角形参数
var (triangle = []float32{0, 0, 0, // top-1, 0, 0, // left0, -1, 0, // right}
)// initOpenGL 初始化OpenGL并返回一个初始化的程序。
func initOpenGL() uint32 {// 1、初始化 OpenGL 库if err := gl.Init(); err != nil {panic(err)}// 2、获取 OpenGL 版本信息并打印日志version := gl.GoStr(gl.GetString(gl.VERSION))log.Println("OpenGL version", version)// 3、编译顶点着色器vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)if err != nil {panic(err)}// 4、编译片段着色器fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)if err != nil {panic(err)}// 5、创建一个 OpenGL 程序对象prog := gl.CreateProgram()// 将顶点着色器和片段着色器附加到程序对象上gl.AttachShader(prog, vertexShader)gl.AttachShader(prog, fragmentShader)// 链接程序对象gl.LinkProgram(prog)// 6、返回链接后的程序对象的标识符return prog
}// makeVao 从提供的点初始化并返回顶点数组。
// 解释如下:
// 1、makeVao 函数的主要目的是创建和配置一个用于存储顶点数据的顶点数组对象(Vertex Array Object,简称 VAO)
// 2、points 数组包含了定义图形(例如三角形)的顶点坐标数据。通过一系列的 OpenGL 函数调用,将这些顶点数据存储在缓冲区对象(VBO)中,并将 VBO 与 VAO 进行关联和配置。
// 3、此处的uint32实际上是一个无符号的标识符,用来指向已经配置好的顶点数组对象,在后续的渲染代码中,可以通过 gl.BindVertexArray(vao) 来绑定这个创建好的 VAO,然后执行绘制操作
func makeVao(points []float32) uint32 {var vbo uint32gl.GenBuffers(1, &vbo)gl.BindBuffer(gl.ARRAY_BUFFER, vbo)gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)var vao uint32gl.GenVertexArrays(1, &vao)gl.BindVertexArray(vao)gl.EnableVertexAttribArray(0)gl.BindBuffer(gl.ARRAY_BUFFER, vbo)gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)return vao
}func draw(vao uint32, window *glfw.Window, program uint32) {// 1、清除颜色缓冲区和深度缓冲区,为新的绘制操作准备干净的画布gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)// 2、使用指定的程序进行后续的绘制操作gl.UseProgram(program)// 3、绑定之前创建的顶点数组对象(VAO)gl.BindVertexArray(vao)// 4、使用 `gl.DrawArrays` 函数进行绘制// `gl.TRIANGLES` 表示绘制三角形// `0` 表示从顶点数组的起始位置开始绘制// `int32(len(triangle)/3)` 表示要绘制的顶点数量,这里假设 `triangle` 存储了顶点数据,并且每 3 个顶点构成一个三角形gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3))// 5、让 GLFW 检查是否有鼠标或键盘等事件glfw.PollEvents()// 6、交换前后缓冲区,将之前在后台缓冲区绘制的内容显示到屏幕上window.SwapBuffers()
}

二、绘制的多颜色三角形(基于 ‘ 简单实现窗口绘制三角形 ’ )

在这里插入图片描述

1、在顶点着色器和片段着色器中添加了颜色的输入和输出处理。

const (vertexShaderSource = `#version 410in vec3 vp;in vec3 color; out vec3 fragColor; void main() {gl_Position = vec4(vp, 1.0);fragColor = color; }` + "\x00"  // GLSL 源代码  顶点着色器fragmentShaderSource = `#version 410in vec3 fragColor; out vec4 outColor;void main() {outColor = vec4(fragColor, 1.0); }` + "\x00"  // GLSL 源代码  片段着色器
)// 三角形参数,包含位置和颜色(前三xyz,后三rgb)
var (triangle = []float32{0, 0, 0, 1.0, 0.0, 0.0, // 红色顶点-1, 0, 0, 0.0, 1.0, 0.0, // 绿色顶点0, -1, 0, 0.0, 0.0, 1.0, // 蓝色顶点}
)

2、makeVao 函数中启用了顶点位置和颜色的属性,并分别设置了它们的属性指针。

gl.EnableVertexAttribArray(0) 修改为:

	// 启用顶点位置和颜色属性gl.EnableVertexAttribArray(0)gl.EnableVertexAttribArray(1)

此处为什么要两个EnableVertexAttribArray,为什么不能一步到位,直接要设置1的哪个?

因为在这个场景中,不能只使用 gl.EnableVertexAttribArray(1) 而省略 gl.EnableVertexAttribArray(0)
原因如下:
在您的顶点着色器中,定义了两个输入属性:顶点位置 vp 和顶点颜色 color 。
gl.EnableVertexAttribArray 用于启用顶点属性数组,以便在渲染时能够使用它们。
gl.EnableVertexAttribArray(0) 启用的是 顶点位置 属性,
gl.EnableVertexAttribArray(1) 启用的是 顶点颜色 属性。
如果只启用 1 而不启用 0 ,那么顶点位置属性将不会被激活,导致在渲染时无法正确获取和使用顶点的位置信息,从而可能导致图形无法正确显示。每个顶点属性都需要单独启用,以确保 OpenGL 知道在渲染时应该从相应的缓冲区中获取和使用这些数据。

然后在 gl.VertexAttribPointer 后面追加

	// 为顶点颜色设置属性指针gl.VertexAttribPointer(1, 3, gl.FLOAT, false, 6*4, gl.PtrOffset(3*4))

3、draw 函数中修改了绘制的顶点数量计算,因为现在每个顶点包含位置和颜色共 6 个值。

gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/3)) 修改为:

	// `int32(len(triangle)/6)` 表示要绘制的顶点数量,每个顶点包含位置和颜色gl.DrawArrays(gl.TRIANGLES, 0, int32(len(triangle)/6))

三、绘制不停变换颜色三角形/正方形(基于 ‘ 简单实现窗口绘制三角形 ’ )

在这里插入图片描述

1、如果需要展示三角形那么 triangle 就不动,如若正方形就需要自己调整对应的xyz坐标

切片包含 9 个值,三角形的每个顶点对应 3 个值。顶线 0、0.5、0 是表示为 X、Y 和 Z 坐标的顶点,第二条线是左顶点,第三条线是右顶点。这三对中的每一对都表示顶点相对于窗口中心的 X、Y 和 Z 坐标,介于 -1 和 1 之间。因此,最高点的 X 为零,因为它的 X 位于窗口的中心,Y 为 0.5 表示它将相对于窗口中心向上移动四分之一(因为范围为 -1 比 1),Z 为零。

正方形为两个三角形组合

	triangle = []float32{0, 0, 0, // top-1, 0, 0, // left0, -1, 0, // right-1, 0, 0, // top-1, -1, 0, // left0, -1, 0, // right}

2、片段着色器中使用时间来计算颜色的变化。

	//使用时间信息来动态计算颜色。这里使用正弦和余弦函数来实现周期性的颜色变化效果。fragmentShaderSource = `#version 410out vec4 frag_colour;uniform float time;void main() {// 根据时间计算颜色float r = sin(time) * 0.5 + 0.5;float g = cos(time) * 0.5 + 0.5;float b = 0.5;frag_colour = vec4(r, g, b, 1.0);}` + "\x00"

3、在draw函数中,获取当前时间并传递给片段着色器的uniform变量。同时,确保在初始化OpenGL时启用垂直同步,以控制渲染速率。

	// 获取当前时间(秒为单位)time := float32(glfw.GetTime())// 获取uniform变量的位置timeUniform := gl.GetUniformLocation(program, gl.Str("time\x00"))// 将时间值传递给片段着色器的uniform变量gl.Uniform1f(timeUniform, time)

或者自己来调整颜色变换的速度

此时 fragmentShaderSource 就不能是常量了,因为多了一个speed来控制速度

	fragmentShaderSource = `#version 410out vec4 frag_colour;uniform float time;uniform float speed;void main() {// 根据时间和速度计算颜色float r = sin(time * speed) * 0.5 + 0.5;float g = cos(time * speed) * 0.5 + 0.5;float b = 0.5;frag_colour = vec4(r, g, b, 1.0);}` + "\x00"  // GLSL 源代码  片段着色器
	// 获取当前时间(秒为单位)time := float32(glfw.GetTime())// 获取uniform变量的位置timeUniform := gl.GetUniformLocation(program, gl.Str("time\x00"))// 获取 speed uniform 变量的位置speedUniform := gl.GetUniformLocation(program, gl.Str("speed\x00"))// 将时间值传递给片段着色器的uniform变量gl.Uniform1f(timeUniform, time)// 设置 speed 的值   (数越大变换的速度越快)gl.Uniform1f(speedUniform, 2.0)

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

反制攻击者-蚁剑低版本

目录 安装 攻击者获取防守方的权限 防守方反制攻击者 防守方获取攻击者的shell权限 安装 安装蚁剑2.0.7版本 链接:https://pan.baidu.com/s/1t40UxkZ2XuSWG6VCdGzvDw?pwd8888 提取码:8888 下载好后先打开Loader文件夹下的.exe文件,打…

赛蓝企业管理系统 AuthToken/Index 身份认证绕过漏洞复现

0x01 产品简介 赛蓝企业管理系统是一款为企业提供全面管理解决方案的软件系统,它能够帮助企业实现精细化管理,提高效率,降低成本。系统集成了多种管理功能,包括但不限于项目管理、财务管理、采购管理、销售管理以及报表分析等&am…

【iOS】——GCD总结

同步和异步的区别 同步执行等待操作完成,而异步执行允许程序在操作完成前继续运行,提高了效率和响应性。这里的关键就是上一个操作需不需要等待当前操作的执行,如果需要就是同步,如果不需要就是异步。 异步有开启新线程的能力但…

合作伙伴中心Partner Center中添加了Copilot预览版

目录 一、引言 二、Copilot 功能概述 2.1 Copilot 简介 2.2 Copilot 的核心功能 2.3 Copilot 的访问和使用 三、Copilot 的使用方法 3.1 Copilot 功能区域 3.2 Copilot 使用示例 3.2.1 编写有效提示 3.2.2 使用反馈循环 四、负责任的人工智能 4.1 Copilot 结果的可…

Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签

本教程主要实现【Golang处理Word文档模板实现标签填充|表格插入|图标绘制和插入|删除段落|删除标签】。 本文源码:https://gitee.com/songfayuan/go-zero-demo 教程源码分支:master 分支(_examples/word-template/fill-word-template.go&…

win 10 局域网共享

1,打开共享 控制面板\网络和 Internet\网络和共享中心\高级共享设置 (在控制面板界面建议使用大图片或小图标容易找到目标) 或者直接复制红色部分,然后打开此电脑,粘贴到地址栏直接回车即可直接到达几面 打开如下2个…

达梦数据库一体机在宜昌市财政局上线了!

财政作为国家治理的基础和重要支柱,其数字化转型已成为构建现代财政制度的必由之路,引领着财政管理体系向更高效、更智能的方向迈进。 达梦数据全面助力财政信息化转型与智能化发展,采用 DAMEGN PAI I 系列数据库一体机,为宜昌市财…

Unity Camera

课程目标 1. 了解摄像机(camera)不同视角的设计与实现;2. 感受在不同摄像机视角下观察虚拟场景。 喜欢玩游戏或者看3D动漫的朋友可以回忆在虚拟场景中摄像头的运动变化带来的视觉感受,例如:摄像头给场景中的主角来个…

马来西亚原生静态IP注册的账号稳定吗?

马来西亚作为东南亚重要的经济体之一,其网络基础设施和互联网服务水平在近年来有了显著提升。静态IP作为一种固定的互联网协议地址,对于某些特定的网络应用和需求非常重要。本文将围绕马来西亚原生静态IP注册的账号稳定性进行探讨,分析其在不…

【Python系列】Python 字典合并

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

pytorch基础模块:Tensorboard、Dataset、Transforms、Dataloader

Tensorboard、Dataset、Transforms、Dataloader 该文档主要参考【土堆】的视频教程:pytorch入门教程–土堆 一、Tensorboard 安装tensorboard:pip install tensorboard 使用步骤: 引入相关库:from torch.utils.tensorboard i…

LinkedList接口源码解读

LinkedList 接口源码解读(一) 前言 因为追求质量,所以写的较慢。大概在接下来的三天内会把LinkedList源码解析出完。大概还有两篇文章。废话不多说,正片开始! 大家都知道,LinkedList是在Java底层中是由 …

手机上音乐如何转换成MP3格式?分享5款音频格式转换APP

手机上音乐如何转换成MP3格式?相信很多外出办公或者不经常使用电脑的工作人士,学生党,媒体从业者都有这样的疑惑和需求。不同设备和应用可能支持不同的音频格式,导致某些情况下需要将音乐文件转换为MP3格式以确保兼容性。下面&…

操作系统|day4.Linux、Linux内核、Linux负载、Linux文件存储

文章目录 LinuxLinux内核定义功能态 Linux负载定义 Linux文件存储链接分类区别使用场景 拷贝 Linux Linux内核 定义 内核是操作系统的核心,具有很多最基本功能,它负责管理系统的进程、内存、设备驱动程序、文件和网络系统,决定着系统的性能…

.NET周刊【7月第4期 2024-07-28】

国内文章 .NET 高性能缓冲队列实现 BufferQueue https://mp.weixin.qq.com/s/fUhJpyPqwcmb3whuV3CDyg BufferQueue 是一个用 .NET 编写的高性能的缓冲队列实现,支持多线程并发操作。 项目地址:https://github.com/eventhorizon-cli/BufferQueue 项目…

【虚拟仿真】Unity3D中实现2DUI显示在3D物体旁边

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址QQ群:398291828大家好,我是佛系工程师☆恬静的小魔龙☆,不定时更新Unity开发技巧,觉得有用记得一键三连哦。 一、前言 这篇文章来实现2DUI显示在3D物体旁边,当我们需要在3D模型旁边显示2DUI的时候,比如人物的对…

grep工具的使用

grep [options]…… pattern [file]…… 工作方式: grep 在一个或者多个文件中搜索字符串模板,如果模板中包括空格,需要使用引号引起来,模 板后的所有字符串会被看作是文件名。 工作结果:如果模板搜索成功&#xf…

Springboot+Vue在线水果(销售)商城管理系统-附源码与配套论文

1.1 研究背景 互联网概念的产生到如今的蓬勃发展,用了短短的几十年时间就风靡全球,使得全球各个行业都进行了互联网的改造升级,标志着互联网浪潮的来临。在这个新的时代,各行各业都充分考虑互联网是否能与本行业进行结合&#xf…

Java:进程和线程

文章目录 进程线程的概念和区别总结如何创建线程1.继承Thread重写run2.实现Runnable重写run3.继承Thread重写run,通过匿名内部类来实现4. 实现Runnable重写run,通过匿名内部类来实现5.基于lambda表达式来创建 虚拟线程 并发编程: 通过写特殊的代码,把多个CPU核心都利…

Shell编程 --基础语法(1)

文章目录 Shell编程基础语法变量定义变量使用变量命令的使用只读变量删除变量 传递参数字符串获取字符串长度字符串截取 数组定义方式关联数组获取数组的长度 总结 Shell编程 Shell是一种程序设计语言。作为命令语言,它交互式解释和执行用户输入的命令或者自动地解…