Specification of json format used for material Cogs definition.
The materials describe how geometry vertex data is transformed and end up as
SurfaceOut
(see Common.hlsl
) values. Then, an engine permutation(not part
of the material) describes how SurfaceOut
values result in rendertarget
values.
A material has a set of permutations (not to be confused with engine
permutations). Only one permutation can be active at a time. In addition, a
material has a set of variants that can be set to values independent of
the permutation. Finally, a material can inherit from other materials. All
materials inherit from MaterialBase
.
A material template is an abstract material that cannot be instanced directly, only inherited, and does not have permutations.
Shaders go through a lot of compositing and transformation when processed by the material system, and when something goes wrong, error messages from the graphics API reference the source transformed source.
Two cogs-variables let you peek into this:
effects.logShaderSource
is true, and the shader fails to compile, the
transformed shader source is dumped to the log at debug loglevel.effects.logShaderInfo
is true, info about attributes
and uniforms as well as shader source is always dumper to the log at
debug loglevel.The cogs material are resources. An application can directly manage these,
creating new resources and setting properties. The renderComponent
binds an
material instance to an entity.
As a simplification, some entities has a MaterialComponent
managed by the
MaterialSystem
. This expose a set of "usual" material properties and manages
a set of DefaultMaterial
-instances behind the scenes.
You have to choose whether you want to manage the material resource for an entity yourself, or let the material system do it for you.
Do not set a MaterialInstance resource if the entity has a MaterialComponent. Choose another entity type or remove the MaterialComponent.
Cogs use hlsl files for shaders and DirectX semantics. For desktop APIs beside DirectX, the shaders get translated by Cogs.Rendering to the APIs native shader language.
For OpenGLES3 (i.e. webgl), shaders are not translated. Thus,
the material author needs to provide glsl translations. The material system
replaces the .hlsl
suffix with es30.glsl
depending on
backend, and use a dedicated shader generator.
A material instance is an actual instance of material, i.e. holds the material data. A material instance has a material, which defines the data. Roughly, a material defines a property diffuseColor, but the material instance sets diffuseColor to the value of red.
serialization/MaterialReader.cpp
: Parses material files, builds material
structures, handles inheritance.resources/MaterialManager.cpp
: Handles runtime instantiation of a material
into a shader, that is, applying permutations and variations and checking
preconditions.resources/ShaderBuilder.cpp
: Transforms a configured material and shaders
into full hlsl shaders passed to Cogs.Rendering. ShaderBuilderES3.cpp
does the same for OpenGLES3
respectively.renderer/RenderMaterial.cpp
: Manages effect bindings, mapping from
material instance, stream and engine permutation to an actual concrete
shader with constant buffer binding points.renderer/RenderMaterialInstance.cpp
: Manages buffer/texture properties
tied to a render material.The basic .material
file structure is an JSON object with either a Material
or MaterialTemplate
key.
{
"Material": { },
// or
"MaterialTemplate": {}
}
Contains exactly one of:
Material
: Combined object with keys from both Material and
material template metadata and
Material effect definition.MaterialTemplate
: Combined object with keys from both Material and
material template metadata and
Material effect definition.Materials and material templates are similar. A material is assumed to be instantiated and have permutations, an material template is assumed to only be used for inheritance and do not have permutations.
{
"name": "",
"flags": "",
"inherits": "",
"requires": ,
"variants": ,
"options": {},
"properties": {},
"sharedProperties": {},
"permutations": {},
"enginePermutations": []
}
name
: String with name of material.flags
: or string array, currently no flags are defined.inherits
: String with name of material to inheritrequire
or requires
: specifies variant requirements, see
Require statements.variants
: specifies variants, see Material variants.options
: Object with string values defining, see Material options.properties
: Object specifying material per-instance properties, see
Material properties.sharedProperties
: Object specifying material properties that are shared
between all material instances, see Material properties.permutations
: Object with permutation definitions, see
Material permutations.enginePermutations
: Array with strings, specifies which Engine
permutations material is valid for. The default enabled are Forward
,
Deferred
, Shadow
and Transparent
.{
"vertexFunction": {
"file": "xyzVS.hlsl"
},
"surfaceInterface": {
"foo": "float4",
"facing": "SV_IsFrontFace"
},
"surfaceFunction": {
"file": "xyzPS.hlsl",
}
}
vertexInterface
: Specifies input to the vertex shader, that is the mesh
streams. Variants in MaterialBase
that add to the vertex interface gets
automatically enabled/disabled depending on mesh contents, so usually it
is not necessary to explicitly specify this.hullInterface
: Input to the hull tessellation shader stage.domainInterface
: Input to the domain tessellation shader stage.geometryInterface
: Input to the geometry shader stage.surfaceInterface
: Input to the surface shader (first part of pixel
shader) stage.vertexFunction
: Specifies vertex shader stage.hullFunction
: Specifies hull tessellation shader stage.domainFunction
: Specifies domain tessellation shader stage.geometryFunction
: Specifies geometry shader stage.surfaceFunction
: Specifies surface shader (first part of pixel shader)
stage.definitions
or defines
: Object with string values that will be passed as
preprocessor defines to all shaders.Object that specifies the input to a shader stage. Each key is a input variable name visible in the shader, the value specifies how this binds to the surrounding stages:
{
"ShaderInputNameA": "float3",
"ShaderInputNameB": ["float3", "TEXCOORD0" ],
"ShaderInputNameC": ["float3", "TEXCOORD1", "linear" ],
"ShaderInputNameD": ["float3", "TEXCOORD2", ["linear", "noperspective"] ]
}
First element is type:
bool
: Boolean valuefloat
, float2
, float3
, float4
: Floating point vector.uint
, uint2
, uint3
, uint4
: Unsigned integer data.int
, int2
, int3
, int4
: Integer data.float4x4
: Float 4x4 matrix.float4[]
: Array of float4, second element is assumed to be array dimension either string or value.VFACE
:SV_IsFrontFace
:SV_InstanceID
:SV_ClipDistance
:SV_Position
:Second element, optional, holds semantic, x denotes optional slot number:
POSITIONx
:NORMALx
:COLORx
:TEXCOORDx
:TANGENTx
:INSTANCEVECTORx
:INSTANCEMATRIXx
:SV_Position
: System provided.SV_VertexID
: System provided.SV_InstanceID
: System provided.SV_ClipDistance
: System provided.VFACE
: System provided.SV_IsFrontFace
: System provided.Third element, optional, holds interpolation modifiers, can be either a string or an array of strings:
linear
:centroid
:nointerpolation
:noperspective
:sample
:Object that specifies the shader snippet for a stage
{
"file": "xyzGS.hlsl",
"entryPoint": "myGeometryFunction",
"attributes": {
"primitivetype": "point",
"maxvertexcount": "4"
}
}
file
: Path to source hlsl shader file.entryPoint
: Optional entry point for shader function.attribute
: Optional attributes, currently these for geometry shaders:
primitivetype
:maxvertexcount
:Require statements require that variants have a particular value.
As a string:
"requires": "DefaultHasNormal"
or an arrayt of string:
"requires": [ "DefaultHasNormal", "DefaultHasTexCood" ]
this requires that the variant DefaultHasNormal
is set etc.
As an object:
"requires": {
"VertexStreamPosition0": "float4|float3"
}
this requires that the VertexStreamPosition0
variant either has a value
float4
or float3
. This is useful to set up mesh requirements for the
materials, as the "VertexStramXXXXN"-variants are automatically populated
with the type of mesh streams.
Material options are a JSON object with string values, handled in
resources/MaterialOptions.cpp
.
Recognized key-value pairs:
CullMode
: Front
or Back
.Transparency
:
On
:Off
:Auto
:Alpha
:DrawOrder
: String containing an integer.BlendMode
: String with BlendMode
from MaterialOptions.h
.DepthWriteEnabled
: String interpreted as bool. Enables/disables writing of
depth buffer, note: requires enabled depth test.DepthTestEnabled
: String interpreted as bool. Enables/disables the depth test.DepthTestAlwaysPass
: String interpreted as bool. Depth test always passes,
useful for writing depth unconditionally.DepthBiasEnabled
: String interpreted as bool. Enables/disables depth bias.DepthBiasConstant
: String interpreted as float. Sets the depth bias
constant, requires enabled DepthBiasEnabled
.DepthBiasSlope
: String interpreted as float. Sets depth bias slope factor,
requires enabled DepthBiasEnabled
.DepthBiasClamp
: String interpreted as float. Sets depth bias clamp limit,
requires enabled DepthBiasEnabled
.The properties section contains all the user-defined properties of the material, like constant buffers (sometimes referred to as material property buffers in Cogs), and textures.
Material property buffers may contain the following data types:
Properties are reordered by Cogs to create a suitable structure layout for use both from native code and shader code.
"properties": {
"MyProperties": {
"myFloatProperty": "float 1.0",
"myColorProperty": "float3 srgb { 1, 1, 1 }",
"myVec4Property": "float4 { 0, 1, 0, 1}",
"myBoolProperty": "bool false"
// Etc...
},
"MyTexture": "Texture2D",
"MyCubeTexture": "TextureCube"
}
The permutations section contains the combinations of shader code and interfaces that together with engine shader code combines to complete runnable shaders and corresponding input/output structures.
As a shorthand, the permutations section may be dropped in favor of a single "default" permutation specified at the material scope.
Variants are toggles for features that can be turned on or off in a material.
A variant has a key, which defines a 'configuration axis' and a type that
defines allowable values along this axis. The configuration value is
forwarded to the shader source via a preprocessor defines, and by using
#ifdef
's, parts of the shader source can be omitted or included before
passing it to the rendering API. Thus, variants are more fine-grained than
material permutations.
Bool variants is either enabled or disabled. Example:
"MyBoolVariant": { // Key
"type": "bool", // Type
"value": "MY_VARIANT_DEFINE", // Shader define value
"trigger": "...", // Optional auto-enable by other variants.
"surfaceInterface": { /*...*/ } // Optional data passed between shader stages
}
Here, setting materialInstance->setVariant('MyBoolVariant', true)
will put
#define MyBoolVariant 1
in the top of the shader source, while setting
it to false
will omit the define. Also shown is the optional trigger
that
allows a bool variant to be automatically enabled if another variant is
enabled. Finally, this variant also add some data to be passed into the
fragment shader stage.
Int variants allow you to pass an integer number to a define
"MyIntVariant": {
"type": "int",
"value": "MY_VARIANT_DEFINE",
"defaultValue": 0
}
Here, setting materialInstance->setVariant('MyIntVariant', 42)
will
put #define MY_VARIANT_DEFINE 42
in the top of the shader source.
Enum variants specifies a set of defines where the one matching the set value is defined to 1, while the others are omitted. Example:
"MyEnumVariant": {
"type": "enum",
"values": {
"ValueA": "PREPROCESSOR_DEFINE_FOR_A",
"ValueB": "PREPROCESSOR_DEFINE_FOR_B",
"ValueC": "PREPROCESSOR_DEFINE_FOR_C"
},
"defaultValue": "ValueA"
}
Here, setting materialInstance->setVariant('MyEnumVariant', 'ValueB')
will
put #define PREPROCESSOR_DEFINE_FOR_B 1
in the top of the shader source.
Format variants is similar to bool variant but contains a format:
"MyFormatVariant": {
"type": "format",
"value": "MY_FORMAT_VARIANT",
"surfaceInterface": {
"foobar": "format"
}
}
Here, setting materialInstance->setVariant ('MyFormatVariant', 'R32G32B32_UINT')
will put #define MY_VARIANT_DEFINE COGS_UINT3
in the top of the shader source and add an uint3
input to the
fragment shader stage. This is mostly used in MaterialBase.material
for
automatic enabling from mesh stream layouts, and is used to trigger more
high-level variants (like presence of normal vectors).
{
"name": "",
"type": "",
"properties": {
},
"options": {
},
"variants": {
},
"permutations": {
},
}