Search code examples
rustbufferrenderingrenderwgpu-rs

Nothing is being rendered when trying to add triangles wgpu-rs


I simply want to make it add triangles at the spot clicked on the screen. Im new to rust and wgpu so I have been following this tutorial. I have used OpenGL a few times so I thought this was pretty similar and stopped at buffers and indices. (Possibly mistake)

I think the issue has to do with the vertex buffer

use crate::tri::Traingle;

pub struct Renderer {
    pub window: Window, // using winit
    surface: wgpu::Surface,
    device: wgpu::Device,
    queue: wgpu::Queue,
    config: wgpu::SurfaceConfiguration,
    pub size: winit::dpi::PhysicalSize<u32>,
    render_pipeline: wgpu::RenderPipeline,
    vertex_buffer: wgpu::Buffer,
    buffer_size: usize,
}

impl Renderer {
    pub async fn new(window: Window) -> Self {
        let size = window.inner_size();
        let instance = wgpu::Instance::new(wgpu::InstanceDescriptor::default());
        let surface = unsafe { instance.create_surface(&window) }.unwrap();

        let adapter = instance.request_adapter(
            &wgpu::RequestAdapterOptions {
                power_preference: wgpu::PowerPreference::default(),
                compatible_surface: Some(&surface),
                force_fallback_adapter: false,
            },
        ).await.unwrap();

        let (device, queue) = adapter.request_device(
            &wgpu::DeviceDescriptor {
                features: wgpu::Features::empty(),
                limits: wgpu::Limits::default(),
                label: None,
            },
            None,
        ).await.unwrap();

        let surface_caps = surface.get_capabilities(&adapter);

        let surface_format = surface_caps.formats.iter()
            .copied()
            .filter(|f| f.is_srgb())
            .next()
            .unwrap_or(surface_caps.formats[0]);
        let config = wgpu::SurfaceConfiguration {
            usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
            format: surface_format,
            width: size.width,
            height: size.height,
            present_mode: surface_caps.present_modes[0],
            alpha_mode: surface_caps.alpha_modes[0],
            view_formats: vec![],
        };
        surface.configure(&device, &config);

        let shader = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));

        let render_pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor::default());
        let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
            label: Some("Render Pipeline"),
            layout: Some(&render_pipeline_layout),
            vertex: wgpu::VertexState {
                module: &shader,
                entry_point: "vs_main",
                buffers: &[
                    Vertex::desc(),
                ],
            },
            fragment: Some(wgpu::FragmentState {
                module: &shader,
                entry_point: "fs_main",
                targets: &[Some(wgpu::ColorTargetState {
                    format: config.format,
                    blend: Some(wgpu::BlendState::REPLACE),
                    write_mask: wgpu::ColorWrites::ALL,
                })],
            }),
            primitive: wgpu::PrimitiveState {
                cull_mode: Some(wgpu::Face::Back),
                ..wgpu::PrimitiveState::default()
            },
            depth_stencil: None,
            multisample: wgpu::MultisampleState::default(),
            multiview: None,
        });

        let vertex_buffer = device.create_buffer(
            &wgpu::BufferDescriptor {
                label: Some("Vertex Buffer"),
                size: 0u64,
                usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
                mapped_at_creation: false,
            }
        );

        let buffer_size = 0usize;

        Self {
            window,
            surface,
            device,
            queue,
            config,
            size,
            render_pipeline,
            vertex_buffer,
            buffer_size,
        }
    }

    pub fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
        if new_size.width > 0 && new_size.height > 0 {
            self.size = new_size;
            self.config.width = new_size.width;
            self.config.height = new_size.height;
            self.surface.configure(&self.device, &self.config);
        }
    }

    pub fn input(&mut self) -> bool {
        false
    }

    pub fn update(&mut self) {}

    pub fn render(&mut self, triangles: &Vec<Triangle>) -> Result<(), wgpu::SurfaceError> {
        let output = self.surface.get_current_texture()?;
        let view = output.texture.create_view(&wgpu::TextureViewDescriptor::default());
        let mut encoder = self.device.create_command_encoder(&wgpu::CommandEncoderDescriptor::default());

        let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
            label: Some("Render Pass"),
            color_attachments: &[
                Some(wgpu::RenderPassColorAttachment {
                    view: &view,
                    resolve_target: None,
                    ops: wgpu::Operations {
                        load: wgpu::LoadOp::Clear(wgpu::Color::BLACK),
                        store: wgpu::StoreOp::Store,
                    },
                })
            ],
            depth_stencil_attachment: None,
            occlusion_query_set: None,
            timestamp_writes: None,
        });

        // creates a vector that will be filled with the vertex positions
        let mut vertices: Vec<Vertex> = vec![Vertex { position: [0., 0., 0.] }; triangles.len() * 3];

        // loops through the triangles and adds their vertices to the list
        for (i, tri) in triangles.iter().enumerate() {
            vertices[i * 3] = Vertex {
                position: [tri.x, tri.y - tri::RADIUS / 800., 0.]
            };
            vertices[i * 3 + 1] = Vertex {
                position: [tri.x - tri::RADIUS / 1200., tri.y + tri::RADIUS / 800., 0.]
            };
            vertices[i * 3 + 2] = Vertex {
                position: [tri.x + tri::RADIUS / 1200., tri.y + tri::RADIUS / 800., 0.]
            };
        }

        // create a new buffer with the right size if its different
        if vertices.len() != self.buffer_size {
            self.vertex_buffer = self.device.create_buffer(
                &wgpu::BufferDescriptor {
                    label: Some("Vertex Buffer"),
                    size: (vertices.len() * std::mem::size_of::<[f32; 3]>()) as u64,
                    usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
                    mapped_at_creation: false,
                }
            );

            self.buffer_size = vertices.len(); 
        }

        self.queue.write_buffer(&self.vertex_buffer, 0u64, bytemuck::cast_slice(vertices.as_slice()));

        let num_vertices = vertices.len() as u32;        

        render_pass.set_pipeline(&self.render_pipeline);
        render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
        render_pass.draw(0..num_vertices, 0..1);

        drop(render_pass);

        self.queue.submit(std::iter::once(encoder.finish()));
        output.present();
    
        Ok(())
    }
}

and heres the part of main.rs where the click function actually adds a triangle.. although this part im 99% sure is functioning correctly

// there is a 'triangles' vec which is passed to Renderer.render()

WindowEvent::MouseInput { button, state, .. } => {
    if button == &MouseButton::Left && state == &ElementState::Pressed {
        triangles.push(Triangle {
            x: mouse_position.x as f32 / WIDTH as f32,
            y: mouse_position.y as f32 / HEIGHT as f32,
        });
    }
}

and finally the wgsl code

struct VertexInput {
    @location(0) position: vec3<f32>,
};

struct VertexOutput {
    @builtin(position) clip_position: vec4<f32>,
};

@vertex
fn vs_main(
    model: VertexInput,
) -> VertexOutput {
    var out: VertexOutput;
    out.clip_position = vec4<f32>(model.position, 1.0);
    return out;
}

@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
    return vec4<f32>(1.0, 0.8, 0.1, 1.0);
}

Maybe I should be using a swapchain? I dont know why that would be the reason it isnt working but again im days into this

all help appreciated


Solution

  • You wrote:

                vertices[i * 3] = Vertex {
                    position: [tri.x, tri.y - tri::RADIUS / 800., 0.]
                };
                vertices[i * 3 + 1] = Vertex {
                    position: [tri.x - tri::RADIUS / 1200., tri.y + tri::RADIUS / 800., 0.]
                };
                vertices[i * 3 + 2] = Vertex {
                    position: [tri.x + tri::RADIUS / 1200., tri.y + tri::RADIUS / 800., 0.]
                };
    

    This is a clockwise-wound triangle, which by your pipeline's PrimitiveState is considered a back-face and culled.

    You should:

    • Swap the ordering of two of these vertices so that the triangle is counterclockwise wound.
    • Until you have gotten the bugs worked out, or permanently if you intend to render only 2D content, consider setting cull_mode to None, so that any further winding / mirroring bugs don't cause your triangles to disappear.

    If you still don't get any visible triangles, I suggest modifying your code to print out the contents of vertices: Vec<Vertex> so that you can see whether the coordinates of the triangles are reasonable. “Everything is accidentally positioned offscreen” is one of the classic ways to get a blank screen.