From 811aa673e9e832ebe38bf56a087f32fdc7aba59c Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 14 Nov 2023 15:48:01 +0100 Subject: Improve module hierarchy of `custom_shader` example --- examples/custom_shader/src/camera.rs | 53 -- examples/custom_shader/src/main.rs | 7 +- examples/custom_shader/src/pipeline.rs | 606 -------------------- examples/custom_shader/src/primitive.rs | 97 ---- examples/custom_shader/src/primitive/buffer.rs | 41 -- examples/custom_shader/src/primitive/cube.rs | 326 ----------- examples/custom_shader/src/primitive/uniforms.rs | 22 - examples/custom_shader/src/primitive/vertex.rs | 31 -- examples/custom_shader/src/scene.rs | 103 +++- examples/custom_shader/src/scene/camera.rs | 53 ++ examples/custom_shader/src/scene/pipeline.rs | 615 +++++++++++++++++++++ .../custom_shader/src/scene/pipeline/buffer.rs | 41 ++ examples/custom_shader/src/scene/pipeline/cube.rs | 326 +++++++++++ .../custom_shader/src/scene/pipeline/uniforms.rs | 23 + .../custom_shader/src/scene/pipeline/vertex.rs | 31 ++ widget/src/shader.rs | 1 + 16 files changed, 1186 insertions(+), 1190 deletions(-) delete mode 100644 examples/custom_shader/src/camera.rs delete mode 100644 examples/custom_shader/src/pipeline.rs delete mode 100644 examples/custom_shader/src/primitive.rs delete mode 100644 examples/custom_shader/src/primitive/buffer.rs delete mode 100644 examples/custom_shader/src/primitive/cube.rs delete mode 100644 examples/custom_shader/src/primitive/uniforms.rs delete mode 100644 examples/custom_shader/src/primitive/vertex.rs create mode 100644 examples/custom_shader/src/scene/camera.rs create mode 100644 examples/custom_shader/src/scene/pipeline.rs create mode 100644 examples/custom_shader/src/scene/pipeline/buffer.rs create mode 100644 examples/custom_shader/src/scene/pipeline/cube.rs create mode 100644 examples/custom_shader/src/scene/pipeline/uniforms.rs create mode 100644 examples/custom_shader/src/scene/pipeline/vertex.rs diff --git a/examples/custom_shader/src/camera.rs b/examples/custom_shader/src/camera.rs deleted file mode 100644 index 2a49c102..00000000 --- a/examples/custom_shader/src/camera.rs +++ /dev/null @@ -1,53 +0,0 @@ -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/main.rs b/examples/custom_shader/src/main.rs index f4853507..2eb1ac4a 100644 --- a/examples/custom_shader/src/main.rs +++ b/examples/custom_shader/src/main.rs @@ -1,11 +1,6 @@ -mod camera; -mod pipeline; -mod primitive; mod scene; -use crate::camera::Camera; -use crate::pipeline::Pipeline; -use crate::scene::Scene; +use scene::Scene; use iced::executor; use iced::time::Instant; diff --git a/examples/custom_shader/src/pipeline.rs b/examples/custom_shader/src/pipeline.rs deleted file mode 100644 index 9343e5e0..00000000 --- a/examples/custom_shader/src/pipeline.rs +++ /dev/null @@ -1,606 +0,0 @@ -use crate::primitive; -use crate::primitive::cube; -use crate::primitive::{Buffer, Uniforms}; -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, - depth_view: wgpu::TextureView, - depth_pipeline: DepthPipeline, -} - -impl Pipeline { - pub fn new( - device: &wgpu::Device, - queue: &wgpu::Queue, - format: wgpu::TextureFormat, - target_size: Size, - ) -> 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::() 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::() 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: &[], - }, - &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: &[], - }, - &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: &[primitive::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) { - 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, - 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::(); - 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, - bounds: Rectangle, - 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( - bounds.x, - bounds.y, - bounds.width, - bounds.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, bounds); - } - } -} - -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, - bounds: Rectangle, - ) { - 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(bounds.x, bounds.y, bounds.width, bounds.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 { - 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 { - 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/primitive.rs b/examples/custom_shader/src/primitive.rs deleted file mode 100644 index f5862ab3..00000000 --- a/examples/custom_shader/src/primitive.rs +++ /dev/null @@ -1,97 +0,0 @@ -pub mod cube; -pub mod vertex; - -mod buffer; -mod uniforms; - -pub use buffer::Buffer; -pub use cube::Cube; -pub use uniforms::Uniforms; -pub use vertex::Vertex; - -use crate::wgpu; -use crate::Camera; -use crate::Pipeline; - -use iced::advanced::graphics::Transformation; -use iced::widget::shader; -use iced::{Color, Rectangle, Size}; - -/// A collection of `Cube`s that can be rendered. -#[derive(Debug)] -pub struct Primitive { - cubes: Vec, - uniforms: 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 = Uniforms::new(camera, bounds, light_color); - - Self { - cubes: cubes - .iter() - .map(cube::Raw::from_cube) - .collect::>(), - uniforms, - show_depth_buffer, - } - } -} - -impl shader::Primitive for Primitive { - fn prepare( - &self, - format: wgpu::TextureFormat, - device: &wgpu::Device, - queue: &wgpu::Queue, - target_size: Size, - _scale_factor: f32, - _transform: Transformation, - storage: &mut shader::Storage, - ) { - if !storage.has::() { - storage.store(Pipeline::new(device, queue, format, target_size)); - } - - let pipeline = storage.get_mut::().unwrap(); - - //upload data to GPU - pipeline.update( - device, - queue, - target_size, - &self.uniforms, - self.cubes.len(), - &self.cubes, - ); - } - - fn render( - &self, - storage: &shader::Storage, - bounds: Rectangle, - target: &wgpu::TextureView, - _target_size: Size, - encoder: &mut wgpu::CommandEncoder, - ) { - //at this point our pipeline should always be initialized - let pipeline = storage.get::().unwrap(); - - //render primitive - pipeline.render( - target, - encoder, - bounds, - self.cubes.len() as u32, - self.show_depth_buffer, - ); - } -} diff --git a/examples/custom_shader/src/primitive/buffer.rs b/examples/custom_shader/src/primitive/buffer.rs deleted file mode 100644 index ef4c41c9..00000000 --- a/examples/custom_shader/src/primitive/buffer.rs +++ /dev/null @@ -1,41 +0,0 @@ -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/primitive/cube.rs b/examples/custom_shader/src/primitive/cube.rs deleted file mode 100644 index 7ece685d..00000000 --- a/examples/custom_shader/src/primitive/cube.rs +++ /dev/null @@ -1,326 +0,0 @@ -use crate::primitive::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::() 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/primitive/uniforms.rs b/examples/custom_shader/src/primitive/uniforms.rs deleted file mode 100644 index 4fcb413b..00000000 --- a/examples/custom_shader/src/primitive/uniforms.rs +++ /dev/null @@ -1,22 +0,0 @@ -use crate::camera::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/primitive/vertex.rs b/examples/custom_shader/src/primitive/vertex.rs deleted file mode 100644 index e64cd926..00000000 --- a/examples/custom_shader/src/primitive/vertex.rs +++ /dev/null @@ -1,31 +0,0 @@ -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::() as wgpu::BufferAddress, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &Self::ATTRIBS, - } - } -} diff --git a/examples/custom_shader/src/scene.rs b/examples/custom_shader/src/scene.rs index ab923093..3b291ce2 100644 --- a/examples/custom_shader/src/scene.rs +++ b/examples/custom_shader/src/scene.rs @@ -1,13 +1,21 @@ -use crate::camera::Camera; -use crate::primitive; -use crate::primitive::cube::Cube; -use glam::Vec3; +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::{mouse, Color, Rectangle}; +use iced::{Color, Rectangle, Size}; + +use glam::Vec3; use rand::Rng; use std::cmp::Ordering; use std::iter; -use std::time::Duration; pub const MAX: u32 = 500; @@ -72,7 +80,7 @@ impl Scene { impl shader::Program for Scene { type State = (); - type Primitive = primitive::Primitive; + type Primitive = Primitive; fn draw( &self, @@ -80,7 +88,7 @@ impl shader::Program for Scene { _cursor: mouse::Cursor, bounds: Rectangle, ) -> Self::Primitive { - primitive::Primitive::new( + Primitive::new( &self.cubes, &self.camera, bounds, @@ -90,6 +98,85 @@ impl shader::Program for Scene { } } +/// A collection of `Cube`s that can be rendered. +#[derive(Debug)] +pub struct Primitive { + cubes: Vec, + 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::>(), + uniforms, + show_depth_buffer, + } + } +} + +impl shader::Primitive for Primitive { + fn prepare( + &self, + format: wgpu::TextureFormat, + device: &wgpu::Device, + queue: &wgpu::Queue, + target_size: Size, + _scale_factor: f32, + _transform: shader::Transformation, + storage: &mut shader::Storage, + ) { + if !storage.has::() { + storage.store(Pipeline::new(device, queue, format, target_size)); + } + + let pipeline = storage.get_mut::().unwrap(); + + //upload data to GPU + pipeline.update( + device, + queue, + target_size, + &self.uniforms, + self.cubes.len(), + &self.cubes, + ); + } + + fn render( + &self, + storage: &shader::Storage, + bounds: Rectangle, + target: &wgpu::TextureView, + _target_size: Size, + encoder: &mut wgpu::CommandEncoder, + ) { + //at this point our pipeline should always be initialized + let pipeline = storage.get::().unwrap(); + + //render primitive + pipeline.render( + target, + encoder, + bounds, + self.cubes.len() as u32, + self.show_depth_buffer, + ); + } +} + fn rnd_origin() -> Vec3 { Vec3::new( rand::thread_rng().gen_range(-4.0..4.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..0967e139 --- /dev/null +++ b/examples/custom_shader/src/scene/pipeline.rs @@ -0,0 +1,615 @@ +pub mod cube; + +mod buffer; +mod uniforms; +mod vertex; + +pub use cube::Cube; +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, + depth_view: wgpu::TextureView, + depth_pipeline: DepthPipeline, +} + +impl Pipeline { + pub fn new( + device: &wgpu::Device, + queue: &wgpu::Queue, + format: wgpu::TextureFormat, + target_size: Size, + ) -> 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::() 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::() 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: &[], + }, + &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: &[], + }, + &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) { + 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, + 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::(); + 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, + bounds: Rectangle, + 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( + bounds.x, + bounds.y, + bounds.width, + bounds.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, bounds); + } + } +} + +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, + bounds: Rectangle, + ) { + 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(bounds.x, bounds.y, bounds.width, bounds.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 { + 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 { + 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::() 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::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &Self::ATTRIBS, + } + } +} diff --git a/widget/src/shader.rs b/widget/src/shader.rs index fe6214db..ca140627 100644 --- a/widget/src/shader.rs +++ b/widget/src/shader.rs @@ -17,6 +17,7 @@ use crate::renderer::wgpu::primitive::pipeline; use std::marker::PhantomData; +pub use crate::graphics::Transformation; pub use crate::renderer::wgpu::wgpu; pub use pipeline::{Primitive, Storage}; -- cgit