renderling/pbr/brdf/
cpu.rs1use craballoc::runtime::WgpuRuntime;
3
4use crate::texture;
5
6#[derive(Clone)]
8pub struct BrdfLut {
9 pub(crate) inner: texture::Texture,
10}
11
12impl BrdfLut {
13 pub fn new(runtime: impl AsRef<WgpuRuntime>) -> Self {
15 let runtime = runtime.as_ref();
16 let device = &runtime.device;
17 let queue = &runtime.queue;
18 let vertex_linkage = crate::linkage::brdf_lut_convolution_vertex::linkage(device);
19 let fragment_linkage = crate::linkage::brdf_lut_convolution_fragment::linkage(device);
20 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
21 label: Some("brdf_lut_convolution"),
22 layout: None,
23 vertex: wgpu::VertexState {
24 module: &vertex_linkage.module,
25 entry_point: Some(vertex_linkage.entry_point),
26 buffers: &[],
27 compilation_options: Default::default(),
28 },
29 primitive: wgpu::PrimitiveState {
30 topology: wgpu::PrimitiveTopology::TriangleList,
31 strip_index_format: None,
32 front_face: wgpu::FrontFace::Ccw,
33 cull_mode: None,
34 unclipped_depth: false,
35 polygon_mode: wgpu::PolygonMode::Fill,
36 conservative: false,
37 },
38 depth_stencil: None,
39 multisample: wgpu::MultisampleState {
40 mask: !0,
41 alpha_to_coverage_enabled: false,
42 count: 1,
43 },
44 fragment: Some(wgpu::FragmentState {
45 module: &fragment_linkage.module,
46 entry_point: Some(fragment_linkage.entry_point),
47 targets: &[Some(wgpu::ColorTargetState {
48 format: wgpu::TextureFormat::Rg16Float,
49 blend: Some(wgpu::BlendState {
50 color: wgpu::BlendComponent::REPLACE,
51 alpha: wgpu::BlendComponent::REPLACE,
52 }),
53 write_mask: wgpu::ColorWrites::ALL,
54 })],
55 compilation_options: Default::default(),
56 }),
57 multiview: None,
58 cache: None,
59 });
60
61 let framebuffer = texture::Texture::new_with(
62 runtime,
63 Some("brdf_lut"),
64 Some(
65 wgpu::TextureUsages::RENDER_ATTACHMENT
66 | wgpu::TextureUsages::TEXTURE_BINDING
67 | wgpu::TextureUsages::COPY_SRC,
68 ),
69 None,
70 wgpu::TextureFormat::Rg16Float,
71 2,
72 2,
73 512,
74 512,
75 1,
76 &[],
77 );
78
79 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());
80 {
81 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
82 label: Some("brdf_lut_convolution"),
83 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
84 view: &framebuffer.view,
85 resolve_target: None,
86 ops: wgpu::Operations {
87 load: wgpu::LoadOp::Clear(wgpu::Color::RED),
88 store: wgpu::StoreOp::Store,
89 },
90 depth_slice: None,
91 })],
92 depth_stencil_attachment: None,
93 ..Default::default()
94 });
95
96 render_pass.set_pipeline(&pipeline);
97 render_pass.draw(0..6, 0..1);
98 }
99 queue.submit([encoder.finish()]);
100
101 BrdfLut { inner: framebuffer }
102 }
103
104 pub fn texture(&self) -> &texture::Texture {
106 &self.inner
107 }
108}
109
110#[cfg(test)]
111mod test {
112 use crate::{context::Context, pbr::brdf::BrdfLut, test::BlockOnFuture, texture::Texture};
113
114 #[test]
115 fn precomputed_brdf() {
116 assert_eq!(2, std::mem::size_of::<u16>());
117 let r = Context::headless(32, 32).block();
118 let brdf_lut = BrdfLut::new(&r);
119 assert_eq!(
120 wgpu::TextureFormat::Rg16Float,
121 brdf_lut.texture().texture.format()
122 );
123 let copied_buffer = Texture::read(&r, &brdf_lut.texture().texture, 512, 512, 2, 2);
124 let pixels = copied_buffer.pixels(r.get_device()).block().unwrap();
125 let pixels: Vec<f32> = bytemuck::cast_slice::<u8, u16>(pixels.as_slice())
126 .iter()
127 .copied()
128 .map(|bits| half::f16::from_bits(bits).to_f32())
129 .collect();
130 assert_eq!(512 * 512 * 2, pixels.len());
131 let pixels: Vec<f32> = pixels
132 .chunks_exact(2)
133 .flat_map(|pixel| match pixel {
134 [r, g] => [*r, *g, 0.0, 1.0],
135 _ => unreachable!(),
136 })
137 .collect();
138
139 let img: image::ImageBuffer<image::Rgba<f32>, Vec<f32>> =
140 image::ImageBuffer::from_vec(512, 512, pixels).unwrap();
141 let img = image::DynamicImage::from(img);
142 let img = img.into_rgba8();
143 img_diff::assert_img_eq("skybox/brdf_lut.png", img);
144 }
145}