feat(渲染器): 添加基础着色器文件并重构形状和精灵渲染器

重构形状渲染器和精灵渲染器,移除内置着色器代码改为使用外部着色器文件
添加基础着色器文件包括形状、精灵、文本和MSDF字体渲染所需着色器
渲染器现在必须设置材质才能工作,提供更灵活的着色器定制能力
This commit is contained in:
ChestnutYueyue 2026-02-23 16:26:34 +08:00
parent 70c2806c77
commit 5158fd713e
16 changed files with 318 additions and 248 deletions

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <extra2d/render/render_types.h>
#include <extra2d/render/render_device.h>
#include <extra2d/render/buffer.h> #include <extra2d/render/buffer.h>
#include <extra2d/render/material.h>
#include <extra2d/render/render_context.h>
#include <extra2d/render/vao.h> #include <extra2d/render/vao.h>
#include <glad/glad.h> #include <glad/glad.h>
#include <glm/mat4x4.hpp> #include <glm/mat4x4.hpp>
@ -23,7 +23,8 @@ struct ShapeVertex {
/** /**
* @brief * @brief
* *
* 线 * 线
* Material
*/ */
class ShapeRenderer { class ShapeRenderer {
public: public:
@ -43,6 +44,18 @@ public:
*/ */
void shutdown(); void shutdown();
/**
* @brief
* @param material
*/
void setMaterial(Ref<Material> material) { material_ = material; }
/**
* @brief
* @return
*/
Ref<Material> material() const { return material_; }
/** /**
* @brief * @brief
*/ */
@ -104,14 +117,11 @@ private:
Array<ShapeVertex> vertices_; Array<ShapeVertex> vertices_;
size_t vertexCount_ = 0; size_t vertexCount_ = 0;
GLuint fillShader_ = 0;
GLuint lineShader_ = 0;
glm::mat4 viewProjection_; glm::mat4 viewProjection_;
Ref<Material> material_;
uint32 drawCalls_ = 0;
float currentLineWidth_ = 1.0f; float currentLineWidth_ = 1.0f;
uint32 drawCalls_ = 0;
bool createShaders();
void flush(GLenum mode); void flush(GLenum mode);
void addVertex(float x, float y, const glm::vec4& color); void addVertex(float x, float y, const glm::vec4& color);
}; };

View File

@ -40,7 +40,8 @@ struct SpriteData {
/** /**
* @brief * @brief
* *
* Material * Material
* Material
*/ */
class SpriteRenderer { class SpriteRenderer {
public: public:
@ -64,7 +65,7 @@ public:
void shutdown(); void shutdown();
/** /**
* @brief * @brief
* @param material * @param material
*/ */
void setMaterial(Ref<Material> material) { material_ = material; } void setMaterial(Ref<Material> material) { material_ = material; }
@ -80,20 +81,14 @@ public:
*/ */
void begin(const glm::mat4 &viewProjection); void begin(const glm::mat4 &viewProjection);
/**
* @brief 使
* @param texture ID
* @param data
*/
void draw(GLuint texture, const SpriteData &data);
/** /**
* @brief 使 * @brief 使
* @param texture ID * @param texture ID
* @param data * @param data
* @param instance * @param instance nullptr 使
*/ */
void draw(GLuint texture, const SpriteData &data, MaterialInstance *instance); void draw(GLuint texture, const SpriteData &data,
MaterialInstance *instance = nullptr);
/** /**
* @brief 使 * @brief 使
@ -123,7 +118,6 @@ private:
Array<SpriteVertex> vertices_; Array<SpriteVertex> vertices_;
size_t vertexCount_ = 0; size_t vertexCount_ = 0;
GLuint shader_ = 0;
GLuint currentTexture_ = 0; GLuint currentTexture_ = 0;
glm::mat4 viewProjection_; glm::mat4 viewProjection_;
@ -133,9 +127,7 @@ private:
uint32 drawCalls_ = 0; uint32 drawCalls_ = 0;
uint32 spriteCount_ = 0; uint32 spriteCount_ = 0;
bool createShader();
void flush(); void flush();
void flushWithMaterial();
void addQuad(const SpriteData &data, GLuint texture); void addQuad(const SpriteData &data, GLuint texture);
}; };

View File

@ -0,0 +1,54 @@
#version 450 core
in vec2 v_texCoord;
uniform sampler2D u_texture;
uniform float u_pxRange;
uniform vec4 u_color;
uniform float u_outlineWidth;
uniform vec4 u_outlineColor;
uniform vec2 u_shadowOffset;
uniform vec4 u_shadowColor;
out vec4 fragColor;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec3 msdf = texture(u_texture, v_texCoord).rgb;
float sigDist = median(msdf.r, msdf.g, msdf.b);
float pxRange = u_pxRange;
float alpha = smoothstep(0.5 - 0.5/pxRange, 0.5 + 0.5/pxRange, sigDist);
float outlineAlpha = 0.0;
if (u_outlineWidth > 0.0) {
outlineAlpha = smoothstep(
0.5 - u_outlineWidth - 0.5/pxRange,
0.5 - u_outlineWidth + 0.5/pxRange,
sigDist
);
}
float shadowAlpha = 0.0;
if (u_shadowOffset != vec2(0.0)) {
vec3 shadowMsdf = texture(u_texture, v_texCoord - u_shadowOffset).rgb;
float shadowDist = median(shadowMsdf.r, shadowMsdf.g, shadowMsdf.b);
shadowAlpha = smoothstep(0.5 - 0.5/pxRange, 0.5 + 0.5/pxRange, shadowDist);
}
vec4 color = vec4(0.0);
if (shadowAlpha > 0.0) {
color = mix(color, u_shadowColor, shadowAlpha * u_shadowColor.a);
}
if (outlineAlpha > 0.0) {
color = mix(color, u_outlineColor, outlineAlpha * u_outlineColor.a);
}
color = mix(color, u_color, alpha * u_color.a);
fragColor = color;
}

View File

@ -0,0 +1,46 @@
{
"name": "msdf_font",
"vertex": "msdf_font.vert",
"fragment": "msdf_font.frag",
"properties": {
"u_texture": {
"type": "sampler2D",
"default": 0
},
"u_color": {
"type": "vec4",
"default": [1.0, 1.0, 1.0, 1.0]
},
"u_pxRange": {
"type": "float",
"default": 4.0
},
"u_outlineWidth": {
"type": "float",
"default": 0.0
},
"u_outlineColor": {
"type": "vec4",
"default": [0.0, 0.0, 0.0, 1.0]
},
"u_shadowOffset": {
"type": "vec2",
"default": [0.0, 0.0]
},
"u_shadowColor": {
"type": "vec4",
"default": [0.0, 0.0, 0.0, 0.5]
}
},
"states": {
"blend": {
"enabled": true,
"src": "SRC_ALPHA",
"dst": "ONE_MINUS_SRC_ALPHA"
},
"depth": {
"enabled": false,
"write": false
}
}
}

View File

@ -0,0 +1,12 @@
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texCoord;
uniform mat4 u_projection;
out vec2 v_texCoord;
void main() {
gl_Position = u_projection * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
}

View File

@ -0,0 +1,8 @@
#version 450 core
in vec4 v_color;
out vec4 fragColor;
void main() {
fragColor = v_color;
}

View File

@ -0,0 +1,22 @@
{
"name": "shape_default",
"vertex": "shape.vert",
"fragment": "shape.frag",
"properties": {
"u_color": {
"type": "vec4",
"default": [1.0, 1.0, 1.0, 1.0]
}
},
"states": {
"blend": {
"enabled": true,
"src": "SRC_ALPHA",
"dst": "ONE_MINUS_SRC_ALPHA"
},
"depth": {
"enabled": false,
"write": false
}
}
}

View File

@ -0,0 +1,12 @@
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec4 a_color;
uniform mat4 u_viewProjection;
out vec4 v_color;
void main() {
gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0);
v_color = a_color;
}

View File

@ -0,0 +1,11 @@
#version 450 core
in vec2 v_texCoord;
in vec4 v_color;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord) * v_color;
}

View File

@ -0,0 +1,26 @@
{
"name": "sprite_default",
"vertex": "sprite.vert",
"fragment": "sprite.frag",
"properties": {
"u_texture": {
"type": "sampler2D",
"default": 0
},
"u_color": {
"type": "vec4",
"default": [1.0, 1.0, 1.0, 1.0]
}
},
"states": {
"blend": {
"enabled": true,
"src": "SRC_ALPHA",
"dst": "ONE_MINUS_SRC_ALPHA"
},
"depth": {
"enabled": false,
"write": false
}
}
}

View File

@ -0,0 +1,15 @@
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texCoord;
layout(location = 2) in vec4 a_color;
uniform mat4 u_viewProjection;
out vec2 v_texCoord;
out vec4 v_color;
void main() {
gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
v_color = a_color;
}

View File

@ -0,0 +1,12 @@
#version 450 core
in vec2 v_texCoord;
in vec4 v_color;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
float alpha = texture(u_texture, v_texCoord).r;
fragColor = vec4(v_color.rgb, v_color.a * alpha);
}

View File

@ -0,0 +1,26 @@
{
"name": "text_default",
"vertex": "text.vert",
"fragment": "text.frag",
"properties": {
"u_texture": {
"type": "sampler2D",
"default": 0
},
"u_color": {
"type": "vec4",
"default": [1.0, 1.0, 1.0, 1.0]
}
},
"states": {
"blend": {
"enabled": true,
"src": "SRC_ALPHA",
"dst": "ONE_MINUS_SRC_ALPHA"
},
"depth": {
"enabled": false,
"write": false
}
}
}

View File

@ -0,0 +1,15 @@
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texCoord;
layout(location = 2) in vec4 a_color;
uniform mat4 u_projection;
out vec2 v_texCoord;
out vec4 v_color;
void main() {
gl_Position = u_projection * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
v_color = a_color;
}

View File

@ -5,32 +5,6 @@
namespace extra2d { namespace extra2d {
static const char* SHAPE_VERTEX_SHADER = R"(
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec4 a_color;
uniform mat4 u_viewProjection;
out vec4 v_color;
void main() {
gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0);
v_color = a_color;
}
)";
static const char* SHAPE_FRAGMENT_SHADER = R"(
#version 450 core
in vec4 v_color;
out vec4 fragColor;
void main() {
fragColor = v_color;
}
)";
ShapeRenderer::ShapeRenderer() { ShapeRenderer::ShapeRenderer() {
vertices_.resize(MAX_VERTICES); vertices_.resize(MAX_VERTICES);
} }
@ -57,22 +31,13 @@ bool ShapeRenderer::init() {
vao_->setFormat(format); vao_->setFormat(format);
vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(ShapeVertex)); vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(ShapeVertex));
if (!createShaders()) return false;
return true; return true;
} }
void ShapeRenderer::shutdown() { void ShapeRenderer::shutdown() {
if (fillShader_) {
glDeleteProgram(fillShader_);
fillShader_ = 0;
}
if (lineShader_) {
glDeleteProgram(lineShader_);
lineShader_ = 0;
}
vao_.reset(); vao_.reset();
vbo_.reset(); vbo_.reset();
material_.reset();
} }
void ShapeRenderer::begin(const glm::mat4& viewProjection) { void ShapeRenderer::begin(const glm::mat4& viewProjection) {
@ -224,52 +189,20 @@ void ShapeRenderer::end() {
} }
} }
bool ShapeRenderer::createShaders() {
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &SHAPE_VERTEX_SHADER, nullptr);
glCompileShader(vs);
GLint success;
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
if (!success) {
glDeleteShader(vs);
return false;
}
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &SHAPE_FRAGMENT_SHADER, nullptr);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
if (!success) {
glDeleteShader(vs);
glDeleteShader(fs);
return false;
}
fillShader_ = glCreateProgram();
glAttachShader(fillShader_, vs);
glAttachShader(fillShader_, fs);
glLinkProgram(fillShader_);
glGetProgramiv(fillShader_, GL_LINK_STATUS, &success);
lineShader_ = fillShader_;
glDeleteShader(vs);
glDeleteShader(fs);
return success == GL_TRUE;
}
void ShapeRenderer::flush(GLenum mode) { void ShapeRenderer::flush(GLenum mode) {
if (vertexCount_ == 0) return; if (vertexCount_ == 0 || !material_) return;
vbo_->update(0, vertexCount_ * sizeof(ShapeVertex), vertices_.data()); vbo_->update(0, vertexCount_ * sizeof(ShapeVertex), vertices_.data());
glUseProgram(fillShader_); material_->apply();
GLint vpLoc = glGetUniformLocation(fillShader_, "u_viewProjection");
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); ShaderAsset* shader = material_->shader();
if (shader) {
GLint vpLoc = shader->getUniformLocation("u_viewProjection");
if (vpLoc >= 0) {
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
}
}
vao_->bind(); vao_->bind();
glDrawArrays(mode, 0, static_cast<GLsizei>(vertexCount_)); glDrawArrays(mode, 0, static_cast<GLsizei>(vertexCount_));

View File

@ -2,42 +2,9 @@
#include <extra2d/render/render_device.h> #include <extra2d/render/render_device.h>
#include <extra2d/render/render_stats.h> #include <extra2d/render/render_stats.h>
#include <cmath> #include <cmath>
#include <glm/gtc/matrix_transform.hpp>
namespace extra2d { namespace extra2d {
static const char* SPRITE_VERTEX_SHADER = R"(
#version 450 core
layout(location = 0) in vec2 a_position;
layout(location = 1) in vec2 a_texCoord;
layout(location = 2) in vec4 a_color;
uniform mat4 u_viewProjection;
out vec2 v_texCoord;
out vec4 v_color;
void main() {
gl_Position = u_viewProjection * vec4(a_position, 0.0, 1.0);
v_texCoord = a_texCoord;
v_color = a_color;
}
)";
static const char* SPRITE_FRAGMENT_SHADER = R"(
#version 450 core
in vec2 v_texCoord;
in vec4 v_color;
uniform sampler2D u_texture;
out vec4 fragColor;
void main() {
fragColor = texture(u_texture, v_texCoord) * v_color;
}
)";
SpriteRenderer::SpriteRenderer() { SpriteRenderer::SpriteRenderer() {
vertices_.resize(MAX_VERTICES); vertices_.resize(MAX_VERTICES);
} }
@ -81,16 +48,10 @@ bool SpriteRenderer::init() {
vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(SpriteVertex)); vao_->bindVertexBuffer(0, *vbo_, 0, sizeof(SpriteVertex));
vao_->bindIndexBuffer(*ibo_); vao_->bindIndexBuffer(*ibo_);
if (!createShader()) return false;
return true; return true;
} }
void SpriteRenderer::shutdown() { void SpriteRenderer::shutdown() {
if (shader_) {
glDeleteProgram(shader_);
shader_ = 0;
}
vao_.reset(); vao_.reset();
vbo_.reset(); vbo_.reset();
ibo_.reset(); ibo_.reset();
@ -106,9 +67,13 @@ void SpriteRenderer::begin(const glm::mat4& viewProjection) {
spriteCount_ = 0; spriteCount_ = 0;
} }
void SpriteRenderer::draw(GLuint texture, const SpriteData& data) { void SpriteRenderer::draw(GLuint texture, const SpriteData& data, MaterialInstance* instance) {
if (texture != currentTexture_ || vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) { if (!material_) return;
if (instance != currentInstance_ || texture != currentTexture_ ||
vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) {
flush(); flush();
currentInstance_ = instance;
currentTexture_ = texture; currentTexture_ = texture;
} }
@ -116,22 +81,9 @@ void SpriteRenderer::draw(GLuint texture, const SpriteData& data) {
spriteCount_++; spriteCount_++;
} }
void SpriteRenderer::draw(GLuint texture, const SpriteData& data, MaterialInstance* instance) {
if (material_ && instance) {
if (instance != currentInstance_ || texture != currentTexture_ ||
vertexCount_ + VERTICES_PER_SPRITE > MAX_VERTICES) {
flushWithMaterial();
currentInstance_ = instance;
currentTexture_ = texture;
}
addQuad(data, texture);
spriteCount_++;
} else {
draw(texture, data);
}
}
void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const MaterialPropertyBlock& block) { void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const MaterialPropertyBlock& block) {
if (!material_) return;
flush(); flush();
currentTexture_ = texture; currentTexture_ = texture;
addQuad(data, texture); addQuad(data, texture);
@ -139,119 +91,43 @@ void SpriteRenderer::draw(GLuint texture, const SpriteData& data, const Material
flush(); flush();
if (material_ && material_->shader()) { if (material_->shader()) {
material_->shader()->bind(); material_->shader()->bind();
block.apply(material_->shader()); block.apply(material_->shader());
} }
} }
void SpriteRenderer::end() { void SpriteRenderer::end() {
if (material_ && currentInstance_) { flush();
flushWithMaterial();
} else {
flush();
}
}
bool SpriteRenderer::createShader() {
GLuint vs = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vs, 1, &SPRITE_VERTEX_SHADER, nullptr);
glCompileShader(vs);
GLint success;
glGetShaderiv(vs, GL_COMPILE_STATUS, &success);
if (!success) {
glDeleteShader(vs);
return false;
}
GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fs, 1, &SPRITE_FRAGMENT_SHADER, nullptr);
glCompileShader(fs);
glGetShaderiv(fs, GL_COMPILE_STATUS, &success);
if (!success) {
glDeleteShader(vs);
glDeleteShader(fs);
return false;
}
shader_ = glCreateProgram();
glAttachShader(shader_, vs);
glAttachShader(shader_, fs);
glLinkProgram(shader_);
glGetProgramiv(shader_, GL_LINK_STATUS, &success);
glDeleteShader(vs);
glDeleteShader(fs);
return success == GL_TRUE;
} }
void SpriteRenderer::flush() { void SpriteRenderer::flush() {
if (vertexCount_ == 0) return; if (vertexCount_ == 0 || !material_) return;
vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data()); vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data());
if (material_ && material_->shader()) { ShaderAsset* shader = nullptr;
material_->shader()->bind();
material_->shader()->applyStates();
GLint vpLoc = material_->shader()->getUniformLocation("u_viewProjection"); if (currentInstance_) {
currentInstance_->apply();
shader = currentInstance_->shader();
} else {
material_->apply();
shader = material_->shader();
}
if (shader) {
GLint vpLoc = shader->getUniformLocation("u_viewProjection");
if (vpLoc >= 0) { if (vpLoc >= 0) {
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]); glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
} }
GLint texLoc = material_->shader()->getUniformLocation("u_texture"); GLint texLoc = shader->getUniformLocation("u_texture");
if (texLoc >= 0) { if (texLoc >= 0) {
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, currentTexture_); glBindTexture(GL_TEXTURE_2D, currentTexture_);
glUniform1i(texLoc, 0); glUniform1i(texLoc, 0);
} }
} else {
glUseProgram(shader_);
GLint vpLoc = glGetUniformLocation(shader_, "u_viewProjection");
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
GLint texLoc = glGetUniformLocation(shader_, "u_texture");
glUniform1i(texLoc, 0);
glBindTextureUnit(0, currentTexture_);
}
vao_->bind();
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(vertexCount_ / VERTICES_PER_SPRITE * INDICES_PER_SPRITE),
GL_UNSIGNED_SHORT, nullptr);
E2D_RENDER_STATS().addDrawCall(static_cast<uint32>(vertexCount_),
static_cast<uint32>(vertexCount_ / 3));
drawCalls_++;
vertexCount_ = 0;
}
void SpriteRenderer::flushWithMaterial() {
if (vertexCount_ == 0) return;
vbo_->update(0, vertexCount_ * sizeof(SpriteVertex), vertices_.data());
if (currentInstance_) {
currentInstance_->apply();
ShaderAsset* shader = currentInstance_->shader();
if (shader) {
GLint vpLoc = shader->getUniformLocation("u_viewProjection");
if (vpLoc >= 0) {
glUniformMatrix4fv(vpLoc, 1, GL_FALSE, &viewProjection_[0][0]);
}
GLint texLoc = shader->getUniformLocation("u_texture");
if (texLoc >= 0) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, currentTexture_);
glUniform1i(texLoc, 0);
}
}
} }
vao_->bind(); vao_->bind();