diff options
Diffstat (limited to 'src/3d')
| -rw-r--r-- | src/3d/renderer.cc | 418 | ||||
| -rw-r--r-- | src/3d/renderer.h | 10 |
2 files changed, 215 insertions, 213 deletions
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index 2e08b4e..0578271 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -2,55 +2,9 @@ // It implements the Renderer3D class. #include "3d/renderer.h" -#include <iostream> +#include <algorithm> #include <cstring> - -// Simple Cube Geometry (Triangle list) -// 36 vertices -static const float kCubeVertices[] = { - // Front face - -1.0, -1.0, 1.0, - 1.0, -1.0, 1.0, - 1.0, 1.0, 1.0, - -1.0, -1.0, 1.0, - 1.0, 1.0, 1.0, - -1.0, 1.0, 1.0, - // Back face - -1.0, -1.0, -1.0, - -1.0, 1.0, -1.0, - 1.0, 1.0, -1.0, - -1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, - 1.0, -1.0, -1.0, - // Top face - -1.0, 1.0, -1.0, - -1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, - -1.0, 1.0, -1.0, - 1.0, 1.0, 1.0, - 1.0, 1.0, -1.0, - // Bottom face - -1.0, -1.0, -1.0, - 1.0, -1.0, -1.0, - 1.0, -1.0, 1.0, - -1.0, -1.0, -1.0, - 1.0, -1.0, 1.0, - -1.0, -1.0, 1.0, - // Right face - 1.0, -1.0, -1.0, - 1.0, 1.0, -1.0, - 1.0, 1.0, 1.0, - 1.0, -1.0, -1.0, - 1.0, 1.0, 1.0, - 1.0, -1.0, 1.0, - // Left face - -1.0, -1.0, -1.0, - -1.0, -1.0, 1.0, - -1.0, 1.0, 1.0, - -1.0, -1.0, -1.0, - -1.0, 1.0, 1.0, - -1.0, 1.0, -1.0, -}; +#include <iostream> static const char* kShaderCode = R"( struct GlobalUniforms { @@ -71,6 +25,8 @@ struct ObjectsBuffer { @group(0) @binding(0) var<uniform> globals: GlobalUniforms; @group(0) @binding(1) var<storage, read> object_data: ObjectsBuffer; +@group(0) @binding(2) var noise_tex: texture_2d<f32>; +@group(0) @binding(3) var noise_sampler: sampler; struct VertexOutput { @builtin(position) position: vec4<f32>, @@ -101,23 +57,18 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32, let p = pos[vertex_index]; let obj = object_data.objects[instance_index]; - - // Model -> World -> Clip let world_pos = obj.model * vec4<f32>(p, 1.0); let clip_pos = globals.view_proj * world_pos; var out: VertexOutput; out.position = clip_pos; - out.local_pos = p; // Proxy geometry local coords (-1 to 1) + out.local_pos = p; out.color = obj.color; out.instance_index = instance_index; out.world_pos = world_pos.xyz; return out; } -// --- SDF Primitives --- -// All primitives are centered at 0,0,0 - fn sdSphere(p: vec3<f32>, r: f32) -> f32 { return length(p) - r; } @@ -132,46 +83,35 @@ fn sdTorus(p: vec3<f32>, t: vec2<f32>) -> f32 { return length(q) - t.y; } -// --- Dispatchers --- - -// Type IDs: 0=Cube(Wireframe proxy), 1=Sphere, 2=Box, 3=Torus -fn get_dist(p: vec3<f32>, type: f32) -> f32 { - if (type == 1.0) { return sdSphere(p, 0.9); } - if (type == 2.0) { return sdBox(p, vec3<f32>(0.7)); } - if (type == 3.0) { return sdTorus(p, vec2<f32>(0.6, 0.25)); } +fn get_dist(p: vec3<f32>, obj_type: f32) -> f32 { + if (obj_type == 1.0) { return sdSphere(p, 0.9); } + if (obj_type == 2.0) { return sdBox(p, vec3<f32>(0.7)); } + if (obj_type == 3.0) { return sdTorus(p, vec2<f32>(0.6, 0.25)); } return 100.0; } -// Analytical normals where possible, fallback to Numerical -fn get_normal(p: vec3<f32>, type: f32) -> vec3<f32> { - if (type == 1.0) { // Sphere - return normalize(p); // Center is 0,0,0 - } - - // Finite Difference for others +fn get_normal(p: vec3<f32>, obj_type: f32) -> vec3<f32> { + if (obj_type == 1.0) { return normalize(p); } let e = vec2<f32>(0.001, 0.0); return normalize(vec3<f32>( - get_dist(p + e.xyy, type) - get_dist(p - e.xyy, type), - get_dist(p + e.yxy, type) - get_dist(p - e.yxy, type), - get_dist(p + e.yyx, type) - get_dist(p - e.yyx, type) + get_dist(p + e.xyy, obj_type) - get_dist(p - e.xyy, obj_type), + get_dist(p + e.yxy, obj_type) - get_dist(p - e.yxy, obj_type), + get_dist(p + e.yyx, obj_type) - get_dist(p - e.yyx, obj_type) )); } @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { let obj = object_data.objects[in.instance_index]; - let type = obj.params.x; + let obj_type = obj.params.x; - // Case 0: The central cube (Wireframe/Solid Box logic) - Proxy only - if (type == 0.0) { + if (obj_type == 0.0) { let d = abs(in.local_pos); let edge_dist = max(max(d.x, d.y), d.z); - var col = in.color.rgb; if (edge_dist > 0.95) { - col = vec3<f32>(1.0, 1.0, 1.0); // White edges + col = vec3<f32>(1.0, 1.0, 1.0); } else { - // Simple face shading let normal = normalize(cross(dpdx(in.local_pos), dpdy(in.local_pos))); let light = normalize(vec3<f32>(0.5, 1.0, 0.5)); let diff = max(dot(normal, light), 0.2); @@ -180,217 +120,259 @@ fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> { return vec4<f32>(col, 1.0); } - // Case 1+: Raymarching inside the proxy box let center = vec3<f32>(obj.model[3].x, obj.model[3].y, obj.model[3].z); - - // Scale: Assume uniform scale from model matrix let scale = length(vec3<f32>(obj.model[0].x, obj.model[0].y, obj.model[0].z)); - let ro = globals.camera_pos; let rd = normalize(in.world_pos - globals.camera_pos); - - // Start marching at proxy surface var t = length(in.world_pos - ro); var p = ro + rd * t; - // Extract rotation (Normalized columns of model matrix) - let mat3 = mat3x3<f32>( - obj.model[0].xyz / scale, - obj.model[1].xyz / scale, - obj.model[2].xyz / scale - ); + let mat3 = mat3x3<f32>(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); var hit = false; - // Raymarch Loop for (var i = 0; i < 40; i++) { - // Transform p to local unscaled space for SDF eval - // q = inv(R) * (p - center) / scale let q = transpose(mat3) * (p - center) / scale; - - let d_local = get_dist(q, type); - let d_world = d_local * scale; - - if (d_world < 0.001) { - hit = true; - break; - } - if (d_world > 3.0 * scale) { - break; - } + let d_world = get_dist(q, obj_type) * scale; + if (d_world < 0.001) { hit = true; break; } + if (d_world > 3.0 * scale) { break; } p = p + rd * d_world; } - if (!hit) { - discard; - } + if (!hit) { discard; } // Shading // Recompute local pos at hit let q_hit = transpose(mat3) * (p - center) / scale; - // Normal calculation: - // Calculate normal in local space, then rotate to world. - let n_local = get_normal(q_hit, type); - let n_world = mat3 * n_local; + // Calculate normal with bump mapping (Displacement method) + // N = normalize(gradient( dist(p) - displacement(p) )) + // We do finite difference on the combined field. - let normal = normalize(n_world); - let light_dir = normalize(vec3<f32>(1.0, 1.0, 1.0)); + let e = vec2<f32>(0.005, 0.0); // Slightly larger epsilon for texture smoothness + + // Helper to get displaced distance + // Note: We only displace for normal calc, not for the raymarch hit (surface detail only) + // or we could refine the hit. For now, just lighting. - let diff = max(dot(normal, light_dir), 0.0); - let amb = 0.1; + // Inline helper for displacement + // We need UVs for any point q + // UV Mapping: Spherical - let lighting = diff + amb; + var n_local = vec3<f32>(0.0); + // Base normal + let n_base = get_normal(q_hit, obj_type); + + // Sample noise at center + let uv_c = vec2<f32>(atan2(q_hit.x, q_hit.z) / 6.28 + 0.5, acos(clamp(q_hit.y / length(q_hit), -1.0, 1.0)) / 3.14); + let h_c = textureSample(noise_tex, noise_sampler, uv_c).r; + + // Evaluate noise gradient via finite difference on UVs? + // Or just 3D finite difference on pos? + // 3D FD is generic but requires 6 texture samples (or 4 tetra). + // Let's try a cheaper trick: Gradient of texture in UV space? + // textureSampleGrad? No, we want world space normal perturbation. + + // Standard tri-planar or 3D noise is better for SDFs, but we have 2D texture. + // Let's stick to the "Gradient by 2D finite difference on UVs" or simply perturb n_base with derivatives. + // simpler: + // float h = texture(...); + // vec3 bump = vec3(dFdx(h), dFdy(h), 0.0); // Screen space derivative? No. + + // Let's go with the robust 3D FD on the displacement field. + // dist_disp(q) = get_dist(q) - 0.02 * noise(q) + + let disp_strength = 0.05; + + let q_x1 = q_hit + e.xyy; + let uv_x1 = vec2<f32>(atan2(q_x1.x, q_x1.z) / 6.28 + 0.5, acos(clamp(q_x1.y / length(q_x1), -1.0, 1.0)) / 3.14); + let h_x1 = textureSample(noise_tex, noise_sampler, uv_x1).r; + let d_x1 = get_dist(q_x1, obj_type) - disp_strength * h_x1; + + let q_x2 = q_hit - e.xyy; + let uv_x2 = vec2<f32>(atan2(q_x2.x, q_x2.z) / 6.28 + 0.5, acos(clamp(q_x2.y / length(q_x2), -1.0, 1.0)) / 3.14); + let h_x2 = textureSample(noise_tex, noise_sampler, uv_x2).r; + let d_x2 = get_dist(q_x2, obj_type) - disp_strength * h_x2; + + let q_y1 = q_hit + e.yxy; + let uv_y1 = vec2<f32>(atan2(q_y1.x, q_y1.z) / 6.28 + 0.5, acos(clamp(q_y1.y / length(q_y1), -1.0, 1.0)) / 3.14); + let h_y1 = textureSample(noise_tex, noise_sampler, uv_y1).r; + let d_y1 = get_dist(q_y1, obj_type) - disp_strength * h_y1; + + let q_y2 = q_hit - e.yxy; + let uv_y2 = vec2<f32>(atan2(q_y2.x, q_y2.z) / 6.28 + 0.5, acos(clamp(q_y2.y / length(q_y2), -1.0, 1.0)) / 3.14); + let h_y2 = textureSample(noise_tex, noise_sampler, uv_y2).r; + let d_y2 = get_dist(q_y2, obj_type) - disp_strength * h_y2; + + let q_z1 = q_hit + e.yyx; + let uv_z1 = vec2<f32>(atan2(q_z1.x, q_z1.z) / 6.28 + 0.5, acos(clamp(q_z1.y / length(q_z1), -1.0, 1.0)) / 3.14); + let h_z1 = textureSample(noise_tex, noise_sampler, uv_z1).r; + let d_z1 = get_dist(q_z1, obj_type) - disp_strength * h_z1; + + let q_z2 = q_hit - e.yyx; + let uv_z2 = vec2<f32>(atan2(q_z2.x, q_z2.z) / 6.28 + 0.5, acos(clamp(q_z2.y / length(q_z2), -1.0, 1.0)) / 3.14); + let h_z2 = textureSample(noise_tex, noise_sampler, uv_z2).r; + let d_z2 = get_dist(q_z2, obj_type) - disp_strength * h_z2; + + n_local = normalize(vec3<f32>(d_x1 - d_x2, d_y1 - d_y2, d_z1 - d_z2)); + + let n_world = mat3 * n_local; + let normal = normalize(n_world); + + let light_dir = normalize(vec3<f32>(1.0, 1.0, 1.0)); + let lighting = max(dot(normal, light_dir), 0.0) + 0.1; return vec4<f32>(in.color.rgb * lighting, 1.0); } )"; -void Renderer3D::init(WGPUDevice device, WGPUQueue queue, WGPUTextureFormat format) { +void Renderer3D::init(WGPUDevice device, WGPUQueue queue, + WGPUTextureFormat format) { device_ = device; queue_ = queue; format_ = format; + WGPUSamplerDescriptor sampler_desc = {}; + sampler_desc.addressModeU = WGPUAddressMode_Repeat; + sampler_desc.addressModeV = WGPUAddressMode_Repeat; + sampler_desc.magFilter = WGPUFilterMode_Linear; + sampler_desc.minFilter = WGPUFilterMode_Linear; + sampler_desc.maxAnisotropy = 1; + default_sampler_ = wgpuDeviceCreateSampler(device_, &sampler_desc); + create_default_resources(); create_pipeline(); } void Renderer3D::shutdown() { - if (pipeline_) wgpuRenderPipelineRelease(pipeline_); - if (bind_group_) wgpuBindGroupRelease(bind_group_); - if (global_uniform_buffer_) wgpuBufferRelease(global_uniform_buffer_); - if (object_storage_buffer_) wgpuBufferRelease(object_storage_buffer_); - if (depth_view_) wgpuTextureViewRelease(depth_view_); - if (depth_texture_) wgpuTextureRelease(depth_texture_); + if (default_sampler_) + wgpuSamplerRelease(default_sampler_); + if (pipeline_) + wgpuRenderPipelineRelease(pipeline_); + if (bind_group_) + wgpuBindGroupRelease(bind_group_); + if (global_uniform_buffer_) + wgpuBufferRelease(global_uniform_buffer_); + if (object_storage_buffer_) + wgpuBufferRelease(object_storage_buffer_); + if (depth_view_) + wgpuTextureViewRelease(depth_view_); + if (depth_texture_) + wgpuTextureRelease(depth_texture_); } void Renderer3D::resize(int width, int height) { - if (width == width_ && height == height_) return; - + if (width == width_ && height == height_) + return; width_ = width; height_ = height; - if (depth_view_) wgpuTextureViewRelease(depth_view_); - if (depth_texture_) wgpuTextureRelease(depth_texture_); + if (depth_view_) + wgpuTextureViewRelease(depth_view_); + if (depth_texture_) + wgpuTextureRelease(depth_texture_); WGPUTextureDescriptor desc = {}; desc.usage = WGPUTextureUsage_RenderAttachment; desc.dimension = WGPUTextureDimension_2D; desc.size = {(uint32_t)width, (uint32_t)height, 1}; - desc.format = WGPUTextureFormat_Depth24Plus; // Common depth format + desc.format = WGPUTextureFormat_Depth24Plus; desc.mipLevelCount = 1; desc.sampleCount = 1; - depth_texture_ = wgpuDeviceCreateTexture(device_, &desc); - + WGPUTextureViewDescriptor view_desc = {}; view_desc.format = WGPUTextureFormat_Depth24Plus; view_desc.dimension = WGPUTextureViewDimension_2D; view_desc.aspect = WGPUTextureAspect_DepthOnly; view_desc.arrayLayerCount = 1; view_desc.mipLevelCount = 1; - depth_view_ = wgpuTextureCreateView(depth_texture_, &view_desc); } void Renderer3D::create_default_resources() { - // Uniform Buffer - global_uniform_buffer_ = gpu_create_buffer(device_, sizeof(GlobalUniforms), - WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, nullptr).buffer; + global_uniform_buffer_ = + gpu_create_buffer(device_, sizeof(GlobalUniforms), + WGPUBufferUsage_Uniform | WGPUBufferUsage_CopyDst, + nullptr) + .buffer; + object_storage_buffer_ = + gpu_create_buffer(device_, sizeof(ObjectData) * kMaxObjects, + WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, + nullptr) + .buffer; +} - // Storage Buffer - size_t storage_size = sizeof(ObjectData) * kMaxObjects; - object_storage_buffer_ = gpu_create_buffer(device_, storage_size, - WGPUBufferUsage_Storage | WGPUBufferUsage_CopyDst, nullptr).buffer; +void Renderer3D::set_noise_texture(WGPUTextureView noise_view) { + noise_texture_view_ = noise_view; + // Note: Bind group needs recreation if texture changes, but we'll do it in + // render for simplicity or just once at init if it's static. For this demo, + // let's recreate in render if changed. } void Renderer3D::create_pipeline() { - // Bind Group Layout - WGPUBindGroupLayoutEntry entries[2] = {}; - - // Binding 0: Globals (Uniform) + WGPUBindGroupLayoutEntry entries[4] = {}; entries[0].binding = 0; entries[0].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; entries[0].buffer.type = WGPUBufferBindingType_Uniform; entries[0].buffer.minBindingSize = sizeof(GlobalUniforms); - // Binding 1: Object Data (Storage) entries[1].binding = 1; entries[1].visibility = WGPUShaderStage_Vertex | WGPUShaderStage_Fragment; entries[1].buffer.type = WGPUBufferBindingType_ReadOnlyStorage; entries[1].buffer.minBindingSize = sizeof(ObjectData) * kMaxObjects; + entries[2].binding = 2; + entries[2].visibility = WGPUShaderStage_Fragment; + entries[2].texture.sampleType = WGPUTextureSampleType_Float; + entries[2].texture.viewDimension = WGPUTextureViewDimension_2D; + + entries[3].binding = 3; + entries[3].visibility = WGPUShaderStage_Fragment; + entries[3].sampler.type = WGPUSamplerBindingType_Filtering; + WGPUBindGroupLayoutDescriptor bgl_desc = {}; - bgl_desc.entryCount = 2; + bgl_desc.entryCount = 4; bgl_desc.entries = entries; WGPUBindGroupLayout bgl = wgpuDeviceCreateBindGroupLayout(device_, &bgl_desc); - // Bind Group - WGPUBindGroupEntry bg_entries[2] = {}; - bg_entries[0].binding = 0; - bg_entries[0].buffer = global_uniform_buffer_; - bg_entries[0].size = sizeof(GlobalUniforms); - - bg_entries[1].binding = 1; - bg_entries[1].buffer = object_storage_buffer_; - bg_entries[1].size = sizeof(ObjectData) * kMaxObjects; - - WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = bgl; - bg_desc.entryCount = 2; - bg_desc.entries = bg_entries; - bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc); - - // Pipeline Layout WGPUPipelineLayoutDescriptor pl_desc = {}; pl_desc.bindGroupLayoutCount = 1; pl_desc.bindGroupLayouts = &bgl; - WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device_, &pl_desc); - - // Shader Code - const char* shader_source = kShaderCode; + WGPUPipelineLayout pipeline_layout = + wgpuDeviceCreatePipelineLayout(device_, &pl_desc); - // Shader Module #if defined(DEMO_CROSS_COMPILE_WIN32) WGPUShaderModuleWGSLDescriptor wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderModuleWGSLDescriptor; - wgsl_desc.code = shader_source; - + wgsl_desc.code = kShaderCode; WGPUShaderModuleDescriptor shader_desc = {}; shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain; #else WGPUShaderSourceWGSL wgsl_desc = {}; wgsl_desc.chain.sType = WGPUSType_ShaderSourceWGSL; - wgsl_desc.code = {shader_source, strlen(shader_source)}; - + wgsl_desc.code = {kShaderCode, strlen(kShaderCode)}; WGPUShaderModuleDescriptor shader_desc = {}; shader_desc.nextInChain = (const WGPUChainedStruct*)&wgsl_desc.chain; #endif + WGPUShaderModule shader_module = + wgpuDeviceCreateShaderModule(device_, &shader_desc); - WGPUShaderModule shader_module = wgpuDeviceCreateShaderModule(device_, &shader_desc); - - // Depth Stencil State WGPUDepthStencilState depth_stencil = {}; depth_stencil.format = WGPUTextureFormat_Depth24Plus; depth_stencil.depthWriteEnabled = WGPUOptionalBool_True; depth_stencil.depthCompare = WGPUCompareFunction_Less; - - // Render Pipeline + WGPURenderPipelineDescriptor desc = {}; desc.layout = pipeline_layout; - - // Vertex desc.vertex.module = shader_module; #if defined(DEMO_CROSS_COMPILE_WIN32) desc.vertex.entryPoint = "vs_main"; #else desc.vertex.entryPoint = {"vs_main", 7}; #endif - - // Fragment WGPUColorTargetState color_target = {}; color_target.format = format_; color_target.writeMask = WGPUColorWriteMask_All; - WGPUFragmentState fragment = {}; fragment.module = shader_module; #if defined(DEMO_CROSS_COMPILE_WIN32) @@ -401,64 +383,83 @@ void Renderer3D::create_pipeline() { fragment.targetCount = 1; fragment.targets = &color_target; desc.fragment = &fragment; - desc.primitive.topology = WGPUPrimitiveTopology_TriangleList; desc.primitive.cullMode = WGPUCullMode_Back; desc.primitive.frontFace = WGPUFrontFace_CCW; - desc.depthStencil = &depth_stencil; desc.multisample.count = 1; desc.multisample.mask = 0xFFFFFFFF; pipeline_ = wgpuDeviceCreateRenderPipeline(device_, &desc); - wgpuBindGroupLayoutRelease(bgl); wgpuPipelineLayoutRelease(pipeline_layout); wgpuShaderModuleRelease(shader_module); } -void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, float time) { - // Update Globals +void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, + float time) { GlobalUniforms globals; globals.view_proj = camera.get_projection_matrix() * camera.get_view_matrix(); globals.camera_pos = camera.position; globals.time = time; - wgpuQueueWriteBuffer(queue_, global_uniform_buffer_, 0, &globals, sizeof(GlobalUniforms)); + wgpuQueueWriteBuffer(queue_, global_uniform_buffer_, 0, &globals, + sizeof(GlobalUniforms)); - // Update Objects std::vector<ObjectData> obj_data; - obj_data.reserve(scene.objects.size()); for (const auto& obj : scene.objects) { ObjectData data; data.model = obj.get_model_matrix(); data.color = obj.color; - // Map ObjectType enum to float ID float type_id = 0.0f; - if (obj.type == ObjectType::SPHERE) type_id = 1.0f; - else if (obj.type == ObjectType::CUBE) type_id = 0.0f; - else if (obj.type == ObjectType::TORUS) type_id = 3.0f; - else if (obj.type == ObjectType::BOX) type_id = 2.0f; - + if (obj.type == ObjectType::SPHERE) + type_id = 1.0f; + else if (obj.type == ObjectType::BOX) + type_id = 2.0f; + else if (obj.type == ObjectType::TORUS) + type_id = 3.0f; data.params = vec4(type_id, 0, 0, 0); obj_data.push_back(data); - if (obj_data.size() >= kMaxObjects) break; + if (obj_data.size() >= kMaxObjects) + break; } - if (!obj_data.empty()) { - wgpuQueueWriteBuffer(queue_, object_storage_buffer_, 0, obj_data.data(), obj_data.size() * sizeof(ObjectData)); + wgpuQueueWriteBuffer(queue_, object_storage_buffer_, 0, obj_data.data(), + obj_data.size() * sizeof(ObjectData)); } } void Renderer3D::render(const Scene& scene, const Camera& camera, float time, - WGPUTextureView target_view, WGPUTextureView depth_view_opt) { + WGPUTextureView target_view, + WGPUTextureView depth_view_opt) { update_uniforms(scene, camera, time); - WGPUTextureView depth_view = depth_view_opt ? depth_view_opt : depth_view_; - if (!depth_view) return; // Should have been created by resize + // Lazy Bind Group creation (since noise_texture might change) + if (bind_group_) + wgpuBindGroupRelease(bind_group_); + + WGPUBindGroupEntry bg_entries[4] = {}; + bg_entries[0].binding = 0; + bg_entries[0].buffer = global_uniform_buffer_; + bg_entries[0].size = sizeof(GlobalUniforms); + bg_entries[1].binding = 1; + bg_entries[1].buffer = object_storage_buffer_; + bg_entries[1].size = sizeof(ObjectData) * kMaxObjects; + bg_entries[2].binding = 2; + bg_entries[2].textureView = noise_texture_view_; + bg_entries[3].binding = 3; + bg_entries[3].sampler = default_sampler_; + WGPUBindGroupDescriptor bg_desc = {}; + bg_desc.layout = wgpuRenderPipelineGetBindGroupLayout(pipeline_, 0); + bg_desc.entryCount = 4; + bg_desc.entries = bg_entries; + bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc); + wgpuBindGroupLayoutRelease(bg_desc.layout); + + WGPUTextureView depth_view = depth_view_opt ? depth_view_opt : depth_view_; WGPURenderPassColorAttachment color_attachment = {}; gpu_init_color_attachment(color_attachment, target_view); - color_attachment.clearValue = {0.05, 0.05, 0.1, 1.0}; // Dark blue-ish background + color_attachment.clearValue = {0.05, 0.05, 0.1, 1.0}; WGPURenderPassDepthStencilAttachment depth_attachment = {}; depth_attachment.view = depth_view; @@ -472,23 +473,18 @@ void Renderer3D::render(const Scene& scene, const Camera& camera, float time, pass_desc.depthStencilAttachment = &depth_attachment; WGPUCommandEncoder encoder = wgpuDeviceCreateCommandEncoder(device_, nullptr); - WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); - + WGPURenderPassEncoder pass = + wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); wgpuRenderPassEncoderSetPipeline(pass, pipeline_); wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - - // Draw all objects (Instance Count = object count) - // Vertex Count = 36 (Cube) - uint32_t instance_count = (uint32_t)std::min((size_t)kMaxObjects, scene.objects.size()); - if (instance_count > 0) { + uint32_t instance_count = + (uint32_t)std::min((size_t)kMaxObjects, scene.objects.size()); + if (instance_count > 0) wgpuRenderPassEncoderDraw(pass, 36, instance_count, 0, 0); - } - wgpuRenderPassEncoderEnd(pass); WGPUCommandBuffer commands = wgpuCommandEncoderFinish(encoder, nullptr); wgpuQueueSubmit(queue_, 1, &commands); - wgpuRenderPassEncoderRelease(pass); wgpuCommandBufferRelease(commands); wgpuCommandEncoderRelease(encoder); -} +}
\ No newline at end of file diff --git a/src/3d/renderer.h b/src/3d/renderer.h index 0dadc32..dda6229 100644 --- a/src/3d/renderer.h +++ b/src/3d/renderer.h @@ -30,7 +30,10 @@ class Renderer3D { // Renders the scene to the given texture view void render(const Scene& scene, const Camera& camera, float time, - WGPUTextureView target_view, WGPUTextureView depth_view_opt = nullptr); + WGPUTextureView target_view, + WGPUTextureView depth_view_opt = nullptr); + + void set_noise_texture(WGPUTextureView noise_view); // Resize handler (if needed for internal buffers) void resize(int width, int height); @@ -48,7 +51,10 @@ class Renderer3D { WGPUBindGroup bind_group_ = nullptr; WGPUBuffer global_uniform_buffer_ = nullptr; WGPUBuffer object_storage_buffer_ = nullptr; - + + WGPUTextureView noise_texture_view_ = nullptr; + WGPUSampler default_sampler_ = nullptr; + // Depth buffer management WGPUTexture depth_texture_ = nullptr; WGPUTextureView depth_view_ = nullptr; |
