summaryrefslogtreecommitdiff
path: root/src/3d
diff options
context:
space:
mode:
Diffstat (limited to 'src/3d')
-rw-r--r--src/3d/object.h3
-rw-r--r--src/3d/renderer.cc159
2 files changed, 137 insertions, 25 deletions
diff --git a/src/3d/object.h b/src/3d/object.h
index f4215aa..ccbb1e1 100644
--- a/src/3d/object.h
+++ b/src/3d/object.h
@@ -10,7 +10,8 @@ enum class ObjectType {
CUBE,
SPHERE,
PLANE,
- TORUS
+ TORUS,
+ BOX
// Add more SDF types here
};
diff --git a/src/3d/renderer.cc b/src/3d/renderer.cc
index 1745a97..2e08b4e 100644
--- a/src/3d/renderer.cc
+++ b/src/3d/renderer.cc
@@ -76,19 +76,14 @@ struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) local_pos: vec3<f32>,
@location(1) color: vec4<f32>,
+ @location(2) @interpolate(flat) instance_index: u32,
+ @location(3) world_pos: vec3<f32>,
};
@vertex
fn vs_main(@builtin(vertex_index) vertex_index: u32,
@builtin(instance_index) instance_index: u32) -> VertexOutput {
- // Hardcoded cube vertices (similar to C++ array but in shader for simplicity if desired,
- // but here we might assume a vertex buffer or just generate logic.
- // For this demo, let's use the buffer-less approach for vertices if we want to save space,
- // but we have a C++ array. Let's just generate a cube on the fly from index?)
- // Actually, let's map the C++ kCubeVertices to a vertex buffer or use a hardcoded array here.
- // For 64k size, hardcoded in shader is good.
-
var pos = array<vec3<f32>, 36>(
vec3(-1.0, -1.0, 1.0), vec3( 1.0, -1.0, 1.0), vec3( 1.0, 1.0, 1.0),
vec3(-1.0, -1.0, 1.0), vec3( 1.0, 1.0, 1.0), vec3(-1.0, 1.0, 1.0),
@@ -113,31 +108,140 @@ fn vs_main(@builtin(vertex_index) vertex_index: u32,
var out: VertexOutput;
out.position = clip_pos;
- out.local_pos = p;
+ out.local_pos = p; // Proxy geometry local coords (-1 to 1)
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;
+}
+
+fn sdBox(p: vec3<f32>, b: vec3<f32>) -> f32 {
+ let q = abs(p) - b;
+ return length(max(q, vec3<f32>(0.0))) + min(max(q.x, max(q.y, q.z)), 0.0);
+}
+
+fn sdTorus(p: vec3<f32>, t: vec2<f32>) -> f32 {
+ let q = vec2<f32>(length(p.xz) - t.x, p.y);
+ 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)); }
+ 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
+ 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)
+ ));
+}
+
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
- // Simple wireframe-ish effect using barycentric coords logic?
- // Or just check proximity to edge of local cube?
- let d = abs(in.local_pos);
- let edge_dist = max(max(d.x, d.y), d.z);
+ let obj = object_data.objects[in.instance_index];
+ let type = obj.params.x;
+
+ // Case 0: The central cube (Wireframe/Solid Box logic) - Proxy only
+ if (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
+ } 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);
+ col = col * diff;
+ }
+ 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);
- // Mix object color with edge highlight
- var col = in.color.rgb;
- if (edge_dist > 0.95) {
- col = vec3<f32>(1.0, 1.0, 1.0); // White edges
- } else {
- // Simple 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);
- col = col * diff;
+ // 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
+ );
+
+ 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;
+ }
+ p = p + rd * d_world;
}
- return vec4<f32>(col, 1.0);
+ 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;
+
+ let normal = normalize(n_world);
+ let light_dir = normalize(vec3<f32>(1.0, 1.0, 1.0));
+
+ let diff = max(dot(normal, light_dir), 0.0);
+ let amb = 0.1;
+
+ let lighting = diff + amb;
+
+ return vec4<f32>(in.color.rgb * lighting, 1.0);
}
)";
@@ -328,7 +432,14 @@ void Renderer3D::update_uniforms(const Scene& scene, const Camera& camera, float
ObjectData data;
data.model = obj.get_model_matrix();
data.color = obj.color;
- // data.params = ...
+ // 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;
+
+ data.params = vec4(type_id, 0, 0, 0);
obj_data.push_back(data);
if (obj_data.size() >= kMaxObjects) break;
}