How to draw a textured cube in WGPU using indices?

326 Views Asked by At

I'm trying to draw a Minecraft-like textured cube in WGPU. I have a vertex buffer and an index buffer. I'm using Repeat for adress_mode on the texture sampler. I use texture coordinates greater or smaller than 0 to repeat the dirt texture. The front, right and left faces render properly. However, the texture coordinates of the back and top faces are messed up. I didn't add the bottom face yet as I wanted to get these working first. The back texture gets drawn twice and inside out and the top one is completely messed up. Here's the code:

#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
pub struct Vertex {
    pub position: [f32; 3],
    pub tex_coords: [f32; 2]
}

/// We arrange the vertices in counter-clockwise order: top, bottom left, bottom right.
pub const VERTICES: &[Vertex] = &[
    // FRONT
    Vertex {position: [-1.0, -1.0, 0.0], tex_coords: [0.0, 1.0]}, // 0
    Vertex {position: [1.0, -1.0, 0.0], tex_coords: [1.0, 1.0]}, // 1
    Vertex {position: [1.0, 1.0, 0.0], tex_coords: [1.0, 0.0]}, // 2
    Vertex {position: [-1.0, 1.0, 0.0], tex_coords: [0.0, 0.0]}, // 3

    // RIGHT
    Vertex {position: [1.0, -1.0, -2.0], tex_coords: [2.0, 1.0]}, // 4
    Vertex {position: [1.0, 1.0, -2.0], tex_coords: [2.0, 0.0]}, // 5

    // LEFT
    Vertex {position: [-1.0, -1.0, -2.0], tex_coords: [-1.0, 1.0]},
    Vertex {position: [-1.0, 1.0, -2.0], tex_coords: [-1.0, 0.0]},
];

pub const INDICES: &[u16] = &[
    // FRONT
    0, 1, 2,
    2, 3, 0,

    // RIGHT
    1, 4, 5,
    5, 2, 1,

    // LEFT
    6, 0, 3,
    3, 7, 6,

    // BACK
    6, 4, 5,
    5, 7, 6,

    // TOP
    3, 2, 5,
    5, 7, 3
];

My texture sampler:

let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
    address_mode_u: wgpu::AddressMode::Repeat,
    address_mode_v: wgpu::AddressMode::Repeat,
    address_mode_w: wgpu::AddressMode::Repeat,
    mag_filter: wgpu::FilterMode::Nearest,
    min_filter: wgpu::FilterMode::Nearest,
    mipmap_filter: wgpu::FilterMode::Nearest,
        ..Default::default()
});
1

There are 1 best solutions below

2
On BEST ANSWER

Look at the first triangle of your BACK face: it uses vertices 6, 4, and 5. In that order, those vertices are:

Vertex {position: [-1.0, -1.0, -2.0], tex_coords: [-1.0, 1.0]}, // 6
Vertex {position: [1.0, -1.0, -2.0], tex_coords: [2.0, 1.0]}, // 4
Vertex {position: [1.0, 1.0, -2.0], tex_coords: [2.0, 0.0]}, // 5

The u texture coordinates span a range from -1 to 2 horizontally. So, they span 3 copies of the repeating texture, and 3 copies will be drawn in the space of your cube face.

It seems that you're trying to share vertices between different cube faces, and wrap the texture around them continuously. But the problem is that, in texture space, there is always going to be a “seam” at one of the edges of your block where instead of repeating again you start over from a low number (-1 in your code) — it's the same kind of problem as texture mapping a sphere. So, at least one edge must have non-shared vertices.

However, in the long run, you will probably want to stop sharing any vertices between different cube faces. This is because in order to do lighting, vertices must (usually) include normal vectors, and normals are different for each face. As a general rule in computer graphics, vertices can be shared between triangles when they are representing a smoothly curved (or flat) surface, but at any sharp edge, like the edges and corners of a cube, you must make separate vertices for each face.

You can still share vertices between the two triangles that make up one cube face.