diff options
author | 2023-09-14 13:58:36 -0700 | |
---|---|---|
committer | 2023-11-14 11:31:44 +0100 | |
commit | 781ef1f94c4859aeeb852f801b72be095b8ff82b (patch) | |
tree | 63e2678eca11dd41c26a40633c04341fd795d733 /examples | |
parent | 817f72868746461891ca4e74473c555f3b5c5703 (diff) | |
download | iced-781ef1f94c4859aeeb852f801b72be095b8ff82b.tar.gz iced-781ef1f94c4859aeeb852f801b72be095b8ff82b.tar.bz2 iced-781ef1f94c4859aeeb852f801b72be095b8ff82b.zip |
Added support for custom shader widget for iced_wgpu backend.
Diffstat (limited to 'examples')
20 files changed, 1620 insertions, 0 deletions
diff --git a/examples/custom_shader/Cargo.toml b/examples/custom_shader/Cargo.toml new file mode 100644 index 00000000..7a927811 --- /dev/null +++ b/examples/custom_shader/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "custom_shader" +version = "0.1.0" +authors = ["Bingus <shankern@protonmail.com>"] +edition = "2021" + +[dependencies] +iced = { path = "../..", features = ["debug", "advanced"]} +image = { version = "0.24.6"} +wgpu = "0.17" +bytemuck = { version = "1.13.1" } +glam = { version = "0.24.0", features = ["bytemuck"] } +rand = "0.8.5" diff --git a/examples/custom_shader/src/camera.rs b/examples/custom_shader/src/camera.rs new file mode 100644 index 00000000..2a49c102 --- /dev/null +++ b/examples/custom_shader/src/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/cubes.rs b/examples/custom_shader/src/cubes.rs new file mode 100644 index 00000000..8dbba4b1 --- /dev/null +++ b/examples/custom_shader/src/cubes.rs @@ -0,0 +1,99 @@ +use crate::camera::Camera; +use crate::primitive; +use crate::primitive::cube::Cube; +use glam::Vec3; +use iced::widget::shader; +use iced::{mouse, Color, Rectangle}; +use rand::Rng; +use std::cmp::Ordering; +use std::iter; +use std::time::Duration; + +pub const MAX: u32 = 500; + +#[derive(Clone)] +pub struct Cubes { + pub size: f32, + pub cubes: Vec<Cube>, + pub camera: Camera, + pub show_depth_buffer: bool, + pub light_color: Color, +} + +impl Cubes { + pub fn new() -> Self { + let mut cubes = Self { + size: 0.2, + cubes: vec![], + camera: Camera::default(), + show_depth_buffer: false, + light_color: Color::WHITE, + }; + + cubes.adjust_num_cubes(MAX); + + cubes + } + + pub fn update(&mut self, time: Duration) { + for cube in self.cubes.iter_mut() { + cube.update(self.size, time.as_secs_f32()); + } + } + + pub fn adjust_num_cubes(&mut self, num_cubes: u32) { + let curr_cubes = self.cubes.len() as u32; + + match num_cubes.cmp(&curr_cubes) { + Ordering::Greater => { + // spawn + let cubes_2_spawn = (num_cubes - 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 - num_cubes; + let new_len = self.cubes.len() - cubes_2_cut as usize; + self.cubes.truncate(new_len); + } + _ => {} + } + } +} + +impl<Message> shader::Program<Message> for Cubes { + type State = (); + type Primitive = primitive::Primitive; + + fn draw( + &self, + _state: &Self::State, + _cursor: mouse::Cursor, + bounds: Rectangle, + ) -> Self::Primitive { + primitive::Primitive::new( + &self.cubes, + &self.camera, + bounds, + self.show_depth_buffer, + self.light_color, + ) + } +} + +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/main.rs b/examples/custom_shader/src/main.rs new file mode 100644 index 00000000..76fa1625 --- /dev/null +++ b/examples/custom_shader/src/main.rs @@ -0,0 +1,174 @@ +mod camera; +mod cubes; +mod pipeline; +mod primitive; + +use crate::cubes::Cubes; +use iced::widget::{ + checkbox, column, container, row, slider, text, vertical_space, Shader, +}; +use iced::{ + executor, window, Alignment, Application, Color, Command, Element, Length, + Renderer, Subscription, Theme, +}; +use std::time::Instant; + +fn main() -> iced::Result { + IcedCubes::run(iced::Settings::default()) +} + +struct IcedCubes { + start: Instant, + cubes: Cubes, + num_cubes_slider: u32, +} + +impl Default for IcedCubes { + fn default() -> Self { + Self { + start: Instant::now(), + cubes: Cubes::new(), + num_cubes_slider: cubes::MAX, + } + } +} + +#[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>) { + (IcedCubes::default(), Command::none()) + } + + fn title(&self) -> String { + "Iced Cubes".to_string() + } + + fn update(&mut self, message: Self::Message) -> Command<Self::Message> { + match message { + Message::CubeAmountChanged(num) => { + self.num_cubes_slider = num; + self.cubes.adjust_num_cubes(num); + } + Message::CubeSizeChanged(size) => { + self.cubes.size = size; + } + Message::Tick(time) => { + self.cubes.update(time - self.start); + } + Message::ShowDepthBuffer(show) => { + self.cubes.show_depth_buffer = show; + } + Message::LightColorChanged(color) => { + self.cubes.light_color = color; + } + } + + Command::none() + } + + fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> { + let top_controls = row![ + control( + "Amount", + slider( + 1..=cubes::MAX, + self.num_cubes_slider, + Message::CubeAmountChanged + ) + .width(100) + ), + control( + "Size", + slider(0.1..=0.25, self.cubes.size, Message::CubeSizeChanged) + .step(0.01) + .width(100), + ), + checkbox( + "Show Depth Buffer", + self.cubes.show_depth_buffer, + Message::ShowDepthBuffer + ), + ] + .spacing(40); + + let bottom_controls = row![ + control( + "R", + slider(0.0..=1.0, self.cubes.light_color.r, move |r| { + Message::LightColorChanged(Color { + r, + ..self.cubes.light_color + }) + }) + .step(0.01) + .width(100) + ), + control( + "G", + slider(0.0..=1.0, self.cubes.light_color.g, move |g| { + Message::LightColorChanged(Color { + g, + ..self.cubes.light_color + }) + }) + .step(0.01) + .width(100) + ), + control( + "B", + slider(0.0..=1.0, self.cubes.light_color.b, move |b| { + Message::LightColorChanged(Color { + b, + ..self.cubes.light_color + }) + }) + .step(0.01) + .width(100) + ) + ] + .spacing(40); + + let controls = column![top_controls, bottom_controls,] + .spacing(10) + .align_items(Alignment::Center); + + let shader = Shader::new(&self.cubes) + .width(Length::Fill) + .height(Length::Fill); + + container( + column![shader, controls, vertical_space(20),] + .spacing(40) + .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/pipeline.rs b/examples/custom_shader/src/pipeline.rs new file mode 100644 index 00000000..9dd154e8 --- /dev/null +++ b/examples/custom_shader/src/pipeline.rs @@ -0,0 +1,600 @@ +use crate::primitive; +use crate::primitive::cube; +use crate::primitive::{Buffer, Uniforms}; +use iced::{Rectangle, Size}; +use wgpu::util::DeviceExt; + +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: &[], + }, + &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<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, + bounds: 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: true, + }, + }, + )], + depth_stencil_attachment: Some( + wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: true, + }), + stencil_ops: 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: Default::default(), + depth_stencil: Some(wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: false, + depth_compare: wgpu::CompareFunction::Less, + stencil: Default::default(), + bias: Default::default(), + }), + multisample: Default::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<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: true, + }, + })], + depth_stencil_attachment: Some( + wgpu::RenderPassDepthStencilAttachment { + view: &self.depth_view, + depth_ops: None, + stencil_ops: 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<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/primitive.rs b/examples/custom_shader/src/primitive.rs new file mode 100644 index 00000000..2201218f --- /dev/null +++ b/examples/custom_shader/src/primitive.rs @@ -0,0 +1,95 @@ +pub mod cube; +pub mod vertex; + +mod buffer; +mod uniforms; + +use crate::camera::Camera; +use crate::pipeline::Pipeline; +use crate::primitive::cube::Cube; +use iced::advanced::graphics::Transformation; +use iced::widget::shader; +use iced::{Color, Rectangle, Size}; + +pub use crate::primitive::vertex::Vertex; +pub use buffer::Buffer; +pub use uniforms::Uniforms; + +/// A collection of `Cube`s that can be rendered. +#[derive(Debug)] +pub struct Primitive { + cubes: Vec<cube::Raw>, + 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::<Vec<cube::Raw>>(), + uniforms, + show_depth_buffer, + } + } +} + +impl shader::Primitive for Primitive { + fn prepare( + &self, + format: wgpu::TextureFormat, + device: &wgpu::Device, + queue: &wgpu::Queue, + target_size: Size<u32>, + _scale_factor: f32, + _transform: Transformation, + 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, + bounds: Rectangle<u32>, + target: &wgpu::TextureView, + _target_size: Size<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, + 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 new file mode 100644 index 00000000..377ce1bb --- /dev/null +++ b/examples/custom_shader/src/primitive/buffer.rs @@ -0,0 +1,39 @@ +// 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 new file mode 100644 index 00000000..c23f2132 --- /dev/null +++ b/examples/custom_shader/src/primitive/cube.rs @@ -0,0 +1,324 @@ +use crate::primitive::Vertex; +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/primitive/uniforms.rs b/examples/custom_shader/src/primitive/uniforms.rs new file mode 100644 index 00000000..4fcb413b --- /dev/null +++ b/examples/custom_shader/src/primitive/uniforms.rs @@ -0,0 +1,22 @@ +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 new file mode 100644 index 00000000..6d17aa0f --- /dev/null +++ b/examples/custom_shader/src/primitive/vertex.rs @@ -0,0 +1,29 @@ +#[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); +} diff --git a/examples/custom_shader/src/textures/ice_cube_normal_map.png b/examples/custom_shader/src/textures/ice_cube_normal_map.png Binary files differnew file mode 100644 index 00000000..7b4b7228 --- /dev/null +++ b/examples/custom_shader/src/textures/ice_cube_normal_map.png diff --git a/examples/custom_shader/src/textures/skybox/neg_x.jpg b/examples/custom_shader/src/textures/skybox/neg_x.jpg Binary files differnew file mode 100644 index 00000000..00cc783d --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/neg_x.jpg diff --git a/examples/custom_shader/src/textures/skybox/neg_y.jpg b/examples/custom_shader/src/textures/skybox/neg_y.jpg Binary files differnew file mode 100644 index 00000000..548f6445 --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/neg_y.jpg diff --git a/examples/custom_shader/src/textures/skybox/neg_z.jpg b/examples/custom_shader/src/textures/skybox/neg_z.jpg Binary files differnew file mode 100644 index 00000000..5698512e --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/neg_z.jpg diff --git a/examples/custom_shader/src/textures/skybox/pos_x.jpg b/examples/custom_shader/src/textures/skybox/pos_x.jpg Binary files differnew file mode 100644 index 00000000..dddecba7 --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/pos_x.jpg diff --git a/examples/custom_shader/src/textures/skybox/pos_y.jpg b/examples/custom_shader/src/textures/skybox/pos_y.jpg Binary files differnew file mode 100644 index 00000000..361427fd --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/pos_y.jpg diff --git a/examples/custom_shader/src/textures/skybox/pos_z.jpg b/examples/custom_shader/src/textures/skybox/pos_z.jpg Binary files differnew file mode 100644 index 00000000..0085a49e --- /dev/null +++ b/examples/custom_shader/src/textures/skybox/pos_z.jpg diff --git a/examples/integration/src/main.rs b/examples/integration/src/main.rs index c26d52fe..0f32fca0 100644 --- a/examples/integration/src/main.rs +++ b/examples/integration/src/main.rs @@ -271,6 +271,7 @@ pub fn main() -> Result<(), Box<dyn std::error::Error>> { &queue, &mut encoder, None, + frame.texture.format(), &view, primitive, &viewport, |