From 29a4da5666c6ef8ea020afa93618af939df4b088 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 27 May 2025 22:25:17 +0200 Subject: [PATCH] texture: First image load --- Cargo.lock | 1 + Cargo.toml | 2 + res/shaders/vertex.frag | 9 +- res/shaders/vertex.vert | 6 +- res/textures/wooden-crate.jpg | Bin 0 -> 49767 bytes src/core/render/mod.rs | 1 + .../render/pipelines/triangle_pipeline.rs | 46 +++++-- src/core/render/primitives/vertex.rs | 4 +- src/core/render/texture.rs | 113 ++++++++++++++++++ src/game/main_scene.rs | 106 ++++++++-------- src/main.rs | 4 +- 11 files changed, 220 insertions(+), 72 deletions(-) create mode 100644 res/textures/wooden-crate.jpg create mode 100644 src/core/render/texture.rs diff --git a/Cargo.lock b/Cargo.lock index d461d0b..54175c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2110,6 +2110,7 @@ dependencies = [ "egui_winit_vulkano", "env_logger", "glam", + "image", "log", "thiserror 2.0.12", "vulkano", diff --git a/Cargo.toml b/Cargo.toml index 372b1a5..50be745 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ vulkano-shaders = "0.35" vulkano-util = "0.35" egui_winit_vulkano = { version = "0.28" } +image = { version = "0.25", features = ["png", "jpeg"] } + # Math glam = { version = "0.30" } diff --git a/res/shaders/vertex.frag b/res/shaders/vertex.frag index 720d192..67831a9 100644 --- a/res/shaders/vertex.frag +++ b/res/shaders/vertex.frag @@ -1,9 +1,12 @@ #version 450 -layout (location = 0) in vec3 color; +layout (location = 0) in vec2 tex_coords; layout (location = 0) out vec4 f_color; +layout(set = 1, binding = 0) uniform sampler mySampler; +layout(set = 1, binding = 1) uniform texture2D myTexture; + void main() { - f_color = vec4(color, 1.0); -} \ No newline at end of file + f_color = texture(sampler2D(myTexture, mySampler), tex_coords); +} diff --git a/res/shaders/vertex.vert b/res/shaders/vertex.vert index bb1c261..0445e54 100644 --- a/res/shaders/vertex.vert +++ b/res/shaders/vertex.vert @@ -1,9 +1,9 @@ #version 450 layout (location = 0) in vec2 position; -layout (location = 1) in vec3 color; +layout (location = 1) in vec2 uv; -layout (location = 0) out vec3 fragColor; +layout (location = 0) out vec2 fragUv; layout (set = 0, binding = 0) uniform MVP { mat4 world; @@ -14,5 +14,5 @@ layout (set = 0, binding = 0) uniform MVP { void main() { mat4 worldview = uniforms.view * uniforms.world; gl_Position = uniforms.projection * worldview * vec4(position, 0.0, 1.0); - fragColor = color; + fragUv = uv; } diff --git a/res/textures/wooden-crate.jpg b/res/textures/wooden-crate.jpg new file mode 100644 index 0000000000000000000000000000000000000000..d1c87341ca55543742aeefa9145fea732c166468 GIT binary patch literal 49767 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::new(); - let mut descriptor_set_layout_binding = - DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer); - descriptor_set_layout_binding.stages = ShaderStages::VERTEX; - bindings.insert(0, descriptor_set_layout_binding); + let vertex_bindings = BTreeMap::::from_iter([( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::VERTEX, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::UniformBuffer) + }, + )]); + let fragment_bindings = BTreeMap::::from_iter([ + ( + 0, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::Sampler) + }, + ), + ( + 1, + DescriptorSetLayoutBinding { + stages: ShaderStages::FRAGMENT, + ..DescriptorSetLayoutBinding::descriptor_type(DescriptorType::SampledImage) + }, + ), + ]); - let descriptor_set_layout = DescriptorSetLayoutCreateInfo { - bindings, + let vertex_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: vertex_bindings, + ..Default::default() + }; + + let fragment_descriptor_set_layout = DescriptorSetLayoutCreateInfo { + bindings: fragment_bindings, ..Default::default() }; let create_info = PipelineDescriptorSetLayoutCreateInfo { - set_layouts: vec![descriptor_set_layout], + set_layouts: vec![vertex_descriptor_set_layout, fragment_descriptor_set_layout], flags: PipelineLayoutCreateFlags::default(), push_constant_ranges: vec![], } @@ -83,7 +106,10 @@ pub fn create_triangle_pipeline( GraphicsPipelineCreateInfo { stages: stages.into_iter().collect(), vertex_input_state: Some(vertex_input_state), - input_assembly_state: Some(InputAssemblyState::default()), + input_assembly_state: Some(InputAssemblyState { + topology: PrimitiveTopology::TriangleStrip, + ..Default::default() + }), viewport_state: Some(ViewportState::default()), rasterization_state: Some(RasterizationState::default()), multisample_state: Some(MultisampleState::default()), diff --git a/src/core/render/primitives/vertex.rs b/src/core/render/primitives/vertex.rs index 9bf133e..b35588c 100644 --- a/src/core/render/primitives/vertex.rs +++ b/src/core/render/primitives/vertex.rs @@ -12,8 +12,8 @@ pub struct Vertex2D { #[format(R32G32_SFLOAT)] pub position: [f32; 2], - #[format(R32G32B32_SFLOAT)] - pub color: [f32; 3], + #[format(R32G32_SFLOAT)] + pub uv: [f32; 2], } impl Vertex2D { diff --git a/src/core/render/texture.rs b/src/core/render/texture.rs new file mode 100644 index 0000000..680ddbf --- /dev/null +++ b/src/core/render/texture.rs @@ -0,0 +1,113 @@ +use std::{path::Path, sync::Arc}; + +use anyhow::Error; +use image::{DynamicImage, EncodableLayout}; +use vulkano::{ + buffer::{Buffer, BufferCreateInfo, BufferUsage}, + command_buffer::{AutoCommandBufferBuilder, CopyBufferToImageInfo, PrimaryAutoCommandBuffer}, + device::Device, + format::Format, + image::{ + Image, ImageCreateInfo, ImageType, ImageUsage, + sampler::{Filter, Sampler, SamplerAddressMode, SamplerCreateInfo}, + view::ImageView, + }, + memory::allocator::{AllocationCreateInfo, MemoryTypeFilter, StandardMemoryAllocator}, +}; + +pub struct Texture { + texture: Arc, + sampler: Arc, +} + +impl Texture { + fn new(texture: Arc, sampler: Arc) -> Self { + Self { texture, sampler } + } + + pub fn from_file( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + path: &str, + ) -> Result { + let image = image::open(path)?; + Self::from_dynamic_image(device, memory_allocator, builder, image) + } + + pub fn from_bytes( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + bytes: &[u8], + ) -> Result { + let image = image::load_from_memory(bytes)?; + Self::from_dynamic_image(device, memory_allocator, builder, image) + } + + pub fn from_dynamic_image( + device: &Arc, + memory_allocator: &Arc, + builder: &mut AutoCommandBufferBuilder, + image: DynamicImage, + ) -> Result { + let image_data = image.to_rgba8(); + let image_dimensions = image_data.dimensions(); + + let upload_buffer = Buffer::from_iter( + memory_allocator.clone(), + BufferCreateInfo { + usage: BufferUsage::TRANSFER_SRC, + ..Default::default() + }, + AllocationCreateInfo { + memory_type_filter: MemoryTypeFilter::PREFER_HOST + | MemoryTypeFilter::HOST_SEQUENTIAL_WRITE, + ..Default::default() + }, + image_data.to_vec(), + )?; + + let image = Image::new( + memory_allocator.clone(), + ImageCreateInfo { + image_type: ImageType::Dim2d, + format: Format::R8G8B8A8_SRGB, + extent: [image_dimensions.0 as u32, image_dimensions.1 as u32, 1], + array_layers: 1, + usage: ImageUsage::TRANSFER_DST | ImageUsage::SAMPLED, + ..Default::default() + }, + AllocationCreateInfo::default(), + )?; + + builder.copy_buffer_to_image(CopyBufferToImageInfo::buffer_image( + upload_buffer, + image.clone(), + ))?; + + let sampler = Sampler::new( + device.clone(), + SamplerCreateInfo { + mag_filter: Filter::Linear, + min_filter: Filter::Linear, + address_mode: [SamplerAddressMode::Repeat; 3], + ..Default::default() + }, + )?; + + let image_view = ImageView::new_default(image)?; + + log::trace!("Texture loaded with dimensions {:?}", image_dimensions); + + Ok(Self::new(image_view, sampler)) + } + + pub fn get_texture(&self) -> &Arc { + &self.texture + } + + pub fn get_sampler(&self) -> &Arc { + &self.sampler + } +} diff --git a/src/game/main_scene.rs b/src/game/main_scene.rs index 1f27f0b..c5a56a0 100644 --- a/src/game/main_scene.rs +++ b/src/game/main_scene.rs @@ -3,67 +3,35 @@ use crate::core::render::pipelines::triangle_pipeline::create_triangle_pipeline; use crate::core::render::primitives::camera::Camera; use crate::core::render::primitives::vertex::Vertex2D; use crate::core::render::render_context::RenderContext; +use crate::core::render::texture::Texture; use crate::core::scene::Scene; use crate::core::timer::Timer; use glam::{Mat4, Quat, Vec3}; use std::sync::Arc; use vulkano::buffer::Subbuffer; -use vulkano::command_buffer::{AutoCommandBufferBuilder, PrimaryAutoCommandBuffer}; +use vulkano::command_buffer::{ + AutoCommandBufferBuilder, CommandBufferUsage, PrimaryAutoCommandBuffer, + PrimaryCommandBufferAbstract, +}; use vulkano::descriptor_set::{DescriptorSet, WriteDescriptorSet}; use vulkano::pipeline::{GraphicsPipeline, Pipeline, PipelineBindPoint}; -const VERTICES: [Vertex2D; 12] = [ - // Triangle en haut à gauche +const VERTICES: [Vertex2D; 4] = [ Vertex2D { - position: [-0.5, -0.75], - color: [1.0, 0.0, 0.0], + position: [-0.5, -0.5], + uv: [0.0, 0.0], }, Vertex2D { - position: [-0.75, -0.25], - color: [0.0, 1.0, 0.0], + position: [-0.5, 0.5], + uv: [0.0, 1.0], }, Vertex2D { - position: [-0.25, -0.25], - color: [0.0, 0.0, 1.0], - }, - // Triangle en bas à gauche - Vertex2D { - position: [-0.5, 0.25], - color: [0.5, 0.5, 0.5], + position: [0.5, -0.5], + uv: [1.0, 0.0], }, Vertex2D { - position: [-0.75, 0.75], - color: [0.2, 0.8, 0.2], - }, - Vertex2D { - position: [-0.25, 0.75], - color: [0.8, 0.2, 0.2], - }, - // Triangle en haut à droite - Vertex2D { - position: [0.5, -0.75], - color: [1.0, 1.0, 0.0], - }, - Vertex2D { - position: [0.25, -0.25], - color: [0.0, 1.0, 1.0], - }, - Vertex2D { - position: [0.75, -0.25], - color: [1.0, 0.0, 1.0], - }, - // Triangle en bas à droite - Vertex2D { - position: [0.5, 0.25], - color: [0.1, 0.5, 0.8], - }, - Vertex2D { - position: [0.25, 0.75], - color: [0.8, 0.6, 0.1], - }, - Vertex2D { - position: [0.75, 0.75], - color: [0.3, 0.4, 0.6], + position: [0.5, 0.5], + uv: [1.0, 1.0], }, ]; @@ -71,6 +39,7 @@ pub struct MainSceneState { pipeline: Arc, vertex_buffer: Subbuffer<[Vertex2D]>, camera: Camera, + texture: Texture, } #[derive(Default)] @@ -105,11 +74,33 @@ impl Scene for MainScene { ), ); + let mut uploads = AutoCommandBufferBuilder::primary( + render_context.command_buffer_allocator().clone(), + render_context.graphics_queue().queue_family_index(), + CommandBufferUsage::OneTimeSubmit, + ) + .unwrap(); + + let texture = Texture::from_file( + render_context.device(), + render_context.memory_allocator(), + &mut uploads, + "res/textures/wooden-crate.jpg", + ) + .unwrap(); + + let _ = uploads + .build() + .unwrap() + .execute(render_context.graphics_queue().clone()) + .unwrap(); + self.state = Some(MainSceneState { pipeline, vertex_buffer, camera, - }) + texture, + }); } fn update( @@ -157,21 +148,32 @@ impl Scene for MainScene { ) { let state = self.state.as_ref().unwrap(); let vertex_count = state.vertex_buffer.len() as u32; - let instance_count = vertex_count / 3; + let instance_count = vertex_count / 4; - let layout = &state.pipeline.layout().set_layouts()[0]; + let layouts = state.pipeline.layout().set_layouts(); let uniform_buffer = state .camera .create_buffer(render_context.memory_allocator()) .unwrap(); - let descriptor_set = DescriptorSet::new( + let uniform_descriptor_set = DescriptorSet::new( render_context.descriptor_set_allocator().clone(), - layout.clone(), + layouts[0].clone(), [WriteDescriptorSet::buffer(0, uniform_buffer)], [], ) .unwrap(); + let texture_descriptor_set = DescriptorSet::new( + render_context.descriptor_set_allocator().clone(), + layouts[1].clone(), + [ + WriteDescriptorSet::sampler(0, state.texture.get_sampler().clone()), + WriteDescriptorSet::image_view(1, state.texture.get_texture().clone()), + ], + [], + ) + .unwrap(); + unsafe { builder .bind_pipeline_graphics(state.pipeline.clone()) @@ -180,7 +182,7 @@ impl Scene for MainScene { PipelineBindPoint::Graphics, state.pipeline.layout().clone(), 0, - descriptor_set, + vec![uniform_descriptor_set, texture_descriptor_set], ) .unwrap() .bind_vertex_buffers(0, state.vertex_buffer.clone()) diff --git a/src/main.rs b/src/main.rs index 6a4984b..7a4284c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,7 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyW), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyS), AxisDirection::Invert), - VirtualBinding::Axis(0, AxisDirection::Normal, 1.0), + VirtualBinding::Axis(0, AxisDirection::Normal, 0.0), ], ), ( @@ -29,7 +29,7 @@ fn main() { vec![ VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyD), AxisDirection::Normal), VirtualBinding::Keyboard(PhysicalKey::Code(KeyCode::KeyA), AxisDirection::Invert), - VirtualBinding::Axis(1, AxisDirection::Normal, 1.0), + VirtualBinding::Axis(1, AxisDirection::Normal, 0.0), ], ), (