Projections

This page describes how to create OSL shaders which reproduce the same UV mapping behavior as built-in Octane image texture nodes, and buit-in Octane projection nodes.

Built-in projection behavior

Built-in projection nodes which yield coordinate values directly based on position yield different values depending on whether the destination is an image or a 3D procedural. Examples are XYZ to UVW, Box, and the V direction of cylindrical:

  • For 3D prodedurals, projections are not modified.
  • For images, any axes that are based directly on geometric coordinates are translated by 0.5 and scaled up by 200%, mapping the UV square [0, 1]² to the [-1, 1]² square in geometric space. (that geometric space is configurable, by default it is object space).

This difference applies to the entire input node tree connected to the projection input.

The UV transform is also applied differently: it is centred on (0.5, 0.5) instead of on the origin. Unlike projections, matrix values are never modified, and this different behavior can be thought of as being part of the logic of those texture nodes.

This behavior is implemented in the texture nodes themselves.

These image show box mapping. The graticule on the background shows the world space grid.

Texture type Mapping of UV (0–1) square Effect of rotation
RGB image
3D procedural [a]

[a]: This is an OSL texture containing a texture() call to fetch the same image, however Octane by default treats OSL textures as prodedural textures in this context.

The green peg shows where the world space origin is. In both cases the rotation rotates around this point, but for images this is the (0.5, 0.5) point in UV space.

OSL textures

To reproduce the usual projection + UV transform inputs on an OSL texture node, you need to add a point and a matrix input.

  • The matrix should have a string type = "uvw" metadatum so that Octane will apply the transforms in UVW transform nodes to this value. By default these behave the same as 3D prodedurals. If your pattern is 2D, it should also have int dim = 2.
  • The canonical way of applying the transform is uvw_to_use = transform(1 / uvwTransform, projection). You need the inverse transform to obtain the usual behaviour that textures appear larger if the scale factor is larger.

If you write an OSL texture which should behave like an image:

  • the point input should have string variant = "image" to request the image behaviour.
  • the matrix input is not changed in any way. If you want rotation around (0.5, 0.5) you apply the transform as follows:
    uvw_to_use = transform(1 / uvwTransform, projection - vector(.5, .5, 0)) + vector(.5, .5, 0)

Examples

Simple examples that produce similar behavior as built-in nodes:

3D prodedural:

1
2
3
4
5
6
7
8
shader OslTexture(
    point projection = P,
    matrix uvwTransform = 1 [[string variant = "uvw"]],
    output color c = 0)
{
    point p = transform(1 / uvwTransform, projection);
    c = noise("chips", 10*p);
}

2D prodedural, mapped as image:

1
2
3
4
5
6
7
8
9
shader OslTexture(
    point projection = point(u, v, 0) [[string variant = "image"]],
    matrix uvwTransform = 1 [[string variant = "uvw", int dim = 2]],
    output color c = 0)
{
    point p = transform(1 / uvwTransform, projection - point(0.5, 0.5, 0)) + point(0.5, 0.5, 0);
    p.z = 0;
    c = p - floor(p);
}

OSL projections

For projections, you may wish to write a mapping which, similarly to built-in mappings, yields different coordinates for images. Or you may want some operator to apply a transform differently for images, similarly to the built-in transform input of image textures.

To know which mode to use, declare an input as follows:
int isImageProjection = 0 [[ int builtin = 1]].

This example reproduces the behaviour of an XYZ to UVW projection node set to world space:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
shader OslProjection(
    int isImageProjection = 0 [[ int builtin = 1]],
    output point uvw = 0)
{
    uvw = P;
    if (isImageProjection)
    {
        uvw = uvw * vector(0.5, 0.5, 1) + point(0.5, 0.5, 0);
    }
}

Last update: December 11, 2025