Using delayed input texture evaluation

The intrinsics _evaluateDelayed() and _evaluateDelayedP() cause an input texture to evaluated only when the control has reached the intrinsic in the current shader. This is different from what OSL normally does, which is evaluating any shaders on the input first, before starting this shader.

Selective execution

Consider a simple use case: a texture shader which randomly picks one of four input textures for each UV tile.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
shader Texture(
    color col1 = color(1, 0, 0),
    color col2 = color(0, 1, 0),
    color col3 = color(0, 0, 1),
    color col4 = color(1, 1, 1),
    output color c = 0)
{
    int index = (int) floor(4 * noise("cell", u, v));

    // random index
    if (index == 0) { c = col1; }
    if (index == 1) { c = col2; }
    if (index == 2) { c = col3; }
    if (index == 3) { c = col4; }
}

tiles

Prior to evaluating this texture, the 4 texture inputs col1 to col4 are evaluated, but only one of them is needed to calculate the output value. This is inefficient if there’s a large amount of different tiles. If this is undesirable we can use delayed evaluation to calculate only the input we need:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
#include <octane-oslintrin.h>
shader Texture(
    color col1 = color(1, 0, 0),
    color col2 = color(0, 1, 0),
    color col3 = color(0, 0, 1),
    color col4 = color(1, 1, 1),
    output color c = 0)
{
    int index = (int) floor(4 * noise("cell", u, v));

    // random index
    if (index == 0) { c = _evaluateDelayed(col1, u, v); }
    if (index == 1) { c = _evaluateDelayed(col2, u, v); }
    if (index == 2) { c = _evaluateDelayed(col3, u, v); }
    if (index == 3) { c = _evaluateDelayed(col4, u, v); }
}

The flip side is that every call to _evaluateDelayed always evaluates the input once. There is thus no longer deduplication if there are multiple such inputs connected to the same source.

Custom UV mapping

Apart from the input variable, there are two more arguments, which let you control the UV mapping of the input texture. To use this UV map change the Projection input of any node connected to this input to an OSL delayed UV projection node.

We can modify our example so the input tiles are rotated randomly at 90° intervals.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <octane-oslintrin.h>
shader Texture(
    color col1 = color(1, 0, 0),
    color col2 = color(0, 1, 0),
    color col3 = color(0, 0, 1),
    color col4 = color(1, 1, 1),
    output color c = 0)
{
    int rnd = (int) floor(16 * noise("cell", u, v));

    // random rotation
    float u2 = (rnd & 1) ? u : v;
    float v2 = (rnd & 1) ? v : -u;
    u2 = (rnd & 2) ? -u2 : u2;
    v2 = (rnd & 2) ? -v2 : v2;

    // random index
    int index = rnd >> 2;
    if (index == 0) { c = _evaluateDelayed(col1, u2, v2); }
    if (index == 1) { c = _evaluateDelayed(col2, u2, v2); }
    if (index == 2) { c = _evaluateDelayed(col3, u2, v2); }
    if (index == 3) { c = _evaluateDelayed(col4, u2, v2); }
}

tiles

Info

To use the custom UV coordinates in another OSL node connected via a delayed input, give it a point input and connect a OSL delayed UV node to the corresponding input pin.

Custom 3D coordinate mapping

The intrinsic function _evaluateDelayedP() is similar to evaluateDelayed() but it overrides the value for P. This is useful if you need to read SDFs or VDB volumes at coordinates which are calculated in your shader.

  • Evaluation is delayed until the control flow reaches this intrinsic.
  • The meaning of projection nodes in the input texture changes in a similar way as projection inputs in Vectron shader trees:
    • P represents the point where the SDF is being evaluated, with this intrinsic this is the passed in pos value.
    • Position based projections with the coordinate space set to ‘object space’ will also use the passed in pos value. Projections set to ‘world space’ remain unchanged.

Last update: November 3, 2023