Array textures
Array textures are a special type of texture that make it possible to access 2048 layers of texture data from a single texture sampler.
Array textures are encapsulated by the ArrayTexture interface
Creation
Array textures are created using the arrayTexture function.
fun main() = application {
program {
// -- create an array texture with 100 layers
val at = arrayTexture(512, 512, 100)
}
}
Writing to array textures
There are several ways to get texture data into array textures. Let’s have a look at them.
One can copy from a ColorBuffer using .copyTo()
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
val cb = colorBuffer(512, 512)
// -- copy from the color buffer to the 4th layer of the array texture
cb.copyTo(at, 4)
}
}
or copy from an array texture layer to another layer
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
// -- copy from the 2nd array texture layer to the 4th array texture layer
at.copyTo(2, at, 4)
}
}
or write to an array texture layer from a direct ByteBuffer
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
val buffer = ByteBuffer.allocateDirect(512 * 512 * 4)
// fill the buffer with random data
for (y in 0 until 512) {
for (x in 0 until 512) {
for (c in 0 until 4) {
buffer.put((Math.random() * 255).toInt().toByte())
}
}
}
buffer.rewind()
// -- write the buffer into the 0th layer
at.write(0, buffer)
}
}
Drawing array textures
Array textures can be drawn using the Drawer.image functions.
As example we show how to draw the 0th layer of an array texture
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
extend {
drawer.image(at, 0)
// -- with position and size arguments
drawer.image(at, 0, 100.0, 100.0, 256.0, 256.0)
}
}
}
We can also render batches of array textures by passing lists of layer indexes and source-target rectangle pairs.
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
extend {
drawer.image(at, 0)
val layers = mutableListOf<Int>()
val rectangles = mutableListOf<Pair<Rectangle, Rectangle>>()
for (i in 0 until 100) {
layers.add((Math.random() * 100).toInt())
val source = Rectangle(
Math.random() * 512.0,
Math.random() * 512.0,
Math.random() * 512.0,
Math.random() * 512.0
)
val target = Rectangle(
Math.random() * 512.0,
Math.random() * 512.0,
Math.random() * 512.0,
Math.random() * 512.0
)
rectangles.add(source to target)
}
drawer.image(at, layers, rectangles)
}
}
}
Drawing into array textures
Array textures can be used as attachment for render targets.
Here we show how to use a single layer from an array texture as an attachment for a render target.
fun main() = application {
program {
val at = arrayTexture(512, 512, 100)
// -- create a render target
val rt = renderTarget(512, 512) {
// -- attach the 0th layer of the array texture
arrayTexture(at, 0)
depthBuffer()
}
extend {
drawer.isolatedWithTarget(rt) {
drawer.ortho(rt)
drawer.clear(ColorRGBa.PINK)
}
drawer.image(at, 0)
}
}
}
Let’s conclude this chapter by means of a small slit scanning demonstration. Here we use a single array texture and a list of render targets, all using different layers of the same array texture.
import org.openrndr.application
import org.openrndr.color.ColorRGBa
import org.openrndr.draw.arrayTexture
import org.openrndr.draw.colorBuffer
import org.openrndr.draw.isolatedWithTarget
import org.openrndr.draw.renderTarget
import org.openrndr.shape.Rectangle
import java.nio.ByteBuffer
import kotlin.math.cos
import kotlin.math.sin
fun main() = application {
program {
val at = arrayTexture(770, 576, 116)
val renderTargets = List(at.layers) {
renderTarget(at.width, at.height) {
arrayTexture(at, it)
}
}
var index = 0
extend {
drawer.clear(ColorRGBa.BLACK)
drawer.isolatedWithTarget(renderTargets[index % renderTargets.size]) {
drawer.clear(ColorRGBa.BLACK)
drawer.fill = ColorRGBa.PINK.opacify(0.5)
drawer.stroke = null
for (i in 0 until 20) {
drawer.circle(
cos(seconds * 5.0 + i) * 256 + width / 2.0,
sin(i + seconds * 6.32) * 256 + height / 2.0,
100.0
)
}
}
val layers = (0 until at.layers).map { (index - it).mod(at.layers) }
val rectangles = (0 until at.layers).map {
val span = Rectangle(0.0, it * 5.0, at.width * 1.0, 5.0)
span to span
}
drawer.image(at, layers, rectangles)
index++
}
}
}