Materials
Materials are the bridge between your texture files and the rendering pipeline. Every brush face and model surface references a material by name. A material tells the engine which textures to bind, what shader to use, and how the surface should behave visually. They’re defined as small JSON files with the .cmt extension.
The .cmt File
A material file is a plain JSON object. The name must be unique across all mounted material folders. Here is a concrete example:
{
“name”: “Concrete Wall”,
“texture”: “Textures/concrete/concreteWallAlbedo”,
“specular”: “Textures/concrete/concreteWallSpecular”,
“normal”: “Textures/concrete/concreteWallNormal”,
“surfaceType”: “concrete”,
“reflectivity”: 0.0,
“transparent”: false
}
Texture paths are relative to your content root and should not include the file extension. All three texture slots are optional. If a slot is omitted, the engine substitutes a neutral default so the surface won’t crash, it’ll just look wrong.
Fields
Mounting Materials
Materials are loaded by calling MaterialLoader.MountMaterials() with a path to a folder. The loader walks the folder and all subdirectories, reads every .cmt file, and registers each material into the global lookup table by name.
The engine already calls this for the default materials folder in MainEngine’s constructor. Call it again to mount materials from additional folders, for example when loading a mod. After mounting all folders, call:
This second pass assigns shader references to each material and tracks loaded world shaders for per-frame parameter updates. If you skip it, materials with custom shaders won’t receive lighting data.
Material names must be globally unique. If two .cmt files share the same name value, the second will fail to register.
How the Shader Uses Your Textures
Understanding what each texture channel does helps you author them correctly.
The diffuse texture is multiplied against the resolved lighting value. Pure white textures show lighting in full; darker diffuse textures absorb it. The alpha channel is only used when transparent is true.
The normal map is sampled and expanded from 0-1 range to -1 to 1. It’s added to the face’s geometric normal to produce a perturbed normal used in lighting and reflection calculations. A high quality normal map has a strong impact on how dynamic and real a surface looks, especially under moving point lights.
The specular texture’s R channel scales specular highlight intensity. The G channel is a scalar for the materials base reflectivity for the specular exponent. The A channel scales the environment cubemap reflection contribution on top of the material’s base reflectivity. For metals: high R and high A. For cloth or rough surfaces: near-zero across both.
Quality Levels
The render engine supports three quality tiers controlled by RenderEngine.ChangeShaderQuality(). When the quality level changes, every loaded world/model shader has its active technique switched to match. If you write a custom shader, it must implement all three techniques with exactly these names:
technique High {
pass Pass1 {
VertexShader = compile VS_SHADERMODEL VertexShaderFunction();
PixelShader = compile PS_SHADERMODEL PixelShaderFunction();
}
}
technique Med { … }
technique Low { … }
If a technique is missing the engine will throw when the quality setting is changed. Lower techniques can fall back to a simpler version of the same pass rather than being empty.
Custom Shaders
Any material can use a custom shader by setting the shaderName field to the name of an HLSL .fx file in the Shaders/ content folder. The engine loads it as a MonoGame Effect asset.
Custom world shaders receive the same global parameters as the built-in shaders because the render engine calls PrepareWorldShaders() each frame on all tracked shaders. Declare these parameters in your shader to receive them:
Custom model shaders are a bit different, but not too much. There is no guide written for those yet, but you could easily copy and modify the ModelDefault.fx from the engine.
float4x4 World;
float4x4 View;
float4x4 Projection;
float3 cameraPos;
float2 screenSize;
float4 fogColor;
float fogIntensity;
float fogStart, fogEnd;
Texture samplers follow the same register convention: sampler 0 is diffuse, sampler 1 is specular, sampler 2 is normal. A custom shader shared across multiple materials is only loaded once and shared.
Transparent Materials
Setting “transparent”: true changes how the pixel shader handles the diffuse alpha channel. In opaque mode the shader clips any pixel below an alpha threshold of 0.1 (binary: fully opaque or fully discarded). In transparent mode the alpha value is preserved and blended.
The render engine uses non-premultiplied alpha blend for world geometry, so a pixel with alpha 0.5 blends at half intensity with what’s behind it. For hard cutout surfaces like foliage and chain-link fences, opaque mode with an alpha-tested diffuse usually looks better and is cheaper. Use true transparency for glass panels and semi-transparent decals.
Transparent faces are not depth-sorted against each other. The engine runs a depth prepass before rendering transparent surfaces, which usually looks good enough, but complex overlapping transparent geometry can produce visual artifacts.