Material Layers

Getting Started

Download

Available in the Godot Asset Store in editor. See all releases.

How does it work

Material Layers introduces 4 new resources.

layerStack

LayerStack: Contains MaterialLayer resource and generates final material.

materialLayer

MaterialLayer: Contains SurfaceMaterial & mask texture or MaskMaterial.

surfaceMaterial

SurfaceMaterial: Material that's blended using mask texture or MaskMaterial. Contains a .gdshader template with new layer specific inputs & outputs. You write your own material, then output them using the new output tokens.

maskMaterial

MaskMaterial: Material used to blend SurfaceMaterials. You write your own blending logic such as height blending, vertex colors or sample textures. You control how each material attribute is blended.

Writing SurfaceMaterials

Create a new SurfaceMaterial, you can create it from the file system dock, a material slot or inside a MaterialLayer. SurfaceMaterial functions the same as a ShaderMaterial, the only difference is it contains a .gdshader template for writing Material Layer shaders. You write material shaders in gdshader as usual. But instead of writing to ALBEDO, ROUGHNESS etc. you write to layer-specific outputs such as LAYER_OUT_ALBEDO, LAYER_OUT_ROUGHNESS. See all tokens

#include "res://addons//materialLayers/src/layer_lib.gdshaderinc" // Essential for Material Layering

void fragment() {

	SETUP_LAYER_FRAGMENT; // Sets up Material Layering

	//-------------------------------------

    vec2 uv = getUV(UVSelect, UV, UV2, custom0, custom1, custom2);
    uv *= UVScale;
    uv = uvManip(uv, vec2(UVScale), UVRot, pivot, UVOffset);

	vec4 norm_ao_height = texture(normAOHeight, uv);
    vec3 albedo = texture(albedoGradient, vec2(norm_ao_height.a, 0.5)).rgb;

	float roughness = histRange(1.0 - norm_ao_height.a, roughnessRange, roughnessPos);
    roughness = mix(roughness, 1.0 - roughness, float(invertRoughness));
    roughness = saturate(roughness);

    vec3 normal = deriveZ(norm_ao_height.r, norm_ao_height.g);
	float ao = norm_ao_height.b;
	float height = norm_ao_height.a;

	albedo *= tint;
	albedo = albedo * (albedoBrightness + 1.0);
	albedo = (albedo - 0.5) * max(albedoContrast + 1.0, 0.0) + 0.5;
	albedo = clamp(albedo, 0.0, 1.0);

	normal = normalStrength(normal, normalIntensity);

	ao += aoLevel;
	ao = clamp(ao, 0.0, 1.0);

	//------------ Outputs Layer maps --------------

	LAYER_OUT_ALBEDO = albedo;
	LAYER_OUT_ROUGHNESS = roughness;
	LAYER_OUT_NORMAL_MAP = normal;
	LAYER_OUT_AO = ao;
	LAYER_OUT_HEIGHT = height;

	//------------ Default Material Outputs --------------

	ALBEDO = albedo;
	ROUGHNESS = roughness;
	NORMAL_MAP = normal;
	AO = ao;

}

Keep in mind, you can still write to the default ALBEDO, ROUGHNESS and use it as a standalone material. It's also needed to generate preview thumbnails.

Writing MaskMaterials

MaskMaterial lets you blend materials using your own logic in .gdshader. You can control how each material attribute is blended, use height blending, vertex colors, position and normal based etc. You just have to use the RESULT_ tokens in order to output the blend results. You are free to do whatever you want, because it's essentially gdshader with some new tokens.

#include "res://addons//materialLayers/src/layer_lib.gdshaderinc" // Essential for Material Layering

void fragment() {

    SETUP_LAYER_FRAGMENT; // Sets up Material Layering

    //-------------------------------------

    float vertex_color = getChannel(COLOR, vertexColor);

    float mask = heightBlend(
        mix(1.0 - LAYER_BELOW_HEIGHT,
        LAYER_BELOW_HEIGHT, float(invertHeight)),
        LAYER_CURRENT_HEIGHT, heightOffset,
        heightContrast, vertex_color);

    //------------ Blend the Layer Below and the Current Layer --------------

    RESULT_ALBEDO = mix(LAYER_BELOW_ALBEDO, LAYER_CURRENT_ALBEDO, mask);
    RESULT_ROUGHNESS = mix(LAYER_BELOW_ROUGHNESS, LAYER_CURRENT_ROUGHNESS, mask);
    RESULT_NORMAL_MAP = normalCombine(LAYER_BELOW_NORMAL_MAP, LAYER_CURRENT_NORMAL_MAP, mask);
    RESULT_AO = LAYER_BELOW_AO * mix(1.0, LAYER_CURRENT_AO, mask);
    RESULT_HEIGHT = LAYER_BELOW_HEIGHT;
    RESULT_METALLIC = mix(LAYER_BELOW_METALLIC, LAYER_CURRENT_METALLIC, mask);

}

Both SurfaceMaterial and MaskMaterial must have the #include at the top, and SETUP_LAYER_FRAGMENT at the top of fragment shader and SETUP_LAYER_VERTEX at the top of vertex shader.

Blending Two SurfaceMaterials

Create a new LayerStack from the material slot.

layerStack2layerStack1

Assign a SurfaceMaterial to the Base Layer slot.

baseLayer baseLayerMat

Then create a new Material Layer and assign a different SurfaceMaterial to the Surface Material slot.

materialLayer1materialLayer2

You can either use a mask texture...

textureMask

or a MaskMaterial

maskMaterial

New Tokens

This plugin introduces some new Macros and Tokens used for Material Layering.

SurfaceMaterial Tokens

Surface Map Tokens

Outputs the layer's Surface mapsGets the layer below's Surface maps
LAYER_OUT_ALBEDOLAYER_BELOW_ALBEDO
LAYER_OUT_NORMAL_MAPLAYER_BELOW_NORMAL_MAP
LAYER_OUT_ROUGHNESSLAYER_BELOW_ROUGHNESS
LAYER_OUT_HEIGHTLAYER_BELOW_HEIGHT
LAYER_OUT_AOLAYER_BELOW_AO
LAYER_OUT_METALLICLAYER_BELOW_METALLIC
LAYER_OUT_EMISSIONLAYER_BELOW_EMISSION

Mesh Map Tokens

Outputs the layer's Mesh mapsGets the layer below's Mesh maps
LAYER_OUT_BENT_NORMALLAYER_BELOW_BENT_NORMAL
LAYER_OUT_MESH_NORMAL_MAPLAYER_BELOW_MESH_NORMAL_MAP
LAYER_OUT_MESH_AOLAYER_BELOW_MESH_AO
LAYER_OUT_MESH_HEIGHTLAYER_BELOW_MESH_HEIGHT
LAYER_OUT_MESH_CURVATURELAYER_BELOW_MESH_CURVATURE
LAYER_OUT_MESH_THICKNESSLAYER_BELOW_MESH_THICKNESS

MaskMaterial Tokens

Surface Map Tokens

Gets the current layer's mapsGets the layer below's mapsOutputs the blended result
LAYER_CURRENT_ALBEDOLAYER_BELOW_ALBEDORESULT_ALBEDO
LAYER_CURRENT_NORMAL_MAPLAYER_BELOW_NORMAL_MAPRESULT_NORMAL_MAP
LAYER_CURRENT_ROUGHNESSLAYER_BELOW_ROUGHNESSRESULT_ROUGHNESS
LAYER_CURRENT_HEIGHTLAYER_BELOW_HEIGHTRESULT_HEIGHT
LAYER_CURRENT_AOLAYER_BELOW_AORESULT_AO
LAYER_CURRENT_METALLICLAYER_BELOW_METALLICRESULT_METALLIC
LAYER_CURRENT_EMISSIONLAYER_BELOW_EMISSIONRESULT_EMISSION

Texture and Mask Tokens

You can assign textures to the TEX tokens and masks to the MASK tokens. These are meant to be used for passing arbitrary data such as noise and masks.

Output texturesGet below layer's textureOutput masksGet below layer's masks
LAYER_OUT_TEX_0LAYER_BELOW_TEX_0LAYER_OUT_MASK_0LAYER_BELOW_MASK_0
LAYER_OUT_TEX_1LAYER_BELOW_TEX_1LAYER_OUT_MASK_1LAYER_BELOW_MASK_1
LAYER_OUT_TEX_2LAYER_BELOW_TEX_2LAYER_OUT_MASK_2LAYER_BELOW_MASK_2
LAYER_OUT_TEX_3LAYER_BELOW_TEX_3LAYER_OUT_MASK_3LAYER_BELOW_MASK_3
LAYER_OUT_TEX_4LAYER_BELOW_TEX_4LAYER_OUT_MASK_4LAYER_BELOW_MASK_4
LAYER_OUT_TEX_5LAYER_BELOW_TEX_5LAYER_OUT_MASK_5LAYER_BELOW_MASK_5
LAYER_OUT_TEX_6LAYER_BELOW_TEX_6LAYER_OUT_MASK_6LAYER_BELOW_MASK_6
LAYER_OUT_TEX_7LAYER_BELOW_TEX_7LAYER_OUT_MASK_7LAYER_BELOW_MASK_7
YoutubePatreonDocsAssetsTwitter

©2026 Foyezes