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 then the GpuShaderDesc is a better choice.

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
}                                     //
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
getCacheID() str
getDynamicProperties() PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
getDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) PyOpenColorIO.DynamicProperty
getFunctionName() str
getLanguage() PyOpenColorIO.GpuLanguage
getNextResourceIndex() int

To avoid texture/unform name clashes always append an increasing number 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
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>
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 three potential scenarios are:
//
//   1. Instantiate the legacy shader description.  The color processor
//      is baked down to contain at most one 3D LUT and no 1D LUTs.
//
//      This is the v1 behavior and will remain part of OCIO v2
//      for backward compatibility.
//
OCIO::GpuShaderDescRcPtr shaderDesc
      = OCIO::GpuShaderDesc::CreateLegacyShaderDesc(LUT3D_EDGE_SIZE);
//
//   2. 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();
//
//   3. 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.

GpuShaderDesc(*args, **kwargs)

Initialize self. See help(type(self)) for accurate signature.

add3DTexture(textureName: str, samplerName: str, edgeLen: int, interpolation: PyOpenColorIO.Interpolation, values: buffer) None
addTexture(textureName: str, samplerName: str, width: int, height: int, channel: PyOpenColorIO.GpuShaderCreator.TextureType, interpolation: PyOpenColorIO.Interpolation, values: buffer) None
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
getCacheID() str
getDynamicProperties() PyOpenColorIO.GpuShaderCreator.DynamicPropertyIterator
getDynamicProperty(type: PyOpenColorIO.DynamicPropertyType) PyOpenColorIO.DynamicProperty
getFunctionName() str
getLanguage() PyOpenColorIO.GpuLanguage
getNextResourceIndex() int

To avoid texture/unform name clashes always append an increasing number 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
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>
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
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