surface points on gpu

This commit is contained in:
Guillaume Vern 2025-12-01 10:43:25 +01:00
parent 0a148d9db1
commit 42ccbebb52
4 changed files with 113 additions and 15 deletions

View File

@ -28,6 +28,21 @@ func compute(world_size: int, threshold: float):
uniform_buf.binding = 0 # this needs to match the "binding" in our shader file
uniform_buf.add_id(buffer)
# Prepare our data. We use floats in the shader, so we need 32 bit.
var surface_total = world_size * world_size * world_size * 3
var surface_input := PackedFloat32Array()
surface_input.resize(surface_total)
var surface_input_bytes := surface_input.to_byte_array()
# Create a storage buffer that can hold our float values.
var surface_buffer := rd.storage_buffer_create(surface_input_bytes.size(), surface_input_bytes)
# Create a uniform to assign the buffer to the rendering device
var surface_uniform_buf := RDUniform.new()
surface_uniform_buf.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
surface_uniform_buf.binding = 2 # this needs to match the "binding" in our shader file
surface_uniform_buf.add_id(surface_buffer)
var peer := StreamPeerBuffer.new()
peer.put_32(world_size)
peer.put_float(threshold)
@ -42,7 +57,7 @@ func compute(world_size: int, threshold: float):
uniform_params.binding = 1
uniform_params.add_id(params_buffer)
var uniform_set := rd.uniform_set_create([uniform_params, uniform_buf], shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file
var uniform_set := rd.uniform_set_create([uniform_params, uniform_buf, surface_uniform_buf], shader, 0) # the last parameter (the 0) needs to match the "set" in our shader file
# Create a compute pipeline
var pipeline := rd.compute_pipeline_create(shader)
@ -59,12 +74,31 @@ func compute(world_size: int, threshold: float):
rd.submit()
rd.sync()
# Read back the data from the buffer
var output_bytes := rd.buffer_get_data(buffer)
var output_bytes := rd.buffer_get_data(surface_buffer)
var output := output_bytes.to_float32_array()
rd.free_rid(buffer)
rd.free_rid(params_buffer)
return output
rd.free_rid(surface_buffer)
return build_surface_dict(world_size, output)
func build_surface_dict(world_size: int, flat_buffer: PackedFloat32Array) -> Dictionary:
var dict := {}
var total = world_size * world_size * world_size
for idx in total:
var base = idx * 3
var x = flat_buffer[base]
var y = flat_buffer[base + 1]
var z = flat_buffer[base + 2]
var voxel_x = idx % world_size
var voxel_y = (idx / world_size) % world_size
var voxel_z = idx / (world_size * world_size)
var voxel_id = Vector3i(voxel_x, voxel_y, voxel_z)
var surface_pos = Vector3(x, y, z)
dict[voxel_id] = surface_pos
return dict

View File

@ -15,10 +15,21 @@ layout(set = 0, binding = 1) uniform Params {
float threshold;
} params;
layout(set = 0, binding = 2) buffer SurfaceBuffer {
float surface_points[];
} surface;
uint index3(uint x, uint y, uint z) {
return x + y * params.world_size + z * params.world_size * params.world_size;
}
void store_surface_point(uint idx, vec3 pos) {
uint base = idx * 3u;
surface.surface_points[base + 0u] = pos.x;
surface.surface_points[base + 1u] = pos.y;
surface.surface_points[base + 2u] = pos.z;
}
void main() {
uvec3 id = gl_GlobalInvocationID;
if (id.x >= uint(params.world_size) ||
@ -26,7 +37,56 @@ void main() {
id.z >= uint(params.world_size))
return;
const ivec3 SURFACE_AXIS[8] = ivec3[](
ivec3(1,0,0),
ivec3(0,1,0),
ivec3(0,0,1),
ivec3(1,0,1),
ivec3(0,1,1),
ivec3(1,1,0),
ivec3(1,1,1),
ivec3(0,0,0)
);
vec3 p = vec3(id) - params.world_size / 2 + 1;
float d = length(p) - params.threshold;
voxels.sample_points[index3(id.x, id.y, id.z)] = d;
uint idx = index3(id.x, id.y, id.z);
voxels.sample_points[idx] = d;
int previous_sign = 0;
vec3 previous_sample_point_coords = vec3(id.x, id.y, id.z);
float previous_sample_point_distance = 0.0;
vec3 intersection_points_sum = vec3(0.0, 0.0, 0.0);
uint intersection_points_count = 0u;
for (int sample_point_to_check_index = 0; sample_point_to_check_index < 8; sample_point_to_check_index++){
ivec3 sample_point_to_check = ivec3(id) + SURFACE_AXIS[sample_point_to_check_index];
// bounds check
if (sample_point_to_check.x < 0 || sample_point_to_check.y < 0 || sample_point_to_check.z < 0) continue;
if (uint(sample_point_to_check.x) >= params.world_size || uint(sample_point_to_check.y) >= params.world_size || uint(sample_point_to_check.z) >= params.world_size) continue;
uint buffer_index = index3(uint(sample_point_to_check.x), uint(sample_point_to_check.y), uint(sample_point_to_check.z));
float sample_point_distance_from_sdf = voxels.sample_points[buffer_index];
int current_sign = (sample_point_distance_from_sdf >= 0.0 ? 1 : -1);
if (previous_sign != 0 && current_sign != previous_sign) {
float t = previous_sample_point_distance / (previous_sample_point_distance - sample_point_distance_from_sdf);
vec3 intersect = mix(vec3(previous_sample_point_coords), vec3(sample_point_to_check), t);
intersection_points_sum += intersect;
intersection_points_count++;
}
previous_sign = current_sign;
previous_sample_point_distance = sample_point_distance_from_sdf;
previous_sample_point_coords = sample_point_to_check;
}
if (intersection_points_count > 0u) {
vec3 avg = intersection_points_sum / float(intersection_points_count);
store_surface_point(idx, avg);
} else {
store_surface_point(idx, vec3(0.0)); // no surface
}
}

View File

@ -20,6 +20,7 @@ var gpu_sdf
@export var clear_the_thing: bool = false
var sdf_buffer = []
var surface_points_buffer = []
var color = Color.CORAL
var surface_point_color = Color.CRIMSON
@ -33,9 +34,10 @@ func _ready() -> void:
func _process(_delta: float) -> void:
clear()
sdf_buffer = gpu_sdf.compute(world_size, RADIUS)
if !sdf_buffer.is_empty():
create_surface_points()
surface_points = gpu_sdf.compute(world_size, RADIUS)
if !surface_points.is_empty():
#create_surface_points_gpu()
#create_surface_points()
if show_surface_points:
draw_surface_points()
if show_surface:
@ -51,6 +53,9 @@ func clear():
if child is MeshInstance3D:
child.queue_free()
func get_index_from_coords(coords: Vector3i):
return coords.x + coords.y * world_size + coords.z * world_size * world_size
func draw_sample_points():
for x in range(world_size):
for y in range(world_size):
@ -60,12 +65,10 @@ func draw_sample_points():
#DebugDraw3D.draw_text(Vector3i(x, y, z), str(Vector3i(x, y, z)))
func draw_surface_points():
for x in range(world_size):
for y in range(world_size):
for z in range(world_size):
if surface_points.has(Vector3i(x, y, z)):
DebugDraw3D.draw_square(surface_points[Vector3i(x, y, z)].position , 0.3, surface_point_color)
DebugDraw3D.draw_text(surface_points[Vector3i(x, y, z)].position, str(surface_points[Vector3i(x, y, z)].position))
for surface_point in surface_points:
if surface_points[surface_point] != Vector3(0, 0, 0):
DebugDraw3D.draw_square(surface_points[surface_point] , 0.3, surface_point_color)
#DebugDraw3D.draw_text(surface_points[Vector3i(x, y, z)], str(surface_points[Vector3i(x, y, z)]))
func create_surface_points():
for x in range(world_size):

View File

@ -5,3 +5,4 @@
[node name="SmoothWorld" type="Node3D"]
script = ExtResource("1_4h467")
RADIUS = 6.789
show_surface_points = true