Gessa Docs
Recipes

Recipe

Materials and looks PBR surfaces color metalness and textures

A mesh needs a look: a color, a metal or plastic feel, glow, transparency, or a texture. Materials are authored through semantic PBR operations, not shader code. Twelve ai_safe PBR ops cover the whole PBR surface; the node-graph topology ops are ai_safe as well, with a small node catalog (see the shader ceiling section).
engine v1.0.234since material-semantic-patch ai_safe.v1, action-catalog.v1.0.232, rendering.v1Copy for LLM

Use this for

coloring and skinning meshes; metal wood plastic stone glass and emissive looks; transparency and double-sided surfaces; assigning texture maps; a simple water look; setting a material once and reusing it across meshes

Not for

custom shader code (raw WGSL/GLSL is rejected by design, see capability-ceiling); node networks beyond the small catalog (the graph ops are ai_safe but the catalog is the ceiling, see capability-ceiling); per-camera color grade and post (see camera-feel); sky and fog (see atmosphere-sky-and-lighting)

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:

  1. createBasicPbrSurface creates or resets a graph-native OpenPBR surface. Run this first.
  2. setPbrBaseColor sets the base color (baseColor, a #rrggbb/#rrggbbaa or normalized color).
  3. setPbrColor sets a color-valued field: field one of baseColor, emissiveColor, sheenColor, attenuationColor; value a color.
  4. setPbrScalar sets a scalar field with a numeric value. 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.
  5. setAlphaMode sets alphaMode one of opaque, masked, blend.
  6. setDoubleSided sets doubleSided (render both faces, for foliage cards).
  7. setTextureSlot assigns a texture asset to a slot: baseColor, normal, metallicRoughness, emissive, occlusion, lightMap, clearcoatNormal, sheenColor, transmission; with optional uvTiling/uvOffset/ uvRotation/uvSet.
  8. clearTextureSlot removes a texture from a slot.
  9. setInstanceAtlas / 10. clearInstanceAtlas declare (or clear) the per-instance texture-array atlas an instanced material MAY select a tile from.
  10. setPreviewGeometry sets editor preview geometry only (sphere, cube, plane, customMesh).
  11. createWaterFoamMaterial builds an appearance-only water material graph.

The recipe

  1. Create a material asset: project_create_asset (project.asset.create) with assetKind: "material".
  2. 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: setPbrColor emissiveColor plus setPbrScalar emissiveIntensity. For glass: setAlphaMode blend plus setPbrScalar transmission and ior.
  3. Skin a mesh: set the mesh entity's RenderableComponent.materialRef to the material asset id (see model-authoring-parametric-csg). One material reused across many meshes is cheaper than many near-identical materials.
  4. 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 createBasicPbrSurface before setting fields, or there is no surface to set.
  • Colors are #rrggbb/#rrggbbaa; scalar values must sit inside their field range (for example ior is 1..2.333, metallic is 0..1).
  • Transparency needs setAlphaMode blend (or masked with alphaCutoff); setting only transmission on an opaque material will not look transparent.
  • setInstanceAtlas only DECLARES what an instance may vary; the per-instance tile selection does not travel through this patch.

Verify

  • project_get_graph_snapshot to confirm the material asset and the mesh's materialRef binding.
  • qa_capture_renderer_viewport to 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 whose nodeType is openpbr.surface (a graph made from plain material values names it surface), then send { "op":"setNodeParameter", "nodeId":"surface", "key":"shadingModel", "value":"toon" }. Do not reach for createBasicPbrSurface here: 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 shadingModel and the value one of pbr, toon or unlit; anything else fails the whole patch.
  • Write no ramp, band or rim keys: the material has no such fields yet.
  • A wrong nodeId leaves the surface unchanged, so confirm shadingModel on the asset with project_get_graph_snapshot and the banding with qa_capture_renderer_viewport.
  • For the grade, the Toon Day environment template (Environment inspector) keeps the clear-day light and changes only the grade.
Was this helpful?Report an issueContact support

On this page