|
| 1 | +### [Index](../index.md) | [Usage](./index.md) | Materials |
| 2 | + |
| 3 | +-------- |
| 4 | + |
| 5 | +# Materials |
| 6 | + |
| 7 | +Starting with Falcor 5.0 there is a new material system that allows creation and rendering of different materials |
| 8 | +types at runtime. In previous versions, a fixed diffuse+GGX material model was assumed. |
| 9 | + |
| 10 | +All materials and their resources are managed by the `MaterialSystem` C++ class and matching Slang module. |
| 11 | +The material system is owned by `Scene` object and bound together with its other resources when rendering. |
| 12 | + |
| 13 | +In order to access the material system in shader code, the following module must be imported: |
| 14 | + |
| 15 | +```c++ |
| 16 | +import Scene.Shading; |
| 17 | +``` |
| 18 | + |
| 19 | +## Data layout |
| 20 | + |
| 21 | +For efficient access, each material is described by a data blob of fixed size (currently 128B). |
| 22 | +The data blob consists of a header (see `MaterialHeader` declared in `Falcor/Scene/Material/MaterialData.slang`) |
| 23 | +followed by a data payload. The header always exists and holds material type ID and auxiliary flags etc. |
| 24 | + |
| 25 | +The format of the data payload is opaque and depends on the material type. If a material needs more data |
| 26 | +than what fits in the payload, it can store additional sideband data in a buffer. |
| 27 | + |
| 28 | +All resources (textures, samplers, buffers) are accessed via bindless resource IDs, where the actual GPU |
| 29 | +resources are managed by `MaterialSystem` and the IDs are allocated when resources are added. |
| 30 | +These bindless IDs are stored as part of the data payload, so that a material is self-contained and fully |
| 31 | +desribed by its data blob. |
| 32 | + |
| 33 | +## Material class (host side) |
| 34 | + |
| 35 | +On the host side, all materials are derived from the `Material` base class declared in `Scene/Material/Material.h`. |
| 36 | + |
| 37 | +In order to add a new material, a new class should be added that inherits from `Material` and implements |
| 38 | +its pure virtual member functions. The most important ones are: |
| 39 | + |
| 40 | +```c++ |
| 41 | +// Called once per frame to prepare the material for rendering. |
| 42 | +Material::UpdateFlags Material::update(MaterialSystem* pOwner); |
| 43 | + |
| 44 | +// Returns the material data blob. |
| 45 | +MaterialDataBlob Material::getDataBlob() const; |
| 46 | +``` |
| 47 | +
|
| 48 | +The base class holds the `MaterialHeader` struct and the derived material class is responsible |
| 49 | +for holding the data payload. The `getDataBlob()` returns the final data blob, which will be uploaded to the |
| 50 | +GPU by the material system for access on the shader side. |
| 51 | +
|
| 52 | +## Python bindings |
| 53 | +
|
| 54 | +To allow creation of materials and setting of the parameters from Python scripts (including `.pyscene` files), |
| 55 | +each material class is expected to export Python bindings. |
| 56 | +
|
| 57 | +These bindings are defined in the `FALCOR_SCRIPT_BINDING` block, usually placed at the bottom of the material's `.cpp` file. |
| 58 | +
|
| 59 | +Example usage: |
| 60 | +
|
| 61 | +```c++ |
| 62 | +glass = StandardMaterial("WindowGlass") |
| 63 | +glass.roughness = 0 |
| 64 | +glass.metallic = 0 |
| 65 | +glass.indexOfRefraction = 1.52 |
| 66 | +glass.specularTransmission = 1 |
| 67 | +glass.doubleSided = True |
| 68 | +glass.nestedPriority = 2 |
| 69 | +glass.volumeAbsorption = float3(2.0, 1.0, 1.5) |
| 70 | +``` |
| 71 | + |
| 72 | +For more examples of how material's are created from Python, refer to the test scenes in `Media/TestScenes/` |
| 73 | +(this directory is automatically fetched when the solution is built the first time). |
| 74 | + |
| 75 | +## Material module (shader side) |
| 76 | + |
| 77 | +On the shader side, each material class has a corresponding Slang module stored in `Falcor/Rendering/Materials/`. |
| 78 | +These modules implement the `IMaterial` Slang interface (see `Rendering/Materials/IMaterial.slang`). |
| 79 | + |
| 80 | +The main purpose of the material module is to: |
| 81 | +1. hold the material data, and |
| 82 | +2. hold the code for setting up a BSDF at a shading point. |
| 83 | + |
| 84 | +The latter is referred to as "pattern generation", which may involve sampling textures, evaluating |
| 85 | +procedural functions, and any other setup needed for shading. |
| 86 | + |
| 87 | +The first data field in the material module has to be the material header. This should be followed by the |
| 88 | +material payload as declared for the material type. For example, the standard material is declared: |
| 89 | + |
| 90 | +```c++ |
| 91 | +struct StandardMaterial : IMaterial |
| 92 | +{ |
| 93 | + MaterialHeader header; |
| 94 | + BasicMaterialData data; |
| 95 | + ... |
| 96 | +}; |
| 97 | +``` |
| 98 | + |
| 99 | +An instance of the material is created by calling the material system as follows: |
| 100 | + |
| 101 | +```c++ |
| 102 | +IMaterial material = gScene.materials.getMaterial(materialID); |
| 103 | +``` |
| 104 | + |
| 105 | +Internally, this function accesses the material header to fetch the material type, and then it calls |
| 106 | +Slang's `createDynamicObject<..>` function to create an instance of the right type. |
| 107 | +The opaque material data blob is cast to the data types used in the material module, so its fields are |
| 108 | +directly accessible internally in the material module. |
| 109 | + |
| 110 | +## BSDF module (shader side) |
| 111 | + |
| 112 | +Each material module has an associated BSDF type, which implements the `IBSDF` Slang interface. |
| 113 | +For example, `StandardMaterial` has an associated `StandardBSDF` type. |
| 114 | + |
| 115 | +An instance of the BSDF type is created for a specific shading point in the scene, and it exposes |
| 116 | +interfaces for evaluating and sampling the BSDF at that point. |
| 117 | +The `IBSDF` interface also has functions for querying additional BSDF properties |
| 118 | +at the shading point, such as albedo, emission, etc. |
| 119 | + |
| 120 | +A BSDF instance is created by calling the following function on the material: |
| 121 | + |
| 122 | +```c++ |
| 123 | +ShadingData sd = ... // struct describing the shading point |
| 124 | +ITextureSampler lod = ... // method for texture level-of-detail computation |
| 125 | + |
| 126 | +IBSDF bsdf = material.setupBSDF(gScene.materials, sd, lod); |
| 127 | +``` |
| 128 | + |
| 129 | +Internally, the `setupBSDF` function accesses the material system to fetch/evaluate all resources needed at the |
| 130 | +shading point. It returns an instance of the material's associated BSDF type, |
| 131 | +which the caller can then use to evaluate or sample the BSDF at the shading point. |
| 132 | + |
| 133 | +Since creating the material followed by instantiating the BSDF is very common, |
| 134 | +there is a convenience function `getBSDF()` on `MaterialSystem` that does both operations in one step: |
| 135 | + |
| 136 | +```c++ |
| 137 | +IBSDF bsdf = gScene.materials.getBSDF(sd, lod); |
| 138 | +``` |
| 139 | + |
| 140 | +In the above interfaces, a `ShadingData` struct is needed to describe the shading point. |
| 141 | +This is generated at a hit point by calling the `prepareShadingData()` function. |
| 142 | +This function is responsible for setting up the shading frame (normal, tangent, bitangent) |
| 143 | +including evaluating normal mapping and material opacity for alpha testing. |
| 144 | + |
| 145 | +In addition to this, a `ITextureSampler` instance is needed to describe how textures should |
| 146 | +be sampled (if there are any). The caller is responsible for deciding this based on which |
| 147 | +method for texture LOD it is using (e.g. ray cones, ray differentials, fixed mip level, etc). |
| 148 | +See available choices in `Scene/Material/TextureSampler.slang`). |
0 commit comments