Files
mirai/docs/shader_widget_examples.md
nanako 5d9cde18a3 feat(widget): add rounded rectangle mask widget and shader widget base
- Implemented `rounded_rect_mask_widget` for creating rounded rectangle masks with customizable corner radii, feathering, and border width.
- Introduced `shader_widget` and `shader_widget_base` classes for managing shader resources, including pipeline and descriptor set creation.
- Added concepts for shader bindings to ensure type safety and proper usage of shader resources.
- Defined enums for shader widget modes and dirty flags to manage widget state and updates effectively.
- Created utility functions for bitwise operations on dirty flags to simplify state management.
2026-01-02 21:33:03 +08:00

20 KiB
Raw Permalink Blame History

MIRAI Shader Widget 使用示例

本文档提供 MIRAI 框架着色器控件系统的详细使用示例,涵盖四种渲染模式和自定义控件开发。

注意:本文档使用标准 GLSL 着色器语言。详见 ADR-003: 迁移到 GLSL 着色器系统

目录

  1. Procedural 模式示例
  2. Backdrop 模式示例
  3. Mask 模式示例
  4. Custom Mesh 模式示例
  5. Push Constant 使用示例
  6. 用户 UBO 使用示例

1. Procedural 模式示例

Procedural 模式用于程序化渲染,基于坐标生成图形。框架提供顶点着色器,用户只需编写片段着色器。

1.1 线性渐变效果

使用 Push Constant 传递颜色和角度参数:

#version 450

// ============================================================
// 线性渐变效果 (Procedural 模式)
// ============================================================

// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Push Constant: 渐变参数
layout(push_constant) uniform PushConstants {
    vec4 color_start;  // 起始颜色
    vec4 color_end;    // 结束颜色
    float angle;       // 渐变角度(弧度)
    float _pad[3];
} pc;

void main() {
    // 计算渐变方向向量
    vec2 dir = vec2(cos(pc.angle), sin(pc.angle));
    
    // 计算当前位置在渐变方向上的投影
    float t = dot(v_local_pos - 0.5, dir) + 0.5;
    
    // 线性插值颜色
    frag_color = mix(pc.color_start, pc.color_end, clamp(t, 0.0, 1.0));
    
    // 应用框架透明度
    frag_color.a *= widget.u_opacity;
}

1.2 径向渐变效果

#version 450

// 框架输入
layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Push Constant: 径向渐变参数
layout(push_constant) uniform PushConstants {
    vec4 color_center;  // 中心颜色
    vec4 color_edge;    // 边缘颜色
    vec2 center;        // 渐变中心 (0-1)
    float radius;       // 渐变半径
    float _pad;
} pc;

void main() {
    // 计算到中心的距离
    float dist = length(v_local_pos - pc.center) / pc.radius;
    
    // 径向插值
    frag_color = mix(pc.color_center, pc.color_edge, clamp(dist, 0.0, 1.0));
    
    frag_color.a *= widget.u_opacity;
}

1.3 棋盘格图案

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

layout(push_constant) uniform PushConstants {
    vec4 color_a;      // 颜色 A
    vec4 color_b;      // 颜色 B
    float grid_size;   // 格子大小
    float _pad[3];
} pc;

void main() {
    // 计算格子坐标
    vec2 grid = floor(v_local_pos * widget.u_bounds.zw / pc.grid_size);
    
    // 棋盘格模式
    float checker = mod(grid.x + grid.y, 2.0);
    
    frag_color = mix(pc.color_a, pc.color_b, checker);
    frag_color.a *= widget.u_opacity;
}

2. Backdrop 模式示例

Backdrop 模式用于后处理效果,框架提供背景纹理 u_backdrop

2.1 高斯模糊效果

使用用户 UBO (Set 1) 传递模糊参数:

#version 450

// ============================================================
// 高斯模糊效果 (Backdrop 模式)
// ============================================================

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

// Set 0: 框架专用
layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// 背景纹理(框架提供)
layout(set = 0, binding = 1) uniform sampler2D u_backdrop;

// Set 1: 用户 UBO - 模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
    float sigma;       // 高斯分布标准差
    int   samples;     // 采样半径
    vec2  texel_size;  // 纹理像素大小 (1.0/width, 1.0/height)
} params;

void main() {
    vec4 color = vec4(0.0);
    float total_weight = 0.0;
    
    // 高斯模糊核
    float sigma2 = params.sigma * params.sigma;
    float inv_2sigma2 = 1.0 / (2.0 * sigma2);
    
    // 双重循环采样
    for (int i = -params.samples; i <= params.samples; i++) {
        for (int j = -params.samples; j <= params.samples; j++) {
            vec2 offset = vec2(float(i), float(j)) * params.texel_size;
            
            // 计算高斯权重
            float dist2 = float(i * i + j * j);
            float weight = exp(-dist2 * inv_2sigma2);
            
            color += texture(u_backdrop, v_uv + offset) * weight;
            total_weight += weight;
        }
    }
    
    frag_color = color / total_weight;
    frag_color.a *= widget.u_opacity;
}

2.2 色彩调整效果

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

layout(set = 0, binding = 1) uniform sampler2D u_backdrop;

// Push Constant: 色彩调整参数
layout(push_constant) uniform PushConstants {
    float brightness;  // 亮度 (-1 to 1)
    float contrast;    // 对比度 (0 to 2)
    float saturation;  // 饱和度 (0 to 2)
    float _pad;
} pc;

void main() {
    vec4 color = texture(u_backdrop, v_uv);
    
    // 亮度调整
    color.rgb += pc.brightness;
    
    // 对比度调整
    color.rgb = (color.rgb - 0.5) * pc.contrast + 0.5;
    
    // 饱和度调整
    float gray = dot(color.rgb, vec3(0.299, 0.587, 0.114));
    color.rgb = mix(vec3(gray), color.rgb, pc.saturation);
    
    frag_color = color;
    frag_color.a *= widget.u_opacity;
}

2.3 毛玻璃效果

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

layout(set = 0, binding = 1) uniform sampler2D u_backdrop;

// Set 1: 模糊参数
layout(set = 1, binding = 0, std140) uniform BlurParams {
    float sigma;
    int   samples;
    vec2  texel_size;
} blur;

// Push Constant: 毛玻璃参数
layout(push_constant) uniform PushConstants {
    vec4 tint_color;   // 着色颜色
    float tint_amount; // 着色强度 (0-1)
    float _pad[3];
} pc;

void main() {
    // 模糊采样
    vec4 color = vec4(0.0);
    float total = 0.0;
    float inv_2sigma2 = 1.0 / (2.0 * blur.sigma * blur.sigma);
    
    for (int i = -blur.samples; i <= blur.samples; i++) {
        for (int j = -blur.samples; j <= blur.samples; j++) {
            vec2 offset = vec2(float(i), float(j)) * blur.texel_size;
            float w = exp(-float(i*i + j*j) * inv_2sigma2);
            color += texture(u_backdrop, v_uv + offset) * w;
            total += w;
        }
    }
    color /= total;
    
    // 应用着色
    color.rgb = mix(color.rgb, pc.tint_color.rgb, pc.tint_amount);
    
    frag_color = color;
    frag_color.a *= widget.u_opacity;
}

3. Mask 模式示例

Mask 模式用于创建遮罩效果,通常输出带有 alpha 通道的颜色。

3.1 圆角矩形遮罩

使用 SDF有符号距离场生成高质量圆角矩形

#version 450

// ============================================================
// 圆角矩形遮罩 (Mask 模式)
// ============================================================

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Push Constant: 遮罩参数
layout(push_constant) uniform PushConstants {
    vec4  color;   // 遮罩颜色
    float radius;  // 圆角半径(像素)
    float _pad[3];
} pc;

// 圆角矩形 SDF
float rounded_rect_sdf(vec2 p, vec2 half_size, float radius) {
    vec2 q = abs(p) - half_size + radius;
    return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - radius;
}

void main() {
    vec2 size = widget.u_bounds.zw;
    vec2 half_size = size * 0.5;
    
    // 转换到以中心为原点的坐标系
    vec2 p = (v_local_pos - 0.5) * size;
    
    // 计算 SDF
    float d = rounded_rect_sdf(p, half_size, pc.radius);
    
    // 抗锯齿
    float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
    
    frag_color = pc.color;
    frag_color.a *= alpha * widget.u_opacity;
}

3.2 圆形遮罩

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

layout(push_constant) uniform PushConstants {
    vec4 color;
    vec2 center;    // 圆心 (0-1)
    float radius;   // 半径 (相对于短边)
    float softness; // 边缘柔和度
} pc;

void main() {
    // 计算宽高比
    vec2 size = widget.u_bounds.zw;
    float aspect = size.x / size.y;
    
    // 调整坐标以保持圆形
    vec2 uv = v_local_pos;
    uv.x *= aspect;
    vec2 c = pc.center;
    c.x *= aspect;
    
    // 计算到圆心的距离
    float dist = length(uv - c);
    
    // 计算遮罩
    float min_size = min(size.x, size.y);
    float r = pc.radius * min_size / size.x;  // 调整半径
    float alpha = 1.0 - smoothstep(r - pc.softness, r + pc.softness, dist);
    
    frag_color = pc.color;
    frag_color.a *= alpha * widget.u_opacity;
}

3.3 四角独立圆角

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

layout(push_constant) uniform PushConstants {
    vec4 color;
    vec4 radii;  // (左上, 右上, 右下, 左下)
} pc;

// 四角独立圆角 SDF
float rounded_rect_sdf_4(vec2 p, vec2 half_size, vec4 radii) {
    // 根据象限选择圆角半径
    float r = (p.x > 0.0) 
        ? ((p.y > 0.0) ? radii.y : radii.z)  // 右上 / 右下
        : ((p.y > 0.0) ? radii.x : radii.w); // 左上 / 左下
    
    vec2 q = abs(p) - half_size + r;
    return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
}

void main() {
    vec2 size = widget.u_bounds.zw;
    vec2 half_size = size * 0.5;
    vec2 p = (v_local_pos - 0.5) * size;
    
    float d = rounded_rect_sdf_4(p, half_size, pc.radii);
    float alpha = 1.0 - smoothstep(-1.0, 1.0, d);
    
    frag_color = pc.color;
    frag_color.a *= alpha * widget.u_opacity;
}

4. Custom Mesh 模式示例

Custom Mesh 模式需要用户同时提供顶点和片段着色器。

4.1 粒子系统

顶点着色器:

#version 450

// 粒子实例数据
layout(location = 0) in vec3 a_position;   // 粒子位置
layout(location = 1) in float a_size;      // 粒子大小
layout(location = 2) in vec4 a_color;      // 粒子颜色
layout(location = 3) in float a_rotation;  // 粒子旋转

layout(location = 0) out vec2 v_uv;
layout(location = 1) out vec4 v_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Push Constant: 变换矩阵
layout(push_constant) uniform PushConstants {
    mat4 view_proj;
} pc;

// 四边形顶点Billboard
const vec2 QUAD_VERTICES[6] = vec2[](
    vec2(-0.5, -0.5), vec2(0.5, -0.5), vec2(0.5, 0.5),
    vec2(-0.5, -0.5), vec2(0.5, 0.5), vec2(-0.5, 0.5)
);

const vec2 QUAD_UVS[6] = vec2[](
    vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(1.0, 1.0),
    vec2(0.0, 0.0), vec2(1.0, 1.0), vec2(0.0, 1.0)
);

void main() {
    // 获取四边形顶点
    vec2 quad_pos = QUAD_VERTICES[gl_VertexIndex % 6];
    v_uv = QUAD_UVS[gl_VertexIndex % 6];
    
    // 应用旋转
    float c = cos(a_rotation);
    float s = sin(a_rotation);
    mat2 rot = mat2(c, -s, s, c);
    quad_pos = rot * quad_pos;
    
    // 应用大小
    quad_pos *= a_size;
    
    // 计算世界位置
    vec3 world_pos = a_position + vec3(quad_pos, 0.0);
    
    gl_Position = pc.view_proj * vec4(world_pos, 1.0);
    v_color = a_color;
}

片段着色器:

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Set 2: 粒子纹理(可选)
layout(set = 2, binding = 0) uniform sampler2D u_particle_texture;

void main() {
    // 采样粒子纹理
    vec4 tex_color = texture(u_particle_texture, v_uv);
    
    frag_color = tex_color * v_color;
    frag_color.a *= widget.u_opacity;
}

4.2 简单圆形粒子(无纹理)

片段着色器:

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec4 v_color;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

void main() {
    // 计算到中心的距离
    vec2 center = v_uv - 0.5;
    float dist = length(center) * 2.0;
    
    // 圆形遮罩,带软边缘
    float alpha = 1.0 - smoothstep(0.8, 1.0, dist);
    
    frag_color = v_color;
    frag_color.a *= alpha * widget.u_opacity;
}

5. Push Constant 使用示例

Push Constant 完全留给用户,最大 128 bytes适合高频更新的参数。

5.1 Push Constant 结构设计

// 示例 1简单颜色参数 (32 bytes)
layout(push_constant) uniform PushConstants {
    vec4 color;        // 16 bytes
    vec4 secondary;    // 16 bytes
} pc;

// 示例 2渐变参数 (48 bytes)
layout(push_constant) uniform PushConstants {
    vec4 color_start;  // 16 bytes
    vec4 color_end;    // 16 bytes
    float angle;       // 4 bytes
    float _pad[3];     // 12 bytes (对齐)
} pc;

// 示例 3变换矩阵 (64 bytes)
layout(push_constant) uniform PushConstants {
    mat4 transform;    // 64 bytes
} pc;

// 示例 4复杂参数 (128 bytes - 最大)
layout(push_constant) uniform PushConstants {
    mat4 transform;    // 64 bytes
    vec4 color;        // 16 bytes
    vec4 params;       // 16 bytes
    vec4 extra[2];     // 32 bytes
} pc;

5.2 C++ 端 Push Constant 更新

// 定义与着色器匹配的结构体
struct GradientPushConstants {
    float color_start[4];
    float color_end[4];
    float angle;
    float _pad[3];
};
static_assert(sizeof(GradientPushConstants) == 48);

// 更新 Push Constant
void update_gradient(vk::CommandBuffer cmd, vk::PipelineLayout layout) {
    GradientPushConstants pc{};
    pc.color_start[0] = 1.0f; pc.color_start[1] = 0.0f; 
    pc.color_start[2] = 0.0f; pc.color_start[3] = 1.0f;
    pc.color_end[0] = 0.0f; pc.color_end[1] = 0.0f; 
    pc.color_end[2] = 1.0f; pc.color_end[3] = 1.0f;
    pc.angle = 0.785f;  // 45 度
    
    cmd.pushConstants(
        layout,
        vk::ShaderStageFlagBits::eFragment,
        0,
        sizeof(pc),
        &pc
    );
}

6. 用户 UBO 使用示例

用户 UBO 使用 Set 1适合不频繁更新的参数。

6.1 UBO 结构设计

// Set 1: 用户 UBO
layout(set = 1, binding = 0, std140) uniform MyParams {
    // 注意 std140 布局规则:
    // - vec4 对齐到 16 bytes
    // - vec3 对齐到 16 bytes
    // - vec2 对齐到 8 bytes
    // - float/int 对齐到 4 bytes
    // - 数组元素对齐到 16 bytes
    
    vec4 color;           // offset: 0,  size: 16
    vec2 scale;           // offset: 16, size: 8
    float intensity;      // offset: 24, size: 4
    float _pad0;          // offset: 28, size: 4 (对齐)
    vec4 extra_params;    // offset: 32, size: 16
} params;
// 总大小: 48 bytes

6.2 C++ 端 UBO 结构

// 必须与着色器中的布局完全匹配
struct alignas(16) MyParamsUBO {
    float color[4];       // vec4
    float scale[2];       // vec2
    float intensity;      // float
    float _pad0;          // padding
    float extra_params[4]; // vec4
};
static_assert(sizeof(MyParamsUBO) == 48);

// 更新 UBO
void update_params(buffer& ubo) {
    MyParamsUBO data{};
    data.color[0] = 1.0f;
    data.color[1] = 0.5f;
    data.color[2] = 0.0f;
    data.color[3] = 1.0f;
    data.scale[0] = 2.0f;
    data.scale[1] = 2.0f;
    data.intensity = 0.8f;
    
    ubo.write(&data, sizeof(data));
}

6.3 多个 UBO 绑定

// 可以在 Set 1 定义多个 UBO
layout(set = 1, binding = 0, std140) uniform MaterialParams {
    vec4 albedo;
    float roughness;
    float metallic;
    vec2 _pad;
} material;

layout(set = 1, binding = 1, std140) uniform LightParams {
    vec4 light_pos;
    vec4 light_color;
    float intensity;
    float _pad[3];
} light;

7. 完整示例:自定义效果控件

7.1 创建波纹效果

着色器 (ripple.frag)

#version 450

layout(location = 0) in vec2 v_uv;
layout(location = 1) in vec2 v_local_pos;

layout(location = 0) out vec4 frag_color;

layout(set = 0, binding = 0, std140) uniform WidgetBounds {
    vec4  u_bounds;
    float u_opacity;
    float _pad0, _pad1, _pad2;
} widget;

// Set 1: 波纹参数
layout(set = 1, binding = 0, std140) uniform RippleParams {
    float time;        // 当前时间
    float frequency;   // 波纹频率
    float amplitude;   // 波纹振幅
    float decay;       // 衰减系数
} params;

// Push Constant: 波纹中心和颜色
layout(push_constant) uniform PushConstants {
    vec4 color;
    vec2 center;
    float _pad[2];
} pc;

void main() {
    // 计算到中心的距离
    float dist = length(v_local_pos - pc.center);
    
    // 计算波纹
    float wave = sin(dist * params.frequency - params.time * 3.0);
    wave *= params.amplitude * exp(-dist * params.decay);
    
    // 应用波纹到颜色亮度
    vec4 color = pc.color;
    color.rgb *= 1.0 + wave;
    
    frag_color = color;
    frag_color.a *= widget.u_opacity;
}

C++ 使用:

// UBO 结构
struct alignas(16) RippleParamsUBO {
    float time;
    float frequency;
    float amplitude;
    float decay;
};

// Push Constant 结构
struct RipplePushConstants {
    float color[4];
    float center[2];
    float _pad[2];
};

class ripple_widget {
public:
    void set_center(float x, float y) {
        push_constants_.center[0] = x;
        push_constants_.center[1] = y;
    }
    
    void set_color(float r, float g, float b, float a) {
        push_constants_.color[0] = r;
        push_constants_.color[1] = g;
        push_constants_.color[2] = b;
        push_constants_.color[3] = a;
    }
    
    void update(float delta_time) {
        params_.time += delta_time;
        ubo_->write(&params_, sizeof(params_));
    }
    
    void render(vk::CommandBuffer cmd) {
        cmd.pushConstants(
            pipeline_layout_,
            vk::ShaderStageFlagBits::eFragment,
            0,
            sizeof(push_constants_),
            &push_constants_
        );
        // ... 绑定管线和描述符集,绘制
    }
    
private:
    RippleParamsUBO params_{0.0f, 10.0f, 0.1f, 2.0f};
    RipplePushConstants push_constants_{};
    std::unique_ptr<buffer> ubo_;
    vk::PipelineLayout pipeline_layout_;
};

8. 相关文档