#[wgpu] What's wrong with my model??

34 messages · Page 1 of 1 (latest)

zenith berry
#

I'm fairly new to graphics (and wgpu) in general, and I've just built a simple playground containing a bunch of experiments -- from a little .obj loader to a basic model viewer.

After adding "shadow" to the model I started noticing that something was off, and then realized that I could see parts of the model right through it (first image).

At first I thought it had to do with the depth buffer but (I think) I set it up correctly (I followed sotrh wgpu tutorial to do it) and it still do not work. I'm so confused and kinda lost right now.

Things to consider:

  • wgpu: v0.16
  • winit: v0.28
  • mesh was loaded from a .obj file using my custom loader
  • For some reason using TriangleStrip instead of TriangleList seems to "work", all though it leaves those line fragments all over the model (second image).
#
  • Depth buffer setup:
/* ... */
let mut depth_tex_desc = wgpu::TextureDescriptor {
        label: Some("depth_tex"),
        size: wgpu::Extent3d {
            width: surface_config.width,
            height: surface_config.height,
            depth_or_array_layers: 1,
        },
        mip_level_count: 1,
        sample_count: 1,
        dimension: wgpu::TextureDimension::D2,
        format: wgpu::TextureFormat::Depth32Float,
        usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING,
        view_formats: &[wgpu::TextureFormat::Depth32Float], // Also tried with empty slice
    };
    let mut depth_tex = device.create_texture(&depth_tex_desc);
    let mut depth_view = depth_tex.create_view(&wgpu::TextureViewDescriptor::default());
/* ... */
#
  • Pipeline setup:
    let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
        label: Some("Default Pipeline Descriptor"),
        push_constant_ranges: &[],
        bind_group_layouts: &[&model_bg_layout],
    });

    let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
        label: Some("Default Pipeline"),
        layout: Some(&pipeline_layout),
        depth_stencil: Some(wgpu::DepthStencilState {
            format: wgpu::TextureFormat::Depth32Float,
            depth_write_enabled: true,
            depth_compare: wgpu::CompareFunction::Less,
            stencil: wgpu::StencilState::default(),
            bias: wgpu::DepthBiasState::default(),
        }),
        multisample: wgpu::MultisampleState {
            count: 1,
            mask: !0,
            alpha_to_coverage_enabled: false,
        },
        multiview: None,
        primitive: wgpu::PrimitiveState {
            unclipped_depth: false,
            strip_index_format: None,
            conservative: false,
            cull_mode: Some(wgpu::Face::Back),
            front_face: wgpu::FrontFace::Ccw,
            polygon_mode: wgpu::PolygonMode::Fill,
            topology: wgpu::PrimitiveTopology::TriangleList,
        },
        vertex: wgpu::VertexState {
            module: &shader,
            entry_point: "vs_main",
            buffers: &[Mesh::vertex_buf_layout()],
        },
        fragment: Some(wgpu::FragmentState {
            module: &shader,
            entry_point: "fs_main",
            targets: &[Some(wgpu::ColorTargetState {
                format: surface_config.format,
                write_mask: wgpu::ColorWrites::ALL,
                blend: Some(wgpu::BlendState::REPLACE),
            })],
        }),
    });
#
  • Render:
let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
  label: Some("Default Render Pass"),
  depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment {
    view: &depth_view,
    depth_ops: Some(wgpu::Operations {
      load: wgpu::LoadOp::Clear(1.0),
      store: true,
    }),
    stencil_ops: None,
  }),
  color_attachments: &[Some(wgpu::RenderPassColorAttachment {
    ops: wgpu::Operations {
      store: true,
      load: wgpu::LoadOp::Clear(wgpu::Color {
        r: 0.1,
        g: 0.2,
        b: 0.4,
        a: 1.0,
      }),
    },
    view: &view,
    resolve_target: None,
  })],
});

pass.set_pipeline(&pipeline);
pass.set_bind_group(0, &model.model_bg, &[]);
pass.set_vertex_buffer(0, model.vb.slice(..));
pass.set_index_buffer(model.ib.slice(..), wgpu::IndexFormat::Uint32);
pass.draw_indexed(0..mesh.indices.len() as u32, 0, 0..1);
#
  • Shader:
struct Vertex {
    @location(0) position: vec3<f32>,
    @location(1) tex: vec2<f32>,
    @location(2) normal: vec3<f32>,
}

@binding(0) @group(0)
var<uniform> model: mat4x4<f32>;

struct VertexOutput {
    @builtin(position) position: vec4<f32>,
    @location(0) normal: vec3<f32>,
}

@vertex
fn vs_main(vertex: Vertex) -> VertexOutput {
    var output: VertexOutput;
    output.position = model * vec4<f32>(vertex.position, 1.0);
    output.normal = vertex.normal;
    return output;
}

@fragment
fn fs_main(vertex: VertexOutput) -> @location(0) vec4<f32> {
    let light_pos = vec3<f32>(1.0, 1.0, 1.0);
    let color = vec3<f32>(0.2, 0.4, 1.0);
    let diff_strength = max(dot(normalize(vertex.normal), light_pos), 0.0);
    let diff = color * diff_strength;

    return vec4<f32>(diff, 1.0);
}
nimble ember
#

I can't tell what's wrong with your first screenshot (these things are generally a lot easier to see in motion/interactively) but it sounds like maybe you have winding order reversed — this means that the back-sides are culled instead of the front-sides.
A quick change to test this is to change the expected order

front_face: wgpu::FrontFace::Ccw,

to be Cw , but that's a workaround, really — CCW is more common, so you should find the case where things are getting reversed and fix that (or possibly you're loading model data that is already in CW order; I don't know whether .obj is reliably one way or the other)

#

the reason TriangleStrip plausibly “works” is because it produces more triangles from the same vertices than the triangle list interpretation of those vertices does, but half of them will have the opposite winding order

zenith berry
#

I'll also put the code on github and upload a video of the behavior to be easier to analyze

nimble ember
#

Yes, those would be the same

#

but — I recommend you take this as an opportunity to build your graphics troubleshooting skill

#

don't try to throw information at other people until they can fix it for you

#

instead, work on understanding how the code you have is producing the result you have

#

you can do this by modifying the code and looking at the result and seeing how the result differs from what you have

#

work backwards from "I have this picture" to "how did this picture happen"

#

this is not simple but it is something you need to have in your toolbox

#

and there are a variety of intuitions you can build

#

here are some things I would suggest changing to help:

#

• add more, different objects to the scene. Right now, it's hard to tell if things are flipped around, inside out, wrong z, … because you have a very featureless environment. If you add more objects you have reference points, and you can see if those other objects are wrong in the same way or fine or different

slow hedgeBOT
#

add

nimble ember
#

…oops

#

• add animation (this doesn't apply to your case but it is broadly useful): make things wiggle and see if they are wiggling the way you want. Mathematically you can think of this as visualizing the derivatives of the math you're doing to generate the scene

#

• add a second light of a different color to your scene; this gives you more information about normals and “up” ness

#

• add models that you procedurally generated, instead of loading from a file; this lets you distinguish "loader interpreting the file wrong" from "GPU interpreting your in-memory mesh data wrong", and is also a skill useful for debug visualizations and stuff

zenith berry
#

I'll do just that, thanks a lot

#

This was way more helpful than I expected

#

I tried a lot to make it work, but I have no experience debugging gfx

#

It's kinda overwhelming actually lol

nimble ember
#

there's a lot of details!

#

but once you get things going it's lots of fun

#

the point you're at right now is one of the harder ones, where you don't have enough "this works" to give context for "but this doesn't"

#

so, as I said — consider adding more stuff to your scene to add context

zenith berry
#

yeah, I basically started learning gfx with raymarching stuff bc of shadertoy, this is actually the "first" time I'm dealing with this kind of problem

#

but thanks a lot man, I'll explore everything you've suggested