smoooooth

This commit is contained in:
Guillaume Vern 2026-02-12 21:22:17 +01:00
parent 8901011b81
commit 3b94b0d335
7 changed files with 50 additions and 38 deletions

View File

@ -1,4 +1,4 @@
extends Node3D
var rd: RenderingDevice var rd: RenderingDevice
var shader_file1: Resource var shader_file1: Resource
var shader_file2: Resource var shader_file2: Resource
@ -7,6 +7,8 @@ var shader_spirv2: RDShaderSPIRV
var shader_pass1: RID var shader_pass1: RID
var shader_pass2: RID var shader_pass2: RID
var start_time := Time.get_ticks_msec() / 1000.0 var start_time := Time.get_ticks_msec() / 1000.0
var color = Color.CORAL
@export var iout_surface_points = []
func create_device(): func create_device():
rd = RenderingServer.create_local_rendering_device() rd = RenderingServer.create_local_rendering_device()
@ -142,6 +144,7 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh:
var out_norms = rd.buffer_get_data(normal_buffer).to_float32_array() var out_norms = rd.buffer_get_data(normal_buffer).to_float32_array()
var out_indices = rd.buffer_get_data(idx_buffer).to_int32_array() var out_indices = rd.buffer_get_data(idx_buffer).to_int32_array()
var final_count = rd.buffer_get_data(counter_buffer).to_int32_array()[0] var final_count = rd.buffer_get_data(counter_buffer).to_int32_array()[0]
var out_surface_points = rd.buffer_get_data(surface_buffer).to_float32_array()
rd.free_rid(buffer) rd.free_rid(buffer)
rd.free_rid(params_buffer) rd.free_rid(params_buffer)
@ -185,7 +188,7 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh:
packed_indices.append(active_map[old_idx]) packed_indices.append(active_map[old_idx])
iout_surface_points = out_surface_points
if packed_verts.size() > 0: if packed_verts.size() > 0:
arrays[Mesh.ARRAY_VERTEX] = packed_verts arrays[Mesh.ARRAY_VERTEX] = packed_verts
arrays[Mesh.ARRAY_NORMAL] = packed_normals arrays[Mesh.ARRAY_NORMAL] = packed_normals
@ -193,7 +196,6 @@ func compute_mesh(world_size: int, threshold: float) -> ArrayMesh:
mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays) mesh.add_surface_from_arrays(Mesh.PRIMITIVE_TRIANGLES, arrays)
return mesh return mesh
func build_sample_dict(world_size: int, flat_buffer: PackedFloat32Array) -> Dictionary: func build_sample_dict(world_size: int, flat_buffer: PackedFloat32Array) -> Dictionary:
var dict := {} var dict := {}

View File

@ -4,6 +4,7 @@
constant = Color(0.30202293, 0.6060653, 0.9109474, 1) constant = Color(0.30202293, 0.6060653, 0.9109474, 1)
[resource] [resource]
modes/cull = 2
nodes/vertex/0/position = Vector2(360, 220) nodes/vertex/0/position = Vector2(360, 220)
nodes/fragment/0/position = Vector2(660, 140) nodes/fragment/0/position = Vector2(660, 140)
nodes/fragment/2/node = SubResource("VisualShaderNodeColorConstant_sxi40") nodes/fragment/2/node = SubResource("VisualShaderNodeColorConstant_sxi40")

View File

@ -17,20 +17,21 @@ bool has_vertex(ivec3 p) {
// Boundary check for the voxel itself // Boundary check for the voxel itself
if (any(lessThan(p, ivec3(0))) || any(greaterThanEqual(p, ivec3(params.world_size)))) return false; if (any(lessThan(p, ivec3(0))) || any(greaterThanEqual(p, ivec3(params.world_size)))) return false;
uint idx = index3(uint(p.x), uint(p.y), uint(p.z)); uint idx = index3(uint(p.x), uint(p.y), uint(p.z));
return surface.surface_points[idx * 3u] > -0.5; return surface.surface_points[idx * 3u] > - 0.5;
} }
// Translated QUAD_POINTS from your GDScript // Translated QUAD_POINTS from your GDScript
const ivec3 QUAD_OFFSETS[3][4] = ivec3[3][4]( const ivec3 QUAD_OFFSETS[3][4] = ivec3[3][4](
// X-Axis Edges // X-Axis Edges
ivec3[](ivec3(0,0,-1), ivec3(0,-1,-1), ivec3(0,-1,0), ivec3(0,0,0)), ivec3[](ivec3(0,0,-1), ivec3(0,-1,-1), ivec3(0,-1,0), ivec3(0,0,0)),
// Y-Axis Edges // Y-Axis Edges
ivec3[](ivec3(0,0,-1), ivec3(0,0,0), ivec3(-1,0,0), ivec3(-1,0,-1)), ivec3[](ivec3(0,0,-1), ivec3(0,0,0), ivec3(-1,0,0), ivec3(-1,0,-1)),
// Z-Axis Edges // Z-Axis Edges
ivec3[](ivec3(0,0,0), ivec3(0,-1,0), ivec3(-1,-1,0), ivec3(-1,0,0)) ivec3[](ivec3(0,0,0), ivec3(0,-1,0), ivec3(-1,-1,0), ivec3(-1,0,0))
); );
void main() { void main() {
memoryBarrierBuffer();
ivec3 id = ivec3(gl_GlobalInvocationID); ivec3 id = ivec3(gl_GlobalInvocationID);
if (id.x >= params.world_size || id.y >= params.world_size || id.z >= params.world_size) return; if (id.x >= params.world_size || id.y >= params.world_size || id.z >= params.world_size) return;
@ -42,20 +43,17 @@ void main() {
for (int i = 0; i < 3; i++) { for (int i = 0; i < 3; i++) {
ivec3 neighbor_id = id + AXIS[i]; ivec3 neighbor_id = id + AXIS[i];
// Ensure neighbor is in bounds before sampling noise if (any(greaterThan(neighbor_id, ivec3(params.world_size)))) continue;
if (any(greaterThanEqual(neighbor_id, ivec3(params.world_size)))) continue;
float d1 = voxels.sample_points[idx]; float d1 = voxels.sample_points[idx];
float d2 = voxels.sample_points[index3(neighbor_id.x, neighbor_id.y, neighbor_id.z)]; float d2 = voxels.sample_points[index3(neighbor_id.x, neighbor_id.y, neighbor_id.z)];
// Sign change check (Crossing detection)
if ((d1 < 0.0) != (d2 < 0.0)) { if ((d1 < 0.0) != (d2 < 0.0)) {
ivec3 p0 = id + QUAD_OFFSETS[i][0]; ivec3 p0 = id + QUAD_OFFSETS[i][0];
ivec3 p1 = id + QUAD_OFFSETS[i][1]; ivec3 p1 = id + QUAD_OFFSETS[i][1];
ivec3 p2 = id + QUAD_OFFSETS[i][2]; ivec3 p2 = id + QUAD_OFFSETS[i][2];
ivec3 p3 = id + QUAD_OFFSETS[i][3]; ivec3 p3 = id + QUAD_OFFSETS[i][3];
// Verify all 4 surrounding voxels have valid vertices
if (has_vertex(p0) && has_vertex(p1) && has_vertex(p2) && has_vertex(p3)) { if (has_vertex(p0) && has_vertex(p1) && has_vertex(p2) && has_vertex(p3)) {
uint v0 = index3(p0.x, p0.y, p0.z); uint v0 = index3(p0.x, p0.y, p0.z);
uint v1 = index3(p1.x, p1.y, p1.z); uint v1 = index3(p1.x, p1.y, p1.z);
@ -64,7 +62,6 @@ void main() {
uint start = atomicAdd(index_count.count, 6); uint start = atomicAdd(index_count.count, 6);
// Use the same winding logic as your add_quad / add_reversed_quad
if (d1 < 0.0) { if (d1 < 0.0) {
mesh_indices.indices[start + 0] = v0; mesh_indices.indices[start + 0] = v0;
mesh_indices.indices[start + 1] = v1; mesh_indices.indices[start + 1] = v1;

View File

@ -181,7 +181,7 @@ const ivec3 CORNERS[8] = ivec3[](
); );
float get_noise_at(vec3 p) { float get_noise_at(vec3 p) {
return snoise((p / 10.0) + vec3(params.u_time * .5)) * 5.0; return snoise((p / 20.0) + vec3(params.u_time * .5));
} }
// Calculate normal using central difference // Calculate normal using central difference
@ -193,19 +193,21 @@ vec3 calculate_normal(vec3 p) {
return normalize(vec3(dx, dy, dz)); return normalize(vec3(dx, dy, dz));
} }
void main() { vec3 grid_to_world(uvec3 grid_id) {
uvec3 id = gl_GlobalInvocationID; return (vec3(grid_id) - params.world_size / 2.0) * 0.5;
if (id.x >= uint(params.world_size) || }
id.y >= uint(params.world_size) ||
id.z >= uint(params.world_size))
return;
vec3 p = vec3(id) - params.world_size / 2; void main() {
ivec3 id = ivec3(gl_GlobalInvocationID);
if (any(greaterThanEqual(id, uvec3(params.world_size)))) return;
vec3 p = grid_to_world(id);
uint idx = index3(id.x, id.y, id.z); uint idx = index3(id.x, id.y, id.z);
voxels.sample_points[idx] = get_noise_at(p); voxels.sample_points[idx] = get_noise_at(p);
barrier();
memoryBarrierBuffer(); memoryBarrierBuffer();
barrier();
vec3 intersection_sum = vec3(0.0); vec3 intersection_sum = vec3(0.0);
uint count = 0; uint count = 0;
@ -223,11 +225,11 @@ void main() {
if (any(greaterThanEqual(c1, uvec3(params.world_size))) || if (any(greaterThanEqual(c1, uvec3(params.world_size))) ||
any(greaterThanEqual(c2, uvec3(params.world_size)))) continue; any(greaterThanEqual(c2, uvec3(params.world_size)))) continue;
float d1 = voxels.sample_points[index3(c1.x, c1.y, c1.z)]; float d1 = get_noise_at(grid_to_world(c1));
float d2 = voxels.sample_points[index3(c2.x, c2.y, c2.z)]; float d2 = get_noise_at(grid_to_world(c2));
// Standard sign-change test // Standard sign-change test
if ((d1 < 0.0) != (d2 < 0.0)) { if ((d1 < 0.0) != (d2 < 0.0) && d1 != -d2) {
// Linear interpolation to find the exact crossing point on the edge // Linear interpolation to find the exact crossing point on the edge
float t = d1 / (d1 - d2); float t = d1 / (d1 - d2);
intersection_sum += mix(vec3(c1), vec3(c2), t); intersection_sum += mix(vec3(c1), vec3(c2), t);
@ -240,7 +242,7 @@ void main() {
store_surface_point(idx, avg_pos); store_surface_point(idx, avg_pos);
// Normals should be calculated at the exact averaged surface point // Normals should be calculated at the exact averaged surface point
vec3 world_p = avg_pos - params.world_size / 2.0; vec3 world_p = (avg_pos - params.world_size / 2.0) * vec3(0.5);
vec3 n = calculate_normal(world_p); vec3 n = calculate_normal(world_p);
uint base = idx * 3u; uint base = idx * 3u;
normal.normals[base + 0] = n.x; normal.normals[base + 0] = n.x;

View File

@ -53,11 +53,21 @@ func _process(_delta: float) -> void:
#regenerate_mesh = false #regenerate_mesh = false
clear() clear()
meshinstance.mesh = gpu_sdf.compute_mesh(world_size, RADIUS) meshinstance.mesh = gpu_sdf.compute_mesh(world_size, RADIUS)
if !surface_points.is_empty(): if show_surface_points:
if show_surface_points: var idx = 0
draw_surface_points() var text_id = 0
if show_sample_points: while idx < gpu_sdf.iout_surface_points.size():
draw_sample_points() text_id += 1
var value = Vector3(gpu_sdf.iout_surface_points.get(idx), gpu_sdf.iout_surface_points.get(idx+1), gpu_sdf.iout_surface_points.get(idx+2)) - Vector3(world_size / 2, world_size / 2, world_size / 2)
if gpu_sdf.iout_surface_points.get(idx) != -1.0:
DebugDraw3D.draw_square(value, 0.2, color.from_rgba8(255, 128, 128, 255))
if text_id % 3 == 0:
DebugDraw3D.draw_text(value, str(value), 35)
idx += 3
else:
idx += 1
if show_sample_points:
draw_sample_points()
func clear(): func clear():

View File

@ -10,7 +10,7 @@ shader = ExtResource("2_an62b")
[node name="SmoothWorld" type="Node3D" unique_id=1592820568] [node name="SmoothWorld" type="Node3D" unique_id=1592820568]
script = ExtResource("1_4h467") script = ExtResource("1_4h467")
RADIUS = 18.524 RADIUS = 18.524
world_size = 64 world_size = 53
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1739626442] [node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1739626442]
material_override = SubResource("ShaderMaterial_iutkt") material_override = SubResource("ShaderMaterial_iutkt")

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=6 format=3 uid="uid://dls7ggmf46ia0"] [gd_scene format=3 uid="uid://dls7ggmf46ia0"]
[ext_resource type="Script" uid="uid://caefudn8tgqpr" path="res://activation.gd" id="1_tlwt5"] [ext_resource type="Script" uid="uid://caefudn8tgqpr" path="res://activation.gd" id="1_tlwt5"]
[ext_resource type="PackedScene" uid="uid://chbs3naovk63w" path="res://player.tscn" id="3_036b0"] [ext_resource type="PackedScene" uid="uid://chbs3naovk63w" path="res://player.tscn" id="3_036b0"]
@ -16,20 +16,20 @@ sky = SubResource("Sky_tlwt5")
tonemap_mode = 2 tonemap_mode = 2
glow_enabled = true glow_enabled = true
[node name="Root" type="Node3D"] [node name="Root" type="Node3D" unique_id=1762688074]
script = ExtResource("1_tlwt5") script = ExtResource("1_tlwt5")
wanted_world = 1 wanted_world = 1
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="." unique_id=340320928]
environment = SubResource("Environment_aqk2v") environment = SubResource("Environment_aqk2v")
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] [node name="DirectionalLight3D" type="DirectionalLight3D" parent="." unique_id=1883705458]
transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0) transform = Transform3D(-0.866023, -0.433016, 0.250001, 0, 0.499998, 0.866027, -0.500003, 0.749999, -0.43301, 0, 0, 0)
shadow_enabled = true shadow_enabled = true
[node name="Player" parent="." instance=ExtResource("3_036b0")] [node name="Player" parent="." unique_id=1950519856 instance=ExtResource("3_036b0")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.182447, 0.6673207, 0.0726881) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9.182447, 0.6673207, 0.0726881)
PLAYER_SPEED = 0.305 PLAYER_SPEED = 0.035
NOCLIP = true NOCLIP = true
[connection signal="change_world" from="." to="." method="_on_change_world"] [connection signal="change_world" from="." to="." method="_on_change_world"]