如何在Wgpu应用中渲染Egui生成的顶点?适配新版本依赖
在自研Wgpu应用中渲染egui顶点的核心步骤
1. 获取egui的渲染输出数据
每次egui界面更新后,调用egui::Context::tessellate生成渲染所需的原始数据:
let clipped_primitives = ctx.tessellate(shapes);
其中shapes是ctx.run(input_state, |ui| { /* 你的UI代码 */ }).output.shapes。每个ClippedPrimitive包含:
primitive:内部是Mesh,存储顶点(pos为屏幕像素坐标、uv、color)和三角形索引。clip_rect:屏幕空间的裁剪矩形,用于后续的视口裁剪。texture_id:对应egui的纹理ID(通常是字体纹理,用于渲染文字)。
2. 同步egui纹理到Wgpu
egui的TextureManager管理所有纹理(包括默认的字体纹理),你需要维护一个TextureId到WgpuTextureView/BindGroup的映射:
- 初始化时,获取egui默认字体纹理:
let font_texture = ctx.texture_manager().texture(&egui::TextureId::Font);,将其像素数据上传到Wgpu的2D纹理(格式用wgpu::TextureFormat::Rgba8UnormSrgb)。 - 监听egui的纹理更新事件:当
ctx.texture_manager().has_changed()返回true时,遍历所有已注册的TextureId,更新对应的Wgpu纹理内容。
3. 坐标转换:屏幕像素→NDC
egui顶点的pos是屏幕左上角为原点的像素坐标,需要转换成Wgpu使用的NDC坐标(范围[-1,1],中心为原点)。通过投影矩阵实现:
let screen_rect = egui::Rect::from_min_max(egui::Pos2::ZERO, egui::Pos2(width as f32, height as f32)); let proj_matrix = egui::emath::projection_matrix(screen_rect, egui::Pos2::ZERO, 1.0);
将这个矩阵作为uniform变量传入顶点着色器,用于转换顶点位置。
4. 编写适配的Wgpu Shader
顶点着色器
struct VertexInput { @location(0) pos: vec2<f32>, @location(1) uv: vec2<f32>, @location(2) color: vec4<f32>, }; struct VertexOutput { @builtin(position) clip_pos: vec4<f32>, @location(0) uv: vec2<f32>, @location(1) color: vec4<f32>, }; @group(0) @binding(0) var<uniform> proj_matrix: mat4x4<f32>; @vertex fn vs_main(input: VertexInput) -> VertexOutput { var output: VertexOutput; output.clip_pos = proj_matrix * vec4<f32>(input.pos, 0.0, 1.0); output.uv = input.uv; output.color = input.color / 255.0; // 将Color32的0-255转成0-1范围 return output; }
片元着色器
@group(1) @binding(0) var egui_texture: texture_2d<f32>; @group(1) @binding(1) var egui_sampler: sampler; @fragment fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> { let tex_color = textureSample(egui_texture, egui_sampler, input.uv); // egui顶点颜色是预乘alpha,直接相乘即可 return input.color * tex_color; }
5. 配置Wgpu渲染管道
关键设置:
- 顶点缓冲布局要匹配egui的
Vertex结构(pos: vec2, uv: vec2, color: u32/vec4)。 - 混合模式使用预乘alpha:
let blend_state = wgpu::BlendState { color: wgpu::BlendComponent { src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, alpha: wgpu::BlendComponent { src_factor: wgpu::BlendFactor::One, dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, operation: wgpu::BlendOperation::Add, }, };
- 启用scissor测试,用于裁剪
clip_rect外的像素。
6. 逐Primitive渲染
遍历每个ClippedPrimitive:
- 根据
clip_rect计算scissor区域,设置到Wgpu的渲染 pass 中。 - 根据
texture_id绑定对应的纹理BindGroup。 - 将当前primitive的顶点和索引数据上传到动态缓冲(或提前准备好的缓冲)。
- 调用
pass.draw_indexed(),传入索引范围、顶点偏移,完成渲染。
关于egui-wgpu的参考
如果官方egui-wgpu crate的示例过时,直接查看其最新源码中的Renderer实现——它完整封装了上述所有逻辑,你可以提取核心代码适配自己的架构,既保证兼容性,又能保留对渲染流程的控制权。
内容的提问来源于stack exchange,提问作者Redirectk
相关产品推荐
相关产品推荐

