# Shader Chunk Migrations

## Introduction

The PlayCanvas Engine's material shader chunk system is undergoing substantial changes in order to support a more flexible material system. Please see [this page](https://github.com/playcanvas/engine/issues/4250) for more context.

In order to help users migrate their existing custom shader chunks, this page lists the changes made to chunks and organizes them by engine release (starting v1.51).

## Chunk API Versions

The debug version of the Engine will report any API changes to the runtime console when it detects overridden chunks. For example:

[Image: Console output]

Once an application's chunks have been updated to the latest API they must be flagged as such. For example, after updating a material's custom chunks to the latest engine release (say v2.8), specify this in the chunks object as follows:

```javascript
const materialChunksGLSL = material.getShaderChunks(pc.SHADERLANGUAGE_GLSL);
materialChunksGLSL.set('diffusePS', '...');
material.shaderChunksVersion = '2.8';
```

By doing this you will no longer see warning messages in the console.

## Chunk changes

The following tables break down the chunk changes by Engine release.

### *Engine v2.23*

#### Scene environment textures use their own uniforms

Until v2.23, the scene's environment atlas and skybox reached a material's shader under the same uniform names as the material's own environment textures, `texture_envAtlas` and `texture_cubeMap`: the engine copied the scene textures into the parameters of every `StandardMaterial` lit by the scene environment. As of v2.23 the renderer publishes the scene textures as `scene_envAtlas` and `scene_skybox`, and a material publishes only the textures it owns. The lit environment chunks declare and sample the texture through the `{LIT_ENV_ATLAS}` and `{LIT_ENV_CUBEMAP}` tokens, which the shader generator resolves to the scene or the material uniform for each shader variant. See [PR #9364](https://github.com/playcanvas/engine/pull/9364) for details.

A custom override of one of these chunks that still hard-codes `texture_envAtlas` or `texture_cubeMap` keeps working on a material that has its own `envAtlas` or `cubeMap`, but receives no texture on a material lit by the scene environment: the sampler is never set, so the shader samples whatever texture happens to be bound to that unit.

Environment resolution is also all-or-nothing from v2.23: a material with any environment texture of its own (`envAtlas`, `cubeMap` or `sphereMap`) uses only material textures for reflections, ambient and refraction, and a material with just a `cubeMap` or `sphereMap` takes its ambient from the constant `ambientLight`. Previously such a material took its ambient from the scene atlas.

Affected chunks:

- `src/scene/shader-lib/glsl/chunks/lit/frag/ambient.js`
- `src/scene/shader-lib/glsl/chunks/lit/frag/reflectionEnv.js`
- `src/scene/shader-lib/glsl/chunks/lit/frag/reflectionEnvHQ.js`
- `src/scene/shader-lib/glsl/chunks/lit/frag/reflectionCube.js`
- `src/scene/shader-lib/wgsl/chunks/lit/frag/ambient.js`
- `src/scene/shader-lib/wgsl/chunks/lit/frag/reflectionEnv.js`
- `src/scene/shader-lib/wgsl/chunks/lit/frag/reflectionEnvHQ.js`
- `src/scene/shader-lib/wgsl/chunks/lit/frag/reflectionCube.js`

**Migration:** In custom `ambientPS`, `reflectionEnvPS`, `reflectionEnvHQPS` and `reflectionCubePS` overrides, replace `texture_envAtlas` with `{LIT_ENV_ATLAS}` and `texture_cubeMap` with `{LIT_ENV_CUBEMAP}`, both in the declaration and at every sample. In WGSL the sampler follows the same pattern, `{LIT_ENV_ATLAS}Sampler`. Then set `material.shaderChunksVersion = '2.23'`.

Before (GLSL):

```glsl
uniform sampler2D texture_envAtlas;

vec3 linearA = {reflectionDecode}(texture2D(texture_envAtlas, uv0));
```

After (GLSL):

```glsl
uniform sampler2D {LIT_ENV_ATLAS};

vec3 linearA = {reflectionDecode}(texture2D({LIT_ENV_ATLAS}, uv0));
```

Before (WGSL):

```wgsl
var texture_envAtlas: texture_2d<f32>;
var texture_envAtlasSampler: sampler;

let raw: vec4f = textureSample(texture_envAtlas, texture_envAtlasSampler, uv);
```

After (WGSL):

```wgsl
var {LIT_ENV_ATLAS}: texture_2d<f32>;
var {LIT_ENV_ATLAS}Sampler: sampler;

let raw: vec4f = textureSample({LIT_ENV_ATLAS}, {LIT_ENV_ATLAS}Sampler, uv);
```

#### `uScreenSize` → `screen_size`

Use `screen_size` in forward shaders. Change the GLSL declaration from `uniform vec4 uScreenSize;` to `uniform vec4 screen_size;`, or the WGSL declaration from `uniform uScreenSize: vec4f;` to `uniform screen_size: vec4f;`, and update every reference. The value remains the canvas drawing-buffer width, height, inverse width, and inverse height.

Existing shaders that explicitly declare `uScreenSize` continue to work, but debug builds emit a deprecation warning once. Changing `material.shaderChunksVersion` does not silence this warning; rename the uniform instead.

See [Built-in Shader Uniforms](https://developer.playcanvas.com/user-manual/graphics/shaders/built-in-uniforms.md) for details.

---

### *Engine v2.20*

#### MSDF text rendering reworked

The `msdfPS` chunk used by `ElementComponent` text was reworked to render glyphs at their true (designer-intended) edge and to anti-alias from the gradient of the distance field, fixing a fixed inward erosion that broke thin junctions (e.g. the crossbar of an `f`). See [PR #8935](https://github.com/playcanvas/engine/pull/8935) for details.

As part of this, the engine no longer sets the `font_pxrange` and `font_textureWidth` uniforms, and the `map()` helper was removed from the chunk. A custom `msdfPS` override copied from an earlier release that still references these uniforms will no longer receive their values (they default to `0`, producing a divide-by-zero in the old smoothing calculation), resulting in broken or invisible text.

Affected chunks:

- `src/scene/shader-lib/glsl/chunks/common/frag/msdf.js`
- `src/scene/shader-lib/wgsl/chunks/common/frag/msdf.js`

**Migration:** Update any custom `msdfPS` override against the latest chunk — remove references to `font_pxrange`, `font_textureWidth` and `map()`, and adopt the new edge/anti-aliasing logic. Deliberate weight adjustments should use the per-font `font_sdfIntensity` (or `outlineThickness`) rather than a global threshold bias.

---

### *Engine v2.16*

#### Gaussian Splat Accessor Function Renames

The gsplat shader accessor functions have been renamed for API consistency. All functions now use the `getXXX()` naming pattern.

| Old | New |
| --- | --- |
| `readCenter(source)` | `getCenter()` |
| `readColor(source)` | `getColor()` |

**Note:** `getCenter()` must be called before `getRotation()`, `getScale()`, or `getColor()` as it loads shared data used by the other functions. The `source` parameter is no longer required.

Affected chunks:

- `src/scene/shader-lib/glsl/chunks/gsplat/vert/gsplat.js`
- `src/scene/shader-lib/wgsl/chunks/gsplat/vert/gsplat.js`

#### gsplatCustomizeVS chunk removed

The `gsplatCustomizeVS` shader chunk has been removed in v2.16. It was deprecated in v2.15 in favor of `gsplatModifyVS`. Any code still using `gsplatCustomizeVS` will now trigger a warning that the chunk has been removed.

The old covariance-based helper functions (`gsplatExtractSize`, `gsplatApplyUniformScale`, `gsplatMakeRound`) have also been removed from `gsplatHelpers.js`.

**Migration:**

Users must migrate to `gsplatModifyVS` as documented in the v2.15 migration guide below. See the [v2.15 migration guide](https://developer.playcanvas.com/user-manual/graphics/shaders/migrations.md#engine-v215) for complete migration instructions.

---

### *Engine v2.15*

#### Gaussian Splat Shader Customization

The `gsplatCustomizeVS` shader chunk has been deprecated and replaced with `gsplatModifyVS`. The new chunk provides a more efficient API that uses rotation quaternion and scale vector instead of covariance matrices. See [PR #8246](https://github.com/playcanvas/engine/pull/8246) for details.

| Old (`gsplatCustomizeVS`) | New (`gsplatModifyVS`) |
| --- | --- |
| `modifyCenter(inout vec3 center)` | `modifySplatCenter(inout vec3 center)` |
| `modifyCovariance(originalCenter, modifiedCenter, inout covA, inout covB)` | `modifySplatRotationScale(originalCenter, modifiedCenter, inout rotation, inout scale)` |
| `modifyColor(center, inout color)` | `modifySplatColor(center, inout color)` |

Helper function changes:

| Old | New |
| --- | --- |
| `gsplatApplyUniformScale(covA, covB, scale)` | `scale *= factor` (direct multiplication) |
| `gsplatExtractSize(covA, covB)` | `gsplatGetSizeFromScale(scale)` |
| `gsplatMakeRound(covA, covB, radius)` | `gsplatMakeSpherical(scale, radius)` |

**Migration example (GLSL):**

Before:

```glsl
void modifyCenter(inout vec3 center) {
    center.y += 1.0;
}

void modifyCovariance(vec3 originalCenter, vec3 modifiedCenter, inout vec3 covA, inout vec3 covB) {
    gsplatApplyUniformScale(covA, covB, 2.0);
}

void modifyColor(vec3 center, inout vec4 color) {
    color.rgb *= 0.5;
}
```

After:

```glsl
void modifySplatCenter(inout vec3 center) {
    center.y += 1.0;
}

void modifySplatRotationScale(vec3 originalCenter, vec3 modifiedCenter, inout vec4 rotation, inout vec3 scale) {
    scale *= 2.0;
}

void modifySplatColor(vec3 center, inout vec4 color) {
    color.rgb *= 0.5;
}
```

**JavaScript usage:**

```javascript
// Before
gsplatMaterial.getShaderChunks(shaderLanguage).set('gsplatCustomizeVS', customShader);

// After
gsplatMaterial.getShaderChunks(shaderLanguage).set('gsplatModifyVS', customShader);
```

---

### *Engine v2.6*

#### Internal engine chunks

The following vertex shader chunks were removed and replaced by a single `litMainVS` chunk:

- `endVS`
- `startVS`
- `baseVS`
- `viewNormalVS`
- `baseNineSlicedVS`

`lightmapDirAddPS` chunk has been removed, and its functionality integrated into `lightmapAddPS` chunk.

`TBNderivativePS` and `TBNObjectSpacePS` chunks were removed, and their functionality integrated into `TBNPS` chunk.

`startPS` chunk has been removed, and a replacement larger chunk will be added at a later stage.

`outputAlphaOpaquePS` and `outputAlphaPremulPS` chunks were merged into `outputAlphaPS` chunk.

`cubeMapProjectBoxPS` and `cubeMapProjectNonePS` chunks were merged into `cubeMapProjectPS` chunk.

`envMultiplyPS` and `envConstPS` were merged into `envProcPS` chunk.

`aoSpecOccSimplePS`, `aoSpecOccConstSimplePS`, `aoSpecOccPS` and `aoSpecOccConstPS` chunks were merged into `aoSpecOccPS` chunk.

`shadowSampleCoordPS` chunk has been removed, and its content is now part of `lightFunctionPS` chunk.

The following reflection related chunks had a slight change in how the texture decode function is provided. `$DECODE` is now `{reflectionDecode}` and `$DECODE_CUBEMAP` is now `{reflectionCubemapDecode}`. These chunks were affected:

- `reflectionEnvPS`
- `reflectionEnvHQPS`
- `reflectionCubePS`
- `reflectionSpherePS`

The following ambient lighting related chunks had been removed, and merged into a single `ambientPS` chunk:

- `ambientConstantPS`
- `ambientEnvPS`
- `ambientSHPS`

### *Engine v2.5*

The following chunks were removed and replaced by a single `fogPS` chunk:

- `fogExpPS`
- `fogExp2PS`
- `fogLinearPS`
- `fogNonePS`

The following chunks were removed and replaced by a single `gammaPS` chunk:

- `gamma1_0PS`
- `gamma2_2PS`

### *Engine v1.70*

| Chunk | Changes |
| ---   | ---     |
| `refractionDynamicPS` | <ul><li>Now accepts additional parameter `float dispersion`.</li></ul> |
| `refractionCubePS` | <ul><li>Now accepts additional parameter `float dispersion`.</li></ul> |

### *Engine v1.65*

In 1.62, global variables used to pass the values between the front end back end chunks were grouped into structures LitShaderArguments, IridescenceArgs, ClearcoatArgs and SheenArgs. Those were causing multiple compatibility issues on Android devices, and so in 1.65, these are being converted back to global variables. For example `litShaderArgs.albedo` is now `litArgs_albedo`.

These are the new global variables:

```glsl
// Surface albedo absorbance
vec3 litArgs_albedo;

// Transparency
float litArgs_opacity;

// Emission color
vec3 litArgs_emission;

// Normal direction in world space
vec3 litArgs_worldNormal;

// Ambient occlusion amount, range [0..1]
float litArgs_ao;

// Light map color
vec3 litArgs_lightmap;

// Light map direction
vec3 litArgs_lightmapDir;

// Surface metalness factor, range [0..1]
float litArgs_metalness;

// The f0 specularity factor
vec3 litArgs_specularity;

// Specularity intensity factor, range [0..1]
float litArgs_specularityFactor;

// The microfacet glossiness factor, range [0..1]
float litArgs_gloss;

// Glossiness of the sheen layer, range [0..1]
float litArgs_sheen_gloss;

// The color of the f0 specularity factor for the sheen layer
vec3 litArgs_sheen_specularity;

// Transmission factor (refraction), range [0..1]
float litArgs_transmission;

// Uniform thickness of medium, used by transmission, range [0..inf]
float litArgs_thickness;

// Index of refraction
float litArgs_ior;

// Iridescence effect intensity, range [0..1]
float litArgs_iridescence_intensity;

// Thickness of the iridescent microfilm layer, value is in nanometers, range [0..1000]
float litArgs_iridescence_thickness;

// The normal used for the clearcoat layer
vec3 litArgs_clearcoat_worldNormal;

// Intensity of the clearcoat layer, range [0..1]
float litArgs_clearcoat_specularity;

// Glossiness of clearcoat layer, range [0..1]
float litArgs_clearcoat_gloss;
```

These are the chunk that had their signature changed to accept individual members, instead of the whole structures:

- endPS
- metalnessModulatePS
- outputAlphaPS
- outputAlphaPremulPS
- fresnelSchlickPS
- iridescenceDiffractionPS
- lightmapAddPS
- lightmapDirAddPS
- refractionCubePS
- refractionDynamicPS

### *Engine v1.62*

In PlayCanvas, we have two sets of shader chunks, one set we refer to as the shader frontend, which provide values for the arguments passed to our lighting algorithm, also called the shader backend.

With 1.62, we are creating a clearer distinction between these two, such that the values passed to the backend are well defined and known in advance, not automatically generated. This allows for writing a fully custom shader that can interface with our lighting code just like how our native materials do.

As a result of that, almost all backend chunks have been changed to accommodate for the split. This means that any custom backend shader chunks must move away from using globals to using the arguments passed to them by the lighting backend.

This change also makes some chunks, such as the clearcoat specific ones, redundant, as their functions have become reusable when they're no longer reliant on global values.

#### Changes

This release breaks most lit/frag chunks. Most of these chunks have had their signatures changed to accept the various values they need, instead of relying on globals. With that said, most globals are still set in the shader. An example of this change is:

```glsl
vec3 combineColor() {
    vec3 ret = vec3(0);
    ret = dAlbedo * dDiffuseLight;
    ...
}
```

Is now expressed:

```glsl
vec3 combineColor(vec3 albedo, vec3 sheenSpecularity, float clearcoatSpecularity) {
    vec3 ret = vec3(0);
    ret = albedo * dDiffuseLight;
    ...
}
```

Where we previously had globals, in 1.62 they are packed into structs, these structs are the primary LitShaderArgs which is defined as such:

```glsl
struct LitShaderArguments
{
    // Transparency
    float opacity;

    // Normal direction in world space
    vec3 worldNormal;

    // Surface albedo absorbance
    vec3 albedo;

    // Transmission factor (refraction), range [0..1]
    float transmission;

    // Uniform thickness of medium, used by transmission, range [0..inf]
    float thickness;

    // The f0 specularity factor
    vec3 specularity;

    // The microfacet glossiness factor, range [0..1]
    float gloss;

    // Surface metalness factor, range [0..1]
    float metalness;

    // Specularity intensity factor, range [0..1]
    float specularityFactor;

    // Ambient occlusion amount, range [0..1]
    float ao;

    // Emission color
    vec3 emission;

    // Light map color
    vec3 lightmap;

    // Light map direction
    vec3 lightmapDir;

    // Iridescence extension arguments
    IridescenceArgs iridescence;

    // Clearcoat extension arguments
    ClearcoatArgs clearcoat;

    // Sheen extension arguments
    SheenArgs sheen;
};
```

The last three arguments are our shading extensions. IridescenceArgs is defined as such:

```glsl
struct IridescenceArgs
{
    // Iridescence effect intensity, range [0..1]
    float intensity;

    // Thickness of the iridescent microfilm layer, value is in nanometers, range [0..1000]
    float thickness;
};
```

ClearcoatArgs:

```glsl
struct ClearcoatArgs
{
    // Intensity of the clearcoat layer, range [0..1]
    float specularity;

    // Glossiness of clearcoat layer, range [0..1]
    float gloss;

    // The normal used for the clearcoat layer
    vec3 worldNormal;
};
```

SheenArgs:

```glsl
struct SheenArgs
{
    // Glossiness of the sheen layer, range [0..1]
    float gloss;

    // The color of the f0 specularity factor for the sheen layer
    vec3 specularity;
};
```

| Chunk | Changes |
| --- | --- |
| `ambient(Constant/Env/SH)` | <ul><li>Accepts a vec3 for the world normal instead of using `dNormalW`</li></ul> |
| `aoDiffuseOcc` | <ul><li>Accepts a float value for the AO, instead of using `dAO`</li></ul> |
| `aoSpec(Occ/OccConst/OccConstSimple/OccSimple)` | <ul><li>Accepts float gloss, float ao, a vec3 world normal and a vec3 view direction instead of using `dGlossiness`, `dAo`, `dNormalW` and `dViewDirW`</li></ul> |
| `combine` | <ul><li>Accepts vec3 for albedo, sheen specularity and a float for clearcoat specularity instead of using `dAlbedo`, `sSpecularity` and `ccSpecularity`</li></ul> |
| `clusteredLight` | <ul><li>Reliance on globals have been reduced to only `dLightPosW`, `dLightDirW`, `dLightDirNormW` and `dShadowCoord` which is initialized per light</li></ul> |
| `clusteredLightShadow` | <ul><li>For omni lights, generates a local variable instead of relying on `dShadowCoord`. For spot lights, accepts the shadow coordinate instead of using `dShadowCoord` as before</li></ul> |
| `combine` | <ul><li>Accepts vec3 albedo, vec3 sheen specularity and float clearcoat specularity instead of using `dAlbedo`, `sSpecularity` and `ccSpecularity`</li></ul> |
| `end` | <ul><li>Passes albedo, sheen specularity and clearcoat specularity to combine using `litShaderArgs`, uses `litShaderArgs.emission` instead of relying on `dEmission`</li></ul> |
| `fallOff(InvSquared/Linear)` | <ul><li>Accepts a float light radius and a vec3 light direction instead of using `dLightDirW`</li></ul> |
| `fresnelSchlick` | <ul><li>Accepts gloss and `IridescenceArgs` instead of relying on `dGlossiness`, `dIridescenceFresnel` and `dIridescence`</li></ul> |
| `iridescenceDiffraction` | <ul><li>Accepts a float as iridescence thickness instead of using `dIridescenceThickness`</li></ul> |
| `lightDiffuseLambert` | <ul><li>Accepts vec3 world normal, a vec3 view direction, a vec3 light direction and a vec3 normalized light direction instead of using `dNormalW`, `dViewDirW`, `dLightDirW` and `dLightDirNormW`</li></ul> |
| `lightSheen` | <ul><li>Accepts a vec3 half vector, a vec3 world normal, a vec3 view direction, a vec3 normalized light direction and a float gloss instead of relying on `dNormalW`, `dViewDirW`, `dLightDirNormW` and `dGlossiness`</li></ul> |
| `lightSpecular(AnisoGGX/Blinn/Phong)` | <ul><li>Accepts a vec3 half vector for the reflection, a vec3 reflection direction (used by Phong only), a vec3 world normal, a vec3 view dir, a float gloss value and a 3x3 matrix for the TBN, instead of relying on `dReflDirW`, `dNormalW`, `dViewDirW`, `dGlossiness/ccGlossiness` and `dTBN`</li></ul> |
| `lightmap(DirAdd/Add)` | <ul><li>Accepts a vec3 lightmap value, a vec3 lightmap direction, a vec3 world normal, a vec3 view direction, float gloss, vec3 specularity, a read-write vec3 normalized light direction, a vec3 geometric normal and IridescenceArgs instead of relying on `dLightMap`, `dLightmapDir`, `dNormalW`, `dViewDirW`, `dGlossiness`, `dVertexNormalW` and `dSpecularity`</li></ul> |
| `ltc` | <ul><li>No longer uses `dViewDirW`, `dNormalW`, `dGlossiness`, `dSpecularity`, `ccGlossiness`, `ccSpecularity` and `dLightDirW`, but instead relies on their values being passed as arguments</li></ul> |
| `metalnessModulate` | <ul><li>Accepts a `LitShaderArguments` struct which is updated by the chunk. Removes the reliance on `dSpecularity`, `dMetalness` and `dAlbedo`</li></ul> |
| `output(Alpha/AlphaPremul)` | <ul><li>Uses `litShaderArgs.opacity` instead of `dAlpha`</li></ul> |
| `reflDir(Aniso)` | <ul><li>Accepts a vec3 world normal, a vec3 view direction, a float value for gloss and 3x3 matrix for the TBN, instead of using `dGlossiness`, `dViewDirW`, `dNormalW` and `dTBN`</li></ul> |
| `reflection(CC/Cube/Env/EnvHQ/Sphere/SphereLow)` | <ul><li>Accepts a vec3 reflection direction and a float gloss value instead of using `dReflDirW`/`ccReflDirW` and `dGlossiness`</li></ul> |
| `reflectionSheen` | <ul><li>Accepts a vec3 world normal, a vec3 view direction and a float gloss value instead of using `dNormalW`, `dViewDirW` and `sGlossiness`</li></ul> |
| `refraction(Cube/Dynamic)` | <ul><li>Accepts a vec3 world normal, float thickness, float gloss, vec3 specularity, vec3 albedo, float transmission and `IridescenceArgs` instead of using `dNormalW`, `dAlbedo`, `dTransmission`, `dThickness`, `dGlossiness`, `dSpecularity` and passes the iridescence arguments to the fresnel function</li></ul> |
| `shadow(Common/Coord/CoordPerspZBuffer` | <ul><li>Accepts a permutation of a vec3 light direction, a vec3 light position, a vec3 normalized light direction and a vec3 geometric normal instead of using `dLightDirW`, `dLightPosW`, `dLightDirNormW` and `dVertexNormalW` and instead accepts them as arguments. The permutation depends on the requirements for the different shadow coordinate functions</li></ul> |
| `shadow(EVSM/EVSMn/Standard/StandardGL2/VSM8)` | <ul><li>Accepts a vec3 shadow sample coordinate instead of using `dShadowCoord`</li></ul> |
| `spot` | <ul><li>Accepts a vec3 normalized light direction instead of using `dLightDirNormW`</li></ul> |
| `TBN(-/ObjectSpace/derivative/fast)` | <ul><li>Accepts a vec3 tangent, binormal and normal instead of using `dTangentW`, `dBinormalW` and `dNormalW`</li></ul> |

---

### Engine v1.60

| Chunk | Changes |
| ---   | ---     |
| `clearCoatGlossPS` | <ul><li>Renamed uniform `material_clearCoatGlossiness` to `material_clearCoatGloss`.</li></ul> |
| `glossPS`          | <ul><li>Renamed uniform `material_glossiness` to `material_gloss`.</li></ul> |
| `sheenGlossPS`     | <ul><li>Renamed uniform `material_sheenGlossiness` to `material_sheenGloss`.</li></ul> |

---

### Engine v1.57

In 1.57, almost all front-end chunks have been changed to minimize the amount of samplers used by the shader. This is an optional feature, however it's recommended to follow the same coding style to reduce the amount of samplers used by the shader. The following chunks are affected by it:

| Chunk  |
| --- |
| `aoPS` |
| `clearCoatPS` |
| `clearCoatGlossPS` |
| `clearCoatNormalPS` |
| `diffusePS` |
| `diffuseDetailMapPS` |
| `emissivePS` |
| `metalnessPS` |
| `normalMapPS` |
| `normalDetailMapPS` |
| `opacityPS` |
| `parallaxPS` |
| `sheenPS` |
| `sheenGlossPS` |
| `specularPS` |
| `specularityFactorPS` |
| `thicknessPS` |
| `transmissionPS` |

This is also supported in custom front-end chunks, given that your chunk piggybacks on the pre-existing material samplers. To support this method in your chunks, what you'd need to do is:

- Remove the sampler uniform declaration from the chunk
- Replace the sampler name with the `$SAMPLER` macro

For example:

```glsl
uniform sampler2D texture_aoMap;
void getAO() {
    dAo = 1.0;

    #ifdef MAPTEXTURE
    dAo *= texture2DBias(texture_aoMap, $UV, textureBias).$CH;
    #endif

    #ifdef MAPVERTEX
    dAo *= saturate(vVertexColor.$VC);
    #endif
}
```

Would be converted to:

```glsl
void getAO() {
    dAo = 1.0;

    #ifdef MAPTEXTURE
    dAo *= texture2DBias($SAMPLER, $UV, textureBias).$CH;
    #endif

    #ifdef MAPVERTEX
    dAo *= saturate(vVertexColor.$VC);
    #endif
}
```

This allows the engine to automatically pick the sampler uniform to use, thus potentially reducing the total number of samplers. But note, this is only supported for front-end chunks.

---

### Engine v1.56

| Chunk | Changes |
| ---   | ---     |
| `combineXXXX` | <ul><li>all combine chunks except for `combinePS` have been deleted.</li><li>instead, combinePS is controlled with a handful of preprocessor defines.</li></ul> |
| `refractionPS` | <ul><li>split into two new chunks, `refractionCubePS` and `refractionDynamicPS`.</li></ul> |
| `refractionCubePS` | <ul><li>the old `refractionPS` is identical to this one, uses a cube map for refractions.</li></ul> |
| `refractionDynamicPS` | <ul><li>new chunk which supports dynamic refractions by using the grab pass, needs `requestSceneColorMap(true);` to be set on the camera to work.</li></ul> |
| `sheenPS` | <ul><li>new chunk to provide sheen (fabric) color.</li></ul> |
| `sheenGlossPS` | <ul><li>new chunk to provide sheen (fabric) glossiness.</li></ul> |
| `reflectionEnvHQPS` | <ul><li>new chunk to provide a high quality specular environment map for reflections and refractions.</li></ul> |
| `thicknessPS` | <ul><li>new chunk to provide thickness which modifies attenuation color for transmissive (transparent/refractive) materials.</li></ul> |
| `bakeDirLmEndPs` | <ul><li>moved to `chunks-lightmapper.js`.</li></ul> |
| `bakeLmEndPS` | <ul><li>moved to `chunks-lightmapper.js`.</li></ul> |

---

### Engine v1.55

| Chunk | Changes |
| --- | --- |
| `clearCoatNormalPS` | <ul><li>refrain from generating world CC reflection, now done on the backend instead</li><li>normalize final world space normal</li></ul> |
| `clusteredLightPS` | <ul><li>remove dead code.</li><li>the `CLUSTER_XXX` macros have been renamed to `LIT_XXX`.</li><li>each light calculates fresnel</li></ul> |
| `combinePS` | <ul><li>new chunk to replace all the other combine chunks.</li></ul> |
| `combineXXXX` | <ul><li>combine chunk variations have been made deprecated and replaced with a single chunk.</li></ul> |
| `diffusePS` | <ul><li>fix gamma handling relative to albedo detail</li></ul> |
| `diffuseDetailMapPS` | <ul><li>gamma correct detail map before combining with base albedo</li></ul> |
| `endPS` | <ul><li>combine emissive with `dEmissive` instead of a call to `getEmission()`</li><li>`CLEARCOAT` macro is now `LIT_CLEARCOAT`.</li></ul> |
| `emissivePS` | <ul><li>set `dEmission` global instead of returning the value in order to bring it in line with the other frontend components</li></ul> |
| `fresnelSchlickPS` | <ul><li>fresnel effect now reacts to index of refraction.</li><li>no longer changes specularity global, but returns value to be used per-light and for the environment</li></ul> |
| `lightmapSingleVert.js` | <ul><li>removed (unused)</li></ul> |
| `lightmapDirPS`, `lightmapSinglePS`| <ul><li>renamed the lightmap function to `getLightMap()` instead of `addLightMap()`</li><li>changed the implementation to write `dLightmap` and `dLightmapDir` global instead of updating `dDiffuseLight` and `dSpecularLight` directly</li><li>backend now handles combining lightmap in `lightmapAddPS` and `lightmapDirAddPS`</li></ul> |
| `lightmapAddPS`, `lightmapDirAddPS` | <ul><li>new chunks for adding the lightmap values passed in from the backend</li><li>`CLEARCOAT` macro replaced with `LIT_CLEARCOAT`.</li></ul> |
| `lightSpecularAnisoGGXPS` | <ul><li>`CLEARCOAT` define replaced with `LIT_CLEARCOAT`</li></ul> |
| `lightSpecularBlinnPS`, `lightSpecularPhongPS` | <ul><li>added clear coat `#define`, removed call to `antiAliasGlossiness()`</li></ul> |
| `ltcPS` | <ul><li>`CLEARCOAT` macro replaced with `LIT_CLEARCOAT`.</li></ul> |
| `normalMapFastPS` | <ul><li>removed</li></ul> |
| `normalMapPS` | <ul><li>added `MAPTEXTURE` #define like the other chunks</li><li>normalize final normal</li><li>when normal texture isn't defined, calculate normal from geometry normal instead</li></ul> |
| `normalDetailMapPS` | <ul><li>remove two (mostly) unnecessary calls to `normalize` - final normal is normalized instead</li></ul> |
| `normalVertexPS` | <ul><li>removed chunk, moved functionality to `normalMapPS` frontend chunk</li></ul> |
| `metalnessPS` | <ul><li>now controls metalness in front end and is not exclusive of `specularPS`</li></ul> |
| `metalnessModulatePS` | <ul><li>new chunk to control how specular color is modulated based on specular color and albedo with regards to metalness</li></ul> |
| `reflectionCC` | <ul><li>`CLEARCOAT` define replaced with `LIT_CLEARCOAT`.</li></ul> |
| `specularAaNonePS`, `specularAaToksvigPS`, `specularAaToksvigFastPS` | <ul><li>removed</li></ul> |
| `startPS` | <ul><li>removed global declarations, generate them on demand instead</li><li>`CLEARCOAT` macro replaced with `LIT_CLEARCOAT`.</li></ul> |
| `specularPS` | <ul><li>only provides specular color, metalness modulation is now done in backend.</li></ul> |
| `specularityFactorPS` | <ul><li>new chunk to control specular intensity for metalness workflow.</li></ul> |
