用 opengl 写一个小游戏 (1)
本节代码 github
环境搭建
基本的环境搭建可以参考我之前的文章在 Eclipse或CLion 中集成 opengl 环境 (windows+mingw)
在这里我们还需要另外两个包,freetype 和 soil。
freetype
FreeType 库是一个完全免费(开源)的、高质量的且可移植的字体引擎,它提供统一的接口来访问多种字体格式文件。我们可以借助它实现文字渲染。可在此网站下载源代码或直接下载二进制文件。
soil
SOIL是简易OpenGL图像库(Simple OpenGL Image Library)的缩写,它支持大多数流行的图像格式,使用起来也很简单。它可以帮助我们在openGL项目中加载图片以实现纹理。这是SOIL库的主页。
glm
由于C/C++标准库中没有几何数学库,这样造成在开发一个三维系统之初往往都需要自行实现一个实用的几何数学库,这样太费时费力了。GLM的出现可以很好的解决这个问题。GLM设计上遵照OpenGL Shading Language风格,使用开放的MIT授权协议。会GLSL的人可以很快上手。因采用了数据结构与函数方法分离的方式,可以很容易扩充函数方法而不改变原文件(增加新的头文件即可,不过得在不同的头文件中找函数方法比较费力)。
下载地址
注意这个库不需要 link。只要要包含头文件即可
注意事项
注意 link 时的顺序,有些包的前后顺序不能倒置。我的顺序是
target_link_libraries(game glew32s glfw3 gdi32 freetype soil opengl32)
game 是游戏程序。后面是需要用到的包。
基本组件
在写游戏的过程中我们需要频繁的使用两个部分:shader 和 texture,即着色器和纹理。我们可以将其封装为两个类。
shader
.h 文件
#ifndef GAME_SHADER_H
#define GAME_SHADER_H
#include <string>
#define GLEW_STATIC
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/type_ptr.hpp>
class Shader{
public:
GLuint id;
//构造函数
Shader(){};
//使用此程序
Shader &use();
//编译 shader 代码, 可选的 geometry shader
void Compile(const GLchar *vertexSource, const GLchar *fragmentSource, const GLchar *geometrySource = nullptr);
//设置参数
void SetFloat (const GLchar *name, GLfloat value, GLboolean useShader = false);
void SetInteger (const GLchar *name, GLint value, GLboolean useShader = false);
void SetVector2f (const GLchar *name, GLfloat x, GLfloat y, GLboolean useShader = false);
void SetVector2f (const GLchar *name, const glm::vec2 &value, GLboolean useShader = false);
void SetVector3f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLboolean useShader = false);
void SetVector3f (const GLchar *name, const glm::vec3 &value, GLboolean useShader = false);
void SetVector4f (const GLchar *name, GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLboolean useShader = false);
void SetVector4f (const GLchar *name, const glm::vec4 &value, GLboolean useShader = false);
void SetMatrix4 (const GLchar *name, const glm::mat4 &matrix, GLboolean useShader = false);
private:
// 检查编译的错误
void checkCompileErrors(GLuint object, std::string type);
};
#endif //GAME_SHADER_H
.cpp 文件
#include "shader.h"
#include <iostream>
Shader &Shader::use()
{
glewExperimental = GL_TRUE;
glewInit();
glUseProgram(this->id);
return *this;
}
void Shader::Compile(const GLchar* vertexSource, const GLchar* fragmentSource, const GLchar* geometrySource)
{
GLuint sVertex, sFragment, gShader;
// Vertex Shader
sVertex = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(sVertex, 1, &vertexSource, NULL);
glCompileShader(sVertex);
checkCompileErrors(sVertex, "VERTEX");
// Fragment Shader
sFragment = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(sFragment, 1, &fragmentSource, NULL);
glCompileShader(sFragment);
checkCompileErrors(sFragment, "FRAGMENT");
// Geometry Shader (可选)
if (geometrySource != nullptr)
{
gShader = glCreateShader(GL_GEOMETRY_SHADER);
glShaderSource(gShader, 1, &geometrySource, NULL);
glCompileShader(gShader);
checkCompileErrors(gShader, "GEOMETRY");
}
// Shader Program
this->id = glCreateProgram();
glAttachShader(this->id, sVertex);
glAttachShader(this->id, sFragment);
if (geometrySource != nullptr)
glAttachShader(this->id, gShader);
glLinkProgram(this->id);
checkCompileErrors(this->id, "PROGRAM");
// 删除 shader 当 link 成功时
glDeleteShader(sVertex);
glDeleteShader(sFragment);
if (geometrySource != nullptr)
glDeleteShader(gShader);
}
void Shader::SetFloat(const GLchar *name, GLfloat value, GLboolean useShader)
{
if (useShader)
this->use();
glUniform1f(glGetUniformLocation(this->id, name), value);
}
void Shader::SetInteger(const GLchar *name, GLint value, GLboolean useShader)
{
if (useShader)
this->use();
glUniform1i(glGetUniformLocation(this->id, name), value);
}
void Shader::SetVector2f(