Shaders

GpuShaderCreator

class PyOpenColorIO.GpuShaderCreator

Inherit from the class to fully customize the implementation of a GPU shader program from a color transformation.

When no customizations are needed and the intermediate in-memory step is acceptable then the GpuShaderDesc is a better choice.

Note

To better decouple the DynamicProperties from their GPU implementation, the code provides several addUniform() methods i.e. one per access function types. For example, an ExposureContrastTransform instance owns three DynamicProperties and they are all implemented by a double. When creating the GPU fragment shader program, the addUniform() with GpuShaderCreator::DoubleGetter is called when property is dynamic, up to three times.

An OCIO shader program could contain:

  • A declaration part e.g., uniform sampled3D tex3;

  • Some helper methods

  • The OCIO shader function may be broken down as:

  • The function header e.g., void OCIODisplay(in vec4 inColor) {

  • The function body e.g., vec4 outColor.rgb = texture3D(tex3, inColor.rgb).rgb;

  • The function footer e.g., return outColor; }

Usage Example:

Below is a code snippet to highlight the different parts of the OCIO shader program.

// All global declarations
uniform sampled3D tex3;

// All helper methods
vec3 computePosition(vec3 color)
{
   vec3 coords = color;
   // Some processing...
   return coords;
}

// The shader function
vec4 OCIODisplay(in vec4 inColor)     //
{                                     // Function Header
   vec4 outColor = inColor;           //

   outColor.rgb = texture3D(tex3, computePosition(inColor.rgb)).rgb;

   return outColor;                   // Function Footer
}                                     //
TEXTURE_1D = <TextureDimensions.TEXTURE_1D: 1>
TEXTURE_2D = <TextureDimensions.TEXTURE_2D: 2>
class TextureDimensions

Dimension enum used to differentiate between 1D and 2D object/resource types.

Members:

TEXTURE_1D

TEXTURE_2D

TEXTURE_1D = <TextureDimensions.TEXTURE_1D: 1>
TEXTURE_2D = <TextureDimensions.TEXTURE_2D: 2>
property name
property value
addToDeclareShaderCode(shaderCode: str) None
addToFunctionFooterShaderCode(shaderCode: str) None
addToFunctionHeaderShaderCode(shaderCode: str) None
addToFunctionShaderCode(shaderCode: str) None
addToHelperShaderCode(shaderCode: str) None
begin(uid: str) None

Start to collect the shader data.

clone() PyOpenColorIO.GpuShaderCreator
createShaderText(shaderDeclarations: str, shaderHelperMethods: str, shaderFunctionHeader: str, shaderFunctionBody: str, shaderFunctionFooter: str) None

Create the OCIO shader program.

Note

The OCIO shader program is decomposed to allow a specific implementation to change some parts. Some product integrations add the color processing within a client shader program, imposing constraints requiring this flexibility.

end() None

End to collect the shader data.

finalize() None
getAllowTexture1D() bool
getCacheID() str
getDynamicProperties() PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
getDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) PyOpenColorIO.DynamicProperty
getFunctionName() str
getLanguage() PyOpenColorIO.GpuLanguage
getNextResourceIndex() int

To avoid global texture sampler and uniform name clashes always append an increasing index to the resource name.

getPixelName() str
getResourcePrefix() str

Note

Some applications require that textures, uniforms, and helper methods be uniquely named because several processor instances could coexist.

getTextureMaxWidth() int
getUniqueID() str
hasDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) bool
setAllowTexture1D(allowed: bool) None

Allow 1D GPU resource type, otherwise always using 2D resources for 1D LUTs.

setFunctionName(name: str) None
setLanguage(language: PyOpenColorIO.GpuLanguage) None

Set the shader program language.

setPixelName(name: str) None

Set the pixel name variable holding the color values.

setResourcePrefix(prefix: str) None

Set a prefix to the resource name.

setTextureMaxWidth(maxWidth: int) None

Some graphic cards could have 1D & 2D textures with size limitations.

setUniqueID(uid: str) None
class PyOpenColorIO.GpuShaderCreator.TextureType

Members:

TEXTURE_RED_CHANNEL

TEXTURE_RGB_CHANNEL

property name str
TEXTURE_RED_CHANNEL = <TextureType.TEXTURE_RED_CHANNEL: 0>
TEXTURE_RGB_CHANNEL = <TextureType.TEXTURE_RGB_CHANNEL: 1>
property value
class PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
self[arg0: int] PyOpenColorIO.DynamicProperty
iter(self) PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
len(self) int
next(self) PyOpenColorIO.DynamicProperty

GpuShaderDesc

class PyOpenColorIO.GpuShaderDesc

This class holds the GPU-related information needed to build a shader program from a specific processor.

This class defines the interface and there are two implementations provided. The “legacy” mode implements the OCIO v1 approach of baking certain ops in order to have at most one 3D-LUT. The “generic” mode is the v2 default and allows all the ops to be processed as-is, without baking, like the CPU renderer. Custom implementations could be written to accommodate the GPU needs of a specific client app.

The complete fragment shader program is decomposed in two main parts: the OCIO shader program for the color processing and the client shader program which consumes the pixel color processing.

The OCIO shader program is fully described by the GpuShaderDesc independently from the client shader program. The only critical point is the agreement on the OCIO function shader name.

To summarize, the complete shader program is:

//                                                                    //
//               The complete fragment shader program                 //
//                                                                    //
//                                                                    //
//   //////////////////////////////////////////////////////////////   //
//   //                                                          //   //
//   //               The OCIO shader program                    //   //
//   //                                                          //   //
//   //////////////////////////////////////////////////////////////   //
//   //                                                          //   //
//   //   // All global declarations                             //   //
//   //   uniform sampled3D tex3;                                //   //
//   //                                                          //   //
//   //   // All helper methods                                  //   //
//   //   vec3 computePos(vec3 color)                            //   //
//   //   {                                                      //   //
//   //      vec3 coords = color;                                //   //
//   //      ...                                                 //   //
//   //      return coords;                                      //   //
//   //   }                                                      //   //
//   //                                                          //   //
//   //   // The OCIO shader function                            //   //
//   //   vec4 OCIODisplay(in vec4 inColor)                      //   //
//   //   {                                                      //   //
//   //      vec4 outColor = inColor;                            //   //
//   //      ...                                                 //   //
//   //      outColor.rbg                                        //   //
//   //         = texture3D(tex3, computePos(inColor.rgb)).rgb;  //   //
//   //      ...                                                 //   //
//   //      return outColor;                                    //   //
//   //   }                                                      //   //
//   //                                                          //   //
//   //////////////////////////////////////////////////////////////   //
//                                                                    //
//   //////////////////////////////////////////////////////////////   //
//   //                                                          //   //
//   //             The client shader program                    //   //
//   //                                                          //   //
//   //////////////////////////////////////////////////////////////   //
//   //                                                          //   //
//   //   uniform sampler2D image;                               //   //
//   //                                                          //   //
//   //   void main()                                            //   //
//   //   {                                                      //   //
//   //      vec4 inColor = texture2D(image, gl_TexCoord[0].st); //   //
//   //      ...                                                 //   //
//   //      vec4 outColor = OCIODisplay(inColor);               //   //
//   //      ...                                                 //   //
//   //      gl_FragColor = outColor;                            //   //
//   //   }                                                      //   //
//   //                                                          //   //
//   //////////////////////////////////////////////////////////////   //
//                                                                    //
////////////////////////////////////////////////////////////////////////

Usage Example: Building a GPU shader

This example is based on the code in: src/apps/ociodisplay/main.cpp

// Get the processor
//
OCIO::ConstConfigRcPtr config = OCIO::Config::CreateFromEnv();
OCIO::ConstProcessorRcPtr processor
   = config->getProcessor("ACES - ACEScg", "Output - sRGB");

// Step 1: Create a GPU shader description
//
// The two potential scenarios are:
//
//   1. Instantiate the generic shader description.  The color processor
//      is used as-is (i.e. without any baking step) and could contain
//      any number of 1D & 3D luts.
//
//      This is the default OCIO v2 behavior and allows a much better
//      match between the CPU and GPU renderers.
//
OCIO::GpuShaderDescRcPtr shaderDesc = OCIO::GpuShaderDesc::Create();
//
//   2. Instantiate a custom shader description.
//
//      Writing a custom shader description is a way to tailor the shaders
//      to the needs of a given client program.  This involves writing a
//      new class inheriting from the pure virtual class GpuShaderDesc.
//
//      Please refer to the GenericGpuShaderDesc class for an example.
//
OCIO::GpuShaderDescRcPtr shaderDesc = MyCustomGpuShader::Create();

shaderDesc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_2);
shaderDesc->setFunctionName("OCIODisplay");

// Step 2: Collect the shader program information for a specific processor
//
processor->extractGpuShaderInfo(shaderDesc);

// Step 3: Create a helper to build the shader. Here we use a helper for
//         OpenGL but there will also be helpers for other languages.
//
OpenGLBuilderRcPtr oglBuilder = OpenGLBuilder::Create(shaderDesc);

// Step 4: Allocate & upload all the LUTs
//
oglBuilder->allocateAllTextures();

// Step 5: Build the complete fragment shader program using
//         g_fragShaderText which is the client shader program.
//
g_programId = oglBuilder->buildProgram(g_fragShaderText);

// Step 6: Enable the fragment shader program, and all needed textures
//
glUseProgram(g_programId);
glUniform1i(glGetUniformLocation(g_programId, "tex1"), 1);  // image texture
oglBuilder->useAllTextures(g_programId);                    // LUT textures

// Step 7: Update uniforms from dynamic property instances.
m_oglBuilder->useAllUniforms();
static CreateShaderDesc(language: PyOpenColorIO.GpuLanguage = <GpuLanguage.GPU_LANGUAGE_GLSL_1_2: 1>, functionName: str = 'OCIOMain', pixelName: str = 'outColor', resourcePrefix: str = 'ocio', uid: str = '') PyOpenColorIO.GpuShaderDesc

Create the default shader description.

TEXTURE_1D = <TextureDimensions.TEXTURE_1D: 1>
TEXTURE_2D = <TextureDimensions.TEXTURE_2D: 2>
class TextureDimensions

Dimension enum used to differentiate between 1D and 2D object/resource types.

Members:

TEXTURE_1D

TEXTURE_2D

TEXTURE_1D = <TextureDimensions.TEXTURE_1D: 1>
TEXTURE_2D = <TextureDimensions.TEXTURE_2D: 2>
TextureDimensions(value: int) None
property name
property value
TextureDimensions(*args, **kwargs)
add3DTexture(textureName: str, samplerName: str, edgeLen: int, interpolation: PyOpenColorIO.Interpolation, values: buffer) None

Add a 3D texture with RGB channel type.

Note

The ‘values’ parameter contains the 3D LUT data which must be used as-is as the dimension and origin are hard-coded in the fragment shader program. So, it means one GPU 3D texture per entry.

addTexture(textureName: str, samplerName: str, width: int, height: int, channel: PyOpenColorIO.GpuShaderCreator.TextureType, dimensions: PyOpenColorIO.GpuShaderCreator.TextureDimensions, interpolation: PyOpenColorIO.Interpolation, values: buffer) None

Add a 1D or 2D texture

Note

The ‘values’ parameter contains the LUT data which must be used as-is as the dimensions and origin are hard-coded in the fragment shader program. So, it means one GPU texture per entry.

addToDeclareShaderCode(shaderCode: str) None
addToFunctionFooterShaderCode(shaderCode: str) None
addToFunctionHeaderShaderCode(shaderCode: str) None
addToFunctionShaderCode(shaderCode: str) None
addToHelperShaderCode(shaderCode: str) None
begin(uid: str) None

Start to collect the shader data.

clone() PyOpenColorIO.GpuShaderCreator
createShaderText(shaderDeclarations: str, shaderHelperMethods: str, shaderFunctionHeader: str, shaderFunctionBody: str, shaderFunctionFooter: str) None

Create the OCIO shader program.

Note

The OCIO shader program is decomposed to allow a specific implementation to change some parts. Some product integrations add the color processing within a client shader program, imposing constraints requiring this flexibility.

end() None

End to collect the shader data.

finalize() None
get3DTextures() PyOpenColorIO.GpuShaderDesc.Texture3DIterator
getAllowTexture1D() bool
getCacheID() str
getDynamicProperties() PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
getDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) PyOpenColorIO.DynamicProperty
getFunctionName() str
getLanguage() PyOpenColorIO.GpuLanguage
getNextResourceIndex() int

To avoid global texture sampler and uniform name clashes always append an increasing index to the resource name.

getPixelName() str
getResourcePrefix() str

Note

Some applications require that textures, uniforms, and helper methods be uniquely named because several processor instances could coexist.

getShaderText() str

Get the complete OCIO shader program.

getTextureMaxWidth() int
getTextures() PyOpenColorIO.GpuShaderDesc.TextureIterator
getUniforms() PyOpenColorIO.GpuShaderDesc.UniformIterator
getUniqueID() str
hasDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) bool
setAllowTexture1D(allowed: bool) None

Allow 1D GPU resource type, otherwise always using 2D resources for 1D LUTs.

setFunctionName(name: str) None
setLanguage(language: PyOpenColorIO.GpuLanguage) None

Set the shader program language.

setPixelName(name: str) None

Set the pixel name variable holding the color values.

setResourcePrefix(prefix: str) None

Set a prefix to the resource name.

setTextureMaxWidth(maxWidth: int) None

Some graphic cards could have 1D & 2D textures with size limitations.

setUniqueID(uid: str) None
class PyOpenColorIO.GpuShaderDesc.TextureType

Members:

TEXTURE_RED_CHANNEL

TEXTURE_RGB_CHANNEL

property name str
TEXTURE_RED_CHANNEL = <TextureType.TEXTURE_RED_CHANNEL: 0>
TEXTURE_RGB_CHANNEL = <TextureType.TEXTURE_RGB_CHANNEL: 1>
property value
class PyOpenColorIO.GpuShaderDesc.UniformData
getBool() bool
getDouble() float
getFloat3() List[float[3]]
getVectorFloat() numpy.ndarray
getVectorInt() numpy.ndarray
property type
class PyOpenColorIO.GpuShaderDesc.Texture
property channel
property dimensions
getValues() numpy.ndarray
property height
property interpolation
property samplerName
property textureName
property width
class PyOpenColorIO.GpuShaderDesc.Texture3D
property edgeLen
getValues() numpy.ndarray
property interpolation
property samplerName
property textureName
class PyOpenColorIO.GpuShaderDesc.UniformIterator
self[arg0: int] tuple
iter(self) PyOpenColorIO.GpuShaderDesc.UniformIterator
len(self) int
next(self) tuple
class PyOpenColorIO.GpuShaderDesc.TextureIterator
self[arg0: int] PyOpenColorIO.GpuShaderDesc.Texture
iter(self) PyOpenColorIO.GpuShaderDesc.TextureIterator
len(self) int
next(self) PyOpenColorIO.GpuShaderDesc.Texture
class PyOpenColorIO.GpuShaderDesc.Texture3DIterator
self[arg0: int] PyOpenColorIO.GpuShaderDesc.Texture3D
iter(self) PyOpenColorIO.GpuShaderDesc.Texture3DIterator
len(self) int
next(self) PyOpenColorIO.GpuShaderDesc.Texture3D
class PyOpenColorIO.GpuShaderDesc.DynamicPropertyIterator
self[arg0: int] PyOpenColorIO.DynamicProperty
iter(self) PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
len(self) int
next(self) PyOpenColorIO.DynamicProperty