Skip to content

Instantly share code, notes, and snippets.

@alsritter
Last active January 3, 2022 06:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alsritter/1dce8f568cfcb1b22dfa4aa1a4f58fcd to your computer and use it in GitHub Desktop.
Save alsritter/1dce8f568cfcb1b22dfa4aa1a4f58fcd to your computer and use it in GitHub Desktop.
Learn OpenGL 02
package main
import (
"fmt"
"log"
"runtime"
"strings"
"github.com/go-gl/gl/v3.3-core/gl"
"github.com/go-gl/glfw/v3.2/glfw"
)
const (
WIDTH = 500
HEIGHT = 500
)
var (
square = []float32{
0.5, 0.5, 0.0, // 右上角
0.5, -0.5, 0.0, // 右下角
-0.5, -0.5, 0.0, // 左下角
-0.5, 0.5, 0.0, // 左上角
}
indices = []uint32{ // 注意索引从0开始!
0, 1, 3, // 第一个三角形
1, 2, 3, // 第二个三角形
}
)
const vertexShaderSource = `
#version 330 core
layout (location = 0) in vec3 aPos;
void main()
{
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
`
const fragmentShaderSource = `
#version 330 core
out vec4 FragColor;
void main()
{
FragColor = vec4(1.0, 0.5, 0.2, 1.0);
}
`
func main() {
runtime.LockOSThread()
window := initGlfw()
defer glfw.Terminate()
program := initOpenGL()
// 必须告诉 OpenGL 渲染窗口的尺寸大小,即视口(Viewport),这样 OpenGL 才只能知道怎样根据窗口大小显示数据和坐标。
gl.Viewport(0, 0, WIDTH, HEIGHT) // 起点为左下角
// 窗口大小被改变的回调函数
window.SetFramebufferSizeCallback(framebuffer_size_callback)
vao, vbo, ebo := makeVao(square, indices)
gl.PolygonMode(gl.FRONT_AND_BACK, gl.FILL) // LINE 是线框模式
//渲染循环
for !window.ShouldClose() {
//用户输入
processInput(window)
draw(vao, window, program)
}
gl.DeleteVertexArrays(1, &vao)
gl.DeleteBuffers(1, &vbo)
gl.DeleteBuffers(1, &ebo)
gl.DeleteProgram(program)
}
func draw(vao uint32, window *glfw.Window, program uint32) {
gl.ClearColor(0.2, 0.3, 0.3, 1.0) //状态设置
gl.Clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
gl.UseProgram(program)
// 绑定对应的 VAO 并绘制图形
gl.BindVertexArray(vao)
// glDrawElements 的第二个参数是count
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
// gl.DrawArrays(gl.TRIANGLES, 0, int32(len(square)/3))
gl.BindVertexArray(0)
glfw.PollEvents()
window.SwapBuffers()
}
// initGlfw initializes glfw and returns a Window to use.
func initGlfw() *glfw.Window {
if err := glfw.Init(); err != nil {
panic(err)
}
// 通过这些枚举值来设置 GLFW 的参数
glfw.WindowHint(glfw.Resizable, glfw.False) // 设置窗口大小无法修改
glfw.WindowHint(glfw.ContextVersionMajor, 3) // OpenGL最大版本
glfw.WindowHint(glfw.ContextVersionMinor, 3) // OpenGl 最小版本
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile) // 明确核心模式
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) // Mac使用时需要加上
window, err := glfw.CreateWindow(WIDTH, HEIGHT, "LearnOpenGL", nil, nil)
log.Println("created window")
if window == nil || err != nil {
panic(err)
}
window.MakeContextCurrent() // 通知 glfw 将当前窗口上下文绑定到当前线程的上下文
return window
}
// initOpenGL initializes OpenGL and returns an intiialized program.
func initOpenGL() uint32 {
if err := gl.Init(); err != nil {
panic(err)
}
version := gl.GoStr(gl.GetString(gl.VERSION))
log.Println("OpenGL version", version)
vertexShader, err := compileShader(vertexShaderSource, gl.VERTEX_SHADER)
if err != nil {
panic(err)
}
fragmentShader, err := compileShader(fragmentShaderSource, gl.FRAGMENT_SHADER)
if err != nil {
panic(err)
}
prog := gl.CreateProgram()
gl.AttachShader(prog, vertexShader)
gl.AttachShader(prog, fragmentShader)
gl.LinkProgram(prog)
return prog
}
// makeVao initializes and returns a vertex array from the points provided.
func makeVao(points []float32, idxs []uint32) (uint32, uint32, uint32) {
var vbo, ebo, vao uint32
gl.GenBuffers(1, &vbo)
gl.GenBuffers(1, &ebo)
gl.GenVertexArrays(1, &vao)
// 1. 绑定顶点数组对象
gl.BindVertexArray(vao)
// 2. 把我们的顶点数组复制到一个顶点缓冲中,供 OpenGL 使用
gl.BindBuffer(gl.ARRAY_BUFFER, vbo)
gl.BufferData(gl.ARRAY_BUFFER, 4*len(points), gl.Ptr(points), gl.STATIC_DRAW)
// 3. 复制我们的索引数组到一个索引缓冲中,供 OpenGL 使用
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, ebo)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, 4*len(idxs), gl.Ptr(idxs), gl.STATIC_DRAW)
// 4. 设定顶点属性指针
gl.VertexAttribPointer(0, 3, gl.FLOAT, false, 0, nil)
gl.EnableVertexAttribArray(0)
return vao, vbo, ebo
}
func compileShader(source string, shaderType uint32) (uint32, error) {
shader := gl.CreateShader(shaderType)
csources, free := gl.Strs(source + "\x00")
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)
// 将某个字符串重复我们可以通过 for 循环 加上字符串拼接来实现。但 Golang 中,提供了一个更简便的方法,即 Strings.Repeat() 。
// \x00 表示 ascii 码为 0 的字符,这里用来表示字符串的结尾
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
}
// 监听进程输入
func processInput(window *glfw.Window) {
if window.GetKey(glfw.KeyEscape) == glfw.Press {
log.Println("escape pressed")
window.SetShouldClose(true)
}
}
// 在调整指定窗口的帧缓冲区大小时调用。
func framebuffer_size_callback(window *glfw.Window, width int, height int) {
log.Printf("resize width:%d, height:%d", width, height)
gl.Viewport(0, 0, int32(width), int32(height))
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment