finished main.rs tutorial 8

pull/6/head
Ben Hansen 4 years ago
parent da4c268fbc
commit aa45a0605f

@ -10,5 +10,4 @@ layout(set = 0, binding = 1) uniform sampler s_diffuse;
void main() {
f_color = texture(sampler2D(t_diffuse, s_diffuse), v_tex_coords);
// f_color = vec4(v_color, 1);
}

@ -0,0 +1,14 @@
#version 450
layout(location=0) in vec2 v_tex_coords;
layout(location=1) in vec3 v_color;
layout(location=0) out vec4 f_color;
layout(set = 0, binding = 0) uniform texture2D t_depth;
layout(set = 0, binding = 1) uniform samplerShadow s_depth;
void main() {
float depth = texture(sampler2DShadow(t_depth, s_depth), vec3(v_tex_coords, 1));
f_color = vec4(depth, 0, 0, 1);
}

@ -48,6 +48,18 @@ const INDICES: &[u16] = &[
2, 3, 4,
];
const DEPTH_VERTICES: &[Vertex] = &[
Vertex { position: [0.0, 0.0, 0.0], tex_coords: [1.0, 1.0]},
Vertex { position: [1.0, 0.0, 0.0], tex_coords: [0.0, 1.0]},
Vertex { position: [1.0, -1.0, 0.0], tex_coords: [0.0, 0.0]},
Vertex { position: [0.0, -1.0, 0.0], tex_coords: [1.0, 0.0]},
];
const DEPTH_INDICES: &[u16] = &[
0, 1, 2,
0, 2, 3,
];
#[cfg_attr(rustfmt, rustfmt_skip)]
pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
1.0, 0.0, 0.0, 0.0,
@ -57,6 +69,7 @@ pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4<f32> = cgmath::Matrix4::new(
);
const NUM_INSTANCES_PER_ROW: u32 = 10;
const NUM_INSTANCES: u32 = NUM_INSTANCES_PER_ROW * NUM_INSTANCES_PER_ROW;
const INSTANCE_DISPLACEMENT: cgmath::Vector3<f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5);
@ -183,8 +196,6 @@ impl CameraController {
}
}
const ROTATION_SPEED: f32 = 2.0 * std::f32::consts::PI / 60.0;
struct Instance {
position: cgmath::Vector3<f32>,
rotation: cgmath::Quaternion<f32>,
@ -196,6 +207,114 @@ impl Instance {
}
}
struct DepthPass {
texture: wgpu::Texture,
view: wgpu::TextureView,
sampler: wgpu::Sampler,
bind_group: wgpu::BindGroup,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
num_depth_indices: u32,
render_pipeline: wgpu::RenderPipeline,
has_saved_to_file: bool,
}
impl DepthPass {
fn new(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor, texture_bind_group_layout: &wgpu::BindGroupLayout) -> Self {
let texture = create_depth_texture(device, sc_desc);
let view = texture.create_default_view();
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Nearest,
lod_min_clamp: -100.0,
lod_max_clamp: 100.0,
compare_function: wgpu::CompareFunction::LessEqual,
});
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
layout: texture_bind_group_layout,
bindings: &[
wgpu::Binding {
binding: 0,
resource: wgpu::BindingResource::TextureView(&view),
},
wgpu::Binding {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
}
],
});
let vertex_buffer = device
.create_buffer_mapped(DEPTH_VERTICES.len(), wgpu::BufferUsage::VERTEX)
.fill_from_slice(DEPTH_VERTICES);
let index_buffer = device
.create_buffer_mapped(DEPTH_INDICES.len(), wgpu::BufferUsage::INDEX)
.fill_from_slice(DEPTH_INDICES);
let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[texture_bind_group_layout],
});
let vs_src = include_str!("challenge.vert");
let fs_src = include_str!("challenge.frag");
let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap();
let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap();
let vs_data = wgpu::read_spirv(vs_spirv).unwrap();
let fs_data = wgpu::read_spirv(fs_spirv).unwrap();
let vs_module = device.create_shader_module(&vs_data);
let fs_module = device.create_shader_module(&fs_data);
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
layout: &layout,
vertex_stage: wgpu::ProgrammableStageDescriptor {
module: &vs_module,
entry_point: "main",
},
fragment_stage: Some(wgpu::ProgrammableStageDescriptor {
module: &fs_module,
entry_point: "main",
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::Back,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
}),
primitive_topology: wgpu::PrimitiveTopology::TriangleList,
color_states: &[
wgpu::ColorStateDescriptor {
format: sc_desc.format,
color_blend: wgpu::BlendDescriptor::REPLACE,
alpha_blend: wgpu::BlendDescriptor::REPLACE,
write_mask: wgpu::ColorWrite::ALL,
},
],
depth_stencil_state: None,
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[
Vertex::desc(),
],
sample_count: 1,
sample_mask: !0,
alpha_to_coverage_enabled: false,
});
Self {
texture, view, sampler, bind_group,
vertex_buffer, index_buffer,
num_depth_indices: DEPTH_INDICES.len() as u32,
render_pipeline,
has_saved_to_file: false,
}
}
}
struct State {
surface: wgpu::Surface,
device: wgpu::Device,
@ -224,36 +343,20 @@ struct State {
instances: Vec<Instance>,
instance_buffer: wgpu::Buffer,
}
fn quat_mul(q: cgmath::Quaternion<f32>, r: cgmath::Quaternion<f32>) -> cgmath::Quaternion<f32> {
// This block uses quaternions of the form of
// q=q0+iq1+jq2+kq3
// and
// r=r0+ir1+jr2+kr3.
// The quaternion product has the form of
// t=q×r=t0+it1+jt2+kt3,
// where
// t0=(r0 q0 r1 q1 r2 q2 r3 q3)
// t1=(r0 q1 + r1 q0 r2 q3 + r3 q2)
// t2=(r0 q2 + r1 q3 + r2 q0 r3 q1)
// t3=(r0 q3 r1 q2 + r2 q1 + r3 q0
let w = r.s * q.s - r.v.x * q.v.x - r.v.y * q.v.y - r.v.z * q.v.z;
let xi = r.s * q.v.x + r.v.x * q.s - r.v.y * q.v.z + r.v.z * q.v.y;
let yj = r.s * q.v.y + r.v.x * q.v.z + r.v.y * q.s - r.v.z * q.v.x;
let zk = r.s * q.v.z - r.v.x * q.v.y + r.v.y * q.v.x + r.v.z * q.s;
depth_pass: DepthPass,
}
cgmath::Quaternion::new(w, xi, yj, zk)
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> wgpu::Texture {
let desc = wgpu::TextureDescriptor {
format: DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT
| wgpu::TextureUsage::SAMPLED
| wgpu::TextureUsage::COPY_SRC,
..sc_desc.to_texture_desc()
};
device.create_texture(&desc)
}
impl State {
@ -400,7 +503,7 @@ impl State {
let rotation = if position.is_zero() {
// this is needed so an object at (0, 0, 0) won't get scaled to zero
// as Quaternions can effect scale if they're not create correctly
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_y(), cgmath::Deg(0.0))
cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0))
} else {
cgmath::Quaternion::from_axis_angle(position.clone().normalize(), cgmath::Deg(45.0))
};
@ -414,7 +517,7 @@ impl State {
let instance_data = instances.iter().map(Instance::to_matrix).collect::<Vec<_>>();
let instance_buffer_size = instance_data.len() * std::mem::size_of::<cgmath::Matrix4<f32>>();
let instance_buffer = device
.create_buffer_mapped(instance_data.len(), wgpu::BufferUsage::STORAGE_READ | wgpu::BufferUsage::COPY_DST)
.create_buffer_mapped(instance_data.len(), wgpu::BufferUsage::STORAGE_READ)
.fill_from_slice(&instance_data);
let uniform_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -457,7 +560,7 @@ impl State {
],
});
let vs_src = include_str!("challenge.vert");
let vs_src = include_str!("shader.vert");
let fs_src = include_str!("shader.frag");
let vs_spirv = glsl_to_spirv::compile(vs_src, glsl_to_spirv::ShaderType::Vertex).unwrap();
let fs_spirv = glsl_to_spirv::compile(fs_src, glsl_to_spirv::ShaderType::Fragment).unwrap();
@ -482,7 +585,7 @@ impl State {
}),
rasterization_state: Some(wgpu::RasterizationStateDescriptor {
front_face: wgpu::FrontFace::Ccw,
cull_mode: wgpu::CullMode::None,
cull_mode: wgpu::CullMode::Back,
depth_bias: 0,
depth_bias_slope_scale: 0.0,
depth_bias_clamp: 0.0,
@ -496,7 +599,15 @@ impl State {
write_mask: wgpu::ColorWrite::ALL,
},
],
depth_stencil_state: None,
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_read_mask: 0,
stencil_write_mask: 0,
}),
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[
Vertex::desc(),
@ -514,6 +625,8 @@ impl State {
.fill_from_slice(INDICES);
let num_indices = INDICES.len() as u32;
let depth_pass = DepthPass::new(&device, &sc_desc, &texture_bind_group_layout);
Self {
surface,
device,
@ -536,6 +649,7 @@ impl State {
size,
instances,
instance_buffer,
depth_pass,
}
}
@ -546,6 +660,9 @@ impl State {
self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_pass.texture = create_depth_texture(&self.device, &self.sc_desc);
self.depth_pass.view = self.depth_pass.texture.create_default_view();
self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32;
}
@ -567,18 +684,6 @@ impl State {
encoder.copy_buffer_to_buffer(&staging_buffer, 0, &self.uniform_buffer, 0, std::mem::size_of::<Uniforms>() as wgpu::BufferAddress);
for instance in &mut self.instances {
let amount = cgmath::Quaternion::from_angle_y(cgmath::Rad(ROTATION_SPEED));
let current = instance.rotation;
instance.rotation = quat_mul(amount, current);
}
let instance_data = self.instances.iter().map(Instance::to_matrix).collect::<Vec<_>>();
let instance_buffer_size = instance_data.len() * std::mem::size_of::<cgmath::Matrix4<f32>>();
let instance_buffer = self.device
.create_buffer_mapped(instance_data.len(), wgpu::BufferUsage::COPY_SRC)
.fill_from_slice(&instance_data);
encoder.copy_buffer_to_buffer(&instance_buffer, 0, &self.instance_buffer, 0, instance_buffer_size as wgpu::BufferAddress);
self.queue.submit(&[encoder.finish()]);
}
@ -605,7 +710,15 @@ impl State {
},
}
],
depth_stencil_attachment: None,
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_pass.view,
depth_load_op: wgpu::LoadOp::Clear,
depth_store_op: wgpu::StoreOp::Store,
clear_depth: 1.0,
stencil_load_op: wgpu::LoadOp::Clear,
stencil_store_op: wgpu::StoreOp::Store,
clear_stencil: 0,
}),
});
render_pass.set_pipeline(&self.render_pipeline);
@ -616,9 +729,106 @@ impl State {
render_pass.draw_indexed(0..self.num_indices, 0, 0..self.instances.len() as u32);
}
{
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[
wgpu::RenderPassColorAttachmentDescriptor {
attachment: &frame.view,
resolve_target: None,
load_op: wgpu::LoadOp::Load,
store_op: wgpu::StoreOp::Store,
clear_color: wgpu::Color::BLACK,
}
],
depth_stencil_attachment: None,
});
render_pass.set_pipeline(&self.depth_pass.render_pipeline);
// render_pass.set_bind_group(0, &self.diffuse_bind_group, &[]);
render_pass.set_bind_group(0, &self.depth_pass.bind_group, &[]);
render_pass.set_vertex_buffers(0, &[(&self.depth_pass.vertex_buffer, 0)]);
render_pass.set_index_buffer(&self.depth_pass.index_buffer, 0);
render_pass.draw_indexed(0..self.depth_pass.num_depth_indices, 0, 0..1);
}
let buffer = if !self.depth_pass.has_saved_to_file {
const U32_SIZE: u32 = std::mem::size_of::<u32>() as u32;
let buffer_size = (U32_SIZE * self.sc_desc.width * self.sc_desc.height) as wgpu::BufferAddress;
let buffer_desc = wgpu::BufferDescriptor {
size: buffer_size,
usage: wgpu::BufferUsage::COPY_DST
| wgpu::BufferUsage::MAP_READ,
};
let buffer = self.device.create_buffer(&buffer_desc);
encoder.copy_texture_to_buffer(
wgpu::TextureCopyView {
texture: &self.depth_pass.texture,
mip_level: 0,
array_layer: 0,
origin: wgpu::Origin3d::ZERO,
},
wgpu::BufferCopyView {
buffer: &buffer,
offset: 0,
row_pitch: U32_SIZE * self.sc_desc.width,
image_height: self.sc_desc.height,
},
wgpu::Extent3d {
width: self.sc_desc.width,
height: self.sc_desc.height,
depth: 1,
},
);
self.depth_pass.has_saved_to_file = true;
Some((buffer, buffer_size))
} else {
None
};
self.queue.submit(&[
encoder.finish()
]);
if let Some((buffer, buffer_size)) = buffer {
let width = self.sc_desc.width;
let height = self.sc_desc.height;
let near = self.camera.znear;
let far = self.camera.zfar;
buffer.map_read_async(0, buffer_size, move |result: wgpu::BufferMapAsyncResult<&[f32]>| {
let mapping = result.unwrap();
let data = mapping.data;
use image::{ImageBuffer, Rgba, Pixel};
let mut buffer = ImageBuffer::<Rgba<u8>, _>::new(
width,
height,
);
let mut x = 0;
let mut y = 0;
for pixel in data {
let z = pixel * 2.0 - 1.0;
let r = (2.0 * near * far) / (far + near - z * (far - near));
let p = (r.floor() * 255.0 / far) as u8;
buffer.put_pixel(x, y, Pixel::from_channels(
p, p, p, 255,
));
x += 1;
if x >= width {
x = 0;
y += 1;
}
}
buffer.save("image.png").unwrap();
});
}
}
}
@ -629,9 +839,6 @@ fn main() {
.unwrap();
let mut state = State::new(&window);
let mut old_time = std::time::Instant::now();
const MSPT: std::time::Duration = std::time::Duration::from_millis(20);
event_loop.run(move |event, _, control_flow| {
match event {
@ -639,7 +846,7 @@ fn main() {
ref event,
window_id,
} if window_id == window.id() => if state.input(event) {
()
*control_flow = ControlFlow::Wait;
} else {
match event {
WindowEvent::CloseRequested => *control_flow = ControlFlow::Exit,
@ -653,34 +860,26 @@ fn main() {
virtual_keycode: Some(VirtualKeyCode::Escape),
..
} => *control_flow = ControlFlow::Exit,
_ => (),
_ => *control_flow = ControlFlow::Wait,
}
}
WindowEvent::Resized(physical_size) => {
state.resize(*physical_size);
()
*control_flow = ControlFlow::Wait;
}
WindowEvent::ScaleFactorChanged { new_inner_size, .. } => {
state.resize(**new_inner_size);
()
*control_flow = ControlFlow::Wait;
}
_ => (),
_ => *control_flow = ControlFlow::Wait,
}
}
Event::MainEventsCleared => {
state.update();
state.render();
let new_time = std::time::Instant::now();
let delta_time = new_time - old_time;
*control_flow = if delta_time > MSPT {
ControlFlow::Poll
} else {
ControlFlow::WaitUntil(old_time + MSPT)
};
old_time = new_time;
*control_flow = ControlFlow::Wait;
}
_ => (),
_ => *control_flow = ControlFlow::Wait,
}
});
}

@ -5,17 +5,7 @@ layout(location=1) in vec2 a_tex_coords;
layout(location=0) out vec2 v_tex_coords;
layout(set=1, binding=0)
uniform Uniforms {
mat4 u_view_proj;
};
layout(set=1, binding=1)
buffer Instances {
mat4 s_models[];
};
void main() {
v_tex_coords = a_tex_coords;
gl_Position = u_view_proj * s_models[gl_InstanceIndex] * vec4(a_position, 1.0);
gl_Position = vec4(a_position, 1.0);
}

@ -223,6 +223,19 @@ struct State {
instances: Vec<Instance>,
instance_buffer: wgpu::Buffer,
depth_texture: wgpu::Texture,
depth_texture_view: wgpu::TextureView,
}
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float;
fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> wgpu::Texture {
let desc = wgpu::TextureDescriptor {
format: DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT,
..sc_desc.to_texture_desc()
};
device.create_texture(&desc)
}
impl State {
@ -435,6 +448,9 @@ impl State {
let vs_module = device.create_shader_module(&vs_data);
let fs_module = device.create_shader_module(&fs_data);
let depth_texture = create_depth_texture(&device, &sc_desc);
let depth_texture_view = depth_texture.create_default_view();
let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
bind_group_layouts: &[&texture_bind_group_layout, &uniform_bind_group_layout],
});
@ -465,7 +481,15 @@ impl State {
write_mask: wgpu::ColorWrite::ALL,
},
],
depth_stencil_state: None,
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less,
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_read_mask: 0,
stencil_write_mask: 0,
}),
index_format: wgpu::IndexFormat::Uint16,
vertex_buffers: &[
Vertex::desc(),
@ -505,6 +529,8 @@ impl State {
size,
instances,
instance_buffer,
depth_texture,
depth_texture_view,
}
}
@ -515,6 +541,9 @@ impl State {
self.sc_desc.height = new_size.height;
self.swap_chain = self.device.create_swap_chain(&self.surface, &self.sc_desc);
self.depth_texture = create_depth_texture(&self.device, &self.sc_desc);
self.depth_texture_view = self.depth_texture.create_default_view();
self.camera.aspect = self.sc_desc.width as f32 / self.sc_desc.height as f32;
}
@ -562,7 +591,15 @@ impl State {
},
}
],
depth_stencil_attachment: None,
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture_view,
depth_load_op: wgpu::LoadOp::Clear,
depth_store_op: wgpu::StoreOp::Store,
clear_depth: 1.0,
stencil_load_op: wgpu::LoadOp::Clear,
stencil_store_op: wgpu::StoreOp::Store,
clear_stencil: 0,
}),
});
render_pass.set_pipeline(&self.render_pipeline);

@ -28,6 +28,7 @@ module.exports = {
'/beginner/tutorial5-textures/',
'/beginner/tutorial6-uniforms/',
'/beginner/tutorial7-instancing/',
'/beginner/tutorial8-depth/',
],
},
{

@ -1 +1 @@
Subproject commit 71929572c9758ec199742695fb668fc91705b7b7
Subproject commit 2d570c17022a083db70f10a067a112ebf96e38d8

@ -0,0 +1,128 @@
# The Depth Buffer
Let's take a closer look at the last example.
![forest_with_zoom.png](./forest_with_zoom.png)
Models that should be in the back are getting rendered ahead of ones that should be in the front. This is caused by the draw order. By default, pixel data from a new object will replace old pixel data.
There are two ways to solve this: sort the data from back to front, use what's known as a depth buffer.
## Sorting from back to front
This is the go to method for 2d rendering as it's pretty easier to know what's supposed to go in front of what. You can just use the z order. In 3d rendering it gets a little more tricky because the order of the objects changes based on the camera angle.
A simple way of doing this is to sort all the objects by their distance to the cameras position. There are flaws with this method though as when a large object is behind a small object, parts of the large object that should be in front of the small object will be rendered behind. We'll also run into issues with objects that that overlap *themselves*.
If want to do this properly we need to have pixel level precision. That's where a *depth buffer* comes in.
## The depth of pixels
A depth buffer is a black and white texture that stores the z-coordinate of rendered pixels. Wgpu can use this when drawing new pixels to determine whether to replace the data or keep it. This technique is called depth testing. This will fix our draw order problem without needing us to sort our objects!
Let's make a function to create the depth texture.
```rust
const DEPTH_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Depth32Float; // 1.
fn create_depth_texture(device: &wgpu::Device, sc_desc: &wgpu::SwapChainDescriptor) -> wgpu::Texture {
let desc = wgpu::TextureDescriptor {
format: DEPTH_FORMAT,
usage: wgpu::TextureUsage::OUTPUT_ATTACHMENT, // 2.
..sc_desc.to_texture_desc() // 3.
};
device.create_texture(&desc)
}
```
1. We need the DEPTH_FORMAT for when we create the depth stage of the `render_pipeline` and creating the depth texture itself.
2. Since we are rendering to this texture, we need to add the `OUTPUT_ATTACHMENT` flag to it.
3. Our depth texture needs to be the same size as our screen if we want things to render correctly. We can use our `sc_desc` to make sure that our depth texture is the same size as our swap chain images.
We create our `depth_texture` and a `depth_texture_view` in `State::new()`.
```rust
let depth_texture = create_depth_texture(&device, &sc_desc);
let depth_texture_view = depth_texture.create_default_view();
```
We need to modify our `render_pipeline` to allow depth testing.
```rust
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
// ...
depth_stencil_state: Some(wgpu::DepthStencilStateDescriptor {
format: DEPTH_FORMAT,
depth_write_enabled: true,
depth_compare: wgpu::CompareFunction::Less, // 1.
stencil_front: wgpu::StencilStateFaceDescriptor::IGNORE, // 2.
stencil_back: wgpu::StencilStateFaceDescriptor::IGNORE,
stencil_read_mask: 0,
stencil_write_mask: 0,
}),
// ...
});
```
1. The compare function tells us when to discard a new pixel. Using `LESS` means pixels will be drawn front to back. Here are all the values you can use.
```rust
#[repr(C)]
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub enum CompareFunction {
Never = 0,
Less = 1,
Equal = 2,
LessEqual = 3,
Greater = 4,
NotEqual = 5,
GreaterEqual = 6,
Always = 7,
}
```
2. There's another type of buffer called a stencil buffer. It's common practive to store the stencil buffer and depth buffer in the same texture. This fields control values for stencil testing. Since we aren't using a stencil buffer, we'll just set all these to falsy values. We'll cover stencil buffers [later](../../todo).
Don't forget to store the `depth_texture`, and `depth_texture_view` in `State`.
```rust
Self {
// ...
depth_texture,
depth_texture_view,
}
```
We need to remember to change the `resize()` method to create a new `depth_texture` and `depth_texture_view`.
```rust
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
// ...
self.depth_texture = create_depth_texture(&self.device, &self.sc_desc);
self.depth_texture_view = self.depth_texture.create_default_view();
// ...
}
```
The last change we need to make is in the `render()` function. We've created the `depth_texture`, but we're not currently using it. We use it by attaching it to the `depth_stencil_attachment` of a render pass.
```rust
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
/// ...
depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachmentDescriptor {
attachment: &self.depth_texture_view,
depth_load_op: wgpu::LoadOp::Clear,
depth_store_op: wgpu::StoreOp::Store,
clear_depth: 1.0,
stencil_load_op: wgpu::LoadOp::Clear,
stencil_store_op: wgpu::StoreOp::Store,
clear_stencil: 0,
}),
});
```
And that's all we have to do! No shader code needed! If you run the application, the depth issues will be fixed.
![forest_fixed.png](./forest_fixed.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 254 KiB

Loading…
Cancel
Save