From c194f59e171a1e58ce1704f37d99ffcd09a42433 Mon Sep 17 00:00:00 2001 From: skal Date: Mon, 2 Feb 2026 15:55:03 +0100 Subject: fix(gpu): Resolve high-DPI squished rendering and 3D shadow bugs - Implemented dynamic resolution support in all shaders and effects. - Added explicit viewport setting for all render passes to ensure correct scaling. - Fixed 3D shadow mapping by adding PLANE support and standardizing soft shadow logic. - Propagated resize events through the Effect hierarchy. - Applied project-wide code formatting. --- src/3d/renderer.cc | 47 ++++++++++++++--------- src/3d/renderer.h | 4 +- src/3d/visual_debug.cc | 100 +++++++++++++++++++++++++++++++------------------ src/3d/visual_debug.h | 10 ++--- 4 files changed, 100 insertions(+), 61 deletions(-) (limited to 'src/3d') diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc index 13917b1..505cd31 100644 --- a/src/3d/renderer.cc +++ b/src/3d/renderer.cc @@ -89,10 +89,15 @@ fn sdTorus(p: vec3, t: vec2) -> f32 { return length(q) - t.y; } +fn sdPlane(p: vec3, n: vec3, h: f32) -> f32 { + return dot(p, n) + h; +} + fn get_dist(p: vec3, obj_type: f32) -> f32 { if (obj_type == 1.0) { return sdSphere(p, 0.9); } if (obj_type == 2.0) { return sdBox(p, vec3(0.7)); } if (obj_type == 3.0) { return sdTorus(p, vec2(0.6, 0.25)); } + if (obj_type == 4.0) { return sdPlane(p, vec3(0.0, 1.0, 0.0), 0.0); } return 100.0; } @@ -100,14 +105,11 @@ fn map_scene(p: vec3) -> f32 { var d = 1000.0; let count = u32(globals.params.x); - // Brute force loop over all objects for (var i = 0u; i < count; i = i + 1u) { let obj = object_data.objects[i]; let obj_type = obj.params.x; - if (obj_type <= 0.0) { continue; } // Skip non-sdf objects + if (obj_type <= 0.0) { continue; } - // Transform world p to local q - // Assuming uniform scale let center = vec3(obj.model[3].x, obj.model[3].y, obj.model[3].z); let scale = length(vec3(obj.model[0].x, obj.model[0].y, obj.model[0].z)); let mat3 = mat3x3(obj.model[0].xyz/scale, obj.model[1].xyz/scale, obj.model[2].xyz/scale); @@ -120,18 +122,18 @@ fn map_scene(p: vec3) -> f32 { } fn calc_shadow(ro: vec3, rd: vec3, tmin: f32, tmax: f32) -> f32 { - var t = tmin; var res = 1.0; - for (var i = 0; i < 30; i = i + 1) { + var t = tmin; + if (t < 0.02) { t = 0.02; } + + for (var i = 0; i < 32; i = i + 1) { let h = map_scene(ro + rd * t); - if (h < 0.001) { - return 0.0; // Hard shadow hit - } - res = min(res, 8.0 * h / t); // Soft shadow k=8 - t = t + h; + if (h < 0.001) { return 0.0; } + res = min(res, 32.0 * h / t); // Harder shadows k=32 + t = t + clamp(h, 0.01, 0.5); if (t > tmax) { break; } } - return res; + return clamp(res, 0.0, 1.0); } fn get_normal(p: vec3, obj_type: f32) -> vec3 { @@ -418,9 +420,12 @@ 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_time = vec4(camera.position.x, camera.position.y, camera.position.z, time); - globals.params = vec4((float)std::min((size_t)kMaxObjects, scene.objects.size()), 0.0f, 0.0f, 0.0f); - + globals.camera_pos_time = + vec4(camera.position.x, camera.position.y, camera.position.z, time); + globals.params = + vec4((float)std::min((size_t)kMaxObjects, scene.objects.size()), 0.0f, + 0.0f, 0.0f); + wgpuQueueWriteBuffer(queue_, global_uniform_buffer_, 0, &globals, sizeof(GlobalUniforms)); @@ -428,7 +433,7 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, for (const auto& obj : scene.objects) { ObjectData data; data.model = obj.get_model_matrix(); - + // Calculate Inverse Transpose for correct normal transformation mat4 inverse = data.model.inverse(); data.model_inverse_transpose = mat4::transpose(inverse); @@ -441,6 +446,8 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, type_id = 2.0f; else if (obj.type == ObjectType::TORUS) type_id = 3.0f; + else if (obj.type == ObjectType::PLANE) + type_id = 4.0f; data.params = vec4(type_id, 0, 0, 0); obj_data.push_back(data); if (obj_data.size() >= kMaxObjects) @@ -510,9 +517,10 @@ void Renderer3D::draw(WGPURenderPassEncoder pass, const Scene& scene, if (s_debug_enabled_) { for (const auto& obj : scene.objects) { // Simple AABB approximation from scale - visual_debug_.add_box(obj.position, obj.scale, vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes + visual_debug_.add_box(obj.position, obj.scale, + vec3(1.0f, 1.0f, 0.0f)); // Yellow boxes } - + // Calculate ViewProj matrix for the debug renderer mat4 view_proj = camera.get_projection_matrix() * camera.get_view_matrix(); visual_debug_.render(pass, view_proj); @@ -558,6 +566,9 @@ void Renderer3D::render(const Scene& scene, const Camera& camera, float time, WGPURenderPassEncoder pass = wgpuCommandEncoderBeginRenderPass(encoder, &pass_desc); + wgpuRenderPassEncoderSetViewport(pass, 0.0f, 0.0f, (float)width_, + (float)height_, 0.0f, 1.0f); + draw(pass, scene, camera, time); wgpuRenderPassEncoderEnd(pass); diff --git a/src/3d/renderer.h b/src/3d/renderer.h index 43e7cfe..a7f29da 100644 --- a/src/3d/renderer.h +++ b/src/3d/renderer.h @@ -34,7 +34,9 @@ class Renderer3D { void shutdown(); #if !defined(STRIP_ALL) - static void SetDebugEnabled(bool enabled) { s_debug_enabled_ = enabled; } + static void SetDebugEnabled(bool enabled) { + s_debug_enabled_ = enabled; + } #endif // Renders the scene to the given texture view (Convenience: creates a pass) diff --git a/src/3d/visual_debug.cc b/src/3d/visual_debug.cc index 361372b..daa1033 100644 --- a/src/3d/visual_debug.cc +++ b/src/3d/visual_debug.cc @@ -5,8 +5,8 @@ #if !defined(STRIP_ALL) -#include #include +#include // Simple shader for drawing colored lines static const char* kDebugShaderCode = R"( @@ -42,10 +42,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4 { void VisualDebug::init(WGPUDevice device, WGPUTextureFormat format) { device_ = device; create_pipeline(format); - + // Initial capacity for vertex buffer (e.g., 1024 lines) - vertex_buffer_capacity_ = 1024 * 2 * sizeof(float) * 6; // 2 verts per line, 6 floats per vert - + vertex_buffer_capacity_ = + 1024 * 2 * sizeof(float) * 6; // 2 verts per line, 6 floats per vert + WGPUBufferDescriptor vb_desc = {}; vb_desc.usage = WGPUBufferUsage_Vertex | WGPUBufferUsage_CopyDst; vb_desc.size = vertex_buffer_capacity_; @@ -58,11 +59,16 @@ void VisualDebug::init(WGPUDevice device, WGPUTextureFormat format) { } void VisualDebug::shutdown() { - if (pipeline_) wgpuRenderPipelineRelease(pipeline_); - if (bind_group_layout_) wgpuBindGroupLayoutRelease(bind_group_layout_); - if (vertex_buffer_) wgpuBufferRelease(vertex_buffer_); - if (uniform_buffer_) wgpuBufferRelease(uniform_buffer_); - if (bind_group_) wgpuBindGroupRelease(bind_group_); + if (pipeline_) + wgpuRenderPipelineRelease(pipeline_); + if (bind_group_layout_) + wgpuBindGroupLayoutRelease(bind_group_layout_); + if (vertex_buffer_) + wgpuBufferRelease(vertex_buffer_); + if (uniform_buffer_) + wgpuBufferRelease(uniform_buffer_); + if (bind_group_) + wgpuBindGroupRelease(bind_group_); } void VisualDebug::create_pipeline(WGPUTextureFormat format) { @@ -82,7 +88,8 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { WGPUPipelineLayoutDescriptor pl_desc = {}; pl_desc.bindGroupLayoutCount = 1; pl_desc.bindGroupLayouts = &bind_group_layout_; - WGPUPipelineLayout pipeline_layout = wgpuDeviceCreatePipelineLayout(device_, &pl_desc); + WGPUPipelineLayout pipeline_layout = + wgpuDeviceCreatePipelineLayout(device_, &pl_desc); // Shader #if defined(DEMO_CROSS_COMPILE_WIN32) @@ -98,7 +105,8 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { 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); // Vertex State WGPUVertexAttribute attributes[2]; @@ -137,7 +145,7 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { fragment_state.entryPoint = {"fs_main", 7}; #endif fragment_state.targetCount = 1; - + WGPUColorTargetState color_target = {}; color_target.format = format; color_target.writeMask = WGPUColorWriteMask_All; @@ -152,7 +160,8 @@ void VisualDebug::create_pipeline(WGPUTextureFormat format) { WGPUDepthStencilState depth_stencil = {}; depth_stencil.format = WGPUTextureFormat_Depth24Plus; depth_stencil.depthWriteEnabled = WGPUOptionalBool_False; // Don't write depth - depth_stencil.depthCompare = WGPUCompareFunction_Less; // But do test against it + depth_stencil.depthCompare = + WGPUCompareFunction_Less; // But do test against it pipeline_desc.depthStencil = &depth_stencil; pipeline_desc.multisample.count = 1; @@ -177,9 +186,12 @@ void VisualDebug::add_box(const vec3& c, const vec3& e, const vec3& color) { // 12 edges (each 2 vertices) DebugLine edges[] = { - {p0, p1, color}, {p1, p2, color}, {p2, p3, color}, {p3, p0, color}, // Front face - {p4, p5, color}, {p5, p6, color}, {p6, p7, color}, {p7, p4, color}, // Back face - {p0, p4, color}, {p1, p5, color}, {p2, p6, color}, {p3, p7, color} // Connecting edges + {p0, p1, color}, {p1, p2, color}, + {p2, p3, color}, {p3, p0, color}, // Front face + {p4, p5, color}, {p5, p6, color}, + {p6, p7, color}, {p7, p4, color}, // Back face + {p0, p4, color}, {p1, p5, color}, + {p2, p6, color}, {p3, p7, color} // Connecting edges }; for (const auto& l : edges) { @@ -189,7 +201,8 @@ void VisualDebug::add_box(const vec3& c, const vec3& e, const vec3& color) { void VisualDebug::update_buffers(const mat4& view_proj) { // Update Uniforms - wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), uniform_buffer_, 0, &view_proj, sizeof(mat4)); + wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), uniform_buffer_, 0, + &view_proj, sizeof(mat4)); // Update Vertices size_t required_size = lines_.size() * 2 * sizeof(float) * 6; @@ -207,38 +220,51 @@ void VisualDebug::update_buffers(const mat4& view_proj) { std::vector vertex_data; vertex_data.reserve(lines_.size() * 12); // 2 verts * 6 floats for (const auto& line : lines_) { - vertex_data.push_back(line.start.x); vertex_data.push_back(line.start.y); vertex_data.push_back(line.start.z); - vertex_data.push_back(line.color.x); vertex_data.push_back(line.color.y); vertex_data.push_back(line.color.z); - - vertex_data.push_back(line.end.x); vertex_data.push_back(line.end.y); vertex_data.push_back(line.end.z); - vertex_data.push_back(line.color.x); vertex_data.push_back(line.color.y); vertex_data.push_back(line.color.z); + vertex_data.push_back(line.start.x); + vertex_data.push_back(line.start.y); + vertex_data.push_back(line.start.z); + vertex_data.push_back(line.color.x); + vertex_data.push_back(line.color.y); + vertex_data.push_back(line.color.z); + + vertex_data.push_back(line.end.x); + vertex_data.push_back(line.end.y); + vertex_data.push_back(line.end.z); + vertex_data.push_back(line.color.x); + vertex_data.push_back(line.color.y); + vertex_data.push_back(line.color.z); } - wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), vertex_buffer_, 0, vertex_data.data(), vertex_data.size() * sizeof(float)); + wgpuQueueWriteBuffer(wgpuDeviceGetQueue(device_), vertex_buffer_, 0, + vertex_data.data(), + vertex_data.size() * sizeof(float)); } - - // Re-create bind group if needed (e.g. if uniform buffer changed, though here it's static) + + // Re-create bind group if needed (e.g. if uniform buffer changed, though here + // it's static) if (!bind_group_) { - WGPUBindGroupEntry bg_entry = {}; - bg_entry.binding = 0; - bg_entry.buffer = uniform_buffer_; - bg_entry.size = sizeof(mat4); - - WGPUBindGroupDescriptor bg_desc = {}; - bg_desc.layout = bind_group_layout_; - bg_desc.entryCount = 1; - bg_desc.entries = &bg_entry; - bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc); + WGPUBindGroupEntry bg_entry = {}; + bg_entry.binding = 0; + bg_entry.buffer = uniform_buffer_; + bg_entry.size = sizeof(mat4); + + WGPUBindGroupDescriptor bg_desc = {}; + bg_desc.layout = bind_group_layout_; + bg_desc.entryCount = 1; + bg_desc.entries = &bg_entry; + bind_group_ = wgpuDeviceCreateBindGroup(device_, &bg_desc); } } void VisualDebug::render(WGPURenderPassEncoder pass, const mat4& view_proj) { - if (lines_.empty()) return; + if (lines_.empty()) + return; update_buffers(view_proj); wgpuRenderPassEncoderSetPipeline(pass, pipeline_); wgpuRenderPassEncoderSetBindGroup(pass, 0, bind_group_, 0, nullptr); - wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vertex_buffer_, 0, lines_.size() * 2 * sizeof(float) * 6); + wgpuRenderPassEncoderSetVertexBuffer(pass, 0, vertex_buffer_, 0, + lines_.size() * 2 * sizeof(float) * 6); wgpuRenderPassEncoderDraw(pass, (uint32_t)lines_.size() * 2, 1, 0, 0); lines_.clear(); // Clear for next frame diff --git a/src/3d/visual_debug.h b/src/3d/visual_debug.h index c757de3..f02ac6b 100644 --- a/src/3d/visual_debug.h +++ b/src/3d/visual_debug.h @@ -6,8 +6,8 @@ #if !defined(STRIP_ALL) -#include "util/mini_math.h" #include "gpu/gpu.h" +#include "util/mini_math.h" #include struct DebugLine { @@ -17,7 +17,7 @@ struct DebugLine { }; class VisualDebug { -public: + public: void init(WGPUDevice device, WGPUTextureFormat format); void shutdown(); @@ -27,13 +27,13 @@ public: // Render all queued primitives and clear the queue void render(WGPURenderPassEncoder pass, const mat4& view_proj); -private: + private: WGPUDevice device_ = nullptr; WGPURenderPipeline pipeline_ = nullptr; WGPUBindGroupLayout bind_group_layout_ = nullptr; - + std::vector lines_; - + // Uniform buffer for ViewProjection matrix WGPUBuffer uniform_buffer_ = nullptr; WGPUBindGroup bind_group_ = nullptr; -- cgit v1.2.3