From 03b1419b1793ffc0426d9037e739e5a3dae6af45 Mon Sep 17 00:00:00 2001 From: tolelom <98kimsungmin@naver.com> Date: Wed, 25 Mar 2026 11:49:27 +0900 Subject: [PATCH] feat(renderer): add GBuffer and fullscreen triangle for deferred rendering Introduces GBuffer struct with 4 render target TextureViews (position/ normal/albedo/material) plus depth, and a fullscreen oversized triangle for screen-space passes. Exports format constants and create helpers. Updates lib.rs with new module declarations and re-exports. Co-Authored-By: Claude Sonnet 4.6 --- crates/voltex_renderer/src/fullscreen_quad.rs | 38 ++++++++++ crates/voltex_renderer/src/gbuffer.rs | 70 +++++++++++++++++++ crates/voltex_renderer/src/lib.rs | 10 +++ 3 files changed, 118 insertions(+) create mode 100644 crates/voltex_renderer/src/fullscreen_quad.rs create mode 100644 crates/voltex_renderer/src/gbuffer.rs diff --git a/crates/voltex_renderer/src/fullscreen_quad.rs b/crates/voltex_renderer/src/fullscreen_quad.rs new file mode 100644 index 0000000..e938fd9 --- /dev/null +++ b/crates/voltex_renderer/src/fullscreen_quad.rs @@ -0,0 +1,38 @@ +use bytemuck::{Pod, Zeroable}; +use wgpu::util::DeviceExt; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Pod, Zeroable)] +pub struct FullscreenVertex { + pub position: [f32; 2], +} + +impl FullscreenVertex { + pub const LAYOUT: wgpu::VertexBufferLayout<'static> = wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::() as wgpu::BufferAddress, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[ + wgpu::VertexAttribute { + offset: 0, + shader_location: 0, + format: wgpu::VertexFormat::Float32x2, + }, + ], + }; +} + +/// Oversized triangle covering the entire screen (clip-space). +/// Three vertices: (-1,-1), (3,-1), (-1,3) +pub const FULLSCREEN_VERTICES: [FullscreenVertex; 3] = [ + FullscreenVertex { position: [-1.0, -1.0] }, + FullscreenVertex { position: [ 3.0, -1.0] }, + FullscreenVertex { position: [-1.0, 3.0] }, +]; + +pub fn create_fullscreen_vertex_buffer(device: &wgpu::Device) -> wgpu::Buffer { + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("Fullscreen Triangle Vertex Buffer"), + contents: bytemuck::cast_slice(&FULLSCREEN_VERTICES), + usage: wgpu::BufferUsages::VERTEX, + }) +} diff --git a/crates/voltex_renderer/src/gbuffer.rs b/crates/voltex_renderer/src/gbuffer.rs new file mode 100644 index 0000000..9c93147 --- /dev/null +++ b/crates/voltex_renderer/src/gbuffer.rs @@ -0,0 +1,70 @@ +use crate::gpu::DEPTH_FORMAT; + +pub const GBUFFER_POSITION_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba32Float; +pub const GBUFFER_NORMAL_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba16Float; +pub const GBUFFER_ALBEDO_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8UnormSrgb; +pub const GBUFFER_MATERIAL_FORMAT: wgpu::TextureFormat = wgpu::TextureFormat::Rgba8Unorm; + +fn create_rt( + device: &wgpu::Device, + w: u32, + h: u32, + format: wgpu::TextureFormat, + label: &str, +) -> wgpu::TextureView { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some(label), + size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + texture.create_view(&wgpu::TextureViewDescriptor::default()) +} + +fn create_depth(device: &wgpu::Device, w: u32, h: u32) -> wgpu::TextureView { + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("GBuffer Depth Texture"), + size: wgpu::Extent3d { width: w, height: h, depth_or_array_layers: 1 }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: DEPTH_FORMAT, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + texture.create_view(&wgpu::TextureViewDescriptor::default()) +} + +pub struct GBuffer { + pub position_view: wgpu::TextureView, + pub normal_view: wgpu::TextureView, + pub albedo_view: wgpu::TextureView, + pub material_view: wgpu::TextureView, + pub depth_view: wgpu::TextureView, +} + +impl GBuffer { + pub fn new(device: &wgpu::Device, width: u32, height: u32) -> Self { + let position_view = create_rt(device, width, height, GBUFFER_POSITION_FORMAT, "GBuffer Position"); + let normal_view = create_rt(device, width, height, GBUFFER_NORMAL_FORMAT, "GBuffer Normal"); + let albedo_view = create_rt(device, width, height, GBUFFER_ALBEDO_FORMAT, "GBuffer Albedo"); + let material_view = create_rt(device, width, height, GBUFFER_MATERIAL_FORMAT, "GBuffer Material"); + let depth_view = create_depth(device, width, height); + + Self { + position_view, + normal_view, + albedo_view, + material_view, + depth_view, + } + } + + pub fn resize(&mut self, device: &wgpu::Device, width: u32, height: u32) { + *self = Self::new(device, width, height); + } +} diff --git a/crates/voltex_renderer/src/lib.rs b/crates/voltex_renderer/src/lib.rs index b659762..28f3b99 100644 --- a/crates/voltex_renderer/src/lib.rs +++ b/crates/voltex_renderer/src/lib.rs @@ -13,6 +13,9 @@ pub mod shadow; pub mod shadow_pipeline; pub mod brdf_lut; pub mod ibl; +pub mod gbuffer; +pub mod fullscreen_quad; +pub mod deferred_pipeline; pub use gpu::{GpuContext, DEPTH_FORMAT}; pub use light::{CameraUniform, LightUniform, LightData, LightsUniform, MAX_LIGHTS, LIGHT_DIRECTIONAL, LIGHT_POINT, LIGHT_SPOT}; @@ -25,3 +28,10 @@ pub use pbr_pipeline::create_pbr_pipeline; pub use shadow::{ShadowMap, ShadowUniform, ShadowPassUniform, SHADOW_MAP_SIZE, SHADOW_FORMAT}; pub use shadow_pipeline::{create_shadow_pipeline, shadow_pass_bind_group_layout}; pub use ibl::IblResources; +pub use gbuffer::GBuffer; +pub use fullscreen_quad::{create_fullscreen_vertex_buffer, FullscreenVertex}; +pub use deferred_pipeline::{ + create_gbuffer_pipeline, create_lighting_pipeline, + gbuffer_camera_bind_group_layout, + lighting_gbuffer_bind_group_layout, lighting_lights_bind_group_layout, lighting_shadow_bind_group_layout, +};