sample points on gpu

This commit is contained in:
Guillaume Vern 2025-11-30 22:16:33 +01:00
parent 5733a3b043
commit d0375a15dc
9 changed files with 162 additions and 56 deletions

View File

@ -4,7 +4,7 @@ var rot_y = 0
@onready var world = preload("res://world.tscn")
@export var PLAYER_SPEED = 1
@export var PLAYER_SPEED = 1.0
@export var PLAYER_REACH = 1000
@export var NOCLIP = false
@export_range(0.00001, 0.01) var LOOK_SENSITIVITY = 0.002

70
compute_test.gd Normal file
View File

@ -0,0 +1,70 @@
var rd: RenderingDevice
var shader_file: Resource
var shader_spirv: RDShaderSPIRV
var shader: RID
func create_device():
rd = RenderingServer.create_local_rendering_device()
shader_file = load("res://sdf_shader.glsl")
shader_spirv = shader_file.get_spirv()
shader = rd.shader_create_from_spirv(shader_spirv)
func compute(world_size: int, threshold: float):
# Prepare our data. We use floats in the shader, so we need 32 bit.
var total = world_size * world_size * world_size
var input := PackedFloat32Array()
input.resize(total)
var input_bytes := input.to_byte_array()
# Create a storage buffer that can hold our float values.
var buffer := rd.storage_buffer_create(input_bytes.size(), input_bytes)
# Create a uniform to assign the buffer to the rendering device
var uniform_buf := RDUniform.new()
uniform_buf.uniform_type = RenderingDevice.UNIFORM_TYPE_STORAGE_BUFFER
uniform_buf.binding = 0 # this needs to match the "binding" in our shader file
uniform_buf.add_id(buffer)
var peer := StreamPeerBuffer.new()
peer.put_32(world_size)
peer.put_float(threshold)
peer.put_32(0)
peer.put_32(0)
var bytes := peer.data_array
var params_buffer := rd.uniform_buffer_create(bytes.size(), bytes)
var uniform_params := RDUniform.new()
uniform_params.uniform_type = RenderingDevice.UNIFORM_TYPE_UNIFORM_BUFFER
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
# Create a compute pipeline
var pipeline := rd.compute_pipeline_create(shader)
var compute_list := rd.compute_list_begin()
rd.compute_list_bind_compute_pipeline(compute_list, pipeline)
rd.compute_list_bind_uniform_set(compute_list, uniform_set, 0)
var dispatch_x = int(ceil(world_size / 8.0))
var dispatch_y = int(ceil(world_size / 8.0))
var dispatch_z = int(ceil(world_size / 1.0)) # local_size_z = 1
rd.compute_list_dispatch(compute_list, dispatch_x, dispatch_y, dispatch_z)
rd.compute_list_end()
# Submit to GPU and wait for sync
rd.submit()
rd.sync()
# Read back the data from the buffer
var output_bytes := rd.buffer_get_data(buffer)
var output := output_bytes.to_float32_array()
rd.free_rid(buffer)
rd.free_rid(params_buffer)
return output

1
compute_test.gd.uid Normal file
View File

@ -0,0 +1 @@
uid://du1xgjbvpa6dk

View File

@ -1,3 +1,2 @@
@export var distance = 0
@export var position = Vector3.ZERO
@export var visible = false

29
sdf_shader.glsl Normal file
View File

@ -0,0 +1,29 @@
#[compute]
#version 450
// Workgroup size
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
// Storage buffer
layout(set = 0, binding = 0, std430) buffer DataBuffer {
float sample_points[];
} voxels;
layout(set = 0, binding = 1) uniform Params {
int world_size;
float threshold;
} params;
void main() {
uvec3 id = gl_GlobalInvocationID;
if (id.x >= uint(params.world_size) ||
id.y >= uint(params.world_size) ||
id.z >= uint(params.world_size))
return;
uint index = id.x + id.y * uint(params.world_size) + id.z * uint(params.world_size) * uint(params.world_size);
vec3 p = vec3(id);
float d = length(p) - params.threshold;
voxels.sample_points[index] = d;
}

14
sdf_shader.glsl.import Normal file
View File

@ -0,0 +1,14 @@
[remap]
importer="glsl"
type="RDShaderFile"
uid="uid://dv4s7mmqwnsqr"
path="res://.godot/imported/sdf_shader.glsl-8a6992e3be4c1416763f59c511add1b8.res"
[deps]
source_file="res://sdf_shader.glsl"
dest_files=["res://.godot/imported/sdf_shader.glsl-8a6992e3be4c1416763f59c511add1b8.res"]
[params]

View File

@ -1,12 +1,15 @@
@tool
#@tool
extends Node3D
const CENTER := Vector3.ZERO
@export_range(0.0, 6.9999) var RADIUS: float = 5.0
@export var RADIUS: float = 5.0
var sample_points = {}
var SamplePoint = preload("res://sample_point.gd")
var surface_points = {}
var SurfacePoint = preload("res://surface_point.gd")
var ComputeSdf = preload("res://compute_test.gd")
var gpu_sdf
@export var world_size = 16
@export var show_sample_points = false
@ -16,6 +19,8 @@ var SurfacePoint = preload("res://surface_point.gd")
@export var do_the_thing: bool = false
@export var clear_the_thing: bool = false
var sdf_buffer = []
var color = Color.CORAL
var surface_point_color = Color.CRIMSON
@ -23,17 +28,21 @@ var mesh
var surface_tool: SurfaceTool = SurfaceTool.new()
func _ready() -> void:
initialize_sample_points()
gpu_sdf = ComputeSdf.new()
gpu_sdf.create_device()
#initialize_sample_points()
func _process(_delta: float) -> void:
clear()
create_surface_points()
if show_surface_points:
draw_surface_points()
if show_surface:
create_surface_mesh(world_size)
if show_sample_points:
draw_sample_points()
sdf_buffer = gpu_sdf.compute(world_size * 2, RADIUS)
if !sdf_buffer.is_empty():
create_surface_points()
if show_surface_points:
draw_surface_points()
if show_surface:
create_surface_mesh(world_size)
if show_sample_points:
draw_sample_points()
func clear():
@ -47,28 +56,21 @@ func draw_sample_points():
for x in range(-world_size + 1, world_size):
for y in range(-world_size + 1, world_size):
for z in range(-world_size + 1, world_size):
DebugDraw3D.draw_square(sample_points[Vector3i(x, y, z)].position, 0.2, color)
if sample_points.has(Vector3i(x, y, z)):
DebugDraw3D.draw_square(Vector3i(x, y, z), 0.2, color)
#DebugDraw3D.draw_text(Vector3i(x, y, z), str(Vector3i(x, y, z)))
func draw_surface_points():
for x in range(-world_size + 1, world_size):
for y in range(-world_size + 1, world_size):
for z in range(-world_size + 1, 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_square(Vector3i(x, y, z), 0.3, surface_point_color)
func get_sample_value(index: Vector3i) -> float:
return CENTER.distance_to(index) - RADIUS
func initialize_sample_points():
for x in range(-world_size + 1, world_size):
for y in range(-world_size + 1, world_size):
for z in range(-world_size + 1, world_size):
var sample_point = SamplePoint.new()
sample_point.distance = get_sample_value(Vector3i(x, y, z))
sample_point.position = Vector3(float(x), float(y), float(z))
sample_point.visible = show_sample_points
sample_points[Vector3i(x, y, z)] = sample_point
sample_points = gpu_sdf.compute(world_size, RADIUS)
print("sample_points initialized", sample_points)
func create_surface_points():
for x in range(-world_size + 1, world_size):
@ -84,19 +86,24 @@ func create_surface_point(voxel: Vector3i):
var intersection_points_number = Vector3.ZERO
for sample_point_to_check_index in range(SURFACE_AXIS.size()):
var sample_point_to_check = voxel + SURFACE_AXIS[sample_point_to_check_index]
var sample_point_distance_from_sdf = get_sample_value(sample_point_to_check)
if previous_sign != 0 && sign(sample_point_distance_from_sdf) != previous_sign:
var intersection_point = (1-previous_sample_point_distance_from_sdf/(previous_sample_point_distance_from_sdf - sample_point_distance_from_sdf))*previous_sample_point+(previous_sample_point_distance_from_sdf/(previous_sample_point_distance_from_sdf-sample_point_distance_from_sdf)*sample_point_to_check)
intersection_points_sum += intersection_point
intersection_points_number += Vector3.ONE
previous_sign = sign(sample_point_distance_from_sdf)
previous_sample_point = sample_point_to_check
previous_sample_point_distance_from_sdf = sample_point_distance_from_sdf
var normal_x = voxel.x + world_size - 1
var normal_y = voxel.y + world_size - 1
var normal_z = voxel.z + world_size - 1
var buffer_index = normal_x + normal_y * world_size + normal_z * world_size * world_size
if buffer_index < sdf_buffer.size():
var sample_point_distance_from_sdf = sdf_buffer[buffer_index]#get_sample_value(sample_point_to_check)
sample_points[voxel] = sample_point_distance_from_sdf
if previous_sign != 0 && sign(sample_point_distance_from_sdf) != previous_sign:
var intersection_point = (1-previous_sample_point_distance_from_sdf/(previous_sample_point_distance_from_sdf - sample_point_distance_from_sdf))*previous_sample_point+(previous_sample_point_distance_from_sdf/(previous_sample_point_distance_from_sdf-sample_point_distance_from_sdf)*sample_point_to_check)
intersection_points_sum += intersection_point
intersection_points_number += Vector3.ONE
previous_sign = sign(sample_point_distance_from_sdf)
previous_sample_point = sample_point_to_check
previous_sample_point_distance_from_sdf = sample_point_distance_from_sdf
if intersection_points_sum != Vector3.ZERO && intersection_points_number != Vector3.ZERO:
var intersection_points_average = intersection_points_sum/intersection_points_number
var surface_point = SurfacePoint.new()
surface_point.position = intersection_points_average
surface_point.visible = show_surface_points
surface_points[voxel] = surface_point
func calculate_surface_point_position(voxel: Vector3i):
@ -109,7 +116,8 @@ func create_surface_mesh(size: int = 6):
for x in range(-size, size):
for y in range(-size, size):
for z in range(-size, size):
create_surface_mesh_quad(Vector3i(x,y,z));
if surface_points.has(Vector3i(x, y, z)):
create_surface_mesh_quad(Vector3i(x,y,z));
mesh = surface_tool.commit()
var meshinstance = MeshInstance3D.new()
meshinstance.mesh = mesh
@ -119,8 +127,8 @@ func create_surface_mesh_quad(index: Vector3i):
for axis_index in range(AXIS.size()):
var axis = AXIS[axis_index]
var sample_value1 = get_sample_value(index)
var sample_value2 = get_sample_value(index + axis)
var sample_value1 = sample_points[index]
var sample_value2 = sample_points[index + axis]
if sample_value1 < 0 and sample_value2 >= 0:
add_quad(index, axis_index)
@ -147,14 +155,6 @@ const SURFACE_AXIS := [
func add_quad(index: Vector3i, axis_index: int):
var points = get_quad_points(index, axis_index)
var normal = Vector3(
(get_sample_value(index + SURFACE_AXIS[0]) - get_sample_value(index + SURFACE_AXIS[7])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[1])) + (get_sample_value(index + SURFACE_AXIS[3]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[6]) - get_sample_value(index + SURFACE_AXIS[4])),
(get_sample_value(index + SURFACE_AXIS[1]) - get_sample_value(index + SURFACE_AXIS[7])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[0])) + (get_sample_value(index + SURFACE_AXIS[4]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[6]) - get_sample_value(index + SURFACE_AXIS[3])),
(get_sample_value(index + SURFACE_AXIS[7]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[0]) - get_sample_value(index + SURFACE_AXIS[3])) + (get_sample_value(index + SURFACE_AXIS[1]) - get_sample_value(index + SURFACE_AXIS[4])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[6]))
)
surface_tool.set_normal(normal)
surface_tool.add_vertex(points[0])
surface_tool.add_vertex(points[1])
surface_tool.add_vertex(points[2])
@ -162,18 +162,11 @@ func add_quad(index: Vector3i, axis_index: int):
surface_tool.add_vertex(points[0])
surface_tool.add_vertex(points[2])
surface_tool.add_vertex(points[3])
surface_tool.generate_normals()
func add_reversed_quad(index: Vector3i, axis_index: int):
var points = get_quad_points(index, axis_index)
var normal = Vector3(
(get_sample_value(index + SURFACE_AXIS[0]) - get_sample_value(index + SURFACE_AXIS[7])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[1])) + (get_sample_value(index + SURFACE_AXIS[3]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[6]) - get_sample_value(index + SURFACE_AXIS[4])),
(get_sample_value(index + SURFACE_AXIS[1]) - get_sample_value(index + SURFACE_AXIS[7])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[0])) + (get_sample_value(index + SURFACE_AXIS[4]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[6]) - get_sample_value(index + SURFACE_AXIS[3])),
(get_sample_value(index + SURFACE_AXIS[7]) - get_sample_value(index + SURFACE_AXIS[2])) + (get_sample_value(index + SURFACE_AXIS[0]) - get_sample_value(index + SURFACE_AXIS[3])) + (get_sample_value(index + SURFACE_AXIS[1]) - get_sample_value(index + SURFACE_AXIS[4])) + (get_sample_value(index + SURFACE_AXIS[5]) - get_sample_value(index + SURFACE_AXIS[6]))
)
surface_tool.set_normal(normal)
surface_tool.add_vertex(points[0])
surface_tool.add_vertex(points[2])
surface_tool.add_vertex(points[1])
@ -181,6 +174,7 @@ func add_reversed_quad(index: Vector3i, axis_index: int):
surface_tool.add_vertex(points[0])
surface_tool.add_vertex(points[3])
surface_tool.add_vertex(points[2])
surface_tool.generate_normals()
func get_quad_points(index: Vector3i, axis_index: int):
return [

View File

@ -4,8 +4,6 @@
[node name="SmoothWorld" type="Node3D"]
script = ExtResource("1_4h467")
RADIUS = 3.971
world_size = 8
RADIUS = 2.059
show_sample_points = true
show_surface_points = true
do_the_thing = true

View File

@ -29,6 +29,7 @@ shadow_enabled = true
[node name="Player" parent="." instance=ExtResource("3_036b0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.182447, 0.6673207, 0.0726881)
PLAYER_SPEED = 0.305
NOCLIP = true
[connection signal="change_world" from="." to="." method="_on_change_world"]