- 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.
20 KiB
20 KiB
MIRAI Shader Widget 使用示例
本文档提供 MIRAI 框架着色器控件系统的详细使用示例,涵盖四种渲染模式和自定义控件开发。
注意:本文档使用标准 GLSL 着色器语言。详见 ADR-003: 迁移到 GLSL 着色器系统。
目录
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(¶ms_, 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. 相关文档
- Shader Widget 架构设计 - 详细的架构设计文档
- ADR-003: 迁移到 GLSL 着色器系统 - 架构决策记录
- GLSL 着色器系统设计 - 详细设计文档