godot-demo-projects/misc/compute_shader_heightmap/compute_shader.glsl

62 lines
2.8 KiB
GLSL

#[compute]
#version 460
// Instruct the GPU to use 8x8x1 = 64 local invocations per workgroup.
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
// Prepare memory for the image, which will be both read and written to
// `restrict` is used to tell the compiler that the memory will only be accessed
// by the `heightmap` variable.
layout(r8, binding = 0) restrict uniform image2D heightmap;
// `readonly` is used to tell the compiler that we will not write to this memory.
// This allows the compiler to make some optimizations it couldn't otherwise.
layout(rgba8, binding = 1) restrict readonly uniform image2D gradient;
// This function is the GPU counterpart of `compute_island_cpu()` in `main.gd`.
void main() {
// Grab the current pixel's position from the ID of this specific invocation ("thread").
ivec2 coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 dimensions = imageSize(heightmap);
// Calculate the center of the image.
// Because we are working with integers ('round numbers') here,
// the result will be floored to an integer.
ivec2 center = dimensions / 2;
// Calculate the smallest distance from center to edge.
int smallest_radius = min(center.x, center.y);
// Calculate the distance from the center of the image to the current pixel.
float dist = distance(coords, center);
// Retrieve the range of the gradient image.
int gradient_max_x = imageSize(gradient).x - 1;
// Calculate the gradient index based on the distance from the center.
// `mix()` functions similarly to `lerp()` in GDScript.
int gradient_x = int(mix(0.0, float(gradient_max_x), dist / float(smallest_radius)));
// Retrieve the gradient value at the calculated position.
ivec2 gradient_pos = ivec2(gradient_x, 0);
vec4 gradient_color = imageLoad(gradient, gradient_pos);
// Even though the image format only has the red channel,
// this will still return a vec4: `vec4(red, 0.0, 0.0, 1.0)`
vec4 pixel = imageLoad(heightmap, coords);
// Multiply the pixel's red channel by the gradient's red channel
// (or any RGB channel, they're all the same except for alpha).
pixel.r *= gradient_color.r;
// If the pixel is below a certain threshold, this sets it to 0.0.
// The `step()` function is like `clamp()`, but it returns 0.0 if the value is
// below the threshold, or 1.0 if it is above.
//
// This is why we multiply it by the pixel's value again: to get the original
// value back if it is above the threshold. This shorthand replaces an `if`
// statement, which would cause branching and thus potentially slow down the
// shader.
pixel.r = step(0.2, pixel.r) * pixel.r;
// Store the pixel back into the image.
// WARNING: make sure you are writing to the same coordinate that you read from.
// If you don't, you may end up writing to a pixel, before that pixel is read
// by a different invocation and cause errors.
imageStore(heightmap, coords, pixel);
}