学习OpenGL的原因

学习OpenGL底层API的念头已经在我心里徘徊很久,现在似乎到了可以付诸实施的时候。乘着编译freeglut的间隙,随便写点东西。

make_freeglut

2015年刚开始接触3D游戏开发时,我对OGL、D3D这种底层API是十分畏惧的。熟悉我的朋友知道,这两年我一直在学习jMonkeyEngine,一款纯Java的3D游戏引擎。但事实上,我接触3D编程的第一本书,是号称“龙书”的《DirectX 9.0 3D游戏开发编程基础》。

d3d9

由于我当时完全不懂计算机图形学、3D数学等基础知识,在学完龙书的前6章后,我就放弃了继续学习D3D的想法。对于当时的我来说,D3D9的API太过底层,我完全不理解画一个三角形为什么要调用那么多函数。那是我第一次尝试编写的3D程序,最终结果只是一个有贴图的方块。这些代码现在还保存在我硬盘的某个角落里。

d3d9_chapter6

相比于直接学习使用API来编程,我更希望能够先建立起一个知识体系,因为我在3D领域完全是一张白纸。所以我暂停了D3D的学习,转而调研3D游戏开发的各个方面:

  • 3D游戏是怎么制作出来的?
  • 制作一款3D游戏,需要哪些专业人员合作,他们在团队中各自承担什么职责?
  • 假如我想成为团队中的某个角色,我要如何学习才能具备足够的技能?
  • 游戏从业者都使用什么工具来提升自己的工作效率?

感谢搜索引擎。在经过几天的调研后,我看不懂的东西越来越多了。虽然每天都被未知的东西冲击着,但却感觉逐渐打开了思路。因为我从什么都不知道,变成了知道自己不知道什么。在调研的过程中,我整理了一份3D游戏术语。虽然内容不多,但也够我琢磨好久了。

在稍后的几天中,我决定先给自己弄一个Road Map,把各种不明觉厉的东西都分门别类放进去,然后逐一搞明白它们到底是干嘛的。对于一个纯粹的门外汉来说,给未知的知识分类是一个十分具有挑战性的工作。因此我并没有太纠结于分类是否合理,甚至也不追求术语的准确性(比如那时我把shader叫做“染色器”),只要它能够起到索引的作用就行。

我给这个东西命名为“游戏开发入门”路线图。

游戏开发入门

这件事完成后,我开始琢磨从哪里入手学习比较合适。俗话说,“不怕千招会,就怕一招精”。虽然很多大佬推荐我学习U3D或者UE4,但作为一个Java程序员,我认为用Java开发的3D游戏引擎更适合我。于是我选择学习jMonkeyEngine,一学就是两年。

在这里我想感谢推荐我学U3D和UE4的大佬,他们的建议都是非常中肯的。如果一开始就从U3D上手,想必我不会走那么多弯路,可能今天我已经得到某个游戏公司/VR公司的Offer了。但我对自己的选择并不后悔,因为当时我的目的非常明确,就是想搞明白自己列出来的那些术语到底是什么意思。我对自己的定位始终都是一名Java工程师,因此没有进入游戏公司我并不感到遗憾。

在接下来的两年中,我陆陆续续完成了自己当初的目标。我重新系统地学习了线性代数、计算机图形学,复习了中学物理;使用Java从头到尾实现了一个软光栅渲染器,实现了3D数学库、渲染管线、纹理采样、场景图、着色器;学习了Blender、3DS Max等3D建模工具,能够制作简单的家具模型。时至今日,那个路线图中的所有术语我大概都搞明白了意思,而且对jMonkeyEngine的各种功能也相当熟悉了。为了验证自己对基本知识的掌握情况,我甚至敢写入门教程(虽然写得不怎么样就是了,毕竟自己还是个半吊子)。

到了这个阶段,有时我会尝试根据自己的需要,来对jMonkeyEngine这样的引擎做一些改动。比如jMonkeyEngine不支持OpenGL的computer shader,我想绕过引擎直接通过底层API来执行。这种动作必须得用到底层API,而我恰恰对OpenGL的API不熟。

由于jMonkeyEngine底层依赖LWJGL,因此我也花了一段时间去学习LWJGL和LWJGL3的API。怎么说呢,任何一个学过LWJGL/LWJGL3的人,可能都会有跟我一样的感觉:干嘛不直接去学glut/glfw呢?其实LWJGL就是对OpenGL的API做了一层Java封装。与其通过Java来学习LWJGL,还不如直接用C/C++学习OpenGL的API呢。

下面我贴两段代码,读者可以直观感受一下。

lwjgl:

private long window;

public void run() {

	if ( !glfwInit() )
		throw new IllegalStateException("Unable to initialize GLFW");
    
	window = glfwCreateWindow(300, 300, "Hello World!", NULL, NULL);
	if ( window == NULL )
		throw new RuntimeException("Failed to create the GLFW window");

	glfwMakeContextCurrent(window);

	glfwShowWindow(window);
    
	while ( !glfwWindowShouldClose(window) ) {
		glClear(GL_COLOR_BUFFER_BIT);
		glfwSwapBuffers(window);
		glfwPollEvents();
	}

	glfwFreeCallbacks(window);
	glfwDestroyWindow(window);

	glfwTerminate();
}

public static void main(String[] args) {
	new HelloWorld().run();
}

glfw:

#include <GLFW/glfw3.h> 
int main(void) {
	GLFWwindow* window;
	
	if (!glfwInit())
		return -1;
	
	window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
	
	if (!window) {
		glfwTerminate();
		return -1;
	}
	
	glfwMakeContextCurrent(window);

	while (!glfwWindowShouldClose(window)) {
		glClear(GL_COLOR_BUFFER_BIT);
		glfwSwapBuffers(window);
		glfwPollEvents();
	}
	
	glfwTerminate();
	return 0;
}

综上所述,这就是我接下来要学习OpenGL的原因了。

当然更主要的原因是,我感觉学习OpenGL很厉害。