diff --git a/examples/scene_graph_demo/romfs/shader/default.frag b/examples/scene_graph_demo/romfs/shader/default.frag new file mode 100644 index 0000000..4a235f0 --- /dev/null +++ b/examples/scene_graph_demo/romfs/shader/default.frag @@ -0,0 +1,40 @@ +#version 320 es +precision highp float; + +// 从顶点着色器输入 +in vec2 vTexCoord; +in vec4 vColor; +in vec4 vTintColor; +in float vOpacity; + +// 纹理采样器 +uniform sampler2D uTexture; + +// 输出颜色 +out vec4 fragColor; + +/** + * @brief 片段着色器入口 + * + * 采样纹理并与顶点颜色、色调和透明度混合 + */ +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; + + // Alpha 测试:丢弃几乎透明的像素 + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/examples/scene_graph_demo/romfs/shader/default.vert b/examples/scene_graph_demo/romfs/shader/default.vert new file mode 100644 index 0000000..95fca8b --- /dev/null +++ b/examples/scene_graph_demo/romfs/shader/default.vert @@ -0,0 +1,48 @@ +#version 320 es +precision highp float; + +// 全局 UBO (binding = 0) - 每帧更新一次 +layout(std140, binding = 0) uniform GlobalUBO { + mat4 uViewProjection; + vec4 uCameraPosition; + float uTime; + float uDeltaTime; + vec2 uScreenSize; +}; + +// 材质 UBO (binding = 1) - 每物体更新 +layout(std140, binding = 1) uniform MaterialUBO { + vec4 uColor; + vec4 uTintColor; + float uOpacity; + float uPadding[3]; // std140 对齐填充 +}; + +// 模型矩阵作为单独的统一变量(每个物体设置) +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; + +/** + * @brief 顶点着色器入口 + * + * 计算顶点在裁剪空间中的位置, + * 并传递纹理坐标和颜色到片段着色器 + */ +void main() { + gl_Position = uViewProjection * uModelMatrix * vec4(aPosition, 0.0, 1.0); + vTexCoord = aTexCoord; + // 混合顶点颜色和材质 UBO 中的颜色 + vColor = aColor * uColor; + vTintColor = uTintColor; + vOpacity = uOpacity; +} diff --git a/examples/scene_graph_demo/romfs/shader/instanced.vert b/examples/scene_graph_demo/romfs/shader/instanced.vert new file mode 100644 index 0000000..b910b52 --- /dev/null +++ b/examples/scene_graph_demo/romfs/shader/instanced.vert @@ -0,0 +1,73 @@ +#version 320 es +precision highp float; + +// 全局 UBO (binding = 0) - 每帧更新一次 +layout(std140, binding = 0) uniform GlobalUBO { + mat4 uViewProjection; + vec4 uCameraPosition; + float uTime; + float uDeltaTime; + vec2 uScreenSize; +}; + +// 材质 UBO (binding = 1) - 每批次更新 +layout(std140, binding = 1) uniform MaterialUBO { + vec4 uColor; + vec4 uTintColor; + float uOpacity; + float uPadding[3]; // std140 对齐填充 +}; + +// 顶点属性 (每个顶点) +layout(location = 0) in vec2 aPosition; +layout(location = 1) in vec2 aTexCoord; +layout(location = 2) in vec4 aColor; + +// 实例属性 (每个实例) - 使用 location 3-6 +layout(location = 3) in vec2 iPosition; // 实例位置 +layout(location = 4) in float iRotation; // 实例旋转 +layout(location = 5) in vec2 iScale; // 实例缩放 +layout(location = 6) in vec4 iColor; // 实例颜色 + +// 输出到片段着色器 +out vec2 vTexCoord; +out vec4 vColor; +out vec4 vTintColor; +out float vOpacity; + +/** + * @brief 从旋转角度构建2D变换矩阵 + * @param angle 旋转角度(弧度) + * @return 2x2旋转矩阵 + */ +mat2 rotate2D(float angle) { + float c = cos(angle); + float s = sin(angle); + return mat2(c, -s, s, c); +} + +/** + * @brief 顶点着色器入口 + * + * 计算顶点在裁剪空间中的位置, + * 应用实例的变换(位置、旋转、缩放), + * 并传递纹理坐标和颜色到片段着色器 + */ +void main() { + // 应用实例缩放和旋转 + vec2 localPos = rotate2D(iRotation) * (aPosition * iScale); + + // 应用实例位置偏移 + vec2 worldPos = localPos + iPosition; + + // 变换到裁剪空间 + gl_Position = uViewProjection * vec4(worldPos, 0.0, 1.0); + + vTexCoord = aTexCoord; + + // 混合顶点颜色、实例颜色和材质颜色 + vColor = aColor * iColor * uColor; + + vTintColor = uTintColor; + vOpacity = uOpacity; +} diff --git a/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag b/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag new file mode 100644 index 0000000..aad250d --- /dev/null +++ b/examples/scene_graph_demo/romfs/shader/sprite_instanced.frag @@ -0,0 +1,35 @@ +#version 320 es +precision highp float; + +// 从顶点着色器输入 +in vec2 vTexCoord; +in vec4 vColor; + +// 纹理采样器 +uniform sampler2D uTexture; + +// 输出颜色 +out vec4 fragColor; + +/** + * @brief 片段着色器入口(实例化版本) + * + * 采样纹理并与顶点颜色混合 + */ +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; + + // Alpha 测试:丢弃几乎透明的像素 + if (fragColor.a < 0.01) { + discard; + } +} diff --git a/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert b/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert new file mode 100644 index 0000000..0675904 --- /dev/null +++ b/examples/scene_graph_demo/romfs/shader/sprite_instanced.vert @@ -0,0 +1,58 @@ +#version 320 es +precision highp float; + +// 全局 UBO (binding = 0) - 每帧更新一次 +layout(std140, binding = 0) uniform GlobalUBO { + mat4 uViewProjection; + vec4 uCameraPosition; + float uTime; + float uDeltaTime; + vec2 uScreenSize; +}; + +// 顶点属性 +layout(location = 0) in vec2 aPosition; // 基础顶点位置 +layout(location = 1) in vec2 aTexCoord; // 基础 UV +layout(location = 2) in vec4 aColor; // 基础颜色 + +// 实例属性 (每个实例) +layout(location = 3) in vec2 aInstancePos; // 实例位置偏移 +layout(location = 4) in float aInstanceRot; // 实例旋转 +layout(location = 5) in vec2 aInstanceScale; // 实例缩放 +layout(location = 6) in vec4 aInstanceColor; // 实例颜色 +layout(location = 7) in vec4 aInstanceUV; // 实例 UV 区域 + +// 输出到片段着色器 +out vec2 vTexCoord; +out vec4 vColor; + +/** + * @brief 顶点着色器入口(实例化版本) + * + * 计算顶点在裁剪空间中的位置,应用实例的变换 + */ +void main() { + // 构建旋转矩阵 + float cosRot = cos(aInstanceRot); + float sinRot = sin(aInstanceRot); + mat2 rotation = mat2( + cosRot, -sinRot, + sinRot, cosRot + ); + + // 应用缩放和旋转 + vec2 scaledPos = aPosition * aInstanceScale; + vec2 rotatedPos = rotation * scaledPos; + + // 应用实例位置偏移 + vec2 worldPos = rotatedPos + aInstancePos; + + // 计算裁剪空间位置 + gl_Position = uViewProjection * vec4(worldPos, 0.0, 1.0); + + // 计算 UV 坐标(应用实例 UV 区域) + vTexCoord = aTexCoord * aInstanceUV.zw + aInstanceUV.xy; + + // 混合顶点颜色和实例颜色 + vColor = aColor * aInstanceColor; +} diff --git a/examples/scene_graph_demo/xmake.lua b/examples/scene_graph_demo/xmake.lua index 2383d66..c076f92 100644 --- a/examples/scene_graph_demo/xmake.lua +++ b/examples/scene_graph_demo/xmake.lua @@ -28,9 +28,25 @@ target("scene_graph_demo") local nacptool = path.join(devkitPro, "tools/bin/nacptool.exe") local elf2nro = path.join(devkitPro, "tools/bin/elf2nro.exe") + -- 确保 romfs 目录存在 + local romfs = path.join(example_dir, "romfs") + if not os.isdir(romfs) then + os.mkdir(romfs) + end + + -- 复制着色器文件到 romfs/shader 目录 + local shader_source = path.join(example_dir, "../../shader") + local shader_target = path.join(romfs, "shader") + 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 to romfs: " .. shader_target) + end + if os.isfile(nacptool) and os.isfile(elf2nro) then os.vrunv(nacptool, {"--create", "Scene Graph Demo", "Extra2D Team", "1.0.0", nacp_file}) - local romfs = path.join(example_dir, "romfs") if os.isdir(romfs) then os.vrunv(elf2nro, {elf_file, nro_file, "--nacp=" .. nacp_file, "--romfsdir=" .. romfs}) else