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 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.
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 {
mod(index - it, 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++
}
}
}