Skip to content

Custom Images

image.<imageName> = <samplerName> <format> <internalFormat> <pixelType> <shouldClearOnNewFrame> <isRelative> <relativeX/absoluteX> <relativeY/absoluteY> <absoluteZ>

Custom images allow to write up to 8 custom full images of data. These images are similar to colortex buffers, except they allow for writing to arbitrary locations and from any shader file. For more info on allowed formats and restrictions, along for their use in shaders, read the Khronos Wiki. Iris custom images require the CUSTOM_IMAGES feature flag.

To declare a custom image, use the following in shaders.properties:

image.<imageName> = <samplerName> <format> <internalFormat> <pixelType> <shouldClearOnNewFrame> <isRelative> <relativeX/absoluteX> <relativeY/absoluteY> <absoluteZ>

Replace:

  • <imageName> with the variable name of the image
  • <samplerName> with the variable name of the sampler
  • <format> with the pixel format of the image (see “Pixel Formats” section)
  • <internalFormat> with the texture format of the image (see “Texture Format” section)
  • <pixelType> with the pixel type of the image (see “Pixel Types” section)
  • <shouldClearOnNewFrame> with true to clear the image after each frame, or false to retain data between frames
  • <isRelative> with true to make the image dimensions relative to the screen dimensions, or false to make the dimensions absolute
  • <relativeX/absoluteX> with the relative (floating point) or absolute (integer) size in the x dimension
  • <relativeY/absoluteY> with the relative (floating point) or absolute (integer) size in the y dimension
  • <absoluteZ> with the absolute (integer) size in the z dimension

For example, to declare a RGBA32F custom image half the screen size that does not clear, use the following:

image.cimage1 = cSampler1 rgba rgba32f float false true 0.5 0.5

And to declare a RGBA8 3D custom image with dimensions of 512x512x512 that clears every frame:

image.cimage1 = cSampler1 rgba rgba8 float true false 512 512 512

This image can be accessed in 2 ways:

  • The image can be written and read per-pixel by declaring it as an image:
layout (rgba8) uniform image2D cimage1;
void main() {
vec4 previousValue = imageLoad(cimage1, ivec2(0, 0)); // Reads from first pixel in the image
imageStore(cimage1, ivec2(0, 0), vec4(1, 0, 0, 1)); // Writes to first pixel in the image
}
  • Or the image can be read with filtering as a sampler:
uniform sampler2D cSampler1;
varying float viewWidth;
varying float viewHeight;
void main() {
gl_FragColor = texture2D(cSampler1, gl_FragCoord.xy / vec2(viewWidth, viewHeight)); // Samples the current pixel of the image with smooth linear filtering.
}