从OpenGL迁移至WGPU:如何在清除前设置Scissor Rectangle?
WGPU中实现局部清除(模拟OpenGL的Scissor清除效果)
WGPU的LoadOp::Clear操作是针对整个纹理视图生效的,后续设置的Scissor不会影响这个清除动作,所以你当前的代码会把整个帧缓冲区都清掉。要实现只清除指定区域的需求,有两种可行方案:
方案一:加载原有内容后,用Scissor裁剪绘制清除颜色
这种方式是先保留帧缓冲区的全部内容,再通过Scissor限制绘制范围,用清除颜色填充目标区域:
- 将颜色附件的
load操作改为LoadOp::Load,保留原帧内容 - 设置Scissor到需要清除的区域
- 绘制一个覆盖Scissor区域的四边形(用全屏四边形即可,Scissor会自动裁剪掉超出范围的部分)
修改后的代码示例:
let mut render_pass = encoder.begin_render_pass( &wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, // 保留原有帧内容 store: true, }, } )], depth_stencil_attachment: None, } ); // 设置Scissor到目标区域 render_pass.set_scissor_rect( render_area.x, render_area.y, render_area.width(), render_area.height() ); // 设置清除颜色的管线(提前创建好的纯色填充管线) render_pass.set_pipeline(&clear_pipeline); // 绘制全屏四边形(可通过无索引绘制实现) render_pass.draw(0..6, 0..1); // 后续执行你的正常渲染操作 // ... render_pass.end();
这里的clear_pipeline需要提前创建:顶点着色器输出全屏顶点,片段着色器输出你需要的清除颜色(r:0.04, g:0.08, b:0.08, a:1.0)。
方案二:创建子纹理视图,针对子视图执行清除
如果不想额外绘制四边形,可以创建原纹理的子视图,只包含需要清除的区域,然后针对这个子视图执行LoadOp::Clear:
- 根据需要清除的区域,创建原纹理的子视图
- 先执行一个针对子视图的RenderPass,完成局部清除
- 再执行正常的渲染Pass,使用完整的纹理视图
代码示例:
// 创建子纹理视图,对应需要清除的区域 let clear_view = view.texture.create_view(&wgpu::TextureViewDescriptor { label: Some("Clear Subview"), format: None, dimension: None, aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: None, base_array_layer: 0, array_layer_count: None, view_dimension: wgpu::TextureViewDimension::D2, bounds: wgpu::ImageSubresourceRange { aspect: wgpu::TextureAspect::All, base_mip_level: 0, mip_level_count: 1, base_array_layer: 0, array_layer_count: 1, }, offset: wgpu::Origin3d { x: render_area.x, y: render_area.y, z: 0, }, size: wgpu::Extent3d { width: render_area.width(), height: render_area.height(), depth_or_array_layers: 1, }, }); // 第一个Pass:清除子视图区域 let mut clear_pass = encoder.begin_render_pass( &wgpu::RenderPassDescriptor { label: Some("Clear Pass"), color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: &clear_view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Clear( wgpu::Color { r: 0.04, g: 0.08, b: 0.08, a: 1.0, } ), store: true, }, } )], depth_stencil_attachment: None, } ); clear_pass.end(); // 第二个Pass:执行正常渲染操作 let mut render_pass = encoder.begin_render_pass( &wgpu::RenderPassDescriptor { label: Some("Render Pass"), color_attachments: &[Some( wgpu::RenderPassColorAttachment { view: &view, resolve_target: None, ops: wgpu::Operations { load: wgpu::LoadOp::Load, // 加载已完成局部清除的帧内容 store: true, }, } )], depth_stencil_attachment: None, } ); // 设置Scissor并执行你的渲染逻辑 render_pass.set_scissor_rect( render_area.x, render_area.y, render_area.width(), render_area.height() ); // ... 你的渲染代码 render_pass.end();
方案对比
- 方案一更适合需要频繁变更清除区域的场景,不需要额外创建纹理视图,只需要维护一个简单的清除管线
- 方案二更适合固定清除区域的场景,利用硬件的清除操作,性能略优,但需要额外管理子视图
内容的提问来源于stack exchange,提问作者Gustav Nilsson
相关产品推荐
相关产品推荐

