Populating a vertexBuffer with a compute shader

Until I add more demos related to compute shaders and vertex buffers, I’ll leave this short example.
The program creates a vertex buffer for 1000 vertices. Then, on every animation frame, we call a compute shader multiple times to populate the buffer, then draw it in 2D. To avoid the compute shader generating always the same result I pass a vec2 uniform containing the time. A shade style is used to modulate the color along the triangle strip. I [ab]use the vertex color alpha property to encode an increasing value. I could have used a float, but I was planning to generate colors for the strips as well. In the current iteration the colors come from a generated palette in which the hue shifts 360 degrees.

Notice that the compute shader runs in parallel, and each produced vertex depends only on its index (and the uniform). If you wanted a vertex to depend on the previous vertex then it couldn’t be calculated in parallel.

Feel free to ask questions. Maybe I even know the answer :slight_smile:

imports
import org.intellij.lang.annotations.Language
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.extensions.Screenshots
import org.openrndr.extra.color.palettes.analogous
import org.openrndr.extra.color.presets.YELLOW_GREEN
import org.openrndr.extra.color.spaces.HSLuv
import org.openrndr.extra.color.tools.shadeLuminosity
import org.openrndr.math.Vector2
import kotlin.math.pow
fun main() = application {
    configure {
        width = 1000
        height = 1000
    }
    program {
        val vb = vertexBuffer(vertexFormat {
            position(3)
            padding(4)
            color(4)
        }, 1000)

        val copies = 50

        @Language("GLSL")
        val glsl = """
            #version 450 core
            
            struct Vertex {
                vec3 position;
                vec4 color;
            };

            layout (local_size_x = 64, local_size_y = 1) in;
            layout (std430, binding=0) buffer VertexBuffer {
                Vertex verts[];
            };
            
            uniform vec2 off;
            
            void main(void) {
                uint idx = gl_GlobalInvocationID.x;
                float t = float(idx) * 0.01 + off.x;
                float k = float(idx % 2) * 0.2 * (sin(t * 5) * 0.4 + 0.6) + 1.0;
                float r = sin(t * 0.11) * 350 + 460;
                verts[idx].position.x = sin(t) * cos(t * 0.51 + sin(-t * 0.25 + off.y)) * r * k + 500.0; 
                verts[idx].position.y = cos(t) * sin(t * 0.49 + off.x * 0.05) * r * k + 500.0;
                verts[idx].color.a = float(idx + off.x);
            }
        """

        val cs2 = ComputeShader.fromCode(glsl, "Comp2")
        cs2.buffer("VertexBuffer", vb)

        val style = shadeStyle {
            fragmentTransform = "x_fill *= sin(va_color.a / $copies.0) * 0.5 + 0.5;"
        }
        val palette = ColorRGBa.YELLOW_GREEN.analogous<HSLuv>(360.0, copies).mapIndexed { i, c ->
            c.shadeLuminosity<HSLuv>((i / (copies - 1.0)).pow(2.0))
        }

        extend {
            repeat(copies) {
                cs2.uniform("off", Vector2(seconds + it, seconds * 0.33 + it / copies.toDouble()))
                cs2.execute(vb.vertexCount)
                drawer.shadeStyle = style
                drawer.fill = palette[it]
                drawer.vertexBuffer(vb, DrawPrimitive.TRIANGLE_STRIP)
            }
        }
    }
}