Tutorial6 improvements

pull/512/head
Blatko1 5 months ago
parent c3abb241c2
commit fb75eac20c
No known key found for this signature in database
GPG Key ID: 8D56CF822AB1CD85

@ -1,6 +1,6 @@
# Uniform buffers and a 3d camera
While all of our previous work has seemed to be in 2d, we've actually been working in 3d the entire time! That's part of the reason why our `Vertex` structure has `position` be an array of 3 floats instead of just 2. We can't really see the 3d-ness of our scene, because we're viewing things head-on. We're going to change our point of view by creating a `Camera`.
While all of our previous work has seemed to be in 2D, we've actually been working in 3d the entire time! That's part of the reason why our `Vertex` structure has `position` as an array of 3 floats instead of just 2. We can't really see the 3d-ness of our scene because we're viewing things head-on. We're going to change our point of view by creating a `Camera`.
## A perspective camera
@ -12,7 +12,7 @@ This tutorial is more about learning to use wgpu and less about linear algebra,
cgmath = "0.18"
```
Now that we have a math library, let's put it to use! Create a `Camera` struct above the `State` struct.
Now that we have a math library let's put it to use! Create a `Camera` struct above the `State` struct.
```rust
struct Camera {
@ -41,7 +41,7 @@ impl Camera {
The `build_view_projection_matrix` is where the magic happens.
1. The `view` matrix moves the world to be at the position and rotation of the camera. It's essentially an inverse of whatever the transform matrix of the camera would be.
2. The `proj` matrix warps the scene to give the effect of depth. Without this, objects up close would be the same size as objects far away.
3. The coordinate system in Wgpu is based on DirectX, and Metal's coordinate systems. That means that in [normalized device coordinates](https://github.com/gfx-rs/gfx/tree/master/src/backend/dx12#normalized-coordinates) the x axis and y axis are in the range of -1.0 to +1.0, and the z axis is 0.0 to +1.0. The `cgmath` crate (as well as most game math crates) is built for OpenGL's coordinate system. This matrix will scale and translate our scene from OpenGL's coordinate system to WGPU's. We'll define it as follows.
3. The coordinate system in Wgpu is based on DirectX and Metal's coordinate systems. That means that in [normalized device coordinates](https://github.com/gfx-rs/gfx/tree/master/src/backend/dx12#normalized-coordinates), the x-axis and y-axis are in the range of -1.0 to +1.0, and the z-axis is 0.0 to +1.0. The `cgmath` crate (as well as most game math crates) is built for OpenGL's coordinate system. This matrix will scale and translate our scene from OpenGL's coordinate system to WGPU's. We'll define it as follows.
```rust
#[rustfmt::skip]
@ -68,7 +68,7 @@ async fn new(window: Window) -> Self {
// let diffuse_bind_group ...
let camera = Camera {
// position the camera one unit up and 2 units back
// position the camera 1 unit up and 2 units back
// +z is out of the screen
eye: (0.0, 1.0, 2.0).into(),
// have it look at the origin
@ -93,7 +93,7 @@ Now that we have our camera, and it can make us a view projection matrix, we nee
## The uniform buffer
Up to this point, we've used `Buffer`s to store our vertex and index data, and even to load our textures. We are going to use them again to create what's known as a uniform buffer. A uniform is a blob of data that is available to every invocation of a set of shaders. We've technically already used uniforms for our texture and sampler. We're going to use them again to store our view projection matrix. To start let's create a struct to hold our uniform.
Up to this point, we've used `Buffer`s to store our vertex and index data, and even to load our textures. We are going to use them again to create what's known as a uniform buffer. A uniform is a blob of data available to every invocation of a set of shaders. Technically, we've already used uniforms for our texture and sampler. We're going to use them again to store our view projection matrix. To start, let's create a struct to hold our uniform.
```rust
// We need this for Rust to store our data correctly for the shaders
@ -101,7 +101,7 @@ Up to this point, we've used `Buffer`s to store our vertex and index data, and e
// This is so we can store this in a buffer
#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct CameraUniform {
// We can't use cgmath with bytemuck directly so we'll have
// We can't use cgmath with bytemuck directly, so we'll have
// to convert the Matrix4 into a 4x4 f32 array
view_proj: [[f32; 4]; 4],
}
@ -139,7 +139,7 @@ let camera_buffer = device.create_buffer_init(
## Uniform buffers and bind groups
Cool, now that we have a uniform buffer, what do we do with it? The answer is we create a bind group for it. First, we have to create the bind group layout.
Cool! Now that we have a uniform buffer, what do we do with it? The answer is we create a bind group for it. First, we have to create the bind group layout.
```rust
let camera_bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
@ -164,12 +164,12 @@ Some things to note:
1. We set `visibility` to `ShaderStages::VERTEX` as we only really need camera information in the vertex shader, as
that's what we'll use to manipulate our vertices.
2. The `has_dynamic_offset` means that the location of the data in the buffer may change. This will be the case if you
store multiple sets of data that vary in size in a single buffer. If you set this to true you'll have to supply the
store multiple data sets that vary in size in a single buffer. If you set this to true, you'll have to supply the
offsets later.
3. `min_binding_size` specifies the smallest size the buffer can be. You don't have to specify this, so we
leave it `None`. If you want to know more you can check [the docs](https://docs.rs/wgpu/latest/wgpu/enum.BindingType.html#variant.Buffer.field.min_binding_size).
leave it `None`. If you want to know more, you can check [the docs](https://docs.rs/wgpu/latest/wgpu/enum.BindingType.html#variant.Buffer.field.min_binding_size).
Now we can create the actual bind group.
Now, we can create the actual bind group.
```rust
let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
@ -273,7 +273,7 @@ fn vs_main(
## A controller for our camera
If you run the code right now, you should get something that looks like this.
If you run the code right now, you should get something like this.
![./static-tree.png](./static-tree.png)
@ -340,7 +340,7 @@ impl CameraController {
let forward_norm = forward.normalize();
let forward_mag = forward.magnitude();
// Prevents glitching when camera gets too close to the
// Prevents glitching when the camera gets too close to the
// center of the scene.
if self.is_forward_pressed && forward_mag > self.speed {
camera.eye += forward_norm * self.speed;
@ -351,13 +351,13 @@ impl CameraController {
let right = forward_norm.cross(camera.up);
// Redo radius calc in case the fowrard/backward is pressed.
// Redo radius calc in case the forward/backward is pressed.
let forward = camera.target - camera.eye;
let forward_mag = forward.magnitude();
if self.is_right_pressed {
// Rescale the distance between the target and eye so
// that it doesn't change. The eye therefore still
// Rescale the distance between the target and the eye so
// that it doesn't change. The eye, therefore, still
// lies on the circle made by the target and eye.
camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag;
}
@ -368,7 +368,7 @@ impl CameraController {
}
```
This code is not perfect. The camera slowly moves back when you rotate it. It works for our purposes though. Feel free to improve it!
This code is not perfect. The camera slowly moves back when you rotate it. It works for our purposes, though. Feel free to improve it!
We still need to plug this into our existing code to make it do anything. Add the controller to `State` and create it in `new()`.
@ -405,8 +405,8 @@ fn input(&mut self, event: &WindowEvent) -> bool {
```
Up to this point, the camera controller isn't actually doing anything. The values in our uniform buffer need to be updated. There are a few main methods to do that.
1. We can create a separate buffer and copy its contents to our `camera_buffer`. The new buffer is known as a staging buffer. This method is usually how it's done as it allows the contents of the main buffer (in this case `camera_buffer`) to only be accessible by the gpu. The gpu can do some speed optimizations which it couldn't if we could access the buffer via the cpu.
2. We can call one of the mapping methods `map_read_async`, and `map_write_async` on the buffer itself. These allow us to access a buffer's contents directly but require us to deal with the `async` aspect of these methods this also requires our buffer to use the `BufferUsages::MAP_READ` and/or `BufferUsages::MAP_WRITE`. We won't talk about it here, but you check out [Wgpu without a window](../../showcase/windowless) tutorial if you want to know more.
1. We can create a separate buffer and copy its contents to our `camera_buffer`. The new buffer is known as a staging buffer. This method is usually how it's done as it allows the contents of the main buffer (in this case, `camera_buffer`) to be accessible only by the GPU. The GPU can do some speed optimizations, which it couldn't if we could access the buffer via the CPU.
2. We can call one of the mapping methods `map_read_async`, and `map_write_async` on the buffer itself. These allow us to access a buffer's contents directly but require us to deal with the `async` aspect of these methods. This also requires our buffer to use the `BufferUsages::MAP_READ` and/or `BufferUsages::MAP_WRITE`. We won't talk about it here, but check out the [Wgpu without a window](../../showcase/windowless) tutorial if you want to know more.
3. We can use `write_buffer` on `queue`.
We're going to use option number 3.
@ -419,7 +419,7 @@ fn update(&mut self) {
}
```
That's all we need to do. If you run the code now you should see a pentagon with our tree texture that you can rotate around and zoom into with the wasd/arrow keys.
That's all we need to do. If you run the code now, you should see a pentagon with our tree texture that you can rotate around and zoom into with the wasd/arrow keys.
## Challenge

Loading…
Cancel
Save