summaryrefslogtreecommitdiff
path: root/src/3d
diff options
context:
space:
mode:
authorskal <pascal.massimino@gmail.com>2026-02-02 15:55:03 +0100
committerskal <pascal.massimino@gmail.com>2026-02-02 15:55:03 +0100
commitc194f59e171a1e58ce1704f37d99ffcd09a42433 (patch)
treef77a32f2c4c23b335209b8df11cc82920388b51a /src/3d
parent316825883c705ed0fe927c32e072f98141d3eaa3 (diff)
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.
Diffstat (limited to 'src/3d')
-rw-r--r--src/3d/renderer.cc47
-rw-r--r--src/3d/renderer.h4
-rw-r--r--src/3d/visual_debug.cc98
-rw-r--r--src/3d/visual_debug.h10
4 files changed, 99 insertions, 60 deletions
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<f32>, t: vec2<f32>) -> f32 {
return length(q) - t.y;
}
+fn sdPlane(p: vec3<f32>, n: vec3<f32>, h: f32) -> f32 {
+ return dot(p, n) + h;
+}
+
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)); }
+ if (obj_type == 4.0) { return sdPlane(p, vec3<f32>(0.0, 1.0, 0.0), 0.0); }
return 100.0;
}
@@ -100,14 +105,11 @@ fn map_scene(p: vec3<f32>) -> 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<f32>(obj.model[3].x, obj.model[3].y, obj.model[3].z);
let scale = length(vec3<f32>(obj.model[0].x, obj.model[0].y, obj.model[0].z));
let mat3 = mat3x3<f32>(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>) -> f32 {
}
fn calc_shadow(ro: vec3<f32>, rd: vec3<f32>, 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<f32>, obj_type: f32) -> vec3<f32> {
@@ -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 <cstring>
#include <cstdio>
+#include <cstring>
// Simple shader for drawing colored lines
static const char* kDebugShaderCode = R"(
@@ -42,10 +42,11 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
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<float> 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);
+ 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);
+ 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 <vector>
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<DebugLine> lines_;
-
+
// Uniform buffer for ViewProjection matrix
WGPUBuffer uniform_buffer_ = nullptr;
WGPUBindGroup bind_group_ = nullptr;