Godot Version
4.2.1
Question
Hi! I’m kinda new to Godot and I’m still learning, so sorry for any mistakes. I want to render 2D particles as Voronoi cells, and to do this I use a trick where each particle is an instance of a cone mesh, and the intersections between these cones generate the Voronoi graph. The problem here is that this effect is produced with depth testing, which should be enabled by default in spatial materials. Here is the effect I want to achieve.
I am using a MultiMesh to draw cones efficiently, since I plan to have thousands of particles, but they are drawn as if depth testing was disabled:
This is the shader I use per-instance:
shader_type spatial;
varying flat int id;
float map(float value, float min1, float max1, float min2, float max2) {
return min2 + (value - min1) * (max2 - min2) / (max1 - min1);
}
void vertex() {
id = INSTANCE_ID;
}
void fragment() {
float r = map(float(id), 0.0, 100.0, 0.0, 1.0);
ALBEDO = vec3(r, 1.0 - r, 0.0);
ALPHA = 1.0;
}
Then I have a node containing the MultiMeshInstance3D object, which has the following script:
extends Node3D
@export var cone_mesh: ArrayMesh
func _ready():
generate_random_particles(100)
func generate_random_particles(number: int):
for i in range(number):
var x = randf_range(-5.0, 5.0)
var y = randf_range(-5.0, 5.0)
var pos = Vector3(x, 0.0, y)
$ParticlesSpace.generate_particle(pos) # ParticlesSpace is the MultiMeshInstance3D
Finally here’s the MultiMeshInstance3D script:
extends MultiMeshInstance3D
const sh_particle = preload("res://shaders/particle.tres")
var array_mesh: ArrayMesh
var multi_mesh_inst: MultiMeshInstance3D
var multi_mesh: MultiMesh
const STARTING_INSTANCE_COUNT = 128
const MULTIMESH_GROWTH_STEP = 64
func _ready():
array_mesh = ArrayMesh.new()
var st = SurfaceTool.new()
st.begin(Mesh.PRIMITIVE_TRIANGLES)
create_cone(st, 0.5, 1.0, 16)
st.commit(array_mesh)
multi_mesh = MultiMesh.new()
multi_mesh.mesh = array_mesh
multi_mesh.transform_format = MultiMesh.TRANSFORM_3D
multi_mesh.instance_count = STARTING_INSTANCE_COUNT # We start with STARTING_INSTANCE_COUNT instances.
multi_mesh.visible_instance_count = 0 # The particles are initially invisible.
multimesh = multi_mesh
material_override = sh_particle
func generate_particle(pos: Vector3):
var part_id = multi_mesh.visible_instance_count
# Not enough particles, grow
if part_id == STARTING_INSTANCE_COUNT:
grow()
print_debug("Not enough particles. Growing. New size: ", multi_mesh.instance_count)
multi_mesh.set_instance_transform(part_id, Transform3D(Basis(), pos))
multi_mesh.visible_instance_count += 1
func grow():
multi_mesh.instance_count += MULTIMESH_GROWTH_STEP
func create_cone(st: SurfaceTool, radius: float, height: float, segments: int):
var angle_step = 2.0 * PI / segments
var top = Vector3(0, height / 2, 0) # cone tip
var bottom_center = Vector3(0, -height / 2, 0) # base center
for i in range(segments):
var rad = i * angle_step
var x = sin(rad) * radius
var z = cos(rad) * radius
var next_rad = (i + 1) * angle_step
var next_x = sin(next_rad) * radius
var next_z = cos(next_rad) * radius
var current_point = Vector3(x, -height / 2, z)
var next_point = Vector3(next_x, -height / 2, next_z)
st.add_vertex(top)
st.add_vertex(current_point)
st.add_vertex(next_point)
st.add_vertex(bottom_center)
st.add_vertex(next_point)
st.add_vertex(current_point)
# Idk if this will be needed
# but it could be useful later
st.generate_normals()
I’m using an orthogonal camera placed towards the cone tips.
Thank you in advance.