上一篇文章我们了解了基础环境搭建,下面我们一起进入最简单的代码部分–你好,窗口(hello window)

本部分教程主要来自learn opengl cn (https://learnopengl-cn.github.io/)可参照学习

Let's begin

让我们试试能不能让GLFW正常工作。首先,新建一个HelloWindow.go文件,然后把下面的代码粘贴到该文件的最前面。

package main
import (
	"log"
	"runtime"
	"github.com/go-gl/gl/v3.3-core/gl"
	"github.com/go-gl/glfw/v3.2/glfw"
)

接下来我们创建main函数,在这个函数中我们将会实例化GLFW窗口:

func main() {
    runtime.LockOSThread()
 
	if err := glfw.Init(); err != nil {
        panic(err)
    }
	// 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使用

首先,我们在main函数中调用glfw.Init函数来初始化GLFW,然后我们可以使用glfw.WindowHint函数来配置GLFW。glfw.WindowHint函数的第一个参数代表选项的名称,我们可以从很多以GLFW_开头的枚举值中选择;第二个参数接受一个整型,用来设置这个选项的值。该函数的所有的选项以及对应的值都可以在 GLFW’s window handling 这篇文档中找到。

由于本站的教程都是基于OpenGL 3.3版本展开讨论的,所以我们需要告诉GLFW我们要使用的OpenGL版本是3.3,这样GLFW会在创建OpenGL上下文时做出适当的调整。这也可以确保用户在没有适当的OpenGL版本支持的情况下无法运行。我们将主版本号(Major)和次版本号(Minor)都设为3。我们同样明确告诉GLFW我们使用的是核心模式(Core-profile)。明确告诉GLFW我们需要使用核心模式意味着我们只能使用OpenGL功能的一个子集(没有我们已不再需要的向后兼容特性)。如果使用的是Mac OS X系统,你还需要加下面这行代码到你的初始化代码中这些配置才能起作用(将上面的代码解除注释):

glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True) //Mac使用

接下来我们创建一个窗口对象,这个窗口对象存放了所有和窗口相关的数据,而且会被GLFW的其他函数频繁地用到。

	window, err := glfw.CreateWindow(800, 600, "LearnOpenGL", nil, nil)
	log.Println("created window")
 
	if  window == nil || err!= nil {
		panic(err)
	}

	window.MakeContextCurrent()//通知glfw将当前窗口上下文设置为线程主上下文

glfw.CreateWindow函数需要窗口的宽和高作为它的前两个参数。第三个参数表示这个窗口的名称(标题),这里我们使用`LearnOpenGL`,当然你也可以使用你喜欢的名称。最后两个参数我们暂时忽略。这个函数将会返回一个GLFWwindow对象,我们会在其它的GLFW操作中使用到。创建完窗口我们就可以通知GLFW将我们窗口的上下文设置为当前线程的主上下文了。

视口

在我们开始渲染之前还有一件重要的事情要做,我们必须告诉OpenGL渲染窗口的尺寸大小,即视口(Viewport),这样OpenGL才只能知道怎样根据窗口大小显示数据和坐标。我们可以通过调用glViewport函数来设置窗口的维度(Dimension):

gl.Viewport(0, 0, 800, 600)

glViewport函数前两个参数控制窗口左下角的位置。第三个和第四个参数控制渲染窗口的宽度和高度(像素)。

我们实际上也可以将视口的维度设置为比GLFW的维度小,这样子之后所有的OpenGL渲染将会在一个更小的窗口中显示,这样子的话我们也可以将一些其它元素显示在OpenGL视口之外。

然而,当用户改变窗口的大小的时候,视口也应该被调整。我们可以对窗口注册一个回调函数(Callback Function),它会在每次窗口大小被调整的时候被调用。

window.SetFramebufferSizeCallback(framebuffer_size_callback)//主流程中设置回调函数
 
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))
}

准备好你的引擎

我们可不希望只绘制一个图像之后我们的应用程序就立即退出并关闭窗口。我们希望程序在我们主动关闭它之前不断绘制图像并能够接受用户输入。因此,我们需要在程序中添加一个while循环,我们可以把它称之为渲染循环(Render Loop),它能在我们让GLFW退出前一直保持运行。下面几行的代码就实现了一个简单的渲染循环:

        //渲染循环
	for !window.ShouldClose(){
		//用户输入
		processInput(window)
 
		//渲染
		gl.ClearColor(0.2, 0.3, 0.3, 1.0)//状态设置
		gl.Clear(gl.COLOR_BUFFER_BIT)//状态使用
 
		//检查调用事件,交换缓冲
		glfw.PollEvents()    
		window.SwapBuffers()
	}
  • glfwWindowShouldClose函数在我们每次循环的开始前检查一次GLFW是否被要求退出,如果是的话该函数返回true然后渲染循环便结束了,之后为我们就可以关闭应用程序了。
  • glfwPollEvents函数检查有没有触发什么事件(比如键盘输入、鼠标移动等)、更新窗口状态,并调用对应的回调函数(可以通过回调方法手动设置)。
  • glfwSwapBuffers函数会交换颜色缓冲(它是一个储存着GLFW窗口每一个像素颜色值的大缓冲),它在这一迭代中被用来绘制,并且将会作为输出显示在屏幕上。

最后一件事

当渲染循环结束后我们需要正确释放/删除之前的分配的所有资源。我们可以在main函数的最后调用glfwTerminate函数来完成。在go语言中,我们通过defer来最终执行这个函数。

defer glfw.Terminate()

这样便能清理所有的资源并正确地退出应用程序。现在你可以尝试编译并运行你的应用程序了,如果没做错的话,你将会看到如下的输出:

大功告成!

本文涉及到的go源文件: