头文件:
Game:
#pragma once
#include <GLFW/glfw3.h>
// 代表了游戏的当前状态
enum GameState {
GAME_ACTIVE,
GAME_MENU,
GAME_WIN
};
class Game
{
public:
GameState state;
GLboolean keys[1024];
GLuint width, height;
Game(GLuint w, GLuint h);
~Game();
void Init();
void ProcessInput(GLfloat dt);
void Update(GLfloat dt);
void Render();
};
Shader:
#pragma once
//这里第一个包含glad/glad.h
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <string>
#include <fstream>
#include <sstream>
#include <iostream>
class Shader
{
public:
// State
GLuint ID;
// Constructor
Shader() { }
// Sets the current shader as active
Shader& Use();
void DelteShader();
// Compiles the shader from given source code
void Compile(const GLchar* vertexSource, const GLchar* fragmentSource, const GLchar* geometrySource = nullptr); // Note: geometry source code is optional
// Utility functions
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:
// Checks if compilation or linking failed and if so, print the error logs
void CheckCompileErrors(GLuint object, std::string type);
};
Texture2D:
#pragma once
#include <GLFW/glfw3.h>
class Texture2D
{
public:
// Holds the ID of the texture object, used for all texture operations to reference to this particlar texture
GLuint ID;
// Texture image dimensions
GLuint Width, Height; // Width and height of loaded image in pixels
// Texture Format
GLuint Internal_Format; // Format of texture object
GLuint Image_Format; // Format of loaded image
// Texture configuration
GLuint Wrap_S; // Wrapping mode on S axis
GLuint Wrap_T; // Wrapping mode on T axis
GLuint Filter_Min; // Filtering mode if texture pixels < screen pixels
GLuint Filter_Max; // Filtering mode if texture pixels > screen pixels
// Constructor (sets default texture modes)
Texture2D();
// Generates texture from image data
void Generate(GLuint width, GLuint height, unsigned char* data);
// Binds the texture as the current active GL_TEXTURE_2D texture object
void Bind() const;
};
ResourceManager:
#pragma once
#include <map>
//shader.h里包含的有glad.h,要放在前面,语言本身的头文件(比如map)不影响
#include "Shader.h"
#include <GLFW/glfw3.h>
#include "Texture2D.h"
class ResourceManager
{
public:
// Resource storage
static std::map<std::string, Shader> Shaders;
static std::map<std::string, Texture2D> Textures;
// Loads (and generates) a shader program from file loading vertex, fragment (and geometry) shader's source code. If gShaderFile is not nullptr, it also loads a geometry shader
static Shader& LoadShader(const GLchar* vShaderFile, const GLchar* fShaderFile, const GLchar* gShaderFile, std::string name);
// Retrieves a stored sader
static Shader& GetShader(std::string name);
// Loads (and generates) a texture from file
static Texture2D& LoadTexture(const GLchar* file, GLboolean alpha, std::string name);
// Retrieves a stored texture
static Texture2D& GetTexture(std::string name);
// Properly de-allocates all loaded resources
static void Clear();
private:
// Private constructor, that is we do not want any actual resource manager objects. Its members and functions should be publicly available (static).
ResourceManager() { }
// Loads and generates a shader from file
static Shader loadShaderFromFile(const GLchar* vShaderFile, const GLchar* fShaderFile, const GLchar* gShaderFile = nullptr);
// Loads a single texture from file
static Texture2D loadTextureFromFile(const GLchar* file, GLboolean alpha);
};
SpriteRenderer:
#pragma once
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include "Shader.h"
#include "Texture2D.h"
class SpriteRenderer
{
public:
SpriteRenderer(Shader& shader);
~SpriteRenderer();
void DrawSprite(Texture2D& texture, glm::vec2 position, glm::vec2 size = glm::vec2(10, 10), GLfloat rotate = 0.0f, glm::vec3 color = glm::vec3(1.0f));
private:
Shader shader;
GLuint quadVAO;
void InitRenderData();
};
CPP源码:
Program.cpp:
//确保#include <glad/glad.h>被第一个包含,不然报错,其他头文件需要也要第一个包含
#include "ResourceManager.h"
#include <GLFW/glfw3.h>
#include "Game.h"
void key_callback(GLFWwindow* window, int key, int scancode, int action, int code);
const GLuint SCREEN_WIDTH = 800;
const GLuint SCREEN_HEIGHT = 600;
Game breakout(SCREEN_WIDTH, SCREEN_HEIGHT);
int main(int argc, char* argv[])
{
// glfw: initialize and configure
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#ifdef __APPLE__
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
#endif
// glfw window creation
GLFWwindow* window = glfwCreateWindow(SCREEN_WIDTH, SCREEN_HEIGHT, "hehehe...", NULL, NULL);
if (window == NULL)
{
std::cout << "Failed to create GLFW window" << std::endl;
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
glfwSetKeyCallback(window, key_callback);
// tell GLFW to capture our mouse
//glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// glad: load all OpenGL function pointers
if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
{
std::cout << "Failed to initialize GLAD" << std::endl;
return -1;
}
// configure global opengl state
// -----------------------------
glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
//init game
breakout.Init();
// DeltaTime variables
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
// Start Game within Menu State
breakout.state = GAME_ACTIVE;
while (!glfwWindowShouldClose(window))
{
// Calculate delta time
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
glfwPollEvents();
// Manage user input
breakout.ProcessInput(deltaTime);
// Update Game state
breakout.Update(deltaTime);
// Render
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
breakout.Render();
glfwSwapBuffers(window);
}
// Delete all resources as loaded using the resource manager
ResourceManager::Clear();
glfwTerminate();
return 0;
}
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
// When a user presses the escape key, we set the WindowShouldClose property to true, closing the application
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if (key >= 0 && key < 1024)
{
if (action == GLFW_PRESS)
breakout.keys[key] = GL_TRUE;
else if (action == GLFW_RELEASE)
breakout.keys[key] = GL_FALSE;
}
}
Game.cpp:
//这里一样:ResourceManager.h(包含有Shader.h,shader.h包含的有glad.h)包含的有glad.h所以要放前面
#include "ResourceManager.h"
#include "Game.h"
#include "SpriteRenderer.h"
SpriteRenderer* renderer;
Game::Game(GLuint w, GLuint h) :state(GAME_ACTIVE), keys(), width(w), height(h)
{
}
Game::~Game()
{
}
void Game::Init()
{
//load shader
ResourceManager::LoadShader("Shaders/Breakout/Sprite.vs", "Shaders/Breakout/Sprite.fs", nullptr, "sprite");
//config shader
glm::mat4 projection = glm::ortho(0.0f, static_cast<GLfloat>(this->width), static_cast<GLfloat>(this->height), 0.0f, -1.0f, 1.0f);
ResourceManager::GetShader("sprite").Use().SetInteger("image", 0);
ResourceManager::GetShader("sprite").SetMatrix4("projection", projection);
//setup render control
renderer = new SpriteRenderer(ResourceManager::GetShader("sprite"));
//load texture
ResourceManager::LoadTexture("Images/Test.jpg", GL_TRUE, "face");
}
void Game::ProcessInput(GLfloat dt)
{
}
void Game::Update(GLfloat dt)
{
}
void Game::Render()
{
renderer->DrawSprite(ResourceManager::GetTexture("face"), glm::vec2(200, 200), glm::vec2(200, 200), 45.0f, glm::vec3(0.0f, 1.0f, 0.0f));
}
Shader.cpp:
#include "Shader.h"
//#include <iostream>
Shader& Shader::Use()
{
glUseProgram(this->ID);
return *this;
}
void Shader::DelteShader()
{
glDeleteShader(ID);
}
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");
// If geometry shader source code is given, also compile 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");
// Delete the shaders as they're linked into our program now and no longer necessery
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(const GLchar* name, GLfloat x, GLfloat y, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform2f(glGetUniformLocation(this->ID, name), x, y);
}
void Shader::SetVector2f(const GLchar* name, const glm::vec2& value, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform2f(glGetUniformLocation(this->ID, name), value.x, value.y);
}
void Shader::SetVector3f(const GLchar* name, GLfloat x, GLfloat y, GLfloat z, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform3f(glGetUniformLocation(this->ID, name), x, y, z);
}
void Shader::SetVector3f(const GLchar* name, const glm::vec3& value, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform3f(glGetUniformLocation(this->ID, name), value.x, value.y, value.z);
}
void Shader::SetVector4f(const GLchar* name, GLfloat x, GLfloat y, GLfloat z, GLfloat w, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform4f(glGetUniformLocation(this->ID, name), x, y, z, w);
}
void Shader::SetVector4f(const GLchar* name, const glm::vec4& value, GLboolean useShader)
{
if (useShader)
this->Use();
glUniform4f(glGetUniformLocation(this->ID, name), value.x, value.y, value.z, value.w);
}
void Shader::SetMatrix4(const GLchar* name, const glm::mat4& matrix, GLboolean useShader)
{
if (useShader)
this->Use();
glUniformMatrix4fv(glGetUniformLocation(this->ID, name), 1, GL_FALSE, glm::value_ptr(matrix));
}
void Shader::CheckCompileErrors(GLuint object, std::string type)
{
GLint success;
GLchar infoLog[1024];
if (type != "PROGRAM")
{
glGetShaderiv(object, GL_COMPILE_STATUS, &success);
if (!success)
{
glGetShaderInfoLog(object, 1024, NULL, infoLog);
std::cout << "| ERROR::SHADER: Compile-time error: Type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- "
<< std::endl;
}
}
else
{
glGetProgramiv(object, GL_LINK_STATUS, &success);
if (!success)
{
glGetProgramInfoLog(object, 1024, NULL, infoLog);
std::cout << "| ERROR::Shader: Link-time error: Type: " << type << "\n"
<< infoLog << "\n -- --------------------------------------------------- -- "
<< std::endl;
}
}
}
Texture2d.cpp:
#include "Texture2D.h"
Texture2D::Texture2D() : Width(0), Height(0), Internal_Format(GL_RGB), Image_Format(GL_RGB), Wrap_S(GL_REPEAT), Wrap_T(GL_REPEAT), Filter_Min(GL_LINEAR), Filter_Max(GL_LINEAR)
{
glGenTextures(1, &this->ID);
}
void Texture2D::Generate(GLuint width, GLuint height, unsigned char* data)
{
this->Width = width;
this->Height = height;
// Create Texture
glBindTexture(GL_TEXTURE_2D, this->ID);
glTexImage2D(GL_TEXTURE_2D, 0, this->Internal_Format, width, height, 0, this->Image_Format, GL_UNSIGNED_BYTE, data);
// Set Texture wrap and filter modes
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, this->Wrap_S);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, this->Wrap_T);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, this->Filter_Min);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, this->Filter_Max);
// Unbind texture
glBindTexture(GL_TEXTURE_2D, 0);
}
void Texture2D::Bind() const
{
glBindTexture(GL_TEXTURE_2D, this->ID);
}
ResourceManager.cpp:
#include "ResourceManager.h"
#include <stb_image.h>
// Instantiate static variables
std::map<std::string, Texture2D> ResourceManager::Textures;
std::map<std::string, Shader> ResourceManager::Shaders;
Shader& ResourceManager::LoadShader(const GLchar* vShaderFile, const GLchar* fShaderFile, const GLchar* gShaderFile, std::string name)
{
Shaders[name] = loadShaderFromFile(vShaderFile, fShaderFile, gShaderFile);
return Shaders[name];
}
Shader& ResourceManager::GetShader(std::string name)
{
return Shaders[name];
}
Texture2D& ResourceManager::LoadTexture(const GLchar* file, GLboolean alpha, std::string name)
{
Textures[name] = loadTextureFromFile(file, alpha);
return Textures[name];
}
Texture2D& ResourceManager::GetTexture(std::string name)
{
return Textures[name];
}
void ResourceManager::Clear()
{
// (Properly) delete all shaders
for (auto iter : Shaders)
glDeleteProgram(iter.second.ID);
// (Properly) delete all textures
for (auto iter : Textures)
glDeleteTextures(1, &iter.second.ID);
}
Shader ResourceManager::loadShaderFromFile(const GLchar* vShaderFile, const GLchar* fShaderFile, const GLchar* gShaderFile)
{
// 1. Retrieve the vertex/fragment source code from filePath
std::string vertexCode;
std::string fragmentCode;
std::string geometryCode;
try
{
// Open files
std::ifstream vertexShaderFile(vShaderFile);
std::ifstream fragmentShaderFile(fShaderFile);
std::stringstream vShaderStream, fShaderStream;
// Read file's buffer contents into streams
vShaderStream << vertexShaderFile.rdbuf();
fShaderStream << fragmentShaderFile.rdbuf();
// close file handlers
vertexShaderFile.close();
fragmentShaderFile.close();
// Convert stream into string
vertexCode = vShaderStream.str();
fragmentCode = fShaderStream.str();
// If geometry shader path is present, also load a geometry shader
if (gShaderFile != nullptr)
{
std::ifstream geometryShaderFile(gShaderFile);
std::stringstream gShaderStream;
gShaderStream << geometryShaderFile.rdbuf();
geometryShaderFile.close();
geometryCode = gShaderStream.str();
}
}
catch (std::exception e)
{
std::cout << "ERROR::SHADER: Failed to read shader files" << std::endl;
}
const GLchar* vShaderCode = vertexCode.c_str();
const GLchar* fShaderCode = fragmentCode.c_str();
const GLchar* gShaderCode = geometryCode.c_str();
// 2. Now create shader object from source code
Shader shader;
shader.Compile(vShaderCode, fShaderCode, gShaderFile != nullptr ? gShaderCode : nullptr);
return shader;
}
Texture2D ResourceManager::loadTextureFromFile(const GLchar* file, GLboolean alpha)
{
// Create Texture object
Texture2D texture;
if (alpha)
{
texture.Internal_Format = GL_RGBA;
texture.Image_Format = GL_RGBA;
}
int width, height, nrComponents;
unsigned char* data = stbi_load(file, &width, &height, &nrComponents, 0);
if (data)
{
if (nrComponents == 1)
{
texture.Internal_Format = GL_RED;
texture.Image_Format = GL_RED;
}
else if (nrComponents == 3)
{
texture.Internal_Format = GL_RGB;
texture.Image_Format = GL_RGB;
}
else if (nrComponents == 4)
{
if (alpha)
{
texture.Internal_Format = GL_RGBA;
texture.Image_Format = GL_RGBA;
}
else
{
texture.Internal_Format = GL_RGB;
texture.Image_Format = GL_RGB;
}
}
texture.Generate(width, height, data);
stbi_image_free(data);
}
return texture;
}
SpriteRenderer.cpp:
#include "SpriteRenderer.h"
SpriteRenderer::SpriteRenderer(Shader& shader)
{
this->shader = shader;
this->InitRenderData();
}
SpriteRenderer::~SpriteRenderer()
{
glDeleteVertexArrays(1, &this->quadVAO);
}
void SpriteRenderer::DrawSprite(Texture2D& texture, glm::vec2 position, glm::vec2 size, GLfloat rotate, glm::vec3 color)
{
this->shader.Use();
glm::mat4 model = glm::mat4(1.0);
//下面这段代码主要是处理变换:代码写的顺序和实际的变换顺序相反的
// First translate(transformations are : scale happens first, then rotation and then finall translation happens; reversed order)
model = glm::translate(model, glm::vec3(position, 0.0f));
//由于原点再图片左上角
//这里代码上是把原点移动到图片的中心,再旋转
// Move origin of rotation to center of quad
model = glm::translate(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.0f));
// Then rotate
model = glm::rotate(model, glm::radians(rotate), glm::vec3(0.0f, 0.0f, 1.0f));
// Move origin back
model = glm::translate(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, 0.0f));
model = glm::scale(model, glm::vec3(size, 1.0f));
//实际:倒着看:先缩放,再反向移动,再旋转,再正向移动,最后平移
this->shader.SetMatrix4("model", model);
this->shader.SetVector3f("spriteColor", color);
glActiveTexture(GL_TEXTURE0);
texture.Bind();
glBindVertexArray(this->quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void SpriteRenderer::InitRenderData()
{
//config VAO VBO
GLuint VBO;
GLfloat vertices[] = {
// 位置 // 纹理
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f, 1.0f,
1.0f, 1.0f, 1.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f
};
glGenVertexArrays(1, &this->quadVAO);
glGenBuffers(1, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindVertexArray(this->quadVAO);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}