A program inspired by a question in Slack. The goal was to create an extruded 3D shape and apply a texture to the front while giving a flat color to the sides.
Normally one could assign uv texture coordinates to all the vertices (basically mapping each vertex to a position in a texture) when creating the mesh, but in this case I only observed the normal and if it’s z component is 0, I knew the corresponding vertex belonged to the sides of the shape.
If you visualize the normals of the shape in your head, you will see that one flat side has va_normal.z = 1.0
, the other side has va_normal.z = -1.0
, and the curved side has va_normal.z = 0.0
.
imports
import org.openrndr.WindowMultisample
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.DrawPrimitive
import org.openrndr.draw.isolatedWithTarget
import org.openrndr.draw.renderTarget
import org.openrndr.draw.shadeStyle
import org.openrndr.extra.camera.Orbital
import org.openrndr.extra.meshgenerators.extrudeShape
import org.openrndr.extra.meshgenerators.meshGenerator
import org.openrndr.extra.noise.uniform
import org.openrndr.extra.shapes.hobbyCurve
import org.openrndr.math.Polar
import org.openrndr.math.Vector2
import org.openrndr.math.Vector3
import org.openrndr.shape.ShapeContour
fun main() {
application {
configure {
width = 900
height = 900
multisample = WindowMultisample.SampleCount(8)
}
program {
// Add an interactive 3D camera
extend(Orbital()) {
this.eye = Vector3(0.0, 0.0, 50.0)
}
// make a smooth 2D star shape with 7 tips
val tips = 7
val c = ShapeContour.fromPoints(List(tips * 2) {
Polar(it * 180.0 / tips, 20.0 + 20 * (it % 2)).cartesian
}, true).hobbyCurve().shape
// extrude the 2D shape into a 3D mesh
val m = meshGenerator {
extrudeShape(c, 10.0)
}
// create a texture to decorate the mesh
val tex = renderTarget(400, 400) {
colorBuffer()
}.also { rt ->
drawer.isolatedWithTarget(rt) {
ortho(rt)
strokeWeight = 20.0
repeat(20) {
stroke = ColorRGBa.fromVector(Vector3.uniform(0.0, 1.0))
lineSegment(
Vector2(it * 20.0, 0.0),
Vector2(it * 20.0, 400.0),
)
}
}
}.colorBuffer(0)
// create a shader to apply the texture to the two flat surfaces of the star
// and a flat color on the curved side
val shader = shadeStyle {
fragmentTransform = """
// calculate a normalized uv coordinate for the texture
vec2 uv = (va_position.xy - p_corner) / p_dimensions;
// normal.z is zero for the curved side area
// in that case use yellow, otherwise, read from the texture
x_fill.rgb = va_normal.z == 0.0 ?
vec3(1.0, 0.9, 0.0) :
texture(p_tex, uv).rgb;
""".trimIndent()
// pass the position and dimensions of the star
parameter("corner", c.bounds.corner)
parameter("dimensions", c.bounds.dimensions)
// pass the texture
parameter("tex", tex)
}
extend {
drawer.clear(ColorRGBa.WHITE)
drawer.shadeStyle = shader
drawer.vertexBuffer(m, DrawPrimitive.TRIANGLES)
}
}
}
}