diff options
Diffstat (limited to 'examples/custom_shader/src')
| -rw-r--r-- | examples/custom_shader/src/main.rs | 163 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene.rs | 186 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/camera.rs | 53 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/pipeline.rs | 621 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/pipeline/buffer.rs | 41 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/pipeline/cube.rs | 326 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/pipeline/uniforms.rs | 23 | ||||
| -rw-r--r-- | examples/custom_shader/src/scene/pipeline/vertex.rs | 31 | ||||
| -rw-r--r-- | examples/custom_shader/src/shaders/cubes.wgsl | 123 | ||||
| -rw-r--r-- | examples/custom_shader/src/shaders/depth.wgsl | 48 | 
10 files changed, 1615 insertions, 0 deletions
| diff --git a/examples/custom_shader/src/main.rs b/examples/custom_shader/src/main.rs new file mode 100644 index 00000000..3bfa3a43 --- /dev/null +++ b/examples/custom_shader/src/main.rs @@ -0,0 +1,163 @@ +mod scene; + +use scene::Scene; + +use iced::executor; +use iced::time::Instant; +use iced::widget::shader::wgpu; +use iced::widget::{checkbox, column, container, row, shader, slider, text}; +use iced::window; +use iced::{ +    Alignment, Application, Color, Command, Element, Length, Renderer, +    Subscription, Theme, +}; + +fn main() -> iced::Result { +    IcedCubes::run(iced::Settings::default()) +} + +struct IcedCubes { +    start: Instant, +    scene: Scene, +} + +#[derive(Debug, Clone)] +enum Message { +    CubeAmountChanged(u32), +    CubeSizeChanged(f32), +    Tick(Instant), +    ShowDepthBuffer(bool), +    LightColorChanged(Color), +} + +impl Application for IcedCubes { +    type Executor = executor::Default; +    type Message = Message; +    type Theme = Theme; +    type Flags = (); + +    fn new(_flags: Self::Flags) -> (Self, Command<Self::Message>) { +        ( +            Self { +                start: Instant::now(), +                scene: Scene::new(), +            }, +            Command::none(), +        ) +    } + +    fn title(&self) -> String { +        "Iced Cubes".to_string() +    } + +    fn update(&mut self, message: Self::Message) -> Command<Self::Message> { +        match message { +            Message::CubeAmountChanged(amount) => { +                self.scene.change_amount(amount); +            } +            Message::CubeSizeChanged(size) => { +                self.scene.size = size; +            } +            Message::Tick(time) => { +                self.scene.update(time - self.start); +            } +            Message::ShowDepthBuffer(show) => { +                self.scene.show_depth_buffer = show; +            } +            Message::LightColorChanged(color) => { +                self.scene.light_color = color; +            } +        } + +        Command::none() +    } + +    fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> { +        let top_controls = row![ +            control( +                "Amount", +                slider( +                    1..=scene::MAX, +                    self.scene.cubes.len() as u32, +                    Message::CubeAmountChanged +                ) +                .width(100) +            ), +            control( +                "Size", +                slider(0.1..=0.25, self.scene.size, Message::CubeSizeChanged) +                    .step(0.01) +                    .width(100), +            ), +            checkbox( +                "Show Depth Buffer", +                self.scene.show_depth_buffer, +                Message::ShowDepthBuffer +            ), +        ] +        .spacing(40); + +        let bottom_controls = row![ +            control( +                "R", +                slider(0.0..=1.0, self.scene.light_color.r, move |r| { +                    Message::LightColorChanged(Color { +                        r, +                        ..self.scene.light_color +                    }) +                }) +                .step(0.01) +                .width(100) +            ), +            control( +                "G", +                slider(0.0..=1.0, self.scene.light_color.g, move |g| { +                    Message::LightColorChanged(Color { +                        g, +                        ..self.scene.light_color +                    }) +                }) +                .step(0.01) +                .width(100) +            ), +            control( +                "B", +                slider(0.0..=1.0, self.scene.light_color.b, move |b| { +                    Message::LightColorChanged(Color { +                        b, +                        ..self.scene.light_color +                    }) +                }) +                .step(0.01) +                .width(100) +            ) +        ] +        .spacing(40); + +        let controls = column![top_controls, bottom_controls,] +            .spacing(10) +            .padding(20) +            .align_items(Alignment::Center); + +        let shader = +            shader(&self.scene).width(Length::Fill).height(Length::Fill); + +        container(column![shader, controls].align_items(Alignment::Center)) +            .width(Length::Fill) +            .height(Length::Fill) +            .center_x() +            .center_y() +            .into() +    } + +    fn subscription(&self) -> Subscription<Self::Message> { +        window::frames().map(Message::Tick) +    } +} + +fn control<'a>( +    label: &'static str, +    control: impl Into<Element<'a, Message>>, +) -> Element<'a, Message> { +    row![text(label), control.into()].spacing(10).into() +} diff --git a/examples/custom_shader/src/scene.rs b/examples/custom_shader/src/scene.rs new file mode 100644 index 00000000..a35efdd9 --- /dev/null +++ b/examples/custom_shader/src/scene.rs @@ -0,0 +1,186 @@ +mod camera; +mod pipeline; + +use camera::Camera; +use pipeline::Pipeline; + +use crate::wgpu; +use pipeline::cube::{self, Cube}; + +use iced::mouse; +use iced::time::Duration; +use iced::widget::shader; +use iced::{Color, Rectangle, Size}; + +use glam::Vec3; +use rand::Rng; +use std::cmp::Ordering; +use std::iter; + +pub const MAX: u32 = 500; + +#[derive(Clone)] +pub struct Scene { +    pub size: f32, +    pub cubes: Vec<Cube>, +    pub camera: Camera, +    pub show_depth_buffer: bool, +    pub light_color: Color, +} + +impl Scene { +    pub fn new() -> Self { +        let mut scene = Self { +            size: 0.2, +            cubes: vec![], +            camera: Camera::default(), +            show_depth_buffer: false, +            light_color: Color::WHITE, +        }; + +        scene.change_amount(MAX); + +        scene +    } + +    pub fn update(&mut self, time: Duration) { +        for cube in self.cubes.iter_mut() { +            cube.update(self.size, time.as_secs_f32()); +        } +    } + +    pub fn change_amount(&mut self, amount: u32) { +        let curr_cubes = self.cubes.len() as u32; + +        match amount.cmp(&curr_cubes) { +            Ordering::Greater => { +                // spawn +                let cubes_2_spawn = (amount - curr_cubes) as usize; + +                let mut cubes = 0; +                self.cubes.extend(iter::from_fn(|| { +                    if cubes < cubes_2_spawn { +                        cubes += 1; +                        Some(Cube::new(self.size, rnd_origin())) +                    } else { +                        None +                    } +                })); +            } +            Ordering::Less => { +                // chop +                let cubes_2_cut = curr_cubes - amount; +                let new_len = self.cubes.len() - cubes_2_cut as usize; +                self.cubes.truncate(new_len); +            } +            Ordering::Equal => {} +        } +    } +} + +impl<Message> shader::Program<Message> for Scene { +    type State = (); +    type Primitive = Primitive; + +    fn draw( +        &self, +        _state: &Self::State, +        _cursor: mouse::Cursor, +        bounds: Rectangle, +    ) -> Self::Primitive { +        Primitive::new( +            &self.cubes, +            &self.camera, +            bounds, +            self.show_depth_buffer, +            self.light_color, +        ) +    } +} + +/// A collection of `Cube`s that can be rendered. +#[derive(Debug)] +pub struct Primitive { +    cubes: Vec<cube::Raw>, +    uniforms: pipeline::Uniforms, +    show_depth_buffer: bool, +} + +impl Primitive { +    pub fn new( +        cubes: &[Cube], +        camera: &Camera, +        bounds: Rectangle, +        show_depth_buffer: bool, +        light_color: Color, +    ) -> Self { +        let uniforms = pipeline::Uniforms::new(camera, bounds, light_color); + +        Self { +            cubes: cubes +                .iter() +                .map(cube::Raw::from_cube) +                .collect::<Vec<cube::Raw>>(), +            uniforms, +            show_depth_buffer, +        } +    } +} + +impl shader::Primitive for Primitive { +    fn prepare( +        &self, +        format: wgpu::TextureFormat, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        _bounds: Rectangle, +        target_size: Size<u32>, +        _scale_factor: f32, +        storage: &mut shader::Storage, +    ) { +        if !storage.has::<Pipeline>() { +            storage.store(Pipeline::new(device, queue, format, target_size)); +        } + +        let pipeline = storage.get_mut::<Pipeline>().unwrap(); + +        //upload data to GPU +        pipeline.update( +            device, +            queue, +            target_size, +            &self.uniforms, +            self.cubes.len(), +            &self.cubes, +        ); +    } + +    fn render( +        &self, +        storage: &shader::Storage, +        target: &wgpu::TextureView, +        _target_size: Size<u32>, +        viewport: Rectangle<u32>, +        encoder: &mut wgpu::CommandEncoder, +    ) { +        //at this point our pipeline should always be initialized +        let pipeline = storage.get::<Pipeline>().unwrap(); + +        //render primitive +        pipeline.render( +            target, +            encoder, +            viewport, +            self.cubes.len() as u32, +            self.show_depth_buffer, +        ); +    } +} + +fn rnd_origin() -> Vec3 { +    Vec3::new( +        rand::thread_rng().gen_range(-4.0..4.0), +        rand::thread_rng().gen_range(-4.0..4.0), +        rand::thread_rng().gen_range(-4.0..2.0), +    ) +} diff --git a/examples/custom_shader/src/scene/camera.rs b/examples/custom_shader/src/scene/camera.rs new file mode 100644 index 00000000..2a49c102 --- /dev/null +++ b/examples/custom_shader/src/scene/camera.rs @@ -0,0 +1,53 @@ +use glam::{mat4, vec3, vec4}; +use iced::Rectangle; + +#[derive(Copy, Clone)] +pub struct Camera { +    eye: glam::Vec3, +    target: glam::Vec3, +    up: glam::Vec3, +    fov_y: f32, +    near: f32, +    far: f32, +} + +impl Default for Camera { +    fn default() -> Self { +        Self { +            eye: vec3(0.0, 2.0, 3.0), +            target: glam::Vec3::ZERO, +            up: glam::Vec3::Y, +            fov_y: 45.0, +            near: 0.1, +            far: 100.0, +        } +    } +} + +pub const OPENGL_TO_WGPU_MATRIX: glam::Mat4 = mat4( +    vec4(1.0, 0.0, 0.0, 0.0), +    vec4(0.0, 1.0, 0.0, 0.0), +    vec4(0.0, 0.0, 0.5, 0.0), +    vec4(0.0, 0.0, 0.5, 1.0), +); + +impl Camera { +    pub fn build_view_proj_matrix(&self, bounds: Rectangle) -> glam::Mat4 { +        //TODO looks distorted without padding; base on surface texture size instead? +        let aspect_ratio = bounds.width / (bounds.height + 150.0); + +        let view = glam::Mat4::look_at_rh(self.eye, self.target, self.up); +        let proj = glam::Mat4::perspective_rh( +            self.fov_y, +            aspect_ratio, +            self.near, +            self.far, +        ); + +        OPENGL_TO_WGPU_MATRIX * proj * view +    } + +    pub fn position(&self) -> glam::Vec4 { +        glam::Vec4::from((self.eye, 0.0)) +    } +} diff --git a/examples/custom_shader/src/scene/pipeline.rs b/examples/custom_shader/src/scene/pipeline.rs new file mode 100644 index 00000000..50b70a98 --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline.rs @@ -0,0 +1,621 @@ +pub mod cube; + +mod buffer; +mod uniforms; +mod vertex; + +pub use uniforms::Uniforms; + +use buffer::Buffer; +use vertex::Vertex; + +use crate::wgpu; +use crate::wgpu::util::DeviceExt; + +use iced::{Rectangle, Size}; + +const SKY_TEXTURE_SIZE: u32 = 128; + +pub struct Pipeline { +    pipeline: wgpu::RenderPipeline, +    vertices: wgpu::Buffer, +    cubes: Buffer, +    uniforms: wgpu::Buffer, +    uniform_bind_group: wgpu::BindGroup, +    depth_texture_size: Size<u32>, +    depth_view: wgpu::TextureView, +    depth_pipeline: DepthPipeline, +} + +impl Pipeline { +    pub fn new( +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        format: wgpu::TextureFormat, +        target_size: Size<u32>, +    ) -> Self { +        //vertices of one cube +        let vertices = +            device.create_buffer_init(&wgpu::util::BufferInitDescriptor { +                label: Some("cubes vertex buffer"), +                contents: bytemuck::cast_slice(&cube::Raw::vertices()), +                usage: wgpu::BufferUsages::VERTEX, +            }); + +        //cube instance data +        let cubes_buffer = Buffer::new( +            device, +            "cubes instance buffer", +            std::mem::size_of::<cube::Raw>() as u64, +            wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, +        ); + +        //uniforms for all cubes +        let uniforms = device.create_buffer(&wgpu::BufferDescriptor { +            label: Some("cubes uniform buffer"), +            size: std::mem::size_of::<Uniforms>() as u64, +            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, +            mapped_at_creation: false, +        }); + +        //depth buffer +        let depth_texture = device.create_texture(&wgpu::TextureDescriptor { +            label: Some("cubes depth texture"), +            size: wgpu::Extent3d { +                width: target_size.width, +                height: target_size.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: &[], +        }); + +        let depth_view = +            depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); + +        let normal_map_data = load_normal_map_data(); + +        //normal map +        let normal_texture = device.create_texture_with_data( +            queue, +            &wgpu::TextureDescriptor { +                label: Some("cubes normal map texture"), +                size: wgpu::Extent3d { +                    width: 1024, +                    height: 1024, +                    depth_or_array_layers: 1, +                }, +                mip_level_count: 1, +                sample_count: 1, +                dimension: wgpu::TextureDimension::D2, +                format: wgpu::TextureFormat::Rgba8Unorm, +                usage: wgpu::TextureUsages::TEXTURE_BINDING, +                view_formats: &[], +            }, +            wgpu::util::TextureDataOrder::LayerMajor, +            &normal_map_data, +        ); + +        let normal_view = +            normal_texture.create_view(&wgpu::TextureViewDescriptor::default()); + +        //skybox texture for reflection/refraction +        let skybox_data = load_skybox_data(); + +        let skybox_texture = device.create_texture_with_data( +            queue, +            &wgpu::TextureDescriptor { +                label: Some("cubes skybox texture"), +                size: wgpu::Extent3d { +                    width: SKY_TEXTURE_SIZE, +                    height: SKY_TEXTURE_SIZE, +                    depth_or_array_layers: 6, //one for each face of the cube +                }, +                mip_level_count: 1, +                sample_count: 1, +                dimension: wgpu::TextureDimension::D2, +                format: wgpu::TextureFormat::Rgba8Unorm, +                usage: wgpu::TextureUsages::TEXTURE_BINDING, +                view_formats: &[], +            }, +            wgpu::util::TextureDataOrder::LayerMajor, +            &skybox_data, +        ); + +        let sky_view = +            skybox_texture.create_view(&wgpu::TextureViewDescriptor { +                label: Some("cubes skybox texture view"), +                dimension: Some(wgpu::TextureViewDimension::Cube), +                ..Default::default() +            }); + +        let sky_sampler = device.create_sampler(&wgpu::SamplerDescriptor { +            label: Some("cubes skybox sampler"), +            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::Linear, +            ..Default::default() +        }); + +        let uniform_bind_group_layout = +            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { +                label: Some("cubes uniform bind group layout"), +                entries: &[ +                    wgpu::BindGroupLayoutEntry { +                        binding: 0, +                        visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, +                        ty: wgpu::BindingType::Buffer { +                            ty: wgpu::BufferBindingType::Uniform, +                            has_dynamic_offset: false, +                            min_binding_size: None, +                        }, +                        count: None, +                    }, +                    wgpu::BindGroupLayoutEntry { +                        binding: 1, +                        visibility: wgpu::ShaderStages::FRAGMENT, +                        ty: wgpu::BindingType::Texture { +                            sample_type: wgpu::TextureSampleType::Float { +                                filterable: true, +                            }, +                            view_dimension: wgpu::TextureViewDimension::Cube, +                            multisampled: false, +                        }, +                        count: None, +                    }, +                    wgpu::BindGroupLayoutEntry { +                        binding: 2, +                        visibility: wgpu::ShaderStages::FRAGMENT, +                        ty: wgpu::BindingType::Sampler( +                            wgpu::SamplerBindingType::Filtering, +                        ), +                        count: None, +                    }, +                    wgpu::BindGroupLayoutEntry { +                        binding: 3, +                        visibility: wgpu::ShaderStages::FRAGMENT, +                        ty: wgpu::BindingType::Texture { +                            sample_type: wgpu::TextureSampleType::Float { +                                filterable: true, +                            }, +                            view_dimension: wgpu::TextureViewDimension::D2, +                            multisampled: false, +                        }, +                        count: None, +                    }, +                ], +            }); + +        let uniform_bind_group = +            device.create_bind_group(&wgpu::BindGroupDescriptor { +                label: Some("cubes uniform bind group"), +                layout: &uniform_bind_group_layout, +                entries: &[ +                    wgpu::BindGroupEntry { +                        binding: 0, +                        resource: uniforms.as_entire_binding(), +                    }, +                    wgpu::BindGroupEntry { +                        binding: 1, +                        resource: wgpu::BindingResource::TextureView(&sky_view), +                    }, +                    wgpu::BindGroupEntry { +                        binding: 2, +                        resource: wgpu::BindingResource::Sampler(&sky_sampler), +                    }, +                    wgpu::BindGroupEntry { +                        binding: 3, +                        resource: wgpu::BindingResource::TextureView( +                            &normal_view, +                        ), +                    }, +                ], +            }); + +        let layout = +            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { +                label: Some("cubes pipeline layout"), +                bind_group_layouts: &[&uniform_bind_group_layout], +                push_constant_ranges: &[], +            }); + +        let shader = +            device.create_shader_module(wgpu::ShaderModuleDescriptor { +                label: Some("cubes shader"), +                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( +                    include_str!("../shaders/cubes.wgsl"), +                )), +            }); + +        let pipeline = +            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { +                label: Some("cubes pipeline"), +                layout: Some(&layout), +                vertex: wgpu::VertexState { +                    module: &shader, +                    entry_point: "vs_main", +                    buffers: &[Vertex::desc(), cube::Raw::desc()], +                }, +                primitive: wgpu::PrimitiveState::default(), +                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, +                }, +                fragment: Some(wgpu::FragmentState { +                    module: &shader, +                    entry_point: "fs_main", +                    targets: &[Some(wgpu::ColorTargetState { +                        format, +                        blend: Some(wgpu::BlendState { +                            color: wgpu::BlendComponent { +                                src_factor: wgpu::BlendFactor::SrcAlpha, +                                dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, +                                operation: wgpu::BlendOperation::Add, +                            }, +                            alpha: wgpu::BlendComponent { +                                src_factor: wgpu::BlendFactor::One, +                                dst_factor: wgpu::BlendFactor::One, +                                operation: wgpu::BlendOperation::Max, +                            }, +                        }), +                        write_mask: wgpu::ColorWrites::ALL, +                    })], +                }), +                multiview: None, +            }); + +        let depth_pipeline = DepthPipeline::new( +            device, +            format, +            depth_texture.create_view(&wgpu::TextureViewDescriptor::default()), +        ); + +        Self { +            pipeline, +            cubes: cubes_buffer, +            uniforms, +            uniform_bind_group, +            vertices, +            depth_texture_size: target_size, +            depth_view, +            depth_pipeline, +        } +    } + +    fn update_depth_texture(&mut self, device: &wgpu::Device, size: Size<u32>) { +        if self.depth_texture_size.height != size.height +            || self.depth_texture_size.width != size.width +        { +            let text = device.create_texture(&wgpu::TextureDescriptor { +                label: Some("cubes depth texture"), +                size: wgpu::Extent3d { +                    width: size.width, +                    height: size.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: &[], +            }); + +            self.depth_view = +                text.create_view(&wgpu::TextureViewDescriptor::default()); +            self.depth_texture_size = size; + +            self.depth_pipeline.update(device, &text); +        } +    } + +    pub fn update( +        &mut self, +        device: &wgpu::Device, +        queue: &wgpu::Queue, +        target_size: Size<u32>, +        uniforms: &Uniforms, +        num_cubes: usize, +        cubes: &[cube::Raw], +    ) { +        //recreate depth texture if surface texture size has changed +        self.update_depth_texture(device, target_size); + +        // update uniforms +        queue.write_buffer(&self.uniforms, 0, bytemuck::bytes_of(uniforms)); + +        //resize cubes vertex buffer if cubes amount changed +        let new_size = num_cubes * std::mem::size_of::<cube::Raw>(); +        self.cubes.resize(device, new_size as u64); + +        //always write new cube data since they are constantly rotating +        queue.write_buffer(&self.cubes.raw, 0, bytemuck::cast_slice(cubes)); +    } + +    pub fn render( +        &self, +        target: &wgpu::TextureView, +        encoder: &mut wgpu::CommandEncoder, +        viewport: Rectangle<u32>, +        num_cubes: u32, +        show_depth: bool, +    ) { +        { +            let mut pass = +                encoder.begin_render_pass(&wgpu::RenderPassDescriptor { +                    label: Some("cubes.pipeline.pass"), +                    color_attachments: &[Some( +                        wgpu::RenderPassColorAttachment { +                            view: target, +                            resolve_target: None, +                            ops: wgpu::Operations { +                                load: wgpu::LoadOp::Load, +                                store: wgpu::StoreOp::Store, +                            }, +                        }, +                    )], +                    depth_stencil_attachment: Some( +                        wgpu::RenderPassDepthStencilAttachment { +                            view: &self.depth_view, +                            depth_ops: Some(wgpu::Operations { +                                load: wgpu::LoadOp::Clear(1.0), +                                store: wgpu::StoreOp::Store, +                            }), +                            stencil_ops: None, +                        }, +                    ), +                    timestamp_writes: None, +                    occlusion_query_set: None, +                }); + +            pass.set_scissor_rect( +                viewport.x, +                viewport.y, +                viewport.width, +                viewport.height, +            ); +            pass.set_pipeline(&self.pipeline); +            pass.set_bind_group(0, &self.uniform_bind_group, &[]); +            pass.set_vertex_buffer(0, self.vertices.slice(..)); +            pass.set_vertex_buffer(1, self.cubes.raw.slice(..)); +            pass.draw(0..36, 0..num_cubes); +        } + +        if show_depth { +            self.depth_pipeline.render(encoder, target, viewport); +        } +    } +} + +struct DepthPipeline { +    pipeline: wgpu::RenderPipeline, +    bind_group_layout: wgpu::BindGroupLayout, +    bind_group: wgpu::BindGroup, +    sampler: wgpu::Sampler, +    depth_view: wgpu::TextureView, +} + +impl DepthPipeline { +    pub fn new( +        device: &wgpu::Device, +        format: wgpu::TextureFormat, +        depth_texture: wgpu::TextureView, +    ) -> Self { +        let sampler = device.create_sampler(&wgpu::SamplerDescriptor { +            label: Some("cubes.depth_pipeline.sampler"), +            ..Default::default() +        }); + +        let bind_group_layout = +            device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { +                label: Some("cubes.depth_pipeline.bind_group_layout"), +                entries: &[ +                    wgpu::BindGroupLayoutEntry { +                        binding: 0, +                        visibility: wgpu::ShaderStages::FRAGMENT, +                        ty: wgpu::BindingType::Sampler( +                            wgpu::SamplerBindingType::NonFiltering, +                        ), +                        count: None, +                    }, +                    wgpu::BindGroupLayoutEntry { +                        binding: 1, +                        visibility: wgpu::ShaderStages::FRAGMENT, +                        ty: wgpu::BindingType::Texture { +                            sample_type: wgpu::TextureSampleType::Float { +                                filterable: false, +                            }, +                            view_dimension: wgpu::TextureViewDimension::D2, +                            multisampled: false, +                        }, +                        count: None, +                    }, +                ], +            }); + +        let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor { +            label: Some("cubes.depth_pipeline.bind_group"), +            layout: &bind_group_layout, +            entries: &[ +                wgpu::BindGroupEntry { +                    binding: 0, +                    resource: wgpu::BindingResource::Sampler(&sampler), +                }, +                wgpu::BindGroupEntry { +                    binding: 1, +                    resource: wgpu::BindingResource::TextureView( +                        &depth_texture, +                    ), +                }, +            ], +        }); + +        let layout = +            device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { +                label: Some("cubes.depth_pipeline.layout"), +                bind_group_layouts: &[&bind_group_layout], +                push_constant_ranges: &[], +            }); + +        let shader = +            device.create_shader_module(wgpu::ShaderModuleDescriptor { +                label: Some("cubes.depth_pipeline.shader"), +                source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( +                    include_str!("../shaders/depth.wgsl"), +                )), +            }); + +        let pipeline = +            device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { +                label: Some("cubes.depth_pipeline.pipeline"), +                layout: Some(&layout), +                vertex: wgpu::VertexState { +                    module: &shader, +                    entry_point: "vs_main", +                    buffers: &[], +                }, +                primitive: wgpu::PrimitiveState::default(), +                depth_stencil: Some(wgpu::DepthStencilState { +                    format: wgpu::TextureFormat::Depth32Float, +                    depth_write_enabled: false, +                    depth_compare: wgpu::CompareFunction::Less, +                    stencil: wgpu::StencilState::default(), +                    bias: wgpu::DepthBiasState::default(), +                }), +                multisample: wgpu::MultisampleState::default(), +                fragment: Some(wgpu::FragmentState { +                    module: &shader, +                    entry_point: "fs_main", +                    targets: &[Some(wgpu::ColorTargetState { +                        format, +                        blend: Some(wgpu::BlendState::REPLACE), +                        write_mask: wgpu::ColorWrites::ALL, +                    })], +                }), +                multiview: None, +            }); + +        Self { +            pipeline, +            bind_group_layout, +            bind_group, +            sampler, +            depth_view: depth_texture, +        } +    } + +    pub fn update( +        &mut self, +        device: &wgpu::Device, +        depth_texture: &wgpu::Texture, +    ) { +        self.depth_view = +            depth_texture.create_view(&wgpu::TextureViewDescriptor::default()); + +        self.bind_group = +            device.create_bind_group(&wgpu::BindGroupDescriptor { +                label: Some("cubes.depth_pipeline.bind_group"), +                layout: &self.bind_group_layout, +                entries: &[ +                    wgpu::BindGroupEntry { +                        binding: 0, +                        resource: wgpu::BindingResource::Sampler(&self.sampler), +                    }, +                    wgpu::BindGroupEntry { +                        binding: 1, +                        resource: wgpu::BindingResource::TextureView( +                            &self.depth_view, +                        ), +                    }, +                ], +            }); +    } + +    pub fn render( +        &self, +        encoder: &mut wgpu::CommandEncoder, +        target: &wgpu::TextureView, +        viewport: Rectangle<u32>, +    ) { +        let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { +            label: Some("cubes.pipeline.depth_pass"), +            color_attachments: &[Some(wgpu::RenderPassColorAttachment { +                view: target, +                resolve_target: None, +                ops: wgpu::Operations { +                    load: wgpu::LoadOp::Load, +                    store: wgpu::StoreOp::Store, +                }, +            })], +            depth_stencil_attachment: Some( +                wgpu::RenderPassDepthStencilAttachment { +                    view: &self.depth_view, +                    depth_ops: None, +                    stencil_ops: None, +                }, +            ), +            timestamp_writes: None, +            occlusion_query_set: None, +        }); + +        pass.set_scissor_rect( +            viewport.x, +            viewport.y, +            viewport.width, +            viewport.height, +        ); +        pass.set_pipeline(&self.pipeline); +        pass.set_bind_group(0, &self.bind_group, &[]); +        pass.draw(0..6, 0..1); +    } +} + +fn load_skybox_data() -> Vec<u8> { +    let pos_x: &[u8] = include_bytes!("../../textures/skybox/pos_x.jpg"); +    let neg_x: &[u8] = include_bytes!("../../textures/skybox/neg_x.jpg"); +    let pos_y: &[u8] = include_bytes!("../../textures/skybox/pos_y.jpg"); +    let neg_y: &[u8] = include_bytes!("../../textures/skybox/neg_y.jpg"); +    let pos_z: &[u8] = include_bytes!("../../textures/skybox/pos_z.jpg"); +    let neg_z: &[u8] = include_bytes!("../../textures/skybox/neg_z.jpg"); + +    let data: [&[u8]; 6] = [pos_x, neg_x, pos_y, neg_y, pos_z, neg_z]; + +    data.iter().fold(vec![], |mut acc, bytes| { +        let i = image::load_from_memory_with_format( +            bytes, +            image::ImageFormat::Jpeg, +        ) +        .unwrap() +        .to_rgba8() +        .into_raw(); + +        acc.extend(i); +        acc +    }) +} + +fn load_normal_map_data() -> Vec<u8> { +    let bytes: &[u8] = include_bytes!("../../textures/ice_cube_normal_map.png"); + +    image::load_from_memory_with_format(bytes, image::ImageFormat::Png) +        .unwrap() +        .to_rgba8() +        .into_raw() +} diff --git a/examples/custom_shader/src/scene/pipeline/buffer.rs b/examples/custom_shader/src/scene/pipeline/buffer.rs new file mode 100644 index 00000000..ef4c41c9 --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline/buffer.rs @@ -0,0 +1,41 @@ +use crate::wgpu; + +// A custom buffer container for dynamic resizing. +pub struct Buffer { +    pub raw: wgpu::Buffer, +    label: &'static str, +    size: u64, +    usage: wgpu::BufferUsages, +} + +impl Buffer { +    pub fn new( +        device: &wgpu::Device, +        label: &'static str, +        size: u64, +        usage: wgpu::BufferUsages, +    ) -> Self { +        Self { +            raw: device.create_buffer(&wgpu::BufferDescriptor { +                label: Some(label), +                size, +                usage, +                mapped_at_creation: false, +            }), +            label, +            size, +            usage, +        } +    } + +    pub fn resize(&mut self, device: &wgpu::Device, new_size: u64) { +        if new_size > self.size { +            self.raw = device.create_buffer(&wgpu::BufferDescriptor { +                label: Some(self.label), +                size: new_size, +                usage: self.usage, +                mapped_at_creation: false, +            }); +        } +    } +} diff --git a/examples/custom_shader/src/scene/pipeline/cube.rs b/examples/custom_shader/src/scene/pipeline/cube.rs new file mode 100644 index 00000000..de8bad6c --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline/cube.rs @@ -0,0 +1,326 @@ +use crate::scene::pipeline::Vertex; +use crate::wgpu; + +use glam::{vec2, vec3, Vec3}; +use rand::{thread_rng, Rng}; + +/// A single instance of a cube. +#[derive(Debug, Clone)] +pub struct Cube { +    pub rotation: glam::Quat, +    pub position: Vec3, +    pub size: f32, +    rotation_dir: f32, +    rotation_axis: glam::Vec3, +} + +impl Default for Cube { +    fn default() -> Self { +        Self { +            rotation: glam::Quat::IDENTITY, +            position: glam::Vec3::ZERO, +            size: 0.1, +            rotation_dir: 1.0, +            rotation_axis: glam::Vec3::Y, +        } +    } +} + +impl Cube { +    pub fn new(size: f32, origin: Vec3) -> Self { +        let rnd = thread_rng().gen_range(0.0..=1.0f32); + +        Self { +            rotation: glam::Quat::IDENTITY, +            position: origin + Vec3::new(0.1, 0.1, 0.1), +            size, +            rotation_dir: if rnd <= 0.5 { -1.0 } else { 1.0 }, +            rotation_axis: if rnd <= 0.33 { +                glam::Vec3::Y +            } else if rnd <= 0.66 { +                glam::Vec3::X +            } else { +                glam::Vec3::Z +            }, +        } +    } + +    pub fn update(&mut self, size: f32, time: f32) { +        self.rotation = glam::Quat::from_axis_angle( +            self.rotation_axis, +            time / 2.0 * self.rotation_dir, +        ); +        self.size = size; +    } +} + +#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable, Debug)] +#[repr(C)] +pub struct Raw { +    transformation: glam::Mat4, +    normal: glam::Mat3, +    _padding: [f32; 3], +} + +impl Raw { +    const ATTRIBS: [wgpu::VertexAttribute; 7] = wgpu::vertex_attr_array![ +        //cube transformation matrix +        4 => Float32x4, +        5 => Float32x4, +        6 => Float32x4, +        7 => Float32x4, +        //normal rotation matrix +        8 => Float32x3, +        9 => Float32x3, +        10 => Float32x3, +    ]; + +    pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { +        wgpu::VertexBufferLayout { +            array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress, +            step_mode: wgpu::VertexStepMode::Instance, +            attributes: &Self::ATTRIBS, +        } +    } +} + +impl Raw { +    pub fn from_cube(cube: &Cube) -> Raw { +        Raw { +            transformation: glam::Mat4::from_scale_rotation_translation( +                glam::vec3(cube.size, cube.size, cube.size), +                cube.rotation, +                cube.position, +            ), +            normal: glam::Mat3::from_quat(cube.rotation), +            _padding: [0.0; 3], +        } +    } + +    pub fn vertices() -> [Vertex; 36] { +        [ +            //face 1 +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(0.0, 0.0, -1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            //face 2 +            Vertex { +                pos: vec3(-0.5, -0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, 0.5), +                normal: vec3(0.0, 0.0, 1.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            //face 3 +            Vertex { +                pos: vec3(-0.5, 0.5, 0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, -0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, 0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, 0.5), +                normal: vec3(-1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 1.0), +            }, +            //face 4 +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, -0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, -0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, -0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, 0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(1.0, 0.0, 0.0), +                tangent: vec3(0.0, 0.0, -1.0), +                uv: vec2(0.0, 1.0), +            }, +            //face 5 +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, -0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, 0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, -0.5, 0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, 0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, -0.5, -0.5), +                normal: vec3(0.0, -1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            //face 6 +            Vertex { +                pos: vec3(-0.5, 0.5, -0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, -0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 1.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(0.5, 0.5, 0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(1.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, 0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 0.0), +            }, +            Vertex { +                pos: vec3(-0.5, 0.5, -0.5), +                normal: vec3(0.0, 1.0, 0.0), +                tangent: vec3(1.0, 0.0, 0.0), +                uv: vec2(0.0, 1.0), +            }, +        ] +    } +} diff --git a/examples/custom_shader/src/scene/pipeline/uniforms.rs b/examples/custom_shader/src/scene/pipeline/uniforms.rs new file mode 100644 index 00000000..1eac8292 --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline/uniforms.rs @@ -0,0 +1,23 @@ +use crate::scene::Camera; + +use iced::{Color, Rectangle}; + +#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Uniforms { +    camera_proj: glam::Mat4, +    camera_pos: glam::Vec4, +    light_color: glam::Vec4, +} + +impl Uniforms { +    pub fn new(camera: &Camera, bounds: Rectangle, light_color: Color) -> Self { +        let camera_proj = camera.build_view_proj_matrix(bounds); + +        Self { +            camera_proj, +            camera_pos: camera.position(), +            light_color: glam::Vec4::from(light_color.into_linear()), +        } +    } +} diff --git a/examples/custom_shader/src/scene/pipeline/vertex.rs b/examples/custom_shader/src/scene/pipeline/vertex.rs new file mode 100644 index 00000000..e64cd926 --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline/vertex.rs @@ -0,0 +1,31 @@ +use crate::wgpu; + +#[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] +pub struct Vertex { +    pub pos: glam::Vec3, +    pub normal: glam::Vec3, +    pub tangent: glam::Vec3, +    pub uv: glam::Vec2, +} + +impl Vertex { +    const ATTRIBS: [wgpu::VertexAttribute; 4] = wgpu::vertex_attr_array![ +        //position +        0 => Float32x3, +        //normal +        1 => Float32x3, +        //tangent +        2 => Float32x3, +        //uv +        3 => Float32x2, +    ]; + +    pub fn desc<'a>() -> wgpu::VertexBufferLayout<'a> { +        wgpu::VertexBufferLayout { +            array_stride: std::mem::size_of::<Self>() as wgpu::BufferAddress, +            step_mode: wgpu::VertexStepMode::Vertex, +            attributes: &Self::ATTRIBS, +        } +    } +} diff --git a/examples/custom_shader/src/shaders/cubes.wgsl b/examples/custom_shader/src/shaders/cubes.wgsl new file mode 100644 index 00000000..cd7f94d8 --- /dev/null +++ b/examples/custom_shader/src/shaders/cubes.wgsl @@ -0,0 +1,123 @@ +struct Uniforms { +    projection: mat4x4<f32>, +    camera_pos: vec4<f32>, +    light_color: vec4<f32>, +} + +const LIGHT_POS: vec3<f32> = vec3<f32>(0.0, 3.0, 3.0); + +@group(0) @binding(0) var<uniform> uniforms: Uniforms; +@group(0) @binding(1) var sky_texture: texture_cube<f32>; +@group(0) @binding(2) var tex_sampler: sampler; +@group(0) @binding(3) var normal_texture: texture_2d<f32>; + +struct Vertex { +    @location(0) position: vec3<f32>, +    @location(1) normal: vec3<f32>, +    @location(2) tangent: vec3<f32>, +    @location(3) uv: vec2<f32>, +} + +struct Cube { +    @location(4) matrix_0: vec4<f32>, +    @location(5) matrix_1: vec4<f32>, +    @location(6) matrix_2: vec4<f32>, +    @location(7) matrix_3: vec4<f32>, +    @location(8) normal_matrix_0: vec3<f32>, +    @location(9) normal_matrix_1: vec3<f32>, +    @location(10) normal_matrix_2: vec3<f32>, +} + +struct Output { +    @builtin(position) clip_pos: vec4<f32>, +    @location(0) uv: vec2<f32>, +    @location(1) tangent_pos: vec3<f32>, +    @location(2) tangent_camera_pos: vec3<f32>, +    @location(3) tangent_light_pos: vec3<f32>, +} + +@vertex +fn vs_main(vertex: Vertex, cube: Cube) -> Output { +     let cube_matrix = mat4x4<f32>( +         cube.matrix_0, +         cube.matrix_1, +         cube.matrix_2, +         cube.matrix_3, +     ); + +    let normal_matrix = mat3x3<f32>( +        cube.normal_matrix_0, +        cube.normal_matrix_1, +        cube.normal_matrix_2, +    ); + +    //convert to tangent space to calculate lighting in same coordinate space as normal map sample +    let tangent = normalize(normal_matrix * vertex.tangent); +    let normal = normalize(normal_matrix * vertex.normal); +    let bitangent = cross(tangent, normal); + +    //shift everything into tangent space +    let tbn = transpose(mat3x3<f32>(tangent, bitangent, normal)); + +    let world_pos = cube_matrix * vec4<f32>(vertex.position, 1.0); + +    var out: Output; +    out.clip_pos = uniforms.projection * world_pos; +    out.uv = vertex.uv; +    out.tangent_pos = tbn * world_pos.xyz; +    out.tangent_camera_pos = tbn * uniforms.camera_pos.xyz; +    out.tangent_light_pos = tbn * LIGHT_POS; + +    return out; +} + +//cube properties +const CUBE_BASE_COLOR: vec4<f32> = vec4<f32>(0.294118, 0.462745, 0.611765, 0.6); +const SHINE_DAMPER: f32 = 1.0; +const REFLECTIVITY: f32 = 0.8; +const REFRACTION_INDEX: f32 = 1.31; + +//fog, for the ~* cinematic effect *~ +const FOG_DENSITY: f32 = 0.15; +const FOG_GRADIENT: f32 = 8.0; +const FOG_COLOR: vec4<f32> = vec4<f32>(1.0, 1.0, 1.0, 1.0); + +@fragment +fn fs_main(in: Output) -> @location(0) vec4<f32> { +    let to_camera = in.tangent_camera_pos - in.tangent_pos; + +    //normal sample from texture +    var normal = textureSample(normal_texture, tex_sampler, in.uv).xyz; +    normal = normal * 2.0 - 1.0; + +    //diffuse +    let dir_to_light: vec3<f32> = normalize(in.tangent_light_pos - in.tangent_pos); +    let brightness = max(dot(normal, dir_to_light), 0.0); +    let diffuse: vec3<f32> = brightness * uniforms.light_color.xyz; + +    //specular +    let dir_to_camera = normalize(to_camera); +    let light_dir = -dir_to_light; +    let reflected_light_dir = reflect(light_dir, normal); +    let specular_factor = max(dot(reflected_light_dir, dir_to_camera), 0.0); +    let damped_factor = pow(specular_factor, SHINE_DAMPER); +    let specular: vec3<f32> = damped_factor * uniforms.light_color.xyz * REFLECTIVITY; + +    //fog +    let distance = length(to_camera); +    let visibility = clamp(exp(-pow((distance * FOG_DENSITY), FOG_GRADIENT)), 0.0, 1.0); + +    //reflection +    let reflection_dir = reflect(dir_to_camera, normal); +    let reflection_color = textureSample(sky_texture, tex_sampler, reflection_dir); +    let refraction_dir = refract(dir_to_camera, normal, REFRACTION_INDEX); +    let refraction_color = textureSample(sky_texture, tex_sampler, refraction_dir); +    let final_reflect_color = mix(reflection_color, refraction_color, 0.5); + +    //mix it all together! +    var color = vec4<f32>(CUBE_BASE_COLOR.xyz * diffuse + specular, CUBE_BASE_COLOR.w); +    color = mix(color, final_reflect_color, 0.8); +    color = mix(FOG_COLOR, color, visibility); + +    return color; +} diff --git a/examples/custom_shader/src/shaders/depth.wgsl b/examples/custom_shader/src/shaders/depth.wgsl new file mode 100644 index 00000000..a3f7e5ec --- /dev/null +++ b/examples/custom_shader/src/shaders/depth.wgsl @@ -0,0 +1,48 @@ +var<private> positions: array<vec2<f32>, 6> = array<vec2<f32>, 6>( +    vec2<f32>(-1.0, 1.0), +    vec2<f32>(-1.0, -1.0), +    vec2<f32>(1.0, -1.0), +    vec2<f32>(-1.0, 1.0), +    vec2<f32>(1.0, 1.0), +    vec2<f32>(1.0, -1.0) +); + +var<private> uvs: array<vec2<f32>, 6> = array<vec2<f32>, 6>( +    vec2<f32>(0.0, 0.0), +    vec2<f32>(0.0, 1.0), +    vec2<f32>(1.0, 1.0), +    vec2<f32>(0.0, 0.0), +    vec2<f32>(1.0, 0.0), +    vec2<f32>(1.0, 1.0) +); + +@group(0) @binding(0) var depth_sampler: sampler; +@group(0) @binding(1) var depth_texture: texture_2d<f32>; + +struct Output { +    @builtin(position) position: vec4<f32>, +    @location(0) uv: vec2<f32>, +} + +@vertex +fn vs_main(@builtin(vertex_index) v_index: u32) -> Output { +    var out: Output; + +    out.position = vec4<f32>(positions[v_index], 0.0, 1.0); +    out.uv = uvs[v_index]; + +    return out; +} + +@fragment +fn fs_main(input: Output) -> @location(0) vec4<f32> { +    let depth = textureSample(depth_texture, depth_sampler, input.uv).r; + +    if (depth > .9999) { +        discard; +    } + +    let c = 1.0 - depth; + +    return vec4<f32>(c, c, c, 1.0); +} | 
