本节介绍了一种能够使用一个 shader 就可以实现用固定颜色填充的矩形以及用纹理填充的矩形的绘制。具体的做法就是:在绘制纯色矩形的时候,创建一张纯色的纹理传给 shader ,就可以实现上述操作了。
在 shader 中,这样实现最后颜色输出:
uniform vec4 u_Color;
uniform sampler2D u_Texture;
void main()
{
color = texture(u_Texture, v_TexCoord * 10.0) * u_Color;
}
当我们想要传入 texture 进行绘制的时候,就将 u_Color设置为白色,当我们想要进行纯色矩形绘制的时候,就创建一个白色 texture 进行绑定。
添加了一个用 width 和 height 进行创建的 Create 函数:
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, "Unknown RendererAPI!");
return nullptr;
}
然后在 OpenGLTexture 中进行实现:
OpenGLTexture2D::OpenGLTexture2D(uint32_t width, uint32_t height)
: m_Width(width), m_Height(height)
{
m_InternalFormat = GL_RGBA8;
m_DataFormat = GL_RGBA;
glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);
glTextureStorage2D(m_RendererID, 1, m_InternalFormat, 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_REPEAT);
glTextureParameteri(m_RendererID, GL_TEXTURE_WRAP_T, GL_REPEAT);
}
这里指定了纹理的形式为 8字节的 RGBA,数据格式为 RGBA,然后设置了纹理采样和平铺的方式。
添加了一个函数 SetData 用于给纹理数据进行设置,也就是为了我们创建一个纯色的 texture,在Renderer2D 中,创建了一个 whiteTexture ,然后设置其数据为纯白:
uint32_t whiteTextureData = 0xffffffff;
s_Data->WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t));
绘制纹理部分,我们将 u_Color设置为白色:
s_Data->TextureShader->SetFloat4("u_Color", glm::vec4(1.0f));
texture->Bind();
然后运行查看结果:

可以看到,红色和蓝色的固定颜色矩形也被绘制上了 texture,这是因为没有给纯色矩形绘制绑定纯白的 texture:
s_Data->TextureShader->SetFloat4("u_Color", color);
s_Data->WhiteTexture->Bind();

这样绘制结果就正确了。
但是在绘制纯色矩形的时候,不应该绑定之前的纹理,所以应该在绘制之后将当前绑定的纹理取消绑定:
//OpenGLRendererAPI.cpp::DrawIndexed
glBindTexture(GL_TEXTURE_2D, 0);
注释掉白色纹理绑定那行代码检验结果,发现绘制的图形是黑色的,说明之前的 texture 已经解除绑定:

补充:
之前将 shared_ptr 用 Ref 进行了封装,但是没有实现对应的 make_shared
template<typename T>
using Scope = std::unique_ptr<T>;
template<typename T, typename ... Args>
constexpr Scope<T> CreateScope(Args&& ... args)
{
return std::make_unique<T>(std::forward<Args>(args)...);
}
template<typename T>
using Ref = std::shared_ptr<T>;
template<typename T, typename ... Args>
constexpr Ref<T> CreateRef(Args&& ... args)
{
return std::make_shared<T>(std::forward<Args>(args)...);
}