版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wade333777/article/details/80044840
一、为何封装
通过前面几节的介绍,大家已经知道要想实现一张图片的渲染,从载入到呈现到屏幕上,中间经历了很多过程,还是很繁琐的,这时候我们需要一个框架把这些细节封装起来,更利于我们的理解和使用。
二、具体封装
1.着色器,shader
// 调用之前已经生成的着色器程序
Shader &Shader::Use()
{
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");
// 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);
}
// 为着色器中的uniform变量设置不同的值
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;
}
}
}
2.纹理,texture
// 生成纹理引用
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);
}
// 绑定到当前ID的纹理引用
void Texture2D::Bind() const
{
glBindTexture(GL_TEXTURE_2D, this->ID);
}
3.资源管理
专门加载游戏相关的资源,对着色器和纹理的加载与引用
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;
}
// Load image
int width, height;
unsigned char* image = SOIL_load_image(file, &width, &height, 0, texture.Image_Format == GL_RGBA ? SOIL_LOAD_RGBA : SOIL_LOAD_RGB);
// Now generate texture
texture.Generate(width, height, image);
// And finally free image data
SOIL_free_image_data(image);
return texture;
}
4.精灵渲染
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)
{
// Prepare transformations
this->shader.Use();
glm::mat4 model;
model = glm::translate(model, glm::vec3(position, 0.0f)); // First translate (transformations are: scale happens first, then rotation and then finall translation happens; reversed order)
model = glm::translate(model, glm::vec3(0.5f * size.x, 0.5f * size.y, 0.0f)); // Move origin of rotation to center of quad
model = glm::rotate(model, rotate, glm::vec3(0.0f, 0.0f, 1.0f)); // Then rotate
model = glm::translate(model, glm::vec3(-0.5f * size.x, -0.5f * size.y, 0.0f)); // Move origin back
model = glm::scale(model, glm::vec3(size, 1.0f)); // Last scale
this->shader.SetMatrix4("model", model);
// Render textured quad
this->shader.SetVector3f("spriteColor", color);
glActiveTexture(GL_TEXTURE0);
texture.Bind();
glBindVertexArray(this->quadVAO);
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
}
void SpriteRenderer::initRenderData()
{
// Configure VAO/VBO
GLuint VBO;
GLfloat vertices[] = {
// Pos // Tex
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);
}
三、实例调用
1.游戏初始化方法
// 加载着色器
ResourceManager::LoadShader("shaders/sprite.vs", "shaders/sprite.frag", nullptr, "sprite");
// 配置着色器
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);
// 加载纹理
ResourceManager::LoadTexture("textures/awesomeface.png", GL_TRUE, "face");
// 生成精灵渲染,并传入shader
Shader shader = ResourceManager::GetShader("sprite");
Renderer = new SpriteRenderer(shader);
2.游戏渲染
Texture2D texture = ResourceManager::GetTexture("face");
Renderer->DrawSprite(texture, glm::vec2(200, 200), glm::vec2(300, 400), 45.0f, glm::vec3(0.0f, 1.0f, 0.0f));