游戏引擎从零开始(30)-2D Renderer transform&纹理

886 阅读11分钟

上一篇中,有个类名笔误写错了,更正为: Renderer2D-->Sandbox2D,代码参考:
github.com/summer-go/H…

前言

当前实现的是一个2D的渲染器,所有的2D的绘制都可以抽象为矩形绘制、2D纹理处理。而所有的2D矩形绘制都可以用相同的4个顶点表达。

可以将Sandbox2D中这部分通用的逻辑抽象出来,要达到的目标是:绘制一个矩形只需要传矩形的坐标、尺寸、纹理即可,不用每次都声明顶点数据。

这里简单处理,约定所有的矩形都是4个坐标,即两组三角形,矩形的中心位于OpenGL的原点。

2D渲染-变换与纹理

Renderer2D实现

仿照Renderer流程设计Renderer2D,除了初始化接口,增加Renderer2D特有的绘制矩形的接口DrawQuad。

DrawQuad()有两个重载的实现,其中一个position传的是vec3类型的,用于表示前后顺序,position第三个分量z越小越靠前,默认为0.
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.h

#pragma once

#include "OrthographicCamera.h"

namespace Hazel {
    class Renderer2D {
    public:
        static void Init();
        static void Shutdown();

        static void BeginScene(const OrthographicCamera& camera);
        static void EndScene();

        // Primitives
        static void DrawQuad(const glm::vec2& position, const glm::vec2& size, const glm::vec4& color);
        static void DrawQuad(const glm::vec3& position, const glm::vec2& size, const glm::vec4& color);
    };
}

大部分的逻辑是从Sandbox2D.cpp中抠出来的,设计Renderer2DStorage数据结构,封装Renderer2D中用到的静态数据。

原作者编程的功底很好,非常注意封装,不会让变量到处散落。

Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.cpp

#include "Renderer2D.h"
#include "VertexArray.h"
#include "Shader.h"
#include "Platform/OpenGL/OpenGLShader.h"
#include "RenderCommand.h"

namespace Hazel {
    struct Renderer2DStorage{
        Ref<VertexArray> QuadVertexArray;
        Ref<Shader> FlatColorShader;
    };

    static Renderer2DStorage* s_Data;

    void Renderer2D::Init() {
        s_Data = new Renderer2DStorage();
        s_Data->QuadVertexArray = Hazel::VertexArray::Create();
        float squareVertices[3 * 4] = {
                -0.5, -0.5f, 0.0f,
                0.5, -0.5f, 0.0f,
                0.5, 0.5f, 0.0f,
                -0.5, 0.5f, 0.0f,
        };

        Hazel::Ref<Hazel::VertexBuffer> squareVB;
        squareVB.reset(Hazel::VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
        squareVB->SetLayout({
                                    {Hazel::ShaderDataType::Float3, "a_Position"}
                            });
        s_Data->QuadVertexArray->AddVertexBuffer(squareVB);

        uint32_t squareIndices[6] = {0, 1, 2, 2,3, 0};
        Hazel::Ref<Hazel::IndexBuffer> squareIB;
        squareIB.reset(Hazel::IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
        s_Data->QuadVertexArray->SetIndexBuffer(squareIB);

        s_Data->FlatColorShader = Hazel::Shader::Create("../assets/shaders/FlatColor.glsl");

    }

    void Renderer2D::Shutdown() {
        delete s_Data;
    }

    void Renderer2D::BeginScene(const OrthographicCamera &camera) {
        s_Data->FlatColorShader->Bind();
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(s_Data->FlatColorShader)->UploadUniformMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(s_Data->FlatColorShader)->UploadUniformMat4("u_Transform", glm::mat4(1.0f));
    }

    void Renderer2D::EndScene() {

    }

    void Renderer2D::DrawQuad(const glm::vec2 &position, const glm::vec2 &size, const glm::vec4 &color) {
        DrawQuad({position.x, position.y, 0.0f}, size, color);
    }

    void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color) {
        s_Data->FlatColorShader->Bind();
        std::dynamic_pointer_cast<Hazel::OpenGLShader>(s_Data->FlatColorShader)->UploadUniformFloat4("u_Color", color);
        s_Data->QuadVertexArray->Bind();
        RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
    }
}

Renderer2D初始化放到Renderer中
Sandbox/Hazel/src/Hazel/Renderer/Renderer.cpp

...
#include "Renderer2D.h"
...
void Renderer::Init() {
    RenderCommand::Init();
    Renderer2D::Init();
}

Sandbox2D改造
Sandbox/src/Sandbox2D.cpp

void Sandbox2D::OnUpdate(Hazel::Timestep ts) {
    // Update
    m_CameraController.OnUpdate(ts);
    // Render
    Hazel::RenderCommand::SetClearColor({0.1f, 0.1f, 0.1f, 1.0});
    Hazel::RenderCommand::Clear();

    Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
    Hazel::Renderer2D::DrawQuad({0.0f, 0.0f}, {1.0f, 1.0f}, {0.8f, 0.2f, 0.3f, 1.0f});
    Hazel::Renderer2D::EndScene();
}

基于Renderer2D,现在绘制一个矩形就非常容易了,一行DrawQuad就完成了。

代码修改参考:
github.com/summer-go/H…

变换(transform)

现在绘制的矩形,position默认值都是0,即处于OpenGL坐标系原点处。这当然不行,我们不可能只绘制一个处于原点的矩形,现在我们在Shader中加上接口,支持更改矩形的坐标。

Sandbox/Hazel/src/Hazel/Renderer/Shader.h

...
virtual const std::string& GetName() const = 0;
virtual void SetMat4(const std::string& name, const glm::mat4& value) = 0;
virtual void SetFloat3(const std::string& name, const glm::vec3& value) = 0;
virtual void SetFloat4(const std::string& name, const glm::vec4& value) = 0;
...

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLShader.h

...
const std::string &GetName() const override;
void SetMat4(const std::string& name, const glm::mat4& value) override;

void SetFloat3(const std::string &name, const glm::vec3 &value) override;

void SetFloat4(const std::string &name, const glm::vec4 &value) override;
...

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLShader.cpp

void OpenGLShader::SetMat4(const std::string& name, const glm::mat4& value) {
    UploadUniformMat4(name, value);
}

void OpenGLShader::SetFloat3(const std::string &name, const glm::vec3 &value) {
    UploadUniformFloat3(name, value);
}

void OpenGLShader::SetFloat4(const std::string &name, const glm::vec4 &value) {
    UploadUniformFloat4(name, value);
}

Renderer2D中增加坐标、缩放等变换
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.cpp

...
#include "RenderCommand.h"

// BeginScene中更新Camera变换矩阵和正交投影变换矩阵
void Renderer2D::BeginScene(const OrthographicCamera &camera) {
    s_Data->FlatColorShader->Bind();
    s_Data->FlatColorShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
//        s_Data->FlatColorShader->SetMat4("u_Transform", glm::mat4(1.0f));
}

// DrawQuad中增加矩形坐标、缩放的处理
void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color) {
    s_Data->FlatColorShader->Bind();
    s_Data->FlatColorShader->SetFloat4("u_Color", color);
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0), {size.x, size.y, 1.0f});
    s_Data->FlatColorShader->SetMat4("u_Transform", transform);
    s_Data->QuadVertexArray->Bind();
    RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
}

Sandbox2D中绘制两个不同位置、不同颜色的矩形
Sandbox/src/Sandbox2D.cpp

Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
Hazel::Renderer2D::DrawQuad({-1.0f, 0.0f}, {0.8f, 0.8f}, {0.8f, 0.2f, 0.3f, 1.0f});
Hazel::Renderer2D::DrawQuad({0.5f, -0.5f}, {0.5f, 0.5f}, {0.2f, 0.3f, 0.8f, 1.0f});
Hazel::Renderer2D::EndScene();

Renderer2d-两个矩形

代码修改参考:
github.com/summer-go/H…

纹理(texture)

纯色的矩形不能表达复杂的外观,我们对Renderer2D拓展,支持纹理的设置。

shader中纹理用int值表达,修改Shader,增加int类型值的设置
Sandbox/Hazel/src/Hazel/Renderer/Shader.h

virtual void SetInt(const std::string& name, const int value) = 0;

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLShader.h

void SetInt(const std::string &name, const int value) override;

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLShader.cpp

void OpenGLShader::SetInt(const std::string &name, const int value) {
    UploadUniformInt(name, value);
}

设置纹理环绕方式为repeat
Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLTexture.cpp

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

Renderer2D中增加接口,支持绘制带纹理的矩形
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.h

static void DrawQuad(const glm::vec2& position, const glm::vec2& size, const Ref<Texture2D>& texture);
static void DrawQuad(const glm::vec3& position, const glm::vec2& size, const Ref<Texture2D>& texture);

Renderer2D.cpp中增加对纹理的支持
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.cpp

// 1. 增加TextureShader
struct Renderer2DStorage{
    Ref<VertexArray> QuadVertexArray;
    Ref<Shader> FlatColorShader;
    Ref<Shader> TextureShader;
};

...
void Renderer2D::Init() {
        s_Data = new Renderer2DStorage();
        s_Data->QuadVertexArray = Hazel::VertexArray::Create();
        
        // 2. 顶点增加纹理坐标
        float squareVertices[5 * 4] = {
                -0.5, -0.5f, 0.0f, 0.0f, 0.0f,
                0.5, -0.5f, 0.0f, 1.0f, 0.0f,
                0.5, 0.5f, 0.0f, 1.0f, 1.0f,
                -0.5, 0.5f, 0.0f, 0.0f, 1.0f
        };

        Hazel::Ref<Hazel::VertexBuffer> squareVB;
        squareVB.reset(Hazel::VertexBuffer::Create(squareVertices, sizeof(squareVertices)));
        
        // 3. Layout增加纹理的布局
        squareVB->SetLayout({
                                    {Hazel::ShaderDataType::Float3, "a_Position"},
                                    {Hazel::ShaderDataType::Float2, "a_TexCoord"},
                            });
        s_Data->QuadVertexArray->AddVertexBuffer(squareVB);

        uint32_t squareIndices[6] = {0, 1, 2, 2,3, 0};
        Hazel::Ref<Hazel::IndexBuffer> squareIB;
        squareIB.reset(Hazel::IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t)));
        s_Data->QuadVertexArray->SetIndexBuffer(squareIB);

        s_Data->FlatColorShader = Hazel::Shader::Create("../assets/shaders/FlatColor.glsl");
        
        // 4. 创建TextureShader
        s_Data->TextureShader = Hazel::Shader::Create("../assets/shaders/Texture.glsl");
        s_Data->TextureShader->Bind();
        s_Data->TextureShader->SetInt("u_Texture", 0);
    }
    
      
      // 5. 初始化Shader(ColorShader、TextureShader)
      void Renderer2D::BeginScene(const OrthographicCamera &camera) {
        s_Data->FlatColorShader->Bind();
        s_Data->FlatColorShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
//        s_Data->FlatColorShader->SetMat4("u_Transform", glm::mat4(1.0f));

        s_Data->TextureShader->Bind();
        s_Data->TextureShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
    }
    
    
void Renderer2D::DrawQuad(const glm::vec2 &position, const glm::vec2 &size, const Ref<Texture2D> &texture) {
    DrawQuad({position.x, position.y, 0.f}, size, texture);
}

// 6. DrawQuad增加纹理的绑定
void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const Ref<Texture2D> &texture) {
    s_Data->TextureShader->Bind();
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0), {size.x, size.y, 1.0f});
    s_Data->TextureShader->SetMat4("u_Transform", transform);
    texture->Bind();

    s_Data->QuadVertexArray->Bind();
    RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
}

后面会将该带纹理的矩形放大,在Texture.glsl中,修改坐标,缩小10倍,让纹理显的小一点,抵消矩形坐标的放大。
Sandbox/assets/shaders/Texture.glsl

...
void main()
{
  // 采样后的颜色做些处理,使其偏向红色
	color = texture(u_Texture, v_TexCoord * 10.0) * vec4(1.0, 0.8, 0.8, 1.0);
}

Sandbox2D中增加纹理
Sandbox/src/Sandbox2D.h

Hazel::Ref<Hazel::Texture2D> m_CheckerboardTexture;

Sandbox/src/Sandbox2D.cpp

void Sandbox2D::OnAttach()
{
  // 纹理初始化
	m_CheckerboardTexture = Hazel::Texture2D::Create("assets/textures/Checkerboard.png");
}

void Sandbox2D::OnDetach()
{
}
void Sandbox2D::OnUpdate(Hazel::Timestep ts)
{
	// Update
	m_CameraController.OnUpdate(ts);
	// Render
	Hazel::RenderCommand::SetClearColor({ 0.1f, 0.1f, 0.1f, 1 });
	Hazel::RenderCommand::Clear();

	Hazel::Renderer2D::BeginScene(m_CameraController.GetCamera());
  
  // 绘制三个矩形,第三个矩形是带纹理的,且放大5倍,作为背景
	Hazel::Renderer2D::DrawQuad({ -1.0f, 0.0f }, { 0.8f, 0.8f }, { 0.8f, 0.2f, 0.3f, 1.0f });
	Hazel::Renderer2D::DrawQuad({ 0.5f, -0.5f }, { 0.5f, 0.75f }, { 0.2f, 0.3f, 0.8f, 1.0f });
	Hazel::Renderer2D::DrawQuad({ 0.0f, 0.0f, -0.1f }, { 5.0f, 5.0f }, m_CheckerboardTexture);
	Hazel::Renderer2D::EndScene();
}

如果代码运行正常,能看到一个偏红色的棋盘,看不到另外两个矩形了。

5倍大小

如果不放大带纹理的矩形,能看到另外两个矩形

原大小

我们打开深度测试,就能看到另外两个矩形了,因为带纹理的矩阵z坐标是-0.1,更靠后,经过深度测试,会被另两个矩形遮挡。

深度测试在OpenGLRendererAPI的Init方法中打开
Hazel/src/Platform/OpenGL/OpenGLRendererAPI.cpp

void OpenGLRendererAPI::Init()
{
  glEnable(GL_BLEND);
  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glEnable(GL_DEPTH_TEST);
}

运行正常能看到另两个矩形摆在前面

开启深度测试

我们操作下w s a d q r,还有鼠标缩放,能看到camera的响应

最终效果

增加纹理的代码:
github.com/summer-go/H…

优化-合并colorShader和TextureShader

当前的代码中,绘制3个矩形用到了两个shader,图形管线中,切换Shader有一定的性能损耗,我们考虑将这两个Shader合并成一个,以提升性能。

修改Texture.glsl
Sandbox/assets/shaders/Texture.glsl

#type fragment
#version 330 core
layout(location = 0) out vec4 color;

in vec2 v_TexCoord;

uniform vec4 u_Color;
uniform sampler2D u_Texture;

void main()
{
	color = texture(u_Texture, v_TexCoord * 10.0) * u_Color;
}

将纹理采样和u_Color点乘,如果渲染成纯色,在外面控制将u_Texture设计成一个白色fffffff,则结果等于u_Color。

如果渲染成纹理色,则在外面将u_Color设置成{1,1,1,1},则结果等于u_Texture的颜色。

这只是一种简单的实现,但并不在工程上通用,这种接口很容易弄错,实际场景中的通用shader可能会实现的非常复杂。

增加按宽高创建纹理和设置纹理的接口,用于手动创建一个单色的纹理缓冲
Sandbox/Hazel/src/Hazel/Renderer/Texture.h

    class Texture {
    public:
        ...
        virtual void SetData(void* data, uint32_t size) = 0;
        virtual void Bind(uint32_t slot = 0) const = 0;
    };
      
    class Texture2D : public Texture {
    public:
        static Ref<Texture2D> Create(const std::string & path);
        static Ref<Texture2D> Create(uint32_t width, uint32_t height);
    };

Sandbox/Hazel/src/Hazel/Renderer/Texture.cpp

    Ref<Texture2D> Texture2D::Create(uint32_t width, uint32_t height) {
        switch (Renderer::GetAPI()) {
            case RendererAPI::API::None: HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr;
            case RendererAPI::API::OpenGL: return CreateRef<OpenGLTexture2D>(width, height);
        }

        HZ_CORE_ASSERT(false, "Unknow RendererAPI!");
        return nullptr;
    }

    Ref<Texture2D> Texture2D::Create(const std::string &path) {
        switch (Renderer::GetAPI()) {
            case RendererAPI::API::None: HZ_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr;
            case RendererAPI::API::OpenGL: return CreateRef<OpenGLTexture2D>(path);
        }

        HZ_CORE_ASSERT(false, "Unknow RendererAPI!");
        return nullptr;
    }

上面用到了一个模板接口,在Core.h中
Sandbox/Hazel/src/Hazel/Core/Core.h

template<typename T, typename ... Args>
constexpr Ref<T> CreateRef(Args&& ...args)
{
    return std::make_shared<T>(std::forward<Args>(args)...);
}

子类实现
Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLTexture.h

...
#include "OpenGLShader.h"
#include "glad/glad.h"

namespace Hazel {
    class OpenGLTexture2D : public Texture2D{
    public:
        explicit OpenGLTexture2D(const std::string& path);
        OpenGLTexture2D(uint32_t width, uint32_t height);
        ...
        void SetData(void *data, uint32_t size) override;

    private:
        ...
        GLint m_InternalFormat = 0;
        GLenum m_DataFormat = 0;
    };
}

Sandbox/Hazel/src/Hazel/Platform/OpenGL/OpenGLTexture.cpp

    
    // 按宽高生成一个空的纹理,先把内存占好
    OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
        : m_width(width), m_height(height)
    {
        m_InternalFormat = GL_RGBA8;
        m_DataFormat = GL_RGBA;
        glGenTextures(1, &m_RendererID);

        glBindTexture(GL_TEXTURE_2D, m_RendererID);
        glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, width, height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    }
    
    ....
    
    // 设置纹理数据,使用glTexSubImage2D
void OpenGLTexture2D::SetData(void *data, uint32_t size) {
      uint32_t bpp = m_DataFormat == GL_RGBA ? 4 : 3;
      HZ_CORE_ASSERT(size == m_width * m_height * bpp, "Data must be entire");

      glBindTexture(GL_TEXTURE_2D, m_RendererID);
      glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_DataFormat, GL_UNSIGNED_BYTE, data);
}

更新Renderer2D逻辑
Sandbox/Hazel/src/Hazel/Renderer/Renderer2D.cpp

...
// 增加WhiteTexture纹理,用于手动生成一个白色的纹理
struct Renderer2DStorage{
    Ref<VertexArray> QuadVertexArray;
    Ref<Shader> TextureShader;
    Ref<Texture2D> WhiteTexture;
};


void Renderer2D::Init() {
    ...
    
    // 初始化时,顶一个一个宽高1*1的纹理
    s_Data->WhiteTexture = Texture2D::Create(1,1);
    // 纹理为纯白色,只有一个像素,一个RGBA像素是32位8*4
    uint32_t whiteTextureData = 0xffffffff;
    s_Data->WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t));

    s_Data->TextureShader = Hazel::Shader::Create("../assets/shaders/Texture.glsl");
    s_Data->TextureShader->Bind();
    s_Data->TextureShader->SetInt("u_Texture", 0);
}

// BeginScene中少了一个Shader的初始化了,只有两行,更清爽了
void Renderer2D::BeginScene(const OrthographicCamera &camera) {
    //s_Data->FlatColorShader->Bind();
    //s_Data->FlatColorShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
    s_Data->TextureShader->Bind();
    s_Data->TextureShader->SetMat4("u_ViewProjection", camera.GetViewProjectionMatrix());
}

// 绘制color型矩形,用TextureShader,替掉FlatColorShader
void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const glm::vec4 &color) {
    s_Data->TextureShader->SetFloat4("u_Color", color);
    s_Data->WhiteTexture->Bind();
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0), {size.x, size.y, 1.0f});
    s_Data->TextureShader->SetMat4("u_Transform", transform);
    s_Data->QuadVertexArray->Bind();
    RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
}


void Renderer2D::DrawQuad(const glm::vec3 &position, const glm::vec2 &size, const Ref<Texture2D> &texture) {
    s_Data->TextureShader->Bind();
    s_Data->TextureShader->SetFloat4("u_Color", glm::vec4(1.0f));
    glm::mat4 transform = glm::translate(glm::mat4(1.0f), position) * glm::scale(glm::mat4(1.0), {size.x, size.y, 1.0f});
    s_Data->TextureShader->SetMat4("u_Transform", transform);
    texture->Bind();

    s_Data->QuadVertexArray->Bind();
    RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
}

合并Shader的代码:
github.com/summer-go/H…

总结

OpenGL4.5+ 创建纹理、设置纹理属性、设置纹理数据等API有一些变化,使用时注意区分版本。

高版本中使用:

// 创建纹理
glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);

// 设置纹理属性
glTextureStorage2D(m_RendererID, 1, m_InternalFormat, m_Width, m_Height);

// 设置纹理真实数据
glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, dataFormat, GL_UNSIGNED_BYTE, data);

低版本中:

// 创建纹理
glGenTextures(1, &m_RendererID);

// 设置纹理属性,也可以同时设置纹理数据
glTexImage2D(GL_TEXTURE_2D, 0, m_InternalFormat, width, height, 0, m_DataFormat, GL_UNSIGNED_BYTE, nullptr);

// 设置纹理数据
glBindTexture(GL_TEXTURE_2D, m_RendererID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_width, m_height, m_DataFormat, GL_UNSIGNED_BYTE, data);