• Assets

Substances

By: SKY ENGINE AI
scroll down ↓to find out more

Substance archives

In this section you will get to know how to use and store Substance files.

Agenda:

  • Workflow with SBSAR
  • SBSAR Inputs
  • SBSAR Outputs

Workflow with SBSAR files

The standard workflow of working with SBSAR files:

  1. You receive the Substance archive (SBSAR) file. It can be custom-made by the CGI artist or downloaded
    from the Adobe Substance 3D Assets or another source. Just make sure it's SBSAR - SBS or other formats
    are not supported.
  2. You put the SBSAR file to its own directory inside renderer/substances folder in the assets directory.
  3. You load the SubstanceTextureProvider giving the path to the directory as an asset definition.
    Direct path to the SBSAR file works too - it may come handy if there are more than 1 SBSAR file in one folder.
  4. You can assign this SubstanceTextureProvider to any object in the scene. The default settings from the SBSAR
    recipe will be applied, the Substance textures will be rendered and applied to the chosen object.
  5. If you go back to your SBSAR file, you'll notice an automatically generated accompanying JSON file there.
    In the file, you'll find all the inputs and outputs of the SBSAR recipe.
  6. You can use this config file to control the inputs values ranges with steps and generated outputs. These ranges
    will be than used by the randomization settings of the objects as the material parameters.

Let's now go step by step through this workflow. Firstly, let's prepare an empty directory wit a single SBSAR
file to simulate the initial circumstances.
To do so, we have to:

  1. Find an asset directory,
    Similar to HDRTextureProvider and FileTextureProvider cases you may access full path
    using renderer context's get_abs_path functionality
    ''' python
    renderer_context.get_abs_path("", AssetsTypes.SUBSTANCES)
    '''
  2. Create a temporary directory and copy substance file,
  3. Create a SubstanceTextureProvider (see PROVIDER_SubstanceTextureProvider tutorial) which will automatically
    create accompanying *.json file.
    import shutil
    from skyrenderer.basic_types.provider import SubstanceTextureProvider
    from skyrenderer.cases.utils.useful_snippets import list_dir
    from skyrenderer.cases.utils import MaterialsSceneComposer
    from skyrenderer.core.asset_manager.decorators import check_asset
    scene_composer = MaterialsSceneComposer(antialiasing_level=32)
    scene_composer.setup_scene()
    marble_red_dir = scene_composer.renderer_context.get_abs_path(
        "marble_red/marble_languedoc.sbsar", SubstanceTextureProvider.ASSETS_TYPE
    )
    list_dir(marble_red_dir.parent, scene_composer.logger)
    temp_marble_red_dir = scene_composer.renderer_context.get_abs_path(
        "marble_red_temp", SubstanceTextureProvider.ASSETS_TYPE
    )
    shutil.rmtree(temp_marble_red_dir, ignore_errors=True)
    temp_marble_red_dir.mkdir()
    check_asset(marble_red_dir)
    shutil.copy(marble_red_dir, temp_marble_red_dir)
    SubstanceTextureProvider(scene_composer.renderer_context, "marble_red_temp")
    list_dir(temp_marble_red_dir, scene_composer.logger)
2025-01-29 14:01:25,687 | skyrenderer.scene.renderer_context |  INFO: Root paths:
- root path: /dli/skyenvironment/skyrenderer/skyrenderer
- assets path: /dli/mount/assets
- config path: /dli/skyenvironment/skyrenderer/skyrenderer/config
- gpu sources path: /dli/skyenvironment/skyrenderer/skyrenderer/optix_sources/sources
- cache path: /dli/mount/cache
- ptx cache path: compiled_ptx/ptx
- ocio path: ocio_configs/aces_1.2/config.ocio

2025-01-29 14:01:25,721 | skyrenderer |  INFO: marble_red/marble_languedoc.sbsar

2025-01-29 14:01:25,722 | skyrenderer |  INFO: marble_red/marble_red.json

2025-01-29 14:01:26,045 | skyrenderer.basic_types.provider.unit_providers.substance_texture_provider |  WARNING: Unable to map purpose diffuse. Map will be parsed with legacy system.
	Purpose not present in ['ambientocclusion', 'basecolor', 'coatweight', 'coatroughness', 'emissive', 'height', 'metallic', 'normal', 'opacity', 'refractioncolor', 'roughness', 'specular', 'scattering', 'scatteringcolor', 'visibility', 'texturesemanticmap', 'any', 'mask']
	See: Texture and substance convention for more details. Remember, purpose mapping is case insensitive. 
	Parsed purpose : diffuse_map

2025-01-29 14:01:26,045 | skyrenderer.basic_types.provider.unit_providers.substance_texture_provider |  WARNING: Unable to map purpose glossiness. Map will be parsed with legacy system.
	Purpose not present in ['ambientocclusion', 'basecolor', 'coatweight', 'coatroughness', 'emissive', 'height', 'metallic', 'normal', 'opacity', 'refractioncolor', 'roughness', 'specular', 'scattering', 'scatteringcolor', 'visibility', 'texturesemanticmap', 'any', 'mask']
	See: Texture and substance convention for more details. Remember, purpose mapping is case insensitive. 
	Parsed purpose : glossiness_map

2025-01-29 14:01:26,047 | skyrenderer |  INFO: marble_red_temp/marble_languedoc.sbsar

2025-01-29 14:01:26,047 | skyrenderer |  INFO: marble_red_temp/marble_red_temp.json

NOTE: It's important to note that the SBSAR file doesn't have to be inside the dedicated assets directory,
you can give the absolute path to the SBSAR file or to the directory containing the SBSAR wherever in the file
system.

    from pathlib import Path
    local_dir = Path(".").joinpath("local_temp_dir").resolve()
    shutil.rmtree(local_dir, ignore_errors=True)
    local_dir.mkdir()
    shutil.copy(marble_red_dir, local_dir)
    local_marble_substance = local_dir.joinpath("marble_languedoc.sbsar")
    SubstanceTextureProvider(scene_composer.renderer_context, asset_def=local_marble_substance)
2025-01-29 14:01:26,064 | skyrenderer.basic_types.provider.unit_providers.substance_texture_provider |  WARNING: Unable to map purpose diffuse. Map will be parsed with legacy system.
	Purpose not present in ['ambientocclusion', 'basecolor', 'coatweight', 'coatroughness', 'emissive', 'height', 'metallic', 'normal', 'opacity', 'refractioncolor', 'roughness', 'specular', 'scattering', 'scatteringcolor', 'visibility', 'texturesemanticmap', 'any', 'mask']
	See: Texture and substance convention for more details. Remember, purpose mapping is case insensitive. 
	Parsed purpose : diffuse_map

2025-01-29 14:01:26,065 | skyrenderer.basic_types.provider.unit_providers.substance_texture_provider |  WARNING: Unable to map purpose glossiness. Map will be parsed with legacy system.
	Purpose not present in ['ambientocclusion', 'basecolor', 'coatweight', 'coatroughness', 'emissive', 'height', 'metallic', 'normal', 'opacity', 'refractioncolor', 'roughness', 'specular', 'scattering', 'scatteringcolor', 'visibility', 'texturesemanticmap', 'any', 'mask']
	See: Texture and substance convention for more details. Remember, purpose mapping is case insensitive. 
	Parsed purpose : glossiness_map

<skyrenderer.basic_types.provider.unit_providers.substance_texture_provider.SubstanceTextureProvider at 0x7f6b403dc240>

Let's see the automatically created JSON file.

    import json
    temp_marble_red_dir.joinpath(f"{temp_marble_red_dir.name}.json")
    with temp_marble_red_dir.joinpath(f"{temp_marble_red_dir.name}.json").open() as json_file:
        scene_composer.logger.info(f"\n{json.dumps(json.load(json_file), indent=4)}")
2025-01-29 14:01:26,080 | skyrenderer |  INFO: 
{
    "dir_name": "/dli/mount/assets/renderer/substances/marble_red_temp",
    "source_path": "marble_languedoc.sbsar",
    "graph": {
        "identifier": "pkg://marble_languedoc",
        "inputs": {
            "$outputsize": {
                "type": "int2",
                "label": null,
                "default": [
                    8,
                    8
                ],
                "min_value": [
                    8,
                    8
                ],
                "max_value": [
                    8,
                    8
                ],
                "step": null,
                "available_range": [
                    null,
                    null
                ],
                "min_available_step": null
            },
            "$randomseed": {
                "type": "int1",
                "label": null,
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": null,
                "available_range": [
                    null,
                    null
                ],
                "min_available_step": null
            },
            "marble_color": {
                "type": "float3",
                "label": "Marble Color",
                "default": [
                    0.560784,
                    0.133333,
                    0.054902
                ],
                "min_value": [
                    0.560784,
                    0.133333,
                    0.054902
                ],
                "max_value": [
                    0.560784,
                    0.133333,
                    0.054902
                ],
                "step": null,
                "available_range": [
                    null,
                    null
                ],
                "min_available_step": null
            },
            "color_adjustment": {
                "type": "float1",
                "label": "Color Adjustment",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "whithe_density": {
                "type": "float1",
                "label": "White Density",
                "default": 0.85,
                "min_value": 0.85,
                "max_value": 0.85,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "marble_roughness": {
                "type": "float1",
                "label": "Marble Roughness",
                "default": 0.3,
                "min_value": 0.3,
                "max_value": 0.3,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "diffuse": {
                "type": "int1",
                "label": "Diffuse",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "basecolor": {
                "type": "int1",
                "label": "Base Color",
                "default": 1,
                "min_value": 1,
                "max_value": 1,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "normal": {
                "type": "int1",
                "label": "Normal",
                "default": 1,
                "min_value": 1,
                "max_value": 1,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "specular": {
                "type": "int1",
                "label": "Specular",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "glossiness": {
                "type": "int1",
                "label": "Glossiness",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "roughness": {
                "type": "int1",
                "label": "Roughness",
                "default": 1,
                "min_value": 1,
                "max_value": 1,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "metallic": {
                "type": "int1",
                "label": "Metallic",
                "default": 1,
                "min_value": 1,
                "max_value": 1,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "height": {
                "type": "int1",
                "label": "Height",
                "default": 1,
                "min_value": 1,
                "max_value": 1,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "luminosity": {
                "type": "float1",
                "label": "Luminosity",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "contrast": {
                "type": "float1",
                "label": "Contrast",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 0.01,
                "available_range": [
                    -1,
                    1
                ],
                "min_available_step": 0.01
            },
            "hue_shift": {
                "type": "float1",
                "label": "Hue Shift",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "saturation": {
                "type": "float1",
                "label": "Saturation",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "normal_intensity": {
                "type": "float1",
                "label": "Normal Intensity",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "normal_format": {
                "type": "int1",
                "label": "Normal Format",
                "default": 0,
                "min_value": 0,
                "max_value": 0,
                "step": 1,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 1
            },
            "height_range": {
                "type": "float1",
                "label": "Height Range",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            },
            "height_position": {
                "type": "float1",
                "label": "Height Position",
                "default": 0.5,
                "min_value": 0.5,
                "max_value": 0.5,
                "step": 0.01,
                "available_range": [
                    0,
                    1
                ],
                "min_available_step": 0.01
            }
        },
        "outputs": {
            "diffuse": {
                "purpose": "diffuse"
            },
            "basecolor": {
                "purpose": "baseColor"
            },
            "normal": {
                "purpose": "normal"
            },
            "specular": {
                "purpose": "specular"
            },
            "glossiness": {
                "purpose": "glossiness"
            },
            "roughness": {
                "purpose": "roughness"
            },
            "metallic": {
                "purpose": "metallic"
            },
            "height": {
                "purpose": "height"
            }
        }
    }
}

SBSAR Inputs

Let's take one of the inputs as an example:

"large_damages_density": {
    "type": "float1",
    "default": 0.75,
    "min_value": 0.5,
    "max_value": 1.0,
    "step": 0.001,
    "available_range": [
        0,
        1
    ],
    "min_available_step": 0.01
}

The inputs are defined by the recipe creator. This one is called "large_damages_density". It's a float input with
one value. The default value specified by the creator is 0.75 and the default randomization range for this
input (min_value, max_value) is 0.5 and 1.0 respectively. The full range of values this input can take
(available_range) is 0-1. If we change the min_value to 0.1 and max_value to 0.9, the input will be automatically
randomized in this range.

The "step" parameter controls the difference between the consecutive values, so if we want to draw just
0.1, 0.2, 0.3, etc., we have to set "step" to 0.1.
There is an input that should be mentioned here, because of its standard presence in almost all SBSAR files.

Output size:

"$outputsize": {
    "type": "int2",
    "default": [
        8,
        8
    ],
    "min_value": [
        8,
        8
    ],
    "max_value": [
        8,
        8
    ],
    "step": null,
    "available_range": [
        null,
        null
    ],
    "min_available_step": null
}

The output size is defined by the power of two exponents. The default value, 8, means that by default this texture
has size: 2^8^ x 2^8^, which is 256x256 pixels.

SBSAR Outputs

The outputs are recognized by the "purpose" value. It's chosen by the recipe creators from a list of fixed
values, so it's more reliable than the output name, that can be chosen freely.

HINT: If you want to block an output map, you can simply remove it from the JSON file.
For example, by removing both baseColor and diffuse maps all the color will be gone, but the normal, roughness,
metallic, etc., are still working.

Summary

In this section you have learnt:

  • Substances are located normally in /renderer/substances folder, but they can be also loaded from any directory.
  • SkyRenderer will generate automatically JSON in the folder containing substance with inputs and outputs.
  • Substance input stores parameters used to procedurally generate textures by Substance.
  • Substance output stores information which maps will be generated.