Godot Version
4.7
Question
I have made a compute shader that computes a heightfield for a mesh in a compute shader to mimic the result of a vertex displacement vertex shader so that I can use the output as a collision model - an attempt to render to vertex buffer. Unfortunately the output says “cannot create uniform set” … here is the code …
func generate_physics_shape(height_map:Image,
splat_map:Image,
tex1:Image,
tex2:Image,
grid_coord:Vector2,
uv_scale:Vector4,
splat_col:int,
HEIGHT_SCALE:float,
mesh_faces:PackedVector3Array):
compute.load_shader("compute_height", "res://assets/compute/compute_physics_model.glsl")
var image = height_map
if image.is_compressed():
image.decompress()
image.convert(Image.FORMAT_RF)
var height_texture = ImageTexture.create_from_image(image)
var source_size = 4096
var splat_image = splat_map
if splat_image.is_compressed():
splat_image.decompress()
splat_image.convert(Image.FORMAT_RGBA8)
var tex1_image = tex1
if tex1_image.is_compressed():
tex1_image.decompress()
tex1_image.convert(Image.FORMAT_RGBA8)
var tex2_image = tex2
if tex2_image.is_compressed():
tex2_image.decompress()
tex2_image.convert(Image.FORMAT_RGBA8)
compute.register_sampler("height_tex", 0, source_size, source_size, image.get_data(), RenderingDevice.DATA_FORMAT_R32_SFLOAT)
compute.register_sampler("splat_tex", 1, source_size, source_size, splat_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)
compute.register_sampler("tex1", 2, source_size, source_size, tex1_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)
compute.register_sampler("tex3", 3, source_size, source_size, tex2_image.get_data(), RenderingDevice.DATA_FORMAT_R8G8B8A8_UNORM)
var mesh_compute_byte_data:PackedByteArray = mesh_faces.to_byte_array()
compute.register_storage_buffer(
"Faces", # Name: used to identify the buffer later
4, # Binding: this is the binding point in the shader
mesh_compute_byte_data.size(), # Size: since we're passing the data, we don't need to specify the size
mesh_compute_byte_data
)
var params = PackedFloat32Array([grid_coord.x, grid_coord.y,
uv_scale.x, uv_scale.y, uv_scale.z, uv_scale.w,
splat_col,
HEIGHT_SCALE])
compute.register_storage_buffer("Params", 5, 0, params.to_byte_array())
compute.register_storage_buffer("OutputFaces", 6,mesh_compute_byte_data.size(),[] )
# FIRST PASS, Find min and max height values
compute.execute("compute_height", 8, 8)
compute.sync()
var final_mesh_data = compute.fetch_buffer("OutputFaces")
var float_data = final_mesh_data.to_vector3_array()
print(float_data)
The mesh_data is created as follows
func get_mesh_faces(mesh:Mesh)->Dictionary:
var result := {
"faces": []
}
var mdt := MeshDataTool.new()
for surface in mesh.get_surface_count():
var err := mdt.create_from_surface(mesh, surface)
if err != OK:
print("Failed to read surface %d" % surface)
continue
# Extract indices (faces are always triangles)
var face_count := mdt.get_face_count()
for f in face_count:
var i1 := mdt.get_face_vertex(f, 0)
var i2 := mdt.get_face_vertex(f, 1)
var i3 := mdt.get_face_vertex(f, 2)
result["faces"].append(mdt.get_vertex(i1))
result["faces"].append(mdt.get_vertex(i2))
result["faces"].append(mdt.get_vertex(i3))
mdt.clear()
return result
Compute Shader:
#[compute]
#version 450
layout(local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
layout(set = 0, binding = 0) uniform sampler2D height_tex;
layout(set = 0, binding = 1) uniform sampler2D splat_tex;
layout(set = 0, binding = 2) uniform sampler2D tex1;
layout(set = 0, binding = 3) uniform sampler2D tex3;
layout(std140,set = 0, binding = 4) readonly buffer Faces{
vec3 data[];
} faces;
layout(set = 0, binding = 5, std430) restrict readonly buffer Params {
vec2 grid_coord;
vec4 uv_scale;
float splat_col;
float HEIGHT_SCALE;
}params;
layout(std140,set = 0, binding = 6) writeonly buffer OutputFaces{
vec3 data[];
} outputfaces;
void main() {
uint index = gl_GlobalInvocationID.x + gl_GlobalInvocationID.y * 127;
if( index < (127 * 127) ) {
vec3 VERTEX = faces.data[index];
float world_pos_x = params.grid_coord.x + VERTEX.x;
float world_pos_z = params.grid_coord.y + VERTEX.z;
vec2 height_UV = vec2( world_pos_x / 4096.0, world_pos_z / 4096.0 );
vec2 height_UV0 = vec2( (world_pos_x - 1.0) / 4096.0, (world_pos_z ) / 4096.0 );
vec2 height_UV1 = vec2( (world_pos_x + 1.0) / 4096.0, (world_pos_z ) / 4096.0 );
vec2 height_UV2 = vec2( (world_pos_x ) / 4096.0, (world_pos_z - 1.0) / 4096.0 );
vec2 height_UV3 = vec2( (world_pos_x ) / 4096.0, (world_pos_z + 1.0) / 4096.0 );
vec2 bump_UV = vec2( world_pos_x /127.0, world_pos_z /127.0 );
float H0 = params.HEIGHT_SCALE*texture( height_tex, height_UV0 ).r;
float H1 = params.HEIGHT_SCALE*texture( height_tex, height_UV1 ).r;
float H2 = params.HEIGHT_SCALE*texture( height_tex, height_UV2 ).r;
float H3 = params.HEIGHT_SCALE*texture( height_tex, height_UV3 ).r;
vec3 NORMAL = -normalize(vec3(2.0*(H1 - H0), -4.0, 2.0*(H3-H2) ));
VERTEX.y += params.HEIGHT_SCALE * texture( height_tex, height_UV ).r;
float h1 =texture( tex1, bump_UV * params.uv_scale.x ).a;
float h3 =texture( tex3, bump_UV * params.uv_scale.z ).a;
vec4 splat_map = texture(splat_tex, height_UV );
float splat_color = 0.0;
if (params.splat_col == 0.0){
splat_color = splat_map.r;
}
else if (params.splat_col == 1.0){
splat_color = splat_map.g;
}
else if (params.splat_col == 2.0){
splat_color = splat_map.b;
}
else {
splat_color = splat_map.a;
}
float hval = (splat_color* h1 + (1.0-splat_color)* h3);
VERTEX += 5.0 * hval * NORMAL;// - 2.5 * NORMAL;
outputfaces.data[index] = VERTEX;
}
}
Any help is appreciated.