wgpu 多渲染目标(MRT)实战:一次 Render Pass 同时输出到多个颜色附件

发布时间:2026/11/11 3:25:01

wgpu 多渲染目标(MRT)实战:一次 Render Pass 同时输出到多个颜色附件 wgpu 多渲染目标MRT实战一次 Render Pass 同时输出到多个颜色附件【免费下载链接】wgpuA cross-platform, safe, pure-Rust graphics API.项目地址: https://gitcode.com/GitHub_Trending/wg/wgpu本指南以 wgpu 仓库中 multiple_render_targets 示例 为核心系统讲解如何在单个 Fragment Shader 中声明多个输出并让同一个 Render Pass 同时写入多个颜色附件。读完本文你将掌握 MRT 的完整实现路径从多目标渲染管线的ColorTargetState配置、WGSL 中多location输出的声明到离屏纹理的创建与分屏视口展示可直接复用到延迟着色、G-buffer 等需要一帧产出多张图的场景。示例目标与运行方式该示例的完整源码位于 mod.rs 与 shader.wgsl。它演示的核心能力是让片段着色器同时向一个 Render Pass 的两个颜色附件输出。具体流程是程序从零生成一张黑白球状径向渐变纹理数据片段着色器采样这张纹理并同时输出两个结果——第一个颜色附件收到红色版本第二个颜色附件收到绿色版本两张离屏纹理各自被渲染到屏幕左右两个视口viewport中分屏显示。运行示例只需在仓库根目录执行cargo run --bin wgpu-examples multiple_render_targets该示例在 examples/features/src/main.rs 中注册标记为webgpu: true, webgl: false——即支持原生 WebGPU 后端但 WebGL 后端不适用WebGL 2 对多渲染目标支持有限这也是 MRT 常用于 WebGPU 原生场景的原因。运行结果如下黑色背景下左半屏为红色渐变圆、右半屏为绿色渐变圆第一步从零生成黑白球状纹理示例没有加载外部图片而是用 CPU 代码逐像素生成一张 256×256 的R8Unorm灰度纹理mod.rsfn create_ball_texture_data(width: usize, height: usize) - Vecu8 { let mut img_data Vec::with_capacity(width * height); let center: glam::Vec2 glam::Vec2::new(width as f32 * 0.5, height as f32 * 0.5); let half_distance width as f32 * 0.5; for y in 0..width { for x in 0..height { let cur_pos glam::Vec2::new(x as f32, y as f32); let distance_to_center_normalized 1.0 - (cur_pos - center).length() / half_distance; let val: u8 (u8::MAX as f32 * distance_to_center_normalized) as u8; img_data.push(val) } } img_data }算法要点以图像中心为圆心像素离圆心越近1.0 - 距离/半宽越接近 1灰度值越高从而形成中心亮、边缘暗的径向渐变。生成的像素数据通过queue.write_texture上传到 GPU 纹理mod.rs。纹理描述符的关键点format: wgpu::TextureFormat::R8Unorm只需红色通道承载黑白数据usage: COPY_DST | TEXTURE_BINDING先作为拷贝目标写入再作为纹理被着色器采样。第二步用 ColorTargetState 配置多输出管线MRT 的关键一步是在创建渲染管线时通过FragmentState::targets提供一个与着色器输出一一对应的ColorTargetState数组mod.rsfragment: Some(wgpu::FragmentState { module: shader, entry_point: Some(fs_multi_main), targets: target_states, // 传入两个 ColorTargetState }),在Example::init中两个目标状态被构造为mod.rs[ Some(wgpu::ColorTargetState { format: config.view_formats[0], blend: None, write_mask: Default::default(), }), Some(wgpu::ColorTargetState { format: config.view_formats[0], blend: None, write_mask: Default::default(), }), ]要点说明数组索引即输出索引targets[0]对应着色器中location(0)的输出targets[1]对应location(1)的输出格式必须匹配每个ColorTargetState.format必须与对应附件的纹理格式一致此处两者都使用表面配置config.view_formats[0]指定的格式blend: None与默认write_mask表示本次演示不启用混合、四通道全写。第三步WGSL 中的多输出片段着色器着色器侧shader.wgsl用结构体声明两个location输出struct FragmentOutput { location(0) red_target : vec4f32, location(1) green_target : vec4f32, } fragment fn fs_multi_main(vs: VertexOutput) - FragmentOutput { let smp textureSample(image_texture, image_sampler, vs.uv).x; var output: FragmentOutput; output.red_target vec4f32(smp, 0.0, 0.0, 1.0); output.green_target vec4f32(0.0, smp, 0.0, 1.0); return output; }逻辑非常直观textureSample取出黑白纹理的红色分量smp把它分别放入红色通道red和绿色通道green其余通道置零——于是同一个采样值被同时写进两个附件形成一红一绿两个渐变圆。顶点着色器vs_main只用builtin(vertex_index)生成一个覆盖全屏的三角形无需顶点缓冲并通过位运算技巧产出 UV 坐标vertex fn vs_main( builtin(vertex_index) vi: u32) - VertexOutput { var out: VertexOutput; out.uv vec2f32( f32((vi 1u) 2u), f32(vi 2u), ); out.clip_position vec4f32(out.uv * 2.0 - 1.0, 0.0, 1.0); out.uv.y 1.0 - out.uv.y; return out; }第四步创建带 RENDER_ATTACHMENT 用法的目标纹理要接收渲染输出离屏纹理必须同时具备TEXTURE_BINDING供后续采样显示和RENDER_ATTACHMENT作为颜色附件两种用途。示例的TextureTargets::new创建了两张完全相同的纹理mod.rslet red_texture device.create_texture(wgpu::TextureDescriptor { size, mip_level_count: 1, sample_count: 1, dimension: wgpu::TextureDimension::D2, format, usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::RENDER_ATTACHMENT, view_formats: [format], });并分别创建对应纹理视图red_view、green_view存入TextureTargetsmod.rs。view_formats: [format]将视图格式显式声明为纹理格式本身便于后续以该格式创建视图。第五步一次 Render Pass 绑定两个颜色附件多输出的落地发生在MultiTargetRenderer::draw——向RenderPassDescriptor::color_attachments传入两个RenderPassColorAttachmentmod.rsfn draw(self, encoder: mut wgpu::CommandEncoder, targets: [Optionwgpu::RenderPassColorAttachment]) { let mut rpass encoder.begin_render_pass(wgpu::RenderPassDescriptor { color_attachments: targets, depth_stencil_attachment: None, timestamp_writes: None, occlusion_query_set: None, multiview_mask: None, }); rpass.set_pipeline(self.pipeline); rpass.set_bind_group(0, self.bindgroup, []); rpass.draw(0..3, 0..1); }在render中两个附件分别指向红、绿纹理视图mod.rsself.multi_target_renderer.draw( mut encoder, [ Some(wgpu::RenderPassColorAttachment { view: self.texture_targets.red_view, depth_slice: None, resolve_target: None, ops: Default::default(), }), Some(wgpu::RenderPassColorAttachment { view: self.texture_targets.green_view, depth_slice: None, resolve_target: None, ops: Default::default(), }), ], );注意这里color_attachments数组的顺序必须与ColorTargetState、着色器location三者保持一致。一次draw调用即可同时写入两个附件这正是 MRT 相比“两次 Pass 两次纹理拷贝”的核心性能优势。第六步用两个视口分屏显示结果TargetRenderer负责把两张离屏纹理搬到屏幕。它创建了两个 Bind Group分别绑定red_view与green_viewmod.rs随后在同一帧内通过set_viewport切换左右半屏各自绘制一次mod.rs// draw results in two separate viewports that split the screen: rpass.set_viewport(0.0, 0.0, half_w, height, 0.0, 1.0); rpass.set_bind_group(0, self.bindgroup_left, []); rpass.draw(0..3, 0..1); rpass.set_viewport(half_w, 0.0, half_w, height, 0.0, 1.0); rpass.set_bind_group(0, self.bindgroup_right, []); rpass.draw(0..3, 0..1);其中half_w width * 0.5因此左视口覆盖[0, half_w]、右视口覆盖[half_w, width]实现左右对称分屏。展示管线的片段着色器fs_display_main直接采样纹理原样输出shader.wgslfragment fn fs_display_main(vs: VertexOutput) - location(0) vec4f32 { let smp textureSample(image_texture, image_sampler, vs.uv).xyz; return vec4f32(smp, 1.0); }窗口尺寸变化时Example::resize会按新尺寸重建TextureTargets并重建两个 Bind Groupmod.rs保证分屏始终铺满窗口。测试与验证示例自带 GPU 测试mod.rs通过wgpu_test::gpu_test!宏注册测试参数与截图基准对齐#[cfg(test)] #[wgpu_test::apply(wgpu_test::gpu_test!)] pub static TEST: crate::framework::ExampleTestParams crate::framework::ExampleTestParams { name: EXAMPLE_NAME, image_path: /examples/features/src/multiple_render_targets/screenshot.png, width: 1024, height: 768, ... comparisons: [wgpu_test::ComparisonType::Mean(0.014)], // Bounded by Apple A9 };该测试同时登记在 lib.rs 的all_tests()中会以 1024×768 渲染一帧并与截图做平均像素对比容差由软件光栅化器 lavapipe 与 Apple A9 的差异共同界定从测试侧验证了 MRT 渲染结果的可复现性。小结通过这个示例可以提炼出在 wgpu 中实现多渲染目标的三步固定套路着色器在 WGSL 片段着色器中用结构体声明多个location(n)输出管线FragmentState::targets传入与输出数量一致、格式匹配的ColorTargetState数组PassRenderPassDescriptor::color_attachments传入等量的RenderPassColorAttachment且保证索引顺序与着色器location一一对应。目标纹理需同时具备RENDER_ATTACHMENT | TEXTURE_BINDING用途才能“先被渲染、再被采样”。掌握这套流程后即可在此基础上扩展出延迟渲染 G-buffer法线/反照率/深度多张输出、多视图渲染等更复杂的应用。深入阅读源码可继续参考 mod.rs 与 shader.wgsl运行命令为cargo run --bin wgpu-examples multiple_render_targets【免费下载链接】wgpuA cross-platform, safe, pure-Rust graphics API.项目地址: https://gitcode.com/GitHub_Trending/wg/wgpu创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
延伸阅读

更多相关文章

2026/11/8 10:43:37

2026年人体工学椅选购黄金期:技术成熟度与国标落地交汇点

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/11/11 3:22:37

电气设计要有电工证 良心建议:证书过期了不知道怎么复审延期

电气设计要有电工证 良心建议:证书过期了不知道怎么复审延期 证书过期了不知道怎么复审延期,这事儿真不是小事。很多电气设计人员因为工作忙,一拖再拖,结果到了关键时刻才发现证过期了,项目做不了,项目负责人还怪你没提前准备。我跟你们说,别不当回事…

2026/11/11 3:22:37

赤壁市电工证查询怎么报名?学历不高怕报不上名的看过来

赤壁市电工证查询怎么报名?学历不高怕报不上名的看过来 你是不是也遇到过这种情况:想报电工证,但又怕自己学历不够,根本报不上名?其实不然,赤壁市电工证查询政策已经越来越人性化,只要你符合基本条件,就能顺利报名。接下来我从政策背景、核心变化、对…

2026/11/11 3:22:37

电工证是国家认可吗真实经历告诉你怎么转证和找工作

电工证是国家认可吗真实经历告诉你怎么转证和找工作 之前考的证在外省能不能转过来?我认识的一个朋友,去年在山东考的低压电工证,结果到了河南工地,老板一看证书,说这个证不认可,得重新考。我问他为啥,他说 电工证是国家认可吗…

2026/11/11 3:22:37

特种工证在哪查询值不值得考?河南房建从业者避坑指南

特种工证在哪查询值不值得考?河南房建从业者避坑指南 怕考不过白交培训费?你不是一个人。现在河南房建行业对特种作业操作证的依赖度越来越高,很多人花了几千块去培训,结果一考试就挂,最后钱花了,证没拿到,还得重新来一遍。这不是个例,是行业普遍存在…

2026/11/10 8:12:38

电工证丢失还能补吗?千万别踩坑,丢了证等于失业

电工证丢失还能补吗?千万别踩坑,丢了证等于失业 你正在找工作,但手里的电工证突然丢了,这下怎么办?别急,先别慌, 电工证丢失还能补吗 ?答案是肯定的,但 千万别踩坑 ,补办流程如果不了解,可能会耽误你上岗、甚至影响到你的收入。…

2026/11/11 0:02:26

考低压电工证通过率考试真题与备考技巧

低压电工证通过率高不高?学历不高也能考,值不值得考? 你是不是总听说电工证能帮你找高薪工作,但又担心自己学历不够,考不上?其实,低压电工证考试门槛并没有你想象中那么高,关键是方法对不对。这篇文章就告诉你, 考低压电工证通过率到底有多高 ,…

2026/11/11 0:02:26

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名

低压电工证学时千万别踩坑,濮阳毕业生怎么避坑报名 你是不是也和我一样,刚毕业就听说低压电工证学时必须达标才能上岗?可一打听,发现报名机构五花八门,价格参差不齐, 不知道去哪报名怕被中介坑…

2026/11/10 20:08:15

面向复杂光照与角度的宠物面部识别鲁棒性增强策略

在智慧养宠时代,基于视觉的宠物身份识别技术正逐步从实验室走向家庭应用。然而,与配合度高的人类不同,宠物是典型的“非合作生物识别对象”——它们不会主动配合摄像头调整姿态,更无法控制光照环境。实际应用中,我们常…

2026/11/10 10:29:37

SSM电子书店源码全解析:结构、原理、部署与排错实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑