Post

1. 在 EngineTypes.h 中添加新的模型枚举

假设新的模型名称为 MercuryToon

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
UENUM()
enum EMaterialShadingModel : int
{
    MSM_Unlit                     UMETA(DisplayName="Unlit"),
    MSM_DefaultLit                UMETA(DisplayName="Default Lit"),
    MSM_Subsurface                UMETA(DisplayName="Subsurface"),
    MSM_PreintegratedSkin         UMETA(DisplayName="Preintegrated Skin"),
    MSM_ClearCoat                 UMETA(DisplayName="Clear Coat"),
    MSM_SubsurfaceProfile         UMETA(DisplayName="Subsurface Profile"),
    MSM_TwoSidedFoliage           UMETA(DisplayName="Two Sided Foliage"),
    MSM_Hair                      UMETA(DisplayName="Hair"),
    MSM_Cloth                     UMETA(DisplayName="Cloth"),
    MSM_Eye                       UMETA(DisplayName="Eye"),
    MSM_SingleLayerWater          UMETA(DisplayName="SingleLayerWater"),
    MSM_ThinTranslucent           UMETA(DisplayName="Thin Translucent"),
    MSM_Strata                    UMETA(DisplayName="Substrate", Hidden),
    // BEGIN Engine Change [Shading Model]: Adding a new shading model enum
    MSM_MercuryToon               UMETA(DisplayName="MercuryToon"),
    // END Engine Change
    /** Number of unique shading models. */
    MSM_NUM                       UMETA(Hidden),
    /** Shading model will be determined by the Material Expression Graph,
        by utilizing the 'Shading Model' MaterialAttribute output pin. */
    MSM_FromMaterialExpression    UMETA(DisplayName="From Material Expression"),
    MSM_MAX
};

2. 在 MaterialExpressionShadingModel.h 中添加新的模型枚举选项

在 UPROPERTY 中添加 MSM_MercuryToon

1
2
UPROPERTY(EditAnywhere, Category=ShadingModel,  meta=(ValidEnumValues="MSM_DefaultLit, MSM_Subsurface, MSM_PreintegratedSkin, MSM_ClearCoat, MSM_SubsurfaceProfile, MSM_TwoSidedFoliage, MSM_Hair, MSM_Cloth, MSM_Eye, MSM_MercuryToon", ShowAsInputPin = "Primary"))  
TEnumAsByte<enum EMaterialShadingModel> ShadingModel = MSM_DefaultLit;

3. 在 MaterialShader.cpp 中添加着色模型到字符串的转换

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/** Converts an EMaterialShadingModel to a string description. */  
FString GetShadingModelString(EMaterialShadingModel ShadingModel)  
{  
    FString ShadingModelName;  
    switch(ShadingModel)  
    {
       case MSM_Unlit:                 ShadingModelName = TEXT("MSM_Unlit"); break;  
       case MSM_DefaultLit:            ShadingModelName = TEXT("MSM_DefaultLit"); break;  
       case MSM_Subsurface:            ShadingModelName = TEXT("MSM_Subsurface"); break;  
       case MSM_PreintegratedSkin:     ShadingModelName = TEXT("MSM_PreintegratedSkin"); break;  
       case MSM_ClearCoat:             ShadingModelName = TEXT("MSM_ClearCoat"); break;  
       case MSM_SubsurfaceProfile:     ShadingModelName = TEXT("MSM_SubsurfaceProfile"); break;  
       case MSM_TwoSidedFoliage:       ShadingModelName = TEXT("MSM_TwoSidedFoliage"); break;  
       case MSM_Hair:                  ShadingModelName = TEXT("MSM_Hair"); break;  
       case MSM_Cloth:                 ShadingModelName = TEXT("MSM_Cloth"); break;  
       case MSM_Eye:                   ShadingModelName = TEXT("MSM_Eye"); break;  
       case MSM_SingleLayerWater:      ShadingModelName = TEXT("MSM_SingleLayerWater"); break;  
       case MSM_ThinTranslucent:       ShadingModelName = TEXT("MSM_ThinTranslucent"); break;  
       // Mercury Toon Begin  
       case MSM_MercuryToon:           ShadingModelName = TEXT("MSM_MercuryToon"); break;  
       // Mercury Toon End  
       default: ShadingModelName = TEXT("Unknown"); break;  
    }
    return ShadingModelName;  
}

4. 在 MaterialShader.cpp 将着色模型设置为光照(Lit)模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void UpdateMaterialShaderCompilingStats(const FMaterial* Material)  
{  
    // ...
    FMaterialShadingModelField ShadingModels = Material->GetShadingModels();  
    if (ShadingModels.HasOnlyShadingModel(MSM_Unlit))  
    {
        INC_DWORD_STAT_BY(STAT_ShaderCompiling_NumUnlitMaterialShaders, 1);  
    }    
    // Mercury Toon Begin  
    else if (ShadingModels.HasAnyShadingModel({ MSM_DefaultLit, MSM_Subsurface, MSM_PreintegratedSkin, MSM_ClearCoat, MSM_Cloth, MSM_SubsurfaceProfile, MSM_TwoSidedFoliage, MSM_SingleLayerWater, MSM_ThinTranslucent, MSM_MercuryToon }))  
    // Mercury Toon End  
    {
        INC_DWORD_STAT_BY(STAT_ShaderCompiling_NumLitMaterialShaders, 1);  
    }  
    // ...
 }

5. 在 ShderMaterial.h 中添加 shading model 定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
struct FShaderMaterialPropertyDefines  
{  
    //DECLARE_TYPE_LAYOUT(FShaderMaterialPropertyDefines, NonVirtual);  

    //void ModifyEnvironment(FShaderCompilerEnvironment& OutEnvironment) const;    //void WriteFrozenVertexFactoryParameters(FMemoryImageWriter& Writer, const TMemoryImagePtr<FShaderMaterialPropertyDefines>& InPropDefines) const;  
    uint8 MATERIAL_ENABLE_TRANSLUCENCY_FOGGING : 1;  

    uint8 MATERIALBLENDING_ANY_TRANSLUCENT : 1;  
    uint8 MATERIAL_USES_SCENE_COLOR_COPY : 1;  
    uint8 MATERIALBLENDING_MASKED_USING_COVERAGE : 1;  

    uint8 MATERIAL_COMPUTE_FOG_PER_PIXEL : 1;  
    uint8 MATERIAL_SHADINGMODEL_UNLIT : 1;  

    uint8 MATERIAL_SHADINGMODEL_DEFAULT_LIT : 1;  
    uint8 MATERIAL_SHADINGMODEL_SUBSURFACE : 1;  
    uint8 MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN : 1;  
    uint8 MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE : 1;  
    uint8 MATERIAL_SHADINGMODEL_CLEAR_COAT : 1;  
    uint8 MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE : 1;  
    uint8 MATERIAL_SHADINGMODEL_HAIR : 1;  
    uint8 MATERIAL_SHADINGMODEL_CLOTH : 1;  
    uint8 MATERIAL_SHADINGMODEL_EYE : 1;  
    uint8 MATERIAL_SHADINGMODEL_SINGLELAYERWATER : 1;  
    uint8 SINGLE_LAYER_WATER_SEPARATED_MAIN_LIGHT : 1;  
    uint8 MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT : 1;  
    // Mercury Toon Begin  
    uint8 MATERIAL_SHADINGMODEL_MERCURY_TOON : 1;  
    // Mercury Toon End

    //...
}

6. 在 HLSLMaterialTranslator.cpp 中的 GetMaterialEnvironment 添加 shading model 环境定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void FHLSLMaterialTranslator::GetMaterialEnvironment(EShaderPlatform InPlatform, FShaderCompilerEnvironment& OutEnvironment)  
{
    if (EnvironmentDefines->bShadingModelsIsLit)  
    {  
        if (EnvironmentDefines->HasShadingModel(MSM_DefaultLit))  
        {
            OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_DEFAULT_LIT"), TEXT("1"));  
        }
        // Mercury Toon Begin  
        if (EnvironmentDefines->HasShadingModel(MSM_MercuryToon))
        {
            OutEnvironment.SetDefine(TEXT("MATERIAL_SHADINGMODEL_MERCURY_TOON"), TEXT("1"));  
        }// Mercury Toon End
    }
}

7. ShaderGenerationUtil.cpp

7.1 ApplyFetchEnvironmentInternal

添加我们的着色器相关指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename ContainerType>
void ApplyFetchEnvironmentInternal(FShaderMaterialPropertyDefines& SrcDefines, const ContainerType& Container)
{
    FETCH_COMPILE_BOOL(MATERIAL_ENABLE_TRANSLUCENCY_FOGGING);
    FETCH_COMPILE_BOOL(MATERIALBLENDING_ANY_TRANSLUCENT);
    FETCH_COMPILE_BOOL(MATERIAL_USES_SCENE_COLOR_COPY);
    FETCH_COMPILE_BOOL(MATERIALBLENDING_MASKED_USING_COVERAGE);

    FETCH_COMPILE_BOOL(MATERIAL_COMPUTE_FOG_PER_PIXEL);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_UNLIT);

    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_DEFAULT_LIT);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_SUBSURFACE);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_PREINTEGRATED_SKIN);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_SUBSURFACE_PROFILE);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_CLEAR_COAT);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_TWOSIDED_FOLIAGE);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_HAIR);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_CLOTH);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_EYE);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_SINGLELAYERWATER);
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_THIN_TRANSLUCENT);

    // Mercury Toon Begin
    FETCH_COMPILE_BOOL(MATERIAL_SHADINGMODEL_MERCURY_TOON);
    // Mercury Toon End

    // rest
}

7.2 SetSlotsForShadingModelType

设置我们的着色器 GBuffer Slot (不太确定有没有用,并且目前只是直接设置与 Unilt 模型相同的值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// If we are storing writing to the GBuffer, what slots does this shading model write? Ignores the other forward-shading effects
// like the dual blending of ThinTranslucent or fogging.
// If we are going one GBuffer for all shader model formats, then we treat all custom data as a single RGBA8 value (bMergeCustom=true). But
// if each shading model has a unique GBuffer format, we set it to false to pack each format as tightly as possible.
static void SetSlotsForShadingModelType(bool Slots[], EMaterialShadingModel ShadingModel, bool bMergeCustom)
{
    switch (ShadingModel)
    {
    // Mercury Toon Begin
    case MSM_MercuryToon:
    // Mercury Toon End    
    case MSM_Unlit:
        Slots[GBS_SceneColor] = true;
        break;
    // Rest
}

7.3 DetermineUsedMaterialSlots

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
static void DetermineUsedMaterialSlots(
    EGBufferSlotUsage Slots[GBS_Num],
    const FShaderMaterialDerivedDefines& Dst,
    const FShaderMaterialPropertyDefines& Mat,
    const FShaderLightmapPropertyDefines& Lightmap,
    const FShaderGlobalDefines& SrcGlobal,
    const FShaderCompilerDefines& Compiler,
    ERHIFeatureLevel::Type FEATURE_LEVEL)
{
    bool bWriteEmissive = Dst.NEEDS_BASEPASS_VERTEX_FOGGING || Mat.USES_EMISSIVE_COLOR || SrcGlobal.ALLOW_STATIC_LIGHTING || Mat.MATERIAL_SHADINGMODEL_SINGLELAYERWATER;
    bool bHasTangent = SrcGlobal.GBUFFER_HAS_TANGENT;
    bool bHasVelocity = SrcGlobal.GBUFFER_HAS_VELOCITY;
    bool bWritesVelocity = Dst.WRITES_VELOCITY_TO_GBUFFER;
    bool bHasStaticLighting = Dst.GBUFFER_HAS_PRECSHADOWFACTOR || Dst.WRITES_PRECSHADOWFACTOR_TO_GBUFFER;
    bool bIsSubstrateMaterial = Mat.SUBSTRATE_ENABLED; // Similarly to FetchFullGBufferInfo, we do not check for MATERIAL_IS_SUBSTRATE as this is decided per project.

    // Substrate doesn't use gbuffer, and thus doesn't need CustomData
    const bool bUseCustomData = !bIsSubstrateMaterial;

    // we have to use if statements, not switch or if/else statements because we can have multiple shader model ids.
    if (Mat.MATERIAL_SHADINGMODEL_UNLIT)
    {
        SetStandardGBufferSlots(Slots, true, false, bHasVelocity, bWritesVelocity, false, bIsSubstrateMaterial);
    }

    // Mercury Toon Begin: Setting the GBuffer slots used by our shading model and other configurations (copying emissive but with some changes). Forward rendering tho so this won't actually be called for us but might as well
    if (Mat.MATERIAL_SHADINGMODEL_LIGHTSMUGGLER)
    {
        SetStandardGBufferSlots(Slots, true, false, bHasVelocity, bWritesVelocity, bHasStaticLighting, bIsSubstrateMaterial);
    }
    // Mercury Toon End
    // Rest
}

8. Definitions.usf

1
2
3
4
5
// Mercury Toon Begin: Adding our shading model define, if it wasn't set from the HLSL Material translator, we set it to 0
#ifndef MATERIAL_SHADINGMODEL_LIGHTSMUGGLER
#define MATERIAL_SHADINGMODEL_LIGHTSMUGGLER                0
#endif 
// Mercury Toon End

9. ShadingCommon.ush

9.1 Shading Model ID Declaration

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
///////////////////////////////////////////////////////////////////////////////
// Shading models

// SHADINGMODELID_* occupy the 4 low bits of an 8bit channel and SKIP_* occupy the 4 high bits
#define SHADINGMODELID_UNLIT                0
#define SHADINGMODELID_DEFAULT_LIT            1
#define SHADINGMODELID_SUBSURFACE            2
#define SHADINGMODELID_PREINTEGRATED_SKIN    3
#define SHADINGMODELID_CLEAR_COAT            4
#define SHADINGMODELID_SUBSURFACE_PROFILE    5
#define SHADINGMODELID_TWOSIDED_FOLIAGE        6
#define SHADINGMODELID_HAIR                    7
#define SHADINGMODELID_CLOTH                8
#define SHADINGMODELID_EYE                    9
#define SHADINGMODELID_SINGLELAYERWATER        10
#define SHADINGMODELID_THIN_TRANSLUCENT        11
#define SHADINGMODELID_SUBSTRATE            12        // Temporary while we convert everything to Substrate
// Mercury Toon Begin
#define SHADINGMODELID_MERCURY_TOON        13
#define SHADINGMODELID_NUM                    14
// Mercury Toon End
#define SHADINGMODELID_MASK                    0xF        // 4 bits reserved for ShadingModelID        

9.2 GetShadingModelColor

给我们的着色模型定一个用于 Debug 显示的颜色

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
	// for debugging and to visualize
float3 GetShadingModelColor(uint ShadingModelID)
{
	// TODO: PS4 doesn't optimize out correctly the switch(), so it thinks it needs all the Samplers even if they get compiled out
	//	This will get fixed after launch per Sony...
#if PS4_PROFILE
		 if (ShadingModelID == SHADINGMODELID_UNLIT) return float3(0.1f, 0.1f, 0.2f); // Dark Blue
	else if (ShadingModelID == SHADINGMODELID_DEFAULT_LIT) return float3(0.1f, 1.0f, 0.1f); // Green
	else if (ShadingModelID == SHADINGMODELID_SUBSURFACE) return float3(1.0f, 0.1f, 0.1f); // Red
	else if (ShadingModelID == SHADINGMODELID_PREINTEGRATED_SKIN) return float3(0.6f, 0.4f, 0.1f); // Brown
	else if (ShadingModelID == SHADINGMODELID_CLEAR_COAT) return float3(0.1f, 0.4f, 0.4f); 
	else if (ShadingModelID == SHADINGMODELID_SUBSURFACE_PROFILE) return float3(0.2f, 0.6f, 0.5f); // Cyan
	else if (ShadingModelID == SHADINGMODELID_TWOSIDED_FOLIAGE) return float3(0.2f, 0.2f, 0.8f); // Blue
	else if (ShadingModelID == SHADINGMODELID_HAIR) return float3(0.6f, 0.1f, 0.5f);
	else if (ShadingModelID == SHADINGMODELID_CLOTH) return float3(0.7f, 1.0f, 1.0f); 
	else if (ShadingModelID == SHADINGMODELID_EYE) return float3(0.3f, 1.0f, 1.0f); 
	else if (ShadingModelID == SHADINGMODELID_SINGLELAYERWATER) return float3(0.5f, 0.5f, 1.0f);
	else if (ShadingModelID == SHADINGMODELID_THIN_TRANSLUCENT) return float3(1.0f, 0.8f, 0.3f);
	else if (ShadingModelID == SHADINGMODELID_SUBSTRATE) return float3(1.0f, 1.0f, 0.0f);
	// Mercury Toon Begin
	else if (ShadingModelID == SHADINGMODELID_Mercury_Toon) return float3(0.7f, 0.1f, 0.9f); // Purplish
	// Mercury Toon END
	else return float3(1.0f, 1.0f, 1.0f); // White
#else
	switch(ShadingModelID)
	{
		case SHADINGMODELID_UNLIT: return float3(0.1f, 0.1f, 0.2f); // Dark Blue
		case SHADINGMODELID_DEFAULT_LIT: return float3(0.1f, 1.0f, 0.1f); // Green
		case SHADINGMODELID_SUBSURFACE: return float3(1.0f, 0.1f, 0.1f); // Red
		case SHADINGMODELID_PREINTEGRATED_SKIN: return float3(0.6f, 0.4f, 0.1f); // Brown
		case SHADINGMODELID_CLEAR_COAT: return float3(0.1f, 0.4f, 0.4f); // Brown
		case SHADINGMODELID_SUBSURFACE_PROFILE: return float3(0.2f, 0.6f, 0.5f); // Cyan
		case SHADINGMODELID_TWOSIDED_FOLIAGE: return float3(0.2f, 0.2f, 0.8f); // Cyan
		case SHADINGMODELID_HAIR: return float3(0.6f, 0.1f, 0.5f);
		case SHADINGMODELID_CLOTH: return float3(0.7f, 1.0f, 1.0f);
		case SHADINGMODELID_EYE: return float3(0.3f, 1.0f, 1.0f);
		case SHADINGMODELID_SINGLELAYERWATER: return float3(0.5f, 0.5f, 1.0f);
		case SHADINGMODELID_THIN_TRANSLUCENT: return float3(1.0f, 0.8f, 0.3f);
		case SHADINGMODELID_SUBSTRATE: return float3(1.0f, 1.0f, 0.0f);
		// Mercury Toon Begin
		case SHADINGMODELID_Mercury_Toon: return float3(0.7f, 0.1f, 0.9f); // Purplish
		// Mercury Toon END
		default: return float3(1.0f, 1.0f, 1.0f); // White
	}
#endif
}

10. ClusteredDeferredShadingPixelShader.usf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/ Prior
 
// Regular lights
#if USE_PASS_PER_SHADING_MODEL
 
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_DEFAULT_LIT,			PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SUBSURFACE,			PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_PREINTEGRATED_SKIN,	PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
	GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_CLEAR_COAT,			PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SUBSURFACE_PROFILE,	PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_TWOSIDED_FOLIAGE,	PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
	GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_HAIR,				PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
	GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_CLOTH,				PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
	GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_EYE,					PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_SINGLELAYERWATER,	PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
 
// Mercury Toon Begin
  GET_LIGHT_GRID_LOCAL_LIGHTING_SINGLE_SM(SHADINGMODELID_LIGHTSMUGGLER,		PixelShadingModelID, CompositedLighting, ScreenUV, CulledLightGridData, Dither, FirstNonSimpleLightIndex);
// Mercury Toon END
	
	// SHADINGMODELID_THIN_TRANSLUCENT - skipping because it can not be opaque
#else // !USE_PASS_PER_SHADING_MODEL
  CompositedLighting += GetLightGridLocalLighting(GetScreenSpaceData(ScreenUV), CulledLightGridData, TranslatedWorldPosition, CameraVector, ScreenUV, SceneDepth, 0, Dither, FirstNonSimpleLightIndex);
#endif // USE_PASS_PER_SHADING_MODEL
 
// Rest
This post is licensed under CC BY 4.0 by the author.