# Compute Casting

Compute casting is an alternate way to cast material compared to using traditional casters and shading networks. The following diagram provides an overview of the compute casting workflow and the major component involved.

Compute Casting Overview

Compute Casting as the name suggest uses a compute shader and Vulkan backend to cast textures. The Vulkan backend can either be a GPU or a CPU fallback API (e.g SwiftShader). Like other caster compute caster takes in MappingImage, Materials and Textures for the casting processing. Unlike other caster you also need to provide MaterialEvaluationShader and a user-defined shader block.

# Dependencies

Before you get started with compute casting in your pipeline the following dependencies should be installed on machines where you intend to use the feature.

  • VulkanSDK (opens new window) (Version 1.2+, ) Simplygon uses a Vulkan-based compute casting processor, and needs the VulkanSDK shader compilation tools:
    • glslc.exe for compiling GLSL shaders. glslc.exe is part of the VulkanSDK distro, so if you install VulkanSDK, Simplygon should be able to find glslc.exe automatically. If not, please make sure it is available in the Windows PATH environment variable.
    • dxc.exe for compiling HLSL shaders. dxc.exe and all needed Spir-V support tools are part of the VulkanSDK distro, so if you install VulkanSDK, Simplygon should be able to find dxc.exe automatically. If not, please make sure it is available in the Windows PATH environment variable. (Please note! This is not the standard dxc.exe which is included with Visual Studio and compiles DXIL code, but a version which is able to output and compile Spir-V for Vulkan.)
  • A Vulkan-compatible GPU, with latest drives, -or-
  • Swift Shader (opens new window) (Optional: CPU based fallback Vulkan API implementation)

# Material Evaluation Shader and user-defined shader block

# Material Evaluation Shader

This object is meta data that is attached to the Material object. This meta data along with the user defined shader block is used by the compute caster to generate a compute shader for the casting process. The ShaderLanguage attribute/property is required.

MaterialEvaluationShader can be defined by either creating API object programmatically or by deserializing a XML file. See Material Evaluation Shader for more info. The following are the main components required for MaterialEvaluationShader.

# Attributes

MaterialEvaluationShaderAttribute object declare a buffer resource that is setup during compute shader generation phase. The buffer refers to a field in the source GeometryData used in the user-defined shader block(e.g Texcoords, Coords, Normals, Tangents and custom fields). The Name property refers to the variable or buffer name in the shader. The GeometryFieldType refers to the field type in GeometryData. See EGeometryFieldType. The AttributeFormat refers to the data type(e.g F32vec2,U32 and etc). See EAttributeFormat. Some geometry fields like Texcoords, Colors, UserCornerField, UserVertexField , UserTriangleField are named fields. The FieldName property should be used to refer to the named field. If a named field is not present in the GeometryData and the frequency is known (e.g Triangle, Vertex, Corner) a zero initialized buffer would be created for use by the generated compute shader.See Material Evaluation Shader Attribute for more information.

# Evaluation Functions

ShaderEvaluationFunction object declare a mapping between MaterialChannel and a method in user defined shader block to use for casting. At least one ShaderEvaluationFunction object is required. (e.g Diffuse, Basecolor, Normals, Roughness and etc.). See Shader Evaluation Function. The EntryPoint is the name of the function in user defined shader block. Channel property should refer to MaterialChannel on the Material object.

# Shader Parameters

The shader parameters are parameter used to setup the generated shader. ShaderParameter is the base class. The following are the concrete types:

# Textures

ShaderParameterTexture and ShaderParameterTextureArray objects are used to define texture references, which can be sampled using a texture sampler (defined below). The ShaderParameterTexture object creates a single texture reference in the shader code, and the ShaderParameterTextureArray creates an array of texture references, which can be indexed in the shader code.

ShaderParameterTexture object has a TextureName property which must refer to an existing Texture in the TextureTable. ShaderParameterTextureArray contains a StringArray parameter, which holds the names and indices of all the textures in the texture array.

# Samplers

ShaderParameterSampler object declare a texture sampler that is generated by the compute caster. A ShaderParameterSampler object per Texture is required(referred in user-defined shader block). ShaderParameterSampler object has an optional TextureName property which should refer to a Texture in the TextureTable. If the TextureName property is set, the sampler is a combined texture and sampler, and bound to one slot. Each ShaderParameterSampler object can use a default state. Users can override sampler state by declaring a ShaderParameterSamplerState object. The SamplerState property can be used to refer to the name of the ShaderParameterSamplerState object to use. If SamplerState property is not set a default SamplerState is used. See Shader Parameter Sampler

# Samplers States

The ShaderParameterSamplerState object declare the behavior of a sampler like filtering and addressing modes. See ESamplerAddressMode, ESamplerFilter and Shader Parameter Sampler state. This shader parameter type is optional. To refer to ShaderParameterSampler object. The SamplerState property on ShaderParameterSampler object should refer to the Name of the ShaderParameterSamplerState object to use.

Note: Since HLSL and GLSL handle sampler states a bit differently, samplers in GLSL are automatically applied to each sampler, while in HLSL, the samplers are explicitly selected in the shader. For HLSL, it is recommended to specify samplers and textures separately, to be able to defined the names of the objects specifically.

# Default Samplers State

Property Default
MagFilter Linear
MinFilter Linear
AddressModeU Repeat
AddressModeV Repeat
AddressModeW Repeat
UnnormalizedCoordinates false

# Preprocessor Defines

MaterialEvaluationShaderDefine object can be used to declare a preprocessor define in the generated compute shader before the user block begins. The define is undefined after the user block ends. The Name property is required. The Value property is optional.

# Setting up Material Evaluation Shader and Exporting the XML

If you have access to the Simplygon API prefer creating MaterialEvaluationShader object over XML. In case your shader export module can not take a dependency on you can export an XML using any XML writer for ingestion into your LOD generation module.

The following code example shows how to setup a MaterialEvaluationShader object. Load a shader from disk and export it as an XML. This is mainly to validate what kind of XML is read by Simplygon and deserialized. You can adapt your XML writer to export similarly.

    # Example Material Evaluation Shader XML

    The following is a an example XML what can be deserialized into a MaterialEvaluationShader object. The following XML was generated using the above sample code. ShaderLanguage attribute is required for appropriate shader bindings generation by compute caster.

    <?xml version="1.0" encoding="UTF-8"?>
    <MaterialEvaluationShader Version="1.0" ShaderLanguage="GLSL">
        <!-- This attribute defines that from source geometry we want to use a named TexCoord field named UVMap -->
        <Attribute Name="TEXCOORD0" FieldType="TexCoords" FieldName="UVMap1" FieldFormat="F32vec2"/>
    
        <!-- A method called CastDiffuse will be used to cast to diffuse channel -->
        <EvaluationFunction Channel="Basecolor" EntryPoint="EvaluateBaseColor"/>
        <!-- A non default sampler state -->
        <ShaderParameterSamplerState Name="LinearRepeat" MinFilter="Linear" MagFilter="Linear" AddressU="MirrorRepeat" AddressV="MirrorRepeat" AddressW="MirrorRepeat" UnNormalizedCoordinates="true"/>
        <!-- A sampler using non default sampler state called LinearRepeat which is used to sample wood_D texture -->
        <ShaderParameterSampler Name="BaseColorTexture" TextureName="wood_D" SamplerState="LinearRepeat"/>
     <!-- A Shader define that would be declared in generated code-->
     <Define Name="FLIP_UV"/>
        <!-- If SetShaderCode or LoadShaderFromFile is used the shader is embedded as cdata -->
        <Shader><![CDATA[
    vec4 EvaluateBaseColor() 
    { 
    #ifdef FLIP_UV 
     return vec4(TEXCOORD0.y,TEXCOORD0.x,0,1); 
    #else 
     return vec4(TEXCOORD0.x,TEXCOORD0.y,0,1); 
    #endif 
    }]]></Shader>
    </MaterialEvaluationShader>
    
    

    Below is an example of the extended XML which also maps the evaluation shader into a material called "wood_material", and also adds the texture "wood_D" (filename: "wood.png") into the texture table. Note that if the material and texture exists in the current scene, their data is replaced. If the material or texture does not exist in the scene, they are added.

    <?xml version="1.0" encoding="UTF-8"?>
    <Scene>
       <TextureTable>
          <Texture Name="wood_D" FilePath="wood.png" ColorSpace="Linear"/>
       </TextureTable>
       <MaterialTable>
          <Material Name="wood_material">
             <MaterialEvaluationShader Version="1.0" ShaderLanguage="GLSL">
                <!-- This attribute defines that from source geometry we want to use a named TexCoord field named UVMap -->
                <Attribute Name="TEXCOORD0" FieldType="TexCoords" FieldName="UVMap1" FieldFormat="F32vec2"/>
    
                <!-- A method called CastDiffuse will be used to cast to diffuse channel -->
                <EvaluationFunction Channel="Basecolor" EntryPoint="EvaluateBaseColor"/>
                <!-- A non default sampler state -->
                <ShaderParameterSamplerState Name="LinearRepeat" MinFilter="Linear" MagFilter="Linear" AddressU="MirrorRepeat" AddressV="MirrorRepeat" AddressW="MirrorRepeat" UnNormalizedCoordinates="true"/>
                <!-- A sampler using non default sampler state called LinearRepeat which is used to sample wood_D texture -->
                <ShaderParameterSampler Name="BaseColorTexture" TextureName="wood_D" SamplerState="LinearRepeat"/>
             <!-- A Shader define that would be declared in generated code-->
             <Define Name="FLIP_UV"/>
                <!-- If SetShaderCode or LoadShaderFromFile is used the shader is embedded as cdata -->
                <Shader><![CDATA[
             vec4 EvaluateBaseColor() 
             { 
             #ifdef FLIP_UV 
             return vec4(TEXCOORD0.y,TEXCOORD0.x,0,1); 
             #else 
             return vec4(TEXCOORD0.x,TEXCOORD0.y,0,1); 
             #endif 
             }]]></Shader>
             </MaterialEvaluationShader>
          </Material>
       </MaterialTable>
    </Scene>
    
    

    # XML Elements & Evaluation Shader Setup

    XML Element API Object *Additional Info
    MaterialEvaluationShader MaterialEvaluationShader Root
    EvaluationFunction ShaderEvaluationFunction Required
    Attribute MaterialEvaluationShaderAttribute Optional
    ShaderParameterSamplerState ShaderParameterSamplerState Optional. (Is a ShaderParameter)
    ShaderParameterSampler ShaderParameterSampler Optional. (Is a ShaderParameter)
    ShaderParameterTexture ShaderParameterTexture Optional. (Is a ShaderParameter)
    ShaderParameterTextureArray ShaderParameterTextureArray Optional. (Is a ShaderParameter)
    Define MaterialEvaluationShaderDefine Optional
    Shader - Optional user (GLSL/HLSL) block as CDATA. Requires ShaderLanguage attribute be set on MaterialEvaluationShader element. Or use LoadMaterialEvaluationShaderFromFile(MaterialEvaluationShader) to load from file.

    # User-defined shader block

    User defined shader block define how to evaluate the pixel for the baked texture. It is a block of GLSL code. Its is a partial code block that is injected into the final compute shader generated by the compute caster. Any resources (i.e textures, geometry data fields from source geometry are define by the MaterialEvaluationShader object.

    vec4 AlphaOffset(in sampler2D alphaMask, vec2 uv, vec4 offset)
    {
     vec4 alphaMaks = texture(alphaMask, uv);
     vec4 maskOffset = alphaMaks - offset;
     vec4 clampOffset = clamp(maskOffset,0.0,1.0);
     return ceil(clampOffset);
    }
    
    vec4 BlendTextures(in sampler2D primary, in sampler2D secondary,vec2 UV,vec4 alphaMasks)
    {
     vec4 allOnes = vec4(1);
     
     vec4 primaryColor = texture(primary,UV);
     vec4 secondaryColor = texture(secondary,UV);
    
        vec4 oneMinus = allOnes-alphaMasks;
    
     vec4 filterPrimary =  oneMinus*primaryColor;
     vec4 filterSecondary =  alphaMasks*secondaryColor;
     return filterPrimary+filterSecondary;
    }
    
    // The Evaluation Function in XML as referenced in
    // TEXCOORD0 DiffuseA, DiffuseB & Mask are also referenced in the XML
    // These are the samplers and buffer resources that will be bound to the generated compute shader
    vec4 CastDiffuse()
    {
     vec4 diffuseColor;
     vec4 alphaMasks = AlphaOffset(Mask,TEXCOORD0,vec4(0.10));
     diffuseColor = BlendTextures(DiffuseA,DiffuseB, TEXCOORD0, alphaMasks);
     return diffuseColor;
    }
    
    

    # Loading Shader block and XML Example

    The following code snippet on how users can use the serializer to deserialize the XML to and MaterialEvaluationShader object and assign it to the Material.

      # Debugging

      To be able to debug compute caster's generated shader users can set a custom path on ComputeCaster object. See DebugOutputShaderFolder setting in Compute Caster.

      # Global override

      Simplygon selects a physical device based on the device properties. If you have multiple devices and want to override which device is used, use the global string setting "VulkanDevice" (SetGlobalVulkanDevice) to set the name of the device, or a substring of the name, and Simplygon will only consider devices which match the name. E.g. "3080", "AMD" or "NVidia GeForce 3070". To see all available device names, please look in the log, using an ELogLevel verbosity setting of at least Info.

      .

      # Limitations

      When using compute casters with pipeline you are limited to using RunScene method. RunSceneFromFile should be avoided as there is no clear way to define and read MaterialEvaluationShader or shader code when using third-party file formats. Therefore RunScene approach is recommended.

      # Example

      This example shows how to use the ComputeCaster material caster to make a custom casting using a compute shader written by the user.