go gl 彩色的三角形

go 彩色三角形
之前在網上想找一個能渲染顏色的go gl圖形程式設計例子,,找了半天都是白色的三角形。。。於是自己研究了半天,大概是研究出來的樣子,記錄到這裡來分享一下。
作者用的是mac開發的,windows的go gl需要麻煩一點的操作,讀者自行裁決吧。
配置
1. go下載
2.配置mac go環境請自行搜尋一下。
3.IDE。作者用的是 goland ,破解的話,也請自行搜尋一下。
go開發包
由於國內的牆比較嚴重,建議用github上的映象下載,然後本地配置一下。

藉助github的golang下載
下載下來之後把包拖動到指定的目錄,比如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) } }