1.渲染器功能使用简介
从主函数文件所在在开始讲起,主函数会创建Application的子类sandbox,这会调用Application和sandbox的构造函数,而Application的构造函数中会有渲染器的开端
Renderer::Init();
-->
void Renderer::Init()
{
RenderCommand::Init();
Renderer2D::Init();
}
------->
根据apl会对应调用RenderCommand渲染指令,例如opengl的渲染指令
void OpenGLRendererAPI::Init()
{
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_DEPTH_TEST);
}
------->
Renderer2D::Init();下面描述
struct Renderer2DStorage
{
Ref<VertexArray> QuadVertexArray;
Ref<Shader> TextureShader;
Ref<Texture2D> WhiteTexture;
};
void Renderer2D::Init()//放在Renderer类的Init->放在Application的构造函数中运行
{
s_Data = new Renderer2DStorage();
//顶点数组的相关配置
s_Data->QuadVertexArray = VertexArray::Create();
float squareVertices[5 * 4] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 1.0f,
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f
};
Ref<VertexBuffer> squareVB = VertexBuffer::Create(squareVertices, sizeof(squareVertices));
//顶点数组属性配置
squareVB->SetLayout
({
{ ShaderDataType::Float3, "a_Position" },
{ ShaderDataType::Float2, "a_TexCoord" }
});
s_Data->QuadVertexArray->AddVertexBuffer(squareVB);
//顶点索引的配置
uint32_t squareIndices[6] = { 0, 1, 2, 2, 3, 0 };
Ref<IndexBuffer> squareIB = IndexBuffer::Create(squareIndices, sizeof(squareIndices) / sizeof(uint32_t));
s_Data->QuadVertexArray->SetIndexBuffer(squareIB);
//纹理着色器的配置,构造纹理着色器,值得注意的是在可以提供全白的纹理以相当于无插入纹理
//如果需要实际的纹理只需要渲染函数中提供纹理覆盖默认的白色纹理即可
//以此对应需纹理和无纹理的着色器只需要一个就行
s_Data->WhiteTexture = Texture2D::Create(1, 1);
uint32_t whiteTextureData = 0xffffffff;
s_Data->WhiteTexture->SetData(&whiteTextureData, sizeof(uint32_t));
s_Data->TextureShader = Shader::Create("assets/shaders/Texture.glsl");
//着色器构造指令都只要Shader::Create指令都可以在着色器类内中自己完成
s_Data->TextureShader->Bind();
s_Data->TextureShader->SetInt("u_Texture", 0);//上传纹理对应的id
}
2.需要实际采样纹理,给矩形上纹理需要的操作
1在着色器构造完成后,纹理的使用只需要提供一个指针运行两个指令,即可完成渲染一个图像
m_TriangleTexture = Texture2D::Create("assets/textures/Triangle.png");
Renderer2D::DrawQuad(pillar.TopPosition, pillar.TopScale, glm::radians(180.0f), m_TriangleTexture, color);
Texture2D::Create会根据对应的api生成对应的Texture2D类的实例,这里以OpenGLTexture2D为例,下面为它的构造函数
OpenGLTexture2D::OpenGLTexture2D(const std::string& path)
: m_Path(path)
{
int width, height, channels;
stbi_set_flip_vertically_on_load(1);//启用图像翻转,gl的采样会导致纹理是倒像,所以需要翻转一下
stbi_uc* data = stbi_load(path.c_str(), &width, &height, &channels, 0);//因为stb_image给予长宽是有符号的整数
HZ_CORE_ASSERT(data, "Failed to load image!");
m_Width = width;
m_Height = height;
//但是opengl中的函数要求处理无符号整数,所以我们需要如上的转化一下
GLenum internalFormat = 0, dataFormat = 0;
if (channels == 4)
{
internalFormat = GL_RGBA8;
dataFormat = GL_RGBA;
}
else if (channels == 3)
{
internalFormat = GL_RGB8;
dataFormat = GL_RGB;
}
m_InternalFormat = internalFormat;
m_DataFormat = dataFormat;
HZ_CORE_ASSERT(internalFormat & dataFormat, "Format not supported!");
glCreateTextures(GL_TEXTURE_2D, 1, &m_RendererID);
glTextureStorage2D(m_RendererID, 1, 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);
glTextureSubImage2D(m_RendererID, 0, 0, 0, m_Width, m_Height, dataFormat, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);//释放储存在cpu中的数据
}
下面是绘制函数指令:
void Renderer2D::DrawQuad(const glm::vec3& position, const glm::vec2& size, float rotation, const Ref<Texture2D>& texture, const glm::vec4& color)
{
s_Data->TextureShader->SetFloat4("u_Color", color);
texture->Bind();
glm::mat4 transform = glm::translate(glm::mat4(1.0f), position)
* glm::rotate(glm::mat4(1.0f), rotation, { 0.0f, 0.0f, 1.0f })
* glm::scale(glm::mat4(1.0f), { size.x, size.y, 1.0f });
s_Data->TextureShader->SetMat4("u_Transform", transform);
s_Data->QuadVertexArray->Bind();
RenderCommand::DrawIndexed(s_Data->QuadVertexArray);
3.小结
得益于面向对象的思想,基本渲染一个图像,分成构造着色器和构建纹理,而着色器构建又分成了,顶点数组类,顶点索引类,顶点数组属性配置类,各类都是一个部分功能的调用,而把这些类实际链接在一起也只需要在需要的链接的类中加入链接类的实例,就可以完成功能的衔接。
实际的代码就是只需要一个地方void Renderer::Init()函数和Texture2D::Create和DrawQuad这三条指令就行了,而void Renderer::Init()提供的着色器一般都是多个渲染任务共用的一个着色器,在2d渲染器中。Texture2D::Create和DrawQuad则根据需要对应的每次调用