之前在网上想找一个能渲染颜色的go gl图形编程例子,,找了半天都是白色的三角形。。。于是自己研究了半天,大概是研究出来的样子,记录到这里来分享一下。
作者用的是mac开发的,windows的go gl需要麻烦一点的操作,读者自行裁决吧。
配置
1.go下载
2.配置mac go环境请自行搜索一下。
3.IDE。作者用的是goland,破解的话,也请自行搜索一下。
go开发包
由于国内的墙比较严重,建议用github上的镜像下载,然后本地配置一下。
下载下来之后把包拖动到指定的目录,比如golang.org中:
核心开发包
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
go get github.com/go-gl
开发
新建一个go项目。
初始化调用。
func init() {
// This is needed to arrange that main() runs on main thread.
// See documentation for functions that are only allowed to be called from the main thread.
runtime.LockOSThread()
}
初始化我们的gl窗口
// initGlfwTest 初始化 glfw 并且返回一个可用的窗口。
func initGlfwTest() *glfw.Window {
if err := glfw.Init(); err != nil {
panic(err)
}
glfw.WindowHint(glfw.Resizable, glfw.False)
glfw.WindowHint(glfw.ContextVersionMajor, 4) // OR 2
glfw.WindowHint(glfw.ContextVersionMinor, 1)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
window, err := glfw.CreateWindow(widthTest, heightTest, "Test", nil, nil)
if err != nil {
panic(err)
}
window.MakeContextCurrent()
return window
}
初始化我们的opengl
// initOpenGLTest 初始化 OpenGL 并且返回一个初始化了的程序。
func initOpenGLTest() uint32 {
if err := gl.Init(); err != nil {
panic(err)
}
version := gl.GoStr(gl.GetString(gl.VERSION))
log.Println("OpenGL version", version)
vertexShader, err := compileShaderTest(vertexShaderSourceTest, gl.VERTEX_SHADER)
if err != nil {
panic(err)
}
fragmentShader, err := compileShaderTest(fragmentShaderSourceTest, gl.FRAGMENT_SHADER)
if err != nil {
panic(err)
}
prog := gl.CreateProgram()
gl.AttachShader(prog, vertexShader)
gl.AttachShader(prog, fragmentShader)
gl.LinkProgram(prog)
return prog
}
编译着色器的代码:
func compileShaderTest(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 (
widthTest = 1280
heightTest = 720
vertexShaderSourceTest = `
#version 410
in vec3 vp;
in vec4 vs_color;
out vec4 fs_color; //传给片段着色器
void main() {
fs_color = vs_color;
gl_Position = vec4(vp, 1.0);
}
` + "\x00"
fragmentShaderSourceTest = `
#version 410
in vec4 fs_color;//从定点着色器过来的值
out vec4 frag_colour;
void main() {
//frag_colour = vec4(1, 1, 1, 1);
frag_colour = fs_color;
}
` + "\x00"
)
网上的教程都是直接用了:
frag_colour = vec4(1, 1, 1, 1);
上面这样写只能渲染出一个白色的三角形。它不能读取我们输入的颜色,当然你可以改写这颜色值,得到不一样的颜色,但是跟我们的五彩斑斓的三角形还是不一样的。
这样就要求我们传入的顶点需要包含颜色值。
定义顶点
var (
vertexPosColor = []float32{
0, 0.5, 0, 1.0, 0.0, 0.0, 1.0, // top
-0.5, -0.5, 0, 0.0, 1.0, 0.0, 1.0, // left
0.5, -0.5, 0, 0.0, 0.0, 1.0, 1.0, // right
}
)
前面3个值就是定点的x,y,z,后面4个就是颜色值啦~,对应rgba
创建我们的VBO(顶点缓存对象),VAO(顶点数组对象)
func makeVboVao() (uint32, uint32) {
var vbo uint32
gl.GenBuffers(1, &vbo)
var vao uint32
gl.GenVertexArrays(1, &vao)
return vbo, vao
}
绑定VBO,VAO
// makeVaoTest2 把我们的顶点数据推入到显卡中
func makeVaoTest2(points []float32, vbo, vao, verPos, verColor uint32) {
singleBytes := int(unsafe.Sizeof(points[0]))
//var vbo uint32
//gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, singleBytes*len(points), gl.Ptr(points), gl.STATIC_DRAW)
//fmt.Println("makeVaoTest=",unsafe.Sizeof(points[0]))
//var vao uint32
//gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
gl.VertexAttribPointer(verPos, 3, gl.FLOAT, false, int32(singleBytes)*7, nil)
gl.EnableVertexAttribArray(verPos)
gl.VertexAttribPointer(verColor, 4, gl.FLOAT, false, int32(singleBytes)*7, gl.PtrOffset(singleBytes*3))
gl.EnableVertexAttribArray(verColor)
}
最后是我们的主函数
func main() {
//init() //init是不会被申明的,所以这里调用会报错
window := initGlfwTest()
defer glfw.Terminate()
program := initOpenGLTest()
//这里是去获取到我们的着色器中顶点位置 vp
attVertex := uint32(gl.GetAttribLocation(program, gl.Str("vp\x00")))
//获取我们的顶点着色器中顶点颜色 vs_color
attColor := uint32(gl.GetAttribLocation(program, gl.Str("vs_color\x00")))
fmt.Println("main 1 =", attVertex, attColor)
//vaoVertex := makeVaoTest(triangle, attVertex, 3)
//makeVaoTest(triangle, attVertex, 3)
//vaoColor := makeVaoTest(vertexColor, attColor, 4)
//makeVaoTest(vertexColor, attVertex, 4)
//fmt.Println("main 2 =", vaoVertex, vaoColor)
//vaoVertex := makeVaoTest(triangle, 3)
vbo, vao := makeVboVao()
//gl.BindBuffer(gl.ARRAY_BUFFER, vaoVertex)
//gl.EnableVertexAttribArray(attVertex)
//gl.VertexAttribPointer(attVertex, 3, gl.FLOAT, false, 0, nil)
var step float32 = 0.01
var nowUnix = time.Now().UnixNano() / 1000000
for !window.ShouldClose() {
makeVaoTest2(vertexPosColor, vbo, vao, attVertex, attColor)
drawTest(window, program)
//让我们的顶点动起来
vertexPosColor[0] += step
if vertexPosColor[0] > 1.0 {
step = -0.01
} else if vertexPosColor[0] < -1.0 {
step = 0.01
}
//计算fps
preUnix := nowUnix
nowUnix = time.Now().UnixNano() / 1000000
var fps = fmt.Sprintf("FPS:%.2v", 1000.0/(nowUnix-preUnix))
fmt.Println(fps)
//显示在窗口的title中
window.SetTitle(fps)
}
}
最后附上项目的完整代码:
package main
import (
"fmt"
"github.com/go-gl/gl/v4.1-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
"log"
"runtime"
"strings"
"time"
"unsafe"
)
const (
widthTest = 1280
heightTest = 720
vertexShaderSourceTest = `
#version 410
in vec3 vp;
in vec4 vs_color;
out vec4 fs_color; //传给片段着色器
void main() {
fs_color = vs_color;
gl_Position = vec4(vp, 1.0);
}
` + "\x00"
fragmentShaderSourceTest = `
#version 410
in vec4 fs_color;//从定点着色器过来的值
out vec4 frag_colour;
void main() {
//frag_colour = vec4(1, 1, 1, 1);
frag_colour = fs_color;
}
` + "\x00"
)
//init 函数
/*
为了使用导入的包,首先必须将其初始化。初始化总是以单线程执行,并且按照包的依赖关系顺序执行。这通过Golang的运行时系统控制,如所示:
初始化导入的包(递归导入)
对包块中声明的变量进行计算和分配初始值
执行包中的init函数
init()函数会在每个包完成初始化后自动执行,并且执行优先级比main函数高。init 函数通常被用来:
对变量进行初始化
检查/修复程序的状态
注册
运行一次计算
*/
func init() {
// This is needed to arrange that main() runs on main thread.
// See documentation for functions that are only allowed to be called from the main thread.
runtime.LockOSThread()
}
// initGlfwTest 初始化 glfw 并且返回一个可用的窗口。
func initGlfwTest() *glfw.Window {
if err := glfw.Init(); err != nil {
panic(err)
}
glfw.WindowHint(glfw.Resizable, glfw.False)
glfw.WindowHint(glfw.ContextVersionMajor, 4) // OR 2
glfw.WindowHint(glfw.ContextVersionMinor, 1)
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
window, err := glfw.CreateWindow(widthTest, heightTest, "Test", nil, nil)
if err != nil {
panic(err)
}
window.MakeContextCurrent()
return window
}
// initOpenGLTest 初始化 OpenGL 并且返回一个初始化了的程序。
func initOpenGLTest() uint32 {
if err := gl.Init(); err != nil {
panic(err)
}
version := gl.GoStr(gl.GetString(gl.VERSION))
log.Println("OpenGL version", version)
vertexShader, err := compileShaderTest(vertexShaderSourceTest, gl.VERTEX_SHADER)
if err != nil {
panic(err)
}
fragmentShader, err := compileShaderTest(fragmentShaderSourceTest, gl.FRAGMENT_SHADER)
if err != nil {
panic(err)
}
prog := gl.CreateProgram()
gl.AttachShader(prog, vertexShader)
gl.AttachShader(prog, fragmentShader)
gl.LinkProgram(prog)
return prog
}
var (
triangle = []float32{
0, 0.5, 0, // top
-0.5, -0.5, 0, // left
0.5, -0.5, 0, // right
}
// 保存顶点的颜色情报的数组
vertexColor = []float32{
1.0, 0.0, 0.0, 1.0,
0.0, 1.0, 0.0, 1.0,
0.0, 0.0, 1.0, 1.0,
}
vertexPosColor = []float32{
0, 0.5, 0, 1.0, 0.0, 0.0, 1.0, // top
-0.5, -0.5, 0, 0.0, 1.0, 0.0, 1.0, // left
0.5, -0.5, 0, 0.0, 0.0, 1.0, 1.0, // right
}
)
// makeVaoTest 执行初始化并从提供的点里面返回一个顶点数组
func makeVaoTest(points []float32, index, size int32) uint32 {
var vbo uint32
gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, int(unsafe.Sizeof(points[0]))*len(points), gl.Ptr(points), gl.STATIC_DRAW)
//fmt.Println("makeVaoTest=",unsafe.Sizeof(points[0]))
var vao uint32
gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao)
gl.EnableVertexAttribArray(uint32(index))
gl.VertexAttribPointer(uint32(index), size, gl.FLOAT, false, 0, nil)
return vao
}
func makeVboVao() (uint32, uint32) {
var vbo uint32
gl.GenBuffers(1, &vbo)
var vao uint32
gl.GenVertexArrays(1, &vao)
return vbo, vao
}
// makeVaoTest2 把我们的顶点数据推入到显卡中
func makeVaoTest2(points []float32, vbo, vao, verPos, verColor uint32) {
singleBytes := int(unsafe.Sizeof(points[0]))
//var vbo uint32
//gl.GenBuffers(1, &vbo)
gl.BindBuffer(gl.ARRAY_BUFFER, vbo) //绑定顶点缓存对象
gl.BufferData(gl.ARRAY_BUFFER, singleBytes*len(points), gl.Ptr(points), gl.STATIC_DRAW) //把顶点数据推入到缓存
//fmt.Println("makeVaoTest=",unsafe.Sizeof(points[0]))
//var vao uint32
//gl.GenVertexArrays(1, &vao)
gl.BindVertexArray(vao) //绑定到我们的顶点数组对象
//3表示我们的顶点只有3个float,然后一个顶点的大小是 8字节*7(3维顶点,4维颜色)
gl.VertexAttribPointer(verPos, 3, gl.FLOAT, false, int32(singleBytes)*7, nil)
gl.EnableVertexAttribArray(verPos)
//4表示我们的顶点只有4个float,然后一个顶点的大小是 8字节*7(3维顶点,4维颜色)
gl.VertexAttribPointer(verColor, 4, gl.FLOAT, false, int32(singleBytes)*7, gl.PtrOffset(singleBytes*3))
gl.EnableVertexAttribArray(verColor)
}
func compileShaderTest(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
}
//这里我们调用了 `makeVaoTest` ,从我们之前定义的 `triangle` 顶点中获得 `vao` 引用,将它作为一个新的参数传递给 `drawTest` 函数:
func drawTest(window *glfw.Window, program uint32) {
//清除画布颜色缓存和深度缓存
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
//使用什么着色器
gl.UseProgram(program)
//0是指从顶点buffer的什么位置开始,count表示我们画3个顶点
gl.DrawArrays(gl.TRIANGLES, 0, 3)
//传递事件
glfw.PollEvents()
//交换缓存到显卡
window.SwapBuffers()
}
func main() {
//init() //init是不会被申明的,所以这里调用会报错
window := initGlfwTest()
defer glfw.Terminate()
program := initOpenGLTest()
//这里是去获取到我们的着色器中顶点位置 vp
attVertex := uint32(gl.GetAttribLocation(program, gl.Str("vp\x00")))
//获取我们的顶点着色器中顶点颜色 vs_color
attColor := uint32(gl.GetAttribLocation(program, gl.Str("vs_color\x00")))
fmt.Println("main 1 =", attVertex, attColor)
//vaoVertex := makeVaoTest(triangle, attVertex, 3)
//makeVaoTest(triangle, attVertex, 3)
//vaoColor := makeVaoTest(vertexColor, attColor, 4)
//makeVaoTest(vertexColor, attVertex, 4)
//fmt.Println("main 2 =", vaoVertex, vaoColor)
//vaoVertex := makeVaoTest(triangle, 3)
vbo, vao := makeVboVao()
//gl.BindBuffer(gl.ARRAY_BUFFER, vaoVertex)
//gl.EnableVertexAttribArray(attVertex)
//gl.VertexAttribPointer(attVertex, 3, gl.FLOAT, false, 0, nil)
var step float32 = 0.01
var nowUnix = time.Now().UnixNano() / 1000000
for !window.ShouldClose() {
makeVaoTest2(vertexPosColor, vbo, vao, attVertex, attColor)
drawTest(window, program)
//让我们的顶点动起来
vertexPosColor[0] += step
if vertexPosColor[0] > 1.0 {
step = -0.01
} else if vertexPosColor[0] < -1.0 {
step = 0.01
}
//计算fps
preUnix := nowUnix
nowUnix = time.Now().UnixNano() / 1000000
var fps = fmt.Sprintf("FPS:%.2v", 1000.0/(nowUnix-preUnix))
fmt.Println(fps)
//显示在窗口的title中
window.SetTitle(fps)
}
}
有疑问加站长微信联系(非本文作者)