Materials and looks PBR surfaces color metalness and textures
Use this for
Not for
Pairs with: Model authoring parametric primitives and CSG for custom meshes, Atmosphere sky lighting fog and time of day, Asset acquisition strategy
The twelve ai_safe ops
All go through apply_material_semantic_patch (project.material.patch.apply), a batch of ops applied to a material asset:
createBasicPbrSurfacecreates or resets a graph-native OpenPBR surface. Run this first.setPbrBaseColorsets the base color (baseColor, a#rrggbb/#rrggbbaaor normalized color).setPbrColorsets a color-valued field:fieldone ofbaseColor,emissiveColor,sheenColor,attenuationColor;valuea color.setPbrScalarsets a scalarfieldwith a numericvalue. Fields:metallic(0..1),roughness(0..1),alphaCutoff,emissiveIntensity(0..100),normalStrength,occlusionIntensity,clearcoat,clearcoatRoughness,sheen,sheenRoughness,transmission,transmissionRoughness,ior(1..2.333),attenuationDistance,anisotropy,anisotropyRotation.setAlphaModesetsalphaModeone ofopaque,masked,blend.setDoubleSidedsetsdoubleSided(render both faces, for foliage cards).setTextureSlotassigns a texture asset to aslot:baseColor,normal,metallicRoughness,emissive,occlusion,lightMap,clearcoatNormal,sheenColor,transmission; with optionaluvTiling/uvOffset/uvRotation/uvSet.clearTextureSlotremoves a texture from a slot.setInstanceAtlas/ 10.clearInstanceAtlasdeclare (or clear) the per-instance texture-array atlas an instanced material MAY select a tile from.setPreviewGeometrysets editor preview geometry only (sphere,cube,plane,customMesh).createWaterFoamMaterialbuilds an appearance-only water material graph.
The recipe
- Create a material asset:
project_create_asset(project.asset.create) withassetKind: "material". - Apply the surface:
apply_material_semantic_patch(project.material.patch.apply) with an op batch, for example a brushed metal:{ "op":"createBasicPbrSurface" }{ "op":"setPbrBaseColor", "baseColor":"#b8bcc4" }{ "op":"setPbrScalar", "field":"metallic", "value": 1 }{ "op":"setPbrScalar", "field":"roughness", "value": 0.35 }For a glowing lamp:setPbrColoremissiveColorplussetPbrScalaremissiveIntensity. For glass:setAlphaModeblendplussetPbrScalartransmissionandior.
- Skin a mesh: set the mesh entity's
RenderableComponent.materialRefto the material asset id (see model-authoring-parametric-csg). One material reused across many meshes is cheaper than many near-identical materials. - Finish the look with the environment: apply a renderer look with
world_apply_visual_profile(renderer.visual_profile.apply) for tone mapping and sky, and light the scene per atmosphere-sky-and-lighting. A material only reads as intended under matching lighting and exposure.
The shader ceiling (do not fight it)
Raw shader source is hard-rejected by design. Any parameter key matching shaderSource, wgsl, glsl, rawShader, fragmentShader, or vertexShader raises raw_shader_source_forbidden. The material NODE-GRAPH ops (addCatalogNode, connectSockets, setNodeParameter and friends) ARE ai_safe, the same alphabet the visual editor gets (replaceGraph stays import-only, and trustedOnly shader-plugin nodes are rejected for every source), but the node CATALOG is small: OpenPBR surface, output, constants, texture sample, water depth, scalar math, color mix, procedural noise. Do not attempt custom shaders. Express the look with the twelve PBR ops; if an effect truly needs a procedural shader beyond that catalog, say so plainly rather than emitting shader strings that will be rejected.
Pitfalls
- Run
createBasicPbrSurfacebefore setting fields, or there is no surface to set. - Colors are
#rrggbb/#rrggbbaa; scalar values must sit inside their field range (for exampleioris 1..2.333,metallicis 0..1). - Transparency needs
setAlphaModeblend(ormaskedwithalphaCutoff); setting onlytransmissionon an opaque material will not look transparent. setInstanceAtlasonly DECLARES what an instance may vary; the per-instance tile selection does not travel through this patch.
Verify
project_get_graph_snapshotto confirm the material asset and the mesh'smaterialRefbinding.qa_capture_renderer_viewportto confirm color, metalness, glow, or transparency read under the scene lighting.
Toon shading on props (provisional)
Shading is a per-material choice: the material's shadingModel is one of pbr, toon or unlit. It belongs to one material, never to the world, and setting it on one material changes no other material. toon today is the stock toon response, a hard lit and shadow step, with no ramp, rim light or shadow tint yet.
It reaches props only: meshes whose RenderableComponent.materialRef names the material. Terrain with texture layers, foliage, water, imported-model material slots and baked lighting ignore it (a lighting bake may refuse a material that is not pbr), so a whole-world toon look is not available yet.
Two spellings write the same field:
- New material: put it in the surface you create, for example
{ "op":"createBasicPbrSurface", "material": { "shadingModel":"toon", "baseColor":"#336699" } }. - Existing material: read the material asset's graph with
project_get_graph_snapshot, find the node whosenodeTypeisopenpbr.surface(a graph made from plain material values names itsurface), then send{ "op":"setNodeParameter", "nodeId":"surface", "key":"shadingModel", "value":"toon" }. Do not reach forcreateBasicPbrSurfacehere: it resets every other field. In the material editor a person uses the Shading control.
The op spelling is provisional until a typed shading op lands; the field and its values are the durable part. Pitfalls:
- The key is exactly
shadingModeland the value one ofpbr,toonorunlit; anything else fails the whole patch. - Write no ramp, band or rim keys: the material has no such fields yet.
- A wrong
nodeIdleaves the surface unchanged, so confirmshadingModelon the asset withproject_get_graph_snapshotand the banding withqa_capture_renderer_viewport. - For the grade, the Toon Day environment template (Environment inspector) keeps the clear-day light and changes only the grade.