Golang之OpenGL(一)

news2024/9/22 20:35:31

使用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 openGL

import (
	"fmt"
	"github.com/go-gl/gl/v4.1-core/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
	"log"
	"runtime"
	"strings"
)

const (
	//定义画布参数
	width  = 600
	height = 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 int32
        gl.GetProgramiv(program, gl.LINK_STATUS, &status)
        if status == gl.FALSE {
            var logLength int32
            gl.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 2
	glfw.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 int32
	gl.GetShaderiv(shader, gl.COMPILE_STATUS, &status)
	if status == gl.FALSE {
		var logLength int32
		gl.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 410
	   in vec3 vp;
	   void main() {
	       gl_Position = vec4(vp, 1.0);
	   }

` + "\x00"  // GLSL 源代码  顶点着色器

	fragmentShaderSource = `
	   #version 410
	   out 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, // left
		0, -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 uint32
	gl.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 uint32
	gl.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 410
        in vec3 vp;
        in vec3 color; 
        out vec3 fragColor; 
        void main() {
            gl_Position = vec4(vp, 1.0);
            fragColor = color; 
        }
    ` + "\x00"  // GLSL 源代码  顶点着色器

	fragmentShaderSource = `
        #version 410
        in 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, // left
		0, -1, 0, // right
		-1, 0, 0, // top
		-1, -1, 0, // left
		0, -1, 0, // right
	}

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

	//使用时间信息来动态计算颜色。这里使用正弦和余弦函数来实现周期性的颜色变化效果。
	fragmentShaderSource = `
		#version 410
		out 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 410
        out 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.coloradmin.cn/o/1972219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【考研高数】反函数的那些事

在这里给同学们推荐来自「荒原之梦考研数学」网的三篇文章,分别是反函数的性质汇总,以及两道相关习题,有需要加强这部分练习的同学,可以长按下方二维码,在「荒原之梦考研数学」官网中打开对应的文章: 《反…

基于单片机的防火防盗报警系统设计

摘要: 该多功能防火防盗系统既具有根据环境温度和烟雾浓度进行火灾检测的功能,也有能对人体检测实现防盗的功能。多功能智能防火防盗控制系统的主控制器是 STC89C52 单片机,环境温度的检测采用 DS18B20 , MQ2 检测烟雾浓度&…

利用canvas 实现图片的标注,把标注像素点传入到后端

背景:我们有一个摄像的产品,拍照传统的水表盘面,我们需要框选水表读数,标注点传到后端,后端根据标注点自动去截取摄像表拍摄回来的图片,然后拿到大模型里面进行训练。由于同一只表拍摄的画面都是一样的&…

C语言之指针函数与函数指针

目录 1 前言2 函数指针与指针函数理解与区分函数指针指针函数 3 函数指针与指针函数常见用法函数指针指针函数 4 总结 1 前言 项目中时常遇到指正函数与函数指正的使用,时间一长容易出现概念混淆。 2 函数指针与指针函数理解与区分 函数指针 原型:返回…

React 项目中如何使用 easyPlayer-pro.js

目录 背景EasyPlayer.js H5播放器简单介绍EasyPlayer.js 简介EasyPlayer.js 功能说明:配置属性事件回调方法 下载 EasyPlayer.js引入使用重写webpack问题处理证清白最后 背景 项目中要使用 easyplayer-pro.js 播放视频,查了下资料,网上基本都…

Axure在数据可视化原型设计中的革新力量

在数据洪流与信息爆炸的当下,产品设计不再局限于界面的美观与功能的堆砌,而是更多地聚焦于如何高效地呈现与解读数据。Axure RP,作为原型设计领域的璀璨明星,正以其独特的魅力,引领着数据可视化原型设计的新风尚。本文…

【云原生】数据库忘记密码怎么办?

相信很多人都会遇到在虚拟机中忘记数据库密码的情况,想必大家都很苦恼,所以今天给大家来讲讲数据库忘记密码了如何修改密码再登录数据库!!! 1、关闭数据库服务 systemctl stop mariadb 2、执行MySQL 服务器在启动时跳…

【LeetCode】146.LRU页面置换

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; &#x1f525;c系列专栏&#xff1a;C/C零基础到精通 &#x1f525; 给大…

C++ string类(你想要的这里都有)

1. string类概述 C语言中&#xff0c;字符串是以“\0”结尾的一些字符的集合&#xff0c;为了操作方便&#xff0c;C标准库中提供了一些str系列的库函数&#xff0c;但是这些库函数与字符串是分离开的&#xff0c;不太符合OOP的思想&#xff0c;而且底层空间需要用户管理&#…

Javaweb项目|ssm基于web的健身中心管理系统的的设计与实现jsp

收藏点赞不迷路 关注作者有好处 文末获取源码 一、系统展示 二、万字文档展示 基于ssm基于web的健身中心管理系统的的设计与实现jsp 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringSpringMVCMyBatisVue 工具&#xff1a;IDEA/Ecilpse、Navicat、…

HTTP常见的请求方法、响应状态码、接口规范介绍

常见的请求方法 GET&#xff08;查询&#xff0c;从服务器获取资源&#xff09;POST&#xff08;新增&#xff0c;在服务器创建资源&#xff09;PUT&#xff08;修改&#xff0c;在服务器更新资源&#xff09;DELETE&#xff08;删除&#xff0c;从服务器删除资源&#xff09;…

跟《经济学人》学英文:2024年07月27日这期 AI firms will soon exhaust most of the internet’s data

AI firms will soon exhaust most of the internet’s data Can they create more? 原文&#xff1a; In 2006 fei-fei li, then at the University of Illinois, now at Stanford University, saw how mining the internet might help to transform AI research. Linguis…

商场购物中心营销怎么玩?附230个参考案例

随着消费市场的不断演变&#xff0c;商场购物中心正面临着前所未有的竞争压力。如何在众多竞争对手中脱颖而出&#xff0c;吸引消费者的目光&#xff1f; 今天道叔将探讨商场购物中心营销的新玩法&#xff0c;帮助您在激烈的市场竞争中占据一席之地。 码字不易&#xff0c;如…

第三方库认识- Mysql 数据库 API 认识

文章目录 一、msyql数据库API接口1.初始化mysql_init()——mysql_init2.链接数据库mysql_real_connect——mysql_real_connect3.设置当前客户端的字符集——mysql_set_character_set4.选择操作的数据库——mysql_select_db5.执行sql语句——mysql_query6.保存查询结果到本地——…

配置frp实现内网穿透(.toml配置文件)

简介 frp 是一款高性能的反向代理应用&#xff0c;专注于内网穿透。它支持多种协议&#xff0c;包括 TCP、UDP、HTTP、HTTPS 等&#xff0c;并且具备 P2P 通信功能。使用 frp&#xff0c;您可以安全、便捷地将内网服务暴露到公网&#xff0c;通过拥有公网 IP 的节点进行中转。…

软件测试---禅道

一、禅道简介 二、安装 三、新手引导 &#xff08;1&#xff09;在系统创建一个新的用户帐号&#xff1a; &#xff08;2&#xff09;在系统创建一个新的项目集&#xff1a; &#xff08;3&#xff09;在系统创建一个新的产品&#xff1a; &#xff08;4&#xff09;在系统创…

【AI学习】[2024北京智源大会]具身智能:面向通用机器人的具身多模态大模型系统

面向通用机器人的具身多模态大模型系统 王 鹤 | 北京大学助理教授&#xff0c;智源学者 边听边做一些记录 一、通用机器人的概念和发展趋势&#xff0c;以及实现通用机器人的基石层、大脑和小脑模型等方面的思考和探索。 主要观点&#xff1a;人形机器人&#xff0c;是未来…

基于SpringBoot+Vue的校园便利平台(带1w+文档)

基于SpringBootVue的校园便利平台(带1w文档) 基于SpringBootVue的校园便利平台(带1w文档) 本平台采用B/S架构、采用的数据库是MySQL&#xff0c;使用JAVA技术开发。该平台的开发方式无论在国内还是国外都比较常见&#xff0c;而且开发完成后使用普遍&#xff0c;可以给平台用户…

多址技术(FDMA,TDMA,CDMA,帧,时隙)(通俗易懂)

多址技术是一种区分用户的技术。 举个例子&#xff0c;一个基站发出信息&#xff0c;如何确定是发给谁的&#xff1f; 这个技术就是解决这个问题的。 多址技术常见的有三种&#xff1a; 频分多址&#xff08;FDMA&#xff09;、时分多址&#xff08;TDMA&#xff09;、码分…

程序员学长 | 快速学习一个算法,UNet

本文来源公众号“程序员学长”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;快速学习一个算法&#xff0c;UNet 今天给大家分享一个超强的算法模型&#xff0c;UNet UNet 是一种卷积神经网络架构&#xff0c;最初由 Olaf Ronne…