20 FEB 2026

rahulmnavneeth

material system

homedocs

Location

include/mop/core/material.h         — Material descriptor + API
include/mop/core/material_graph.h   — Node-graph authoring (optional)
src/core/viewport.c                 — Draw call population
src/rasterizer/rasterizer.c         — GGX + Lambert shading (CPU)
src/backend/vulkan/shaders/         — PBR shaders (Vulkan)

Types

MopMaterial

typedef struct MopMaterial {
    MopColor    base_color;                /* Multiplied with vertex color  */
    float       metallic;                  /* [0, 1], 0 = dielectric        */
    float       roughness;                 /* [0, 1], 1 = fully rough       */
    MopVec3     emissive;                  /* Self-illumination (post-light) */
    MopTexture *albedo_map;                /* Optional albedo texture       */
    MopTexture *normal_map;                /* Optional tangent-space normals */
    MopTexture *metallic_roughness_map;    /* G = roughness, B = metallic   */
    MopTexture *ao_map;                    /* R = ambient-occlusion factor  */
} MopMaterial;

Default values (from mop_material_default()):

FieldDefault
base_color{1, 1, 1, 1} (white)
metallic0.0
roughness0.5
emissive{0, 0, 0}
albedo_mapNULL
normal_mapNULL
metallic_roughness_mapNULL
ao_mapNULL

Functions

mop_material_default

MopMaterial mop_material_default(void);

Returns a material with sensible defaults. Use this as a starting point and override individual fields.

mop_mesh_set_material

void mop_mesh_set_material(MopMesh *mesh, const MopMaterial *material);

Assigns a material to a mesh. The material struct is copied — the application may free or modify its copy after the call.

Texture pointers (albedo_map, normal_map) are retained by reference. The textures must remain alive as long as the mesh uses them.

Shading Model

MOP uses a physically-based shading model on both CPU and Vulkan backends. The math is identical between backends; the Vulkan path simply runs it on the GPU.

Specular — GGX Cook-Torrance

roughness squared is used as the GGX alpha. metallic blends the albedo into the Fresnel F0 (F0 = mix(0.04, base_color, metallic)).

Diffuse — Lambert

Plain Lambert, no π division. The missing π cancels out with the Blender directional-light mapping described below, so numbers match the reference renderer.

Attenuation

Physical inverse-square 1 / d², clamped to d² >= 0.01 to avoid singularities near the light.

Blender Energy Matching

MOP intensity maps to Blender light strength as follows:

Blender lightMOP intensity
Sunstrength × π
Point / Spotstrength × 4π²

Following these conventions, SSIM vs EEVEE is ≥ 0.92 on the reference quality-pipeline scenes.

Node Graph Authoring (Optional)

include/mop/core/material_graph.h exposes a DAG of MopMatNodes (constants, texture samples, normal map, mix, multiply, add, fresnel, UV transform, vertex color, output) that can be compiled to a flat MopMaterial. Use this when you need procedural material construction or want to ship material presets as JSON.

MopMaterialGraph g;
mop_mat_graph_init(&g, "brass");
mop_mat_graph_preset_pbr(&g);

MopMaterial out;
mop_mat_graph_compile(&g, vp, &out);
mop_mesh_set_material(mesh, &out);

mop_mat_graph_to_json / mop_mat_graph_from_json round-trip graphs for persistence.

Interaction with Other Systems