go gl 彩色的三角形

2019-02-08  本文已影响0人  甚解_4703
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)
    }
}

上一篇下一篇

猜你喜欢

热点阅读