summaryrefslogtreecommitdiff
path: root/src/3d/renderer.cc
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-01 11:31:00 +0100
committerskal <pascal.massimino@gmail.com>2026-02-01 11:31:00 +0100
commitf80e37bd61e447f1d66fbb5eb4c1ab7a8a77cf0f (patch)
treed6c06e4c9e6d2570458d88d35acba9e64231cbc0 /src/3d/renderer.cc
parentf307cde4ac1126e38c5595ce61a26d50cdd7ad4a (diff)
feat: Add seamless bump mapping with procedural noise
- Replaced white noise with smooth value-like noise. - Implemented periodic texture generation (seam blending). - Integrated bump mapping into Renderer3D using finite difference of displaced SDF. - Updated test_3d_render with noise texture and multiple SDF shapes (Box, Sphere, Torus).
Diffstat (limited to 'src/3d/renderer.cc')
-rw-r--r--src/3d/renderer.cc418
1 files changed, 207 insertions, 211 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