Textures
本节主要是进行 Texture 的导入和 OpenGLTextture 的抽象,为了载入外部 PNG 图像,这里使用了 stb_image 开源库函数,首先复制 stb_image.h 中的内容到 vendor 中新创建的文件夹 stb_image.h 当中,然后创建 stb_image.cpp 文件进行编译:
#include "hzpch.h"
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"
之后首先在 renderer 文件夹中创建 Texture.h
和 Texture.cpp
,进行 Texture 的抽象
在头文件中,创建了两个类,一个是最基本的 Texture 类,设置为抽象类用于继承,然后创建 Texture2D 类,用于 2D 纹理的加载和使用,之后还可以创建 3DTexture,也就是我们所说的 cubeMap。
class Texture
{
public:
virtual ~Texture() = default;
virtual uint32_t GetWidth() const = 0;
virtual uint32_t GetHeight() const = 0;
virtual void Bind(uint32_t slot = 0) const = 0;
};
class Texture2D : public Texture
{
public:
static Ref<Texture2D> Create(const std::string& path);
};
和 VertexBuffer 一样,在 Create 函数中我们还是根据不同的 API 返回对应的 Texture 实例:
不同的是,这里我们返回了一个 shared_ptr
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 std::make_shared<OpenGLTexture2D>(path);
}
HZ_CORE_ASSERT(false, "Unknown RendererAPI!");
return nullptr;
}
然后实现 OpenGL 对应的 Texture 类,创建 OpenGLTexture.h
和 OpenGLTexture.cpp
class OpenGLTexture2D : public Texture2D
{
public:
OpenGLTexture2D(const std::string& path);
virtual ~OpenGLTexture2D();
virtual uint32_t GetWidth() const override { return m_Width; }
virtual uint32_t GetHeight() const override { return m_Height; }
virtual void Bind(uint32_t slot = 0) const override;
private:
std::string m_Path;
uint32_t m_Width, m_Height;
uint32_t m_RendererID;
};
// .cpp
OpenGLTexture2D::OpenGLTexture2D(const std::string& path)
: m_Path(path)
{
int width, height, channels;
stbi_set_flip_vertically_on_load(1);
stbi_uc* data = stbi_load(path.c_str(), &width, &height, &channels, 0);
HZ_CORE_ASSERT(data, "Failed to load image!");
m_Width = width;
m_Height = height;
glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);
glTextureStorage2D(m_RendererID, 1, GL_RGB8, m_Width, m_Height);
glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
OpenGLTexture2D::~OpenGLTexture2D()
{
glDeleteTextures(1, &m_RendererID);
}
void OpenGLTexture2D::Bind(uint32_t slot) const
{
glBindTextureUnit(slot, m_RendererID);
}
这里我按照 Cherno 的做法是能够正确显示他那个棋盘纹理的, 但是当我换了一张网上随便下载的纹理之后会有问题:
之后按照之前学习 OpenGL 的时候进行了一些修改之后:
OpenGLTexture2D::OpenGLTexture2D(const std::string& path) : m_path(path)
{
int width, height, channels;
stbi_set_flip_vertically_on_load(1);
stbi_uc* data = stbi_load(path.c_str(), &width, &height, &channels, 4);
HZ_CORE_ASSERT(data, "Failed to load image!");
m_Width = width;
m_Height = height;
glGenTextures(1, &m_RendererID);
glBindTexture(GL_TEXTURE_2D, m_RendererID);
//glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);
//glTextureStorage2D(m_RendererID, 1, GL_RGBA8, m_Width, m_Height);
glTextureParameteri(m_RendererID, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(m_RendererID, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, m_Width, m_Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
//glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
}
OpenGLTexture2D::~OpenGLTexture2D()
{
glDeleteTextures(1, &m_RendererID);
}
void OpenGLTexture2D::Bind(uint32_t slot) const
{
glActiveTexture(GL_TEXTURE0 + slot);
glBindTexture(GL_TEXTURE_2D, m_RendererID);
//glBindTextureUnit(slot, m_RendererID);
}
可以正常加载 Texture 了。此外就是我们存储图像的时候是从上到下存储的,但是在读取的时候是从下往上的,因此图片会反转,所以要使用 stb——image 中的函数进行一下翻转:stbi_set_flip_vertically_on_load(1);