|
| 1 | +--- |
| 2 | +id: materials |
| 3 | +title: Materials |
| 4 | +sidebar_position: 14 |
| 5 | +--- |
| 6 | + |
| 7 | +# Using Materials in Untold Engine |
| 8 | + |
| 9 | +The engine uses a PBR (Physically Based Rendering) material model. Each entity's mesh can contain one or more submeshes, and each submesh holds its own `Material`. You can read and write individual material properties at runtime using the functions below. |
| 10 | + |
| 11 | +All material functions accept optional `meshIndex` and `submeshIndex` parameters (both default to `0`) so you can target a specific submesh when an entity contains more than one. |
| 12 | + |
| 13 | +> **Note:** Every update function automatically refreshes static batching for the affected entity, so you do not need to do this manually. |
| 14 | +
|
| 15 | +--- |
| 16 | + |
| 17 | +## Base Color |
| 18 | + |
| 19 | +The base color is stored as a `simd_float4` (RGBA). The `.w` component doubles as the opacity channel. |
| 20 | + |
| 21 | +### Get Base Color |
| 22 | + |
| 23 | +```swift |
| 24 | +let color = getMaterialBaseColor(entityId: entity) |
| 25 | +// color.x = red, color.y = green, color.z = blue, color.w = alpha |
| 26 | +``` |
| 27 | + |
| 28 | +### Set Base Color via SwiftUI Color |
| 29 | + |
| 30 | +```swift |
| 31 | +updateMaterialColor(entityId: entity, color: .red) |
| 32 | +``` |
| 33 | + |
| 34 | +This converts the SwiftUI `Color` to RGBA internally. If the alpha is below `1.0`, the material automatically switches to `.blend` alpha mode. |
| 35 | + |
| 36 | +--- |
| 37 | + |
| 38 | +## Roughness |
| 39 | + |
| 40 | +Controls how rough or smooth a surface appears. A value of `0.0` is perfectly smooth (mirror-like reflections) and `1.0` is fully rough (diffuse). |
| 41 | + |
| 42 | +### Get Roughness |
| 43 | + |
| 44 | +```swift |
| 45 | +let roughness = getMaterialRoughness(entityId: entity) |
| 46 | +``` |
| 47 | + |
| 48 | +### Set Roughness |
| 49 | + |
| 50 | +```swift |
| 51 | +updateMaterialRoughness(entityId: entity, roughness: 0.3) |
| 52 | +``` |
| 53 | + |
| 54 | +> When a roughness **texture** is present, the scalar value acts as a modulator (multiplied with the texture sample in the shader). If you remove the texture, the scalar value is used directly. |
| 55 | +
|
| 56 | +--- |
| 57 | + |
| 58 | +## Metallic |
| 59 | + |
| 60 | +Controls how metallic a surface appears. `0.0` is fully dielectric (plastic, wood, etc.) and `1.0` is fully metallic. |
| 61 | + |
| 62 | +### Get Metallic |
| 63 | + |
| 64 | +```swift |
| 65 | +let metallic = getMaterialMetallic(entityId: entity) |
| 66 | +``` |
| 67 | + |
| 68 | +### Set Metallic |
| 69 | + |
| 70 | +```swift |
| 71 | +updateMaterialMetallic(entityId: entity, metallic: 1.0) |
| 72 | +``` |
| 73 | + |
| 74 | +> Like roughness, the scalar value modulates the metallic texture when one is present. |
| 75 | +
|
| 76 | +--- |
| 77 | + |
| 78 | +## Emissive |
| 79 | + |
| 80 | +Controls self-illumination. The value is a `simd_float3` (RGB) representing the emitted light color and intensity. A value of `.zero` means no emission. |
| 81 | + |
| 82 | +### Get Emissive |
| 83 | + |
| 84 | +```swift |
| 85 | +let emissive = getMaterialEmmissive(entityId: entity) |
| 86 | +``` |
| 87 | + |
| 88 | +### Set Emissive |
| 89 | + |
| 90 | +```swift |
| 91 | +updateMaterialEmmisive(entityId: entity, emmissive: simd_float3(1.0, 0.5, 0.0)) |
| 92 | +``` |
| 93 | + |
| 94 | +> **Spelling note:** The API currently uses `getMaterialEmmissive` / `updateMaterialEmmisive` (with double-m). Use these exact names when calling the functions. |
| 95 | +
|
| 96 | +--- |
| 97 | + |
| 98 | +## Alpha Mode |
| 99 | + |
| 100 | +Determines how the renderer handles transparency for this material. |
| 101 | + |
| 102 | +### Available Modes (`MaterialAlphaMode`) |
| 103 | + |
| 104 | +- **`.opaque`** — Fully opaque. Alpha channel is ignored. |
| 105 | +- **`.mask`** — Binary transparency. Pixels with alpha below the cutoff are discarded; the rest are fully opaque. Useful for foliage, fences, etc. |
| 106 | +- **`.blend`** — Smooth alpha blending. Pixels are composited based on their alpha value. |
| 107 | + |
| 108 | +### Get Alpha Mode |
| 109 | + |
| 110 | +```swift |
| 111 | +let mode = getMaterialAlphaMode(entityId: entity) // returns MaterialAlphaMode |
| 112 | +``` |
| 113 | + |
| 114 | +### Set Alpha Mode |
| 115 | + |
| 116 | +```swift |
| 117 | +updateMaterialAlphaMode(entityId: entity, mode: .blend) |
| 118 | +``` |
| 119 | + |
| 120 | +--- |
| 121 | + |
| 122 | +## Alpha Cutoff |
| 123 | + |
| 124 | +Used only when the alpha mode is `.mask`. Pixels with alpha below this threshold are discarded. The value is clamped to `0.0 ... 1.0`. Default is `0.5`. |
| 125 | + |
| 126 | +### Get Alpha Cutoff |
| 127 | + |
| 128 | +```swift |
| 129 | +let cutoff = getMaterialAlphaCutoff(entityId: entity) |
| 130 | +``` |
| 131 | + |
| 132 | +### Set Alpha Cutoff |
| 133 | + |
| 134 | +```swift |
| 135 | +updateMaterialAlphaCutoff(entityId: entity, cutoff: 0.3) |
| 136 | +``` |
| 137 | + |
| 138 | +--- |
| 139 | + |
| 140 | +## Opacity |
| 141 | + |
| 142 | +A convenience layer over the base color's alpha channel (`.w`). The value is clamped to `0.0 ... 1.0`. Setting opacity below `1.0` automatically switches the alpha mode to `.blend`. |
| 143 | + |
| 144 | +### Get Opacity |
| 145 | + |
| 146 | +```swift |
| 147 | +let opacity = getMaterialOpacity(entityId: entity) |
| 148 | +``` |
| 149 | + |
| 150 | +### Set Opacity (all submeshes) |
| 151 | + |
| 152 | +```swift |
| 153 | +updateMaterialOpacity(entityId: entity, opacity: 0.5) |
| 154 | +``` |
| 155 | + |
| 156 | +By default this applies to **every submesh** on the entity. To target a single submesh instead: |
| 157 | + |
| 158 | +```swift |
| 159 | +updateMaterialOpacity(entityId: entity, opacity: 0.5, applyToAllSubmeshes: false) |
| 160 | +``` |
| 161 | + |
| 162 | +Or specify exact indices: |
| 163 | + |
| 164 | +```swift |
| 165 | +updateMaterialOpacity(entityId: entity, opacity: 0.5, meshIndex: 0, submeshIndex: 1) |
| 166 | +``` |
| 167 | + |
| 168 | +--- |
| 169 | + |
| 170 | +## Quick Reference |
| 171 | + |
| 172 | +- `getMaterialBaseColor(entityId:meshIndex:submeshIndex:)` → `simd_float4` |
| 173 | +- `updateMaterialColor(entityId:color:meshIndex:submeshIndex:)` — sets base color from SwiftUI `Color` |
| 174 | +- `getMaterialRoughness(entityId:meshIndex:submeshIndex:)` → `Float` |
| 175 | +- `updateMaterialRoughness(entityId:roughness:meshIndex:submeshIndex:)` |
| 176 | +- `getMaterialMetallic(entityId:meshIndex:submeshIndex:)` → `Float` |
| 177 | +- `updateMaterialMetallic(entityId:metallic:meshIndex:submeshIndex:)` |
| 178 | +- `getMaterialEmmissive(entityId:meshIndex:submeshIndex:)` → `simd_float3` |
| 179 | +- `updateMaterialEmmisive(entityId:emmissive:meshIndex:submeshIndex:)` |
| 180 | +- `getMaterialAlphaMode(entityId:meshIndex:submeshIndex:)` → `MaterialAlphaMode` |
| 181 | +- `updateMaterialAlphaMode(entityId:mode:meshIndex:submeshIndex:)` |
| 182 | +- `getMaterialAlphaCutoff(entityId:meshIndex:submeshIndex:)` → `Float` |
| 183 | +- `updateMaterialAlphaCutoff(entityId:cutoff:meshIndex:submeshIndex:)` |
| 184 | +- `getMaterialOpacity(entityId:meshIndex:submeshIndex:)` → `Float` |
| 185 | +- `updateMaterialOpacity(entityId:opacity:applyToAllSubmeshes:)` |
| 186 | +- `updateMaterialOpacity(entityId:opacity:meshIndex:submeshIndex:)` |
0 commit comments