Drawing per-vertex colored meshes and variable thickness lines

I just wrote a simple program drawing 300 triangles with unique colors in each vertex, the shader doing color interpolation. I like gradients :slight_smile:

imports
import aBeLibs.geometry.randomPoint
import org.openrndr.application
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.shadeStyle
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.extra.noise.Random
// openrndr 0.4
fun main() = application {
    configure {
        width = 800
        height = 800
    }
    program {
        // create a buffer and specify it's format and size.
        val geometry = vertexBuffer(vertexFormat {
            position(3)
            color(4)
        }, 3 * 100)

        // create an area with some padding around the edges
        val area = drawer.bounds.offsetEdges(-50.0)
        // populate the vertex buffer.
        geometry.put {
            for (i in 0 until geometry.vertexCount) {
                write(area.randomPoint().vector3(z = 0.0))
                write(Random.vector4(0.0, 1.0))
            }
        }
        extend {
            // shader using the color attributes from our buffer
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = "x_fill = va_color;"
            }
            drawer.vertexBuffer(geometry, DrawPrimitive.TRIANGLES)
        }
    }
}

/**
 * Returns a random point inside a rectangle
 */

fun Rectangle.randomPoint(): Vector2 {
    return Vector2.uniform(this.corner, this.corner + this.dimensions)
}
1 Like

The code above was a preparation for this program, which converts an open animated shapeContour into a mesh (a triangle strip) with variable width and unique colors per vertex.

imports
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.shadeStyle
import org.openrndr.draw.vertexBuffer
import org.openrndr.draw.vertexFormat
import org.openrndr.extensions.Screenshots
import org.openrndr.extra.noise.Random
import org.openrndr.math.Polar
import org.openrndr.shape.ShapeContour
fun main() = application {
    configure {
        width = 800
        height = 800
    }
    program {
        val geometry = vertexBuffer(vertexFormat {
            position(3)
            color(4)
        }, 100)

        extend(Screenshots())
        extend {
            drawer.clear(ColorRGBa.WHITE)
            drawer.shadeStyle = shadeStyle {
                fragmentTransform = "x_fill = va_color;"
            }
            val line = ShapeContour.fromPoints(List(geometry.vertexCount / 2) {
                val theta = it * 10.0 + seconds * 20
                val radius = 200.0 + 80 * Random.perlin(theta * 0.01, 1.0)
                Polar(theta, radius).cartesian + drawer.bounds.center
            }, false)
            
            val points = geometry.vertexCount / 2
            geometry.put {
                for (i in 0 until points) {
                    val pc = i / (points - 1.0)
                    val color = ColorRGBa.GRAY.mix(ColorRGBa.PINK, pc).toVector4()
                    val pos = line.position(pc)
                    val normal = line.normal(pc).normalized *
                            (40 + 20 * Random.perlin(pos * 0.01))
                    write((pos + normal).vector3(z = 0.0))
                    write(color)
                    write((pos - normal).vector3(z = 0.0))
                    write(color)
                }
            }
            drawer.vertexBuffer(geometry, DrawPrimitive.TRIANGLE_STRIP)
        }
    }
}

1 Like

Thickline

A class to draw variable width lines

apps4.WigglyLine2-2022-01-29-21.06.05

imports
import org.openrndr.KEY_ESCAPE
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.*
import org.openrndr.math.Vector2
import org.openrndr.shape.ShapeContour
import org.openrndr.extra.videoprofiles.gif
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
fun main() = application {
    configure {
        width = 400
        height = 400
    }
    program {
        val thickLine = ThickLine(
            200, ColorRGBa.WHITE, ColorRGBa.GRAY
        ) { t -> 10.0 - 10.0 * cos(t * PI * 2) }

        // extend(ScreenRecorder()) { gif() }

        extend {
            drawer.clear(ColorRGBa.WHITE)

            val thinLine = ShapeContour.fromPoints(List(50) {
                val a = seconds * 2 + it * 0.08
                Vector2(
                    sin(a + sin(a * 0.31)),
                    sin(a * 0.53 + sin(a * 0.73))
                ) * 160.0 + drawer.bounds.center
            }, false)

            thickLine.draw(drawer, thinLine)
        }
        keyboard.keyDown.listen {
            if(it.key == KEY_ESCAPE) {
                application.exit()
            }
        }
    }
}

/**
 * A class to draw [ShapeContour] objects as a thick line. The color is 
 * linearly interpolated from [color0] to [color1] from line start to end.
 * The thickness of the line is defined by a function that takes a normalized
 * `t` Double value as input and outputs the desired width. Get a uniform width 
 * using something like `{ t -> 10.0 }`
 */
class ThickLine(
    private val pointCount: Int,
    private val color0: ColorRGBa,
    private val color1: ColorRGBa,
    val width: (Double) -> Double
) {
    val geometry = vertexBuffer(vertexFormat {
        position(3)
        color(4)
    }, pointCount * 2)

    fun draw(drawer: Drawer, thinLine: ShapeContour) {
        drawer.shadeStyle = shadeStyle {
            fragmentTransform = "x_fill = va_color;"
        }
        geometry.put {
            for (i in 0 until pointCount) {
                val pc = i / (pointCount - 1.0)
                val color = color0.mix(color1, pc).toVector4()
                val pos = thinLine.position(pc)
                val normal = thinLine.normal(pc).normalized * width(pc) / 2.0
                write((pos + normal).vector3(z = 0.0))
                write(color)
                write((pos - normal).vector3(z = 0.0))
                write(color)
            }
        }
        drawer.vertexBuffer(geometry, DrawPrimitive.TRIANGLE_STRIP)
    }
}
1 Like