Textures and Images
Textures are read through their samplers, filtered or not. Storage images are read and written texel by texel. Both are declared as uniforms; see Uniforms and Resources. Their types are listed in Types.
A texture or image can be passed to a shader function as a parameter, but cannot be stored in a variable, computed with, or returned:
(define-function tinted ((image :sampler2d) (uv :vec2) (tint :vec4))
(* (sample image uv) tint))
What a texel is
| Texture type | Texel |
|---|---|
:sampler... |
:vec4 |
:isampler... |
:ivec4 |
:usampler... |
:uvec4 |
:sampler...-shadow with :compare |
:float, the filtered comparison result from 0 to 1 |
The same holds for storage images: :image... gives :vec4, :iimage... :ivec4, and :uimage... :uvec4.
cl-gsl knows which texture kinds GLSL can read in each way, and says so when one does not fit, rather than leaving glslang to reject the GLSL:
GLSL cannot read a :SAMPLER-CUBE this way. It reads 1d, 2d, 3d, 1d-array,
2d-array, buffer, 2d-ms, 2d-ms-array textures this way.
Coordinates
| Texture shape | sample and gather coordinates |
fetch coordinates |
|---|---|---|
| 1D | :float |
:int |
| 1D array | :vec2: x, layer |
:ivec2 |
| 2D | :vec2 |
:ivec2 |
| 2D array | :vec3: x, y, layer |
:ivec3 |
| 3D | :vec3 |
:ivec3 |
| Cube | :vec3, a direction |
not fetched |
| Cube array | :vec4: direction, layer |
not fetched |
| Buffer | not sampled | :int |
| Multisampled | not sampled | as 2D or 2D array, with :sample |
Filtered coordinates run from 0 to 1 across each dimension. Integer coordinates count texels from 0.
sample: filtered reads
(sample texture coordinates &key bias level ddx ddy offset compare)
(sample image uv) ; level from derivatives, in a fragment shader
(sample image uv :level 2) ; from mipmap level 2
(sample image uv :bias 1 :offset (ivec2 1 0)) ; one level blurrier, one texel to the right
(sample image uv :ddx (dfdx uv) :ddy (dfdy uv)); level from explicit derivatives
(sample shadow-map uv :compare depth) ; a :sampler2d-shadow, giving a :float
| Key | Meaning |
|---|---|
| (none) | In a fragment shader, the mipmap level follows from how fast the coordinates change. In any other stage it is level 0. |
:bias |
A float added to that level. Fragment shaders only. |
:level |
A float that chooses the level outright. |
:ddx, :ddy |
The changes of the coordinates to the next pixel across and down, which choose the level as derivatives do. |
:offset |
An :int or :ivecN of constants, moving the texel read by that many texels. |
:compare |
For a shadow texture: the depth to compare with. |
An :isampler... or :usampler... is read with sample too; its sampler must not filter linearly.
Multisampled and buffer textures are read with fetch.
fetch: unfiltered reads
(fetch texture texel &key level sample offset)
(fetch atlas (ivec2 x y) :level 0) ; one texel, integer coordinates
(fetch samples (ivec2 x y) :sample 3) ; sample 3 of a multisampled texture
(fetch lookup i) ; a buffer texture
| Key | Meaning |
|---|---|
:level |
The mipmap level, an :int, 0 by default. |
:sample |
For a multisampled texture, which sample, an :int. It takes no level. |
:offset |
An :int or :ivecN of constants. |
A buffer texture takes neither a level nor a sample. Reading outside the texture is undefined.
gather: the four texels filtering would blend
(gather texture coordinates &key component offset compare)
(gather image uv) ; component 0 (red) of each of the four texels
(gather image uv :component 3) ; their alpha
(gather shadow-map uv :compare depth) ; four comparison results, a :vec4
component is a constant from 0 to 3. The result is a vector of four.
Queries
| Form | Gives |
|---|---|
(texture-size texture [level]) |
The size in texels at level (0 by default): width, height and depth, as many as it has, then an array's layer count, as an :int or :ivecN. Multisampled and buffer textures take no level. |
(texture-levels texture) |
The number of mipmap levels, an :int. |
(texture-samples texture) |
The samples per texel of a multisampled texture, an :int. |
(texture-query-lod texture coordinates) |
The mipmap level sample would read at coordinates, without its array layer, as a :vec2: the level the sampler chooses from, then the level relative to the base. Fragment shaders only. |
Storage images
Storage images are declared with a :format and an :access; see Uniforms and Resources.
| Form | Does |
|---|---|
(image-load image texel &key sample) |
Reads the texel at integer coordinates, as for fetch. |
(image-store image texel value &key sample) |
Writes value, a :vec4, :ivec4 or :uvec4 by the image's type. |
(image-size image) |
The size in texels, and an array's layer count, as an :int or :ivecN. |
(image-samples image) |
The samples per texel of a multisampled image. |
A multisampled image takes :sample, an :int, in image-load and image-store.
(define-shader invert (:compute :workgroup-size (8 8))
(&uniform (picture :image2d :format :rgba8-unorm :access :read-write))
(let ((texel (ivec2 (swizzle (global-invocation-id) :xy))))
(when (all (< texel (image-size picture)))
(let ((c (image-load picture texel)))
(image-store picture texel (vec4 (- 1 (swizzle c :xyz)) (w c)))))))
Components are named x, y, z and w for colours too.
Writes to an image become visible to other invocations after memory-barrier-image; see Compute Shaders.
Derivatives
Implicit-level sample depends on derivatives, and so do dfdx, dfdy and fwidth. All are fragment-shader only, and undefined where neighbouring fragments take different branches. To sample in a branch, compute the derivatives outside it and pass :ddx and :ddy, or choose a :level.
(let ((dx (dfdx uv)) (dy (dfdy uv)))
(if (> mask 0.5)
(sample image uv :ddx dx :ddy dy)
(vec4 0)))