feat: 添加图像显示示例并支持纹理覆盖
- 新增 image_display_demo 示例项目,展示如何加载和显示纹理 - 在 SpriteRenderer 中添加纹理覆盖支持,允许组件指定独立纹理 - 修改渲染管线,在绑定着色器时设置默认纹理单元 - 更新纹理加载逻辑,简化纹理数据上传接口 - 扩展渲染命令结构,包含可选的纹理覆盖句柄 - 调整命令队列提交接口,支持纹理覆盖参数传递
This commit is contained in:
parent
8f03fa80fb
commit
dcb3162525
|
|
@ -0,0 +1,98 @@
|
|||
#include <cstdio>
|
||||
#include <extra2d.h>
|
||||
#include <renderer/rhi_module.h>
|
||||
|
||||
using namespace extra2d;
|
||||
|
||||
class ImageDisplayScene : public Scene {
|
||||
public:
|
||||
void onEnter() override {
|
||||
Scene::onEnter();
|
||||
createCamera();
|
||||
createImageSprite();
|
||||
}
|
||||
|
||||
void update(float dt) override {
|
||||
Scene::update(dt);
|
||||
if (textureLoaded_) {
|
||||
return;
|
||||
}
|
||||
auto *rhiModule = RHIModule::get();
|
||||
if (!rhiModule || !rhiModule->getDevice()) {
|
||||
return;
|
||||
}
|
||||
auto assets = getAssets();
|
||||
if (!assets || !spriteRenderer_) {
|
||||
return;
|
||||
}
|
||||
auto texture = assets->load<Texture>("assets/test.png");
|
||||
if (texture.isValid()) {
|
||||
spriteRenderer_->setTexture(texture);
|
||||
printf("图片加载成功: assets/test.png\n");
|
||||
} else {
|
||||
printf("图片加载失败: assets/test.png\n");
|
||||
}
|
||||
textureLoaded_ = true;
|
||||
}
|
||||
|
||||
private:
|
||||
void createCamera() {
|
||||
auto cameraNode = makePtr<Node>();
|
||||
cameraNode->setName("MainCamera");
|
||||
cameraNode->setPosition(0.0f, 0.0f);
|
||||
|
||||
auto camera = makePtr<CameraComponent>();
|
||||
camera->setOrtho(0.0f, 1280.0f, 720.0f, 0.0f, -1.0f, 1.0f);
|
||||
cameraNode->addComponent(camera);
|
||||
setMainCamera(camera);
|
||||
addChild(cameraNode);
|
||||
}
|
||||
|
||||
void createImageSprite() {
|
||||
auto spriteNode = makePtr<Node>();
|
||||
spriteNode->setName("ImageSprite");
|
||||
spriteNode->setPosition(640.0f, 360.0f);
|
||||
// spriteNode->setSize(384.0f, 384.0f);
|
||||
spriteNode->setAnchor(0.5f, 0.5f);
|
||||
|
||||
spriteRenderer_ = makePtr<SpriteRenderer>();
|
||||
// spriteRenderer_->setColor(Color::White);
|
||||
spriteNode->addComponent(spriteRenderer_);
|
||||
addChild(spriteNode);
|
||||
}
|
||||
|
||||
Ptr<SpriteRenderer> spriteRenderer_;
|
||||
bool textureLoaded_ = false;
|
||||
};
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
auto app = Application::create();
|
||||
|
||||
AppConfig config;
|
||||
config.title = "Image Display Demo - Extra2D";
|
||||
config.width = 1280;
|
||||
config.height = 720;
|
||||
|
||||
if (!app->init(config)) {
|
||||
printf("应用程序初始化失败!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
SceneModule *sceneModule = app->getModule<SceneModule>();
|
||||
if (!sceneModule) {
|
||||
printf("获取场景模块失败!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
Director *director = sceneModule->getDirector();
|
||||
if (!director) {
|
||||
printf("获取导演器失败!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto scene = makePtr<ImageDisplayScene>();
|
||||
director->runScene(scene);
|
||||
|
||||
app->run();
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 644 KiB |
|
|
@ -0,0 +1,65 @@
|
|||
#type vertex
|
||||
#version 320 es
|
||||
precision highp float;
|
||||
|
||||
layout(std140, binding = 0) uniform GlobalUBO {
|
||||
mat4 uViewProjection;
|
||||
vec4 uCameraPosition;
|
||||
float uTime;
|
||||
float uDeltaTime;
|
||||
vec2 uScreenSize;
|
||||
};
|
||||
|
||||
layout(std140, binding = 1) uniform MaterialUBO {
|
||||
vec4 uColor;
|
||||
vec4 uTintColor;
|
||||
float uOpacity;
|
||||
float uPadding[3];
|
||||
};
|
||||
|
||||
uniform mat4 uModelMatrix;
|
||||
|
||||
layout(location = 0) in vec2 aPosition;
|
||||
layout(location = 1) in vec2 aTexCoord;
|
||||
layout(location = 2) in vec4 aColor;
|
||||
|
||||
out vec2 vTexCoord;
|
||||
out vec4 vColor;
|
||||
out vec4 vTintColor;
|
||||
out float vOpacity;
|
||||
|
||||
void main() {
|
||||
gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0);
|
||||
vTexCoord = aTexCoord;
|
||||
vColor = aColor * uColor;
|
||||
vTintColor = uTintColor;
|
||||
vOpacity = uOpacity;
|
||||
}
|
||||
|
||||
#type fragment
|
||||
#version 320 es
|
||||
precision highp float;
|
||||
|
||||
in vec2 vTexCoord;
|
||||
in vec4 vColor;
|
||||
in vec4 vTintColor;
|
||||
in float vOpacity;
|
||||
|
||||
uniform sampler2D uTexture;
|
||||
|
||||
out vec4 fragColor;
|
||||
|
||||
void main() {
|
||||
vec4 texColor = texture(uTexture, vTexCoord);
|
||||
|
||||
if (texColor.rgb == vec3(0.0) || texColor.a < 0.01) {
|
||||
texColor = vec4(1.0, 1.0, 1.0, 1.0);
|
||||
}
|
||||
|
||||
fragColor = texColor * vColor * vTintColor;
|
||||
fragColor.a *= vOpacity;
|
||||
|
||||
if (fragColor.a < 0.01) {
|
||||
discard;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,79 @@
|
|||
local example_dir = os.scriptdir()
|
||||
|
||||
target("image_display_demo")
|
||||
set_kind("binary")
|
||||
add_files("main.cpp")
|
||||
add_includedirs("../../include")
|
||||
add_deps("extra2d")
|
||||
|
||||
if is_plat("switch") then
|
||||
set_targetdir("../../build/examples/image_display_demo")
|
||||
|
||||
after_build(function (target)
|
||||
local devkitPro = os.getenv("DEVKITPRO") or "C:/devkitPro"
|
||||
local elf_file = target:targetfile()
|
||||
local output_dir = path.directory(elf_file)
|
||||
local nacp_file = path.join(output_dir, "image_display_demo.nacp")
|
||||
local nro_file = path.join(output_dir, "image_display_demo.nro")
|
||||
local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe")
|
||||
local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe")
|
||||
local romfs = path.join(example_dir, "romfs")
|
||||
local assets_source = path.join(example_dir, "../scene_graph_demo/romfs/assets")
|
||||
local assets_target = path.join(romfs, "assets")
|
||||
local shader_source = path.join(example_dir, "../../shader")
|
||||
local shader_target = path.join(romfs, "shader")
|
||||
|
||||
if not os.isdir(romfs) then
|
||||
os.mkdir(romfs)
|
||||
end
|
||||
if not os.isdir(assets_target) then
|
||||
os.mkdir(assets_target)
|
||||
end
|
||||
if os.isdir(assets_source) then
|
||||
os.cp(path.join(assets_source, "**"), assets_target)
|
||||
end
|
||||
|
||||
if os.isdir(shader_source) then
|
||||
if not os.isdir(shader_target) then
|
||||
os.mkdir(shader_target)
|
||||
end
|
||||
os.cp(path.join(shader_source, "*"), shader_target)
|
||||
end
|
||||
|
||||
if os.isfile(nacptool) and os.isfile(elf2nro) then
|
||||
os.vrunv(nacptool, {"--create", "Image Display Demo", "Extra2D Team", "1.0.0", nacp_file})
|
||||
if os.isdir(romfs) then
|
||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs})
|
||||
else
|
||||
os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file})
|
||||
end
|
||||
end
|
||||
end)
|
||||
elseif is_plat("mingw") then
|
||||
set_targetdir("../../build/examples/image_display_demo")
|
||||
|
||||
after_build(function (target)
|
||||
local target_dir = path.directory(target:targetfile())
|
||||
local assets_dir = path.join(target_dir, "assets")
|
||||
local assets_source = path.join(example_dir, "../scene_graph_demo/romfs/assets")
|
||||
local shader_source = path.join(example_dir, "../../shader")
|
||||
local shader_target = path.join(target_dir, "shader")
|
||||
|
||||
if not os.isdir(assets_dir) then
|
||||
os.mkdir(assets_dir)
|
||||
end
|
||||
if os.isdir(assets_source) then
|
||||
os.cp(path.join(assets_source, "**"), assets_dir)
|
||||
print("Copied assets from " .. assets_source .. " to " .. assets_dir)
|
||||
end
|
||||
|
||||
if os.isdir(shader_source) then
|
||||
if not os.isdir(shader_target) then
|
||||
os.mkdir(shader_target)
|
||||
end
|
||||
os.cp(path.join(shader_source, "*"), shader_target)
|
||||
print("Copied shaders from " .. shader_source .. " to " .. shader_target)
|
||||
end
|
||||
end)
|
||||
end
|
||||
target_end()
|
||||
|
|
@ -16,6 +16,7 @@ namespace extra2d {
|
|||
class CommandQueue;
|
||||
class Material;
|
||||
class Mesh;
|
||||
class Texture;
|
||||
class UniformBufferManager;
|
||||
template <typename T> class Ptr;
|
||||
|
||||
|
|
@ -295,6 +296,7 @@ public:
|
|||
* @param color 颜色
|
||||
*/
|
||||
void submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||
Ptr<Texture> textureOverride,
|
||||
const struct Transform &transform, const Color &color,
|
||||
uint32_t sortKey = 0);
|
||||
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ struct RenderCommand {
|
|||
struct DrawMeshData {
|
||||
Handle<Mesh> mesh; // 网格句柄
|
||||
Handle<Material> material; // 材质句柄
|
||||
Handle<Texture> texture; // 纹理句柄(用于默认材质覆盖)
|
||||
Vec2 pos; // 位置
|
||||
Vec2 scale; // 缩放
|
||||
float rot; // 旋转角度
|
||||
|
|
@ -69,6 +70,7 @@ struct RenderCommand {
|
|||
RenderCommand() : type(RenderCommandType::DrawMesh), sortKey(0) {
|
||||
drawMesh.mesh = Handle<Mesh>::invalid();
|
||||
drawMesh.material = Handle<Material>::invalid();
|
||||
drawMesh.texture = Handle<Texture>::invalid();
|
||||
drawMesh.pos = Vec2(0.0f, 0.0f);
|
||||
drawMesh.scale = Vec2(1.0f, 1.0f);
|
||||
drawMesh.rot = 0.0f;
|
||||
|
|
|
|||
|
|
@ -360,6 +360,7 @@ UniformBuffer *CommandQueue::getCurrentMaterialUBO() const {
|
|||
}
|
||||
|
||||
void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
||||
Ptr<Texture> textureOverride,
|
||||
const struct Transform &transform,
|
||||
const Color &color, uint32_t sortKey) {
|
||||
if (!material || !mesh || !material->getShader()) {
|
||||
|
|
@ -391,12 +392,18 @@ void CommandQueue::submitDraw(Ptr<Material> material, Ptr<Mesh> mesh,
|
|||
|
||||
// 设置纹理
|
||||
const auto &textures = material->getTextures();
|
||||
cmd.textureCount =
|
||||
static_cast<uint32_t>(std::min(textures.size(), size_t(8)));
|
||||
for (uint32_t i = 0; i < cmd.textureCount; ++i) {
|
||||
if (textures[i].texture) {
|
||||
cmd.textures[i] = textures[i].texture->getHandle();
|
||||
cmd.textureCount = 0;
|
||||
for (const auto &textureSlot : textures) {
|
||||
if (!textureSlot.texture) {
|
||||
continue;
|
||||
}
|
||||
uint32_t slot = std::min(textureSlot.slot, 7u);
|
||||
cmd.textures[slot] = textureSlot.texture->getHandle();
|
||||
cmd.textureCount = std::max(cmd.textureCount, slot + 1);
|
||||
}
|
||||
if (textureOverride) {
|
||||
cmd.textures[0] = textureOverride->getHandle();
|
||||
cmd.textureCount = std::max(cmd.textureCount, 1u);
|
||||
}
|
||||
|
||||
// 分配材质 UBO 空间(使用 CPU 缓冲区)
|
||||
|
|
|
|||
|
|
@ -199,11 +199,13 @@ void RendererModule::onRenderSubmit(const RenderCommand &cmd) {
|
|||
defaultMeshPtr_ = mesh;
|
||||
}
|
||||
|
||||
Texture *texture = assets->get(cmd.drawMesh.texture);
|
||||
if (material && mesh) {
|
||||
Transform transform(cmd.drawMesh.pos, cmd.drawMesh.scale,
|
||||
cmd.drawMesh.rot);
|
||||
commandQueue_->submitDraw(Ptr<Material>(material), Ptr<Mesh>(mesh),
|
||||
transform, cmd.drawMesh.color, cmd.sortKey);
|
||||
Ptr<Texture>(texture), transform,
|
||||
cmd.drawMesh.color, cmd.sortKey);
|
||||
stats_.commandsSubmitted++;
|
||||
} else {
|
||||
E2D_WARN("提交绘制命令失败: 材质={}, 网格={}", material ? "有效" : "空",
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ void GLPipeline::bind() {
|
|||
// 绑定着色器程序
|
||||
if (shaderProgram_ != 0) {
|
||||
glUseProgram(shaderProgram_);
|
||||
GLint textureLocation = glGetUniformLocation(shaderProgram_, "uTexture");
|
||||
if (textureLocation != -1) {
|
||||
glUniform1i(textureLocation, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// 注意:VAO 需要与具体的顶点缓冲区绑定才能工作
|
||||
|
|
|
|||
|
|
@ -94,12 +94,7 @@ bool Texture::loadFromMemory(const uint8_t *data, int width, int height,
|
|||
|
||||
// 上传纹理数据
|
||||
if (data) {
|
||||
// 注意:update 方法的参数需要根据实际 RHI 接口调整
|
||||
// 这里假设 update 接受数据指针
|
||||
texture->update(
|
||||
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||||
// 生成 mipmap
|
||||
// 注意:generateMipmap 方法需要在 RHI 接口中实现
|
||||
texture->update(data, 0);
|
||||
}
|
||||
|
||||
// 获取纹理句柄
|
||||
|
|
@ -227,8 +222,7 @@ bool Texture::reloadFromMemory(const uint8_t *data, int width, int height,
|
|||
|
||||
// 上传纹理数据
|
||||
if (data) {
|
||||
texture->update(
|
||||
data, static_cast<size_t>(width * height * getBytesPerPixel(format)));
|
||||
texture->update(data, 0);
|
||||
}
|
||||
|
||||
// 替换旧的纹理句柄
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ void SpriteRenderer::render() {
|
|||
// 如果没有指定网格,使用默认的四边形网格
|
||||
cmd.drawMesh.mesh = Handle<Mesh>::invalid(); // RendererModule 会使用默认网格
|
||||
cmd.drawMesh.material = material_.isValid() ? material_ : Handle<Material>::invalid();
|
||||
cmd.drawMesh.texture = texture_.isValid() ? texture_ : Handle<Texture>::invalid();
|
||||
cmd.setTransform(worldTransform);
|
||||
|
||||
cmd.setColor(color_);
|
||||
|
|
|
|||
|
|
@ -95,6 +95,7 @@ define_extra2d_engine()
|
|||
if is_config("examples","true") then
|
||||
includes("examples/hello_world", {rootdir = "examples/hello_world"})
|
||||
includes("examples/scene_graph_demo", {rootdir = "examples/scene_graph_demo"})
|
||||
includes("examples/image_display_demo", {rootdir = "examples/image_display_demo"})
|
||||
end
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue