-
Notifications
You must be signed in to change notification settings - Fork 7
HMAT file format (0.1.0): Custom Materials
HMAT
is Helix's custom JSON
-based material format, made for easy editing and testing. It's made to write and load custom materials.
An HMAT
looks like the following:
{
"version": "0.1",
"geometry": {
"vertexShader": "glsl/vertex_shader.glsl",
"fragmentShader": "glsl/fragment_shader.glsl"
},
"lightingModel": "lighting_model_shader.glsl",
"uniforms": {
"someUniformFloat1": 0.01,
"someUniformVec2": [ 0.0, 0.0 ],
"someUniformVec3": [ 0.2, 0.1, 0.09 ]
},
"textures": {
"someSampleUniform": "filename.png"
},
"cullMode": "back",
"writeDepth": true,
"writeColor": true,
"blend": {
"source": "one",
"destination": "one"
"operator": "add"
},
}
Most properties except the version and the geometry
block are optional and take Helix's default value. We'll take a look at all the possible blocks individually.
The version of the HMAT format. Must currently be "0.1.0"
This contains links to the glsl files containing the geometry stage of the Material. Remember: lighting and Geometry code is separated in Helix. How the shaders are written can be seen below. geometry
is itself a block with two properties:
-
vertexShader
: evidently, the link to the glsl file containing the vertex shader for the geometry -
fragmentShader
: evidently, the link to the glsl file containing the fragment shader for the geometry. This computes all elements required in the lighting stage.
This either contains a "shorthand" for Helix's built-in lighting models, or a link to a glsl file implementing a custom lighting model. See below for details. If not provided, it will use the default lighting model defined in InitOptions
. The available shorthands are currently:
unlit
ggx
ggx_full
blinn
Any of the following values:
front
back
both
none
Whether the material writes depth or color values, respectively. Either true
or false
.
This defines the blend state used by the material. It has 3 sub-fields:
-
source
: the source factor in the blending equation. It has any of the following values:one
zero
sourceColor
oneMinusSourceColor
sourceAlpha
oneMinusSourceAlpha
destinationColor
oneMinusDestinationColor
destinationAlpha
oneMinusDestinationAlpha
sourceAlphaSaturate
-
destination
: the destination factor in the blending equation. It has any of the following values:one
zero
sourceColor
oneMinusSourceColor
sourceAlpha
oneMinusSourceAlpha
destinationColor
oneMinusDestinationColor
destinationAlpha
oneMinusDestinationAlpha
sourceAlphaSaturate
-
operator
: the operator in the blending equation. One of the following:add
subtract
reverseSubtract
The vertex shader provides anything the fragment shader needs to calculate the values it needs in a function called hx_geometry
. For example:
vertex_attribute vec4 hx_position;
vertex_attribute vec3 hx_normal;
varying_out vec2 uv;
varying_out vec3 vertexNormal;
uniform mat4 hx_worldMatrix;
uniform mat4 hx_viewProjectionMatrix;
uniform mat3 hx_normalWorldViewMatrix;
void hx_geometry()
{
vec4 worldPos = hx_worldMatrix * hx_position;
uv = worldPos.xy * 0.1;
vertexNormal = hx_normalWorldViewMatrix * hx_normal;
gl_Position = proj = hx_viewProjectionMatrix * worldPos;
}
Note the difference with vanilla GLSL in that the attribute keyword in Helix is vertex_attribute
, and the outgoing varying is varying_out
. This is done to internally support WebGL 1 and 2. The calculations are done in hx_geometry
rather than in main
(which is used internally instead).
Another hx_geometry
function here, but this time it returns an object of type HX_GeometryData
. This object looks like this:
struct HX_GeometryData
{
vec4 color; // the albedo color and alpha for dielectrics or the specular color for metallics. This must be in linear space, not gamma!
vec3 normal; // the surface normal vector. Must be in view space!
float metallicness; // 0 for dielectrics, 1 for metallics
float normalSpecularReflectance; // The reflection strength looking at a surface head on for dielectrics. When in doubt, use 0.027
float roughness; // The roughness from 0 to 1 of the specular reflection
float occlusion; // The amount of ambient occlusion for this point. This is a multiplier, so 1.0 is unoccluded
vec3 emission; // The color emitted by this surface.
vec4 data; // this can be any extra data a custom lighting model requires
}
A fragment shader that generates this could look like this:
varying_in vec2 uv;
varying in vec3 vertexNormal;
uniform sampler2D albedoMap;
HX_GeometryData hx_geometry()
{
HX_GeometryData data;
data.color = hx_gammaToLinear(texture2D(albedoMap, uv));
data.normal = normalize(vertexNormal);
data.metallicness = 0.0;
data.normalSpecularReflectance = 0.027;
data.roughness = 0.5;
data.occlusion = 1.0;
data.emission = vec3(0.0);
return data;
}
Similar to before, notice the use of varying_in
.
Lighting Model shaders are a single glsl
file containing a BRDF function. The signature of the function looks like this:
void hx_brdf(in HX_GeometryData geometry, in vec3 lightDir, in vec3 viewDir, in vec3 viewPos, in vec3 lightColor, vec3 normalSpecularReflectance, out vec3 diffuseColor, out vec3 specularColor)
{
// your lighting code, assign diffuse contribution (fe: Lambert, Oren-Nayar) to diffuseColor, and specular reflections (Trowbridge-Reitz, Phong, Ward, ...) to specularColor
}
Implementing lighting models is an advanced topic. If you know what you're doing, you'll need to know about the conventions used.
-
lightDir
points from the light to the surface -
viewDir
points from the camera to the surface -
normalSpecularReflectance
is either the same dielectric value as provided togeometry
, or - for metallics - it contains the metallic color. You can still choose to implement a custom metallic shader with the data fromgeometry
if you want to be more physically correct. -
diffuseColor
,specularColor
: The outputs are expected in linear (not gamma) space.
The following default attributes should be used for the vertex shader, rather than creating your own for the same type of data. All coordinates are in object space (bind space when skinned).
-
vec4 hx_position
: The vertex position -
vec3 hx_normal
: The vertex normal -
vec4 hx_tangent
: The tangent normal. Thew
-component is used to calculate the bitangent:bitangent = cross(hx_tangent.xyz, normal) * hx_tangent.w)
-
vec2 hx_texCoord
: The texture coordinate -
vec4 hx_jointIndices
: 4 joint indices the current vertex is bound to -
vec4 hx_jointWeights
: 4 joint weights defining the influence of each joint
Helix has a set of built-in uniforms you can access in your shaders when you declare them.
-
mat4 hx_viewMatrix
: The matrix transforming from world to view space. -
mat4 hx_projectionMatrix
: The matrix transforming from view to ndc space. -
mat4 hx_inverseProjectionMatrix
: The matrix transforming from ndc to view space. -
mat4 hx_viewProjectionMatrix
: The matrix transforming from world to ndc space. -
mat4 hx_inverseViewProjectionMatrix
: The matrix transforming from ndc to world space . -
mat4 hx_worldMatrix
: Tranforms from object space to world space. -
mat4 hx_worldViewMatrix
: Tranforms from object space to view space. -
mat4 hx_wvpMatrix
: The full world-view-projection matrix. -
mat4 hx_inverseWVPMatrix
: The inverse of the world-view-projection matrix. -
mat3 hx_normalWorldMatrix
: The matrix used to transform normals from object to world space. -
mat3 hx_normalWorldViewMatrix
: The matrix used to transform normals from object to view space. -
mat4 hx_cameraWorldMatrix
: The matrix transforming from view space to world space -
vec3 hx_cameraWorldPosition
: The position of the camera in world space -
float hx_cameraFrustumRange
: The camera'sfarDistance - nearDistance
-
float hx_rcpCameraFrustumRange
:1.0 / (farDistance - nearDistance)
-
float hx_cameraNearPlaneDistance
: The camera's near clip plane distance -
float hx_cameraFarPlaneDistance
: The camera's far clip plane distance -
vec2 hx_renderTargetResolution
: The current render target size -
vec2 hx_rcpRenderTargetResolution
: The reciprocal of the current render target size -
vec2 hx_dither2DTextureScale
: Maps screen coordinates to dither texture coordinates. Used with thehx_dither2D
texture. -
vec3 hx_ambientColor
: The ambient color of the scene. -
vec2 hx_poissonDisk[64]
: The default set of Poisson disk points (array size allowed to be smaller) -
vec3 hx_poissonSphere[64]
: The default set of 3D Poisson sphere points (array size allowed to be smaller) -
mat4 hx_bindShapeMatrix
: The matrix transforming from object space to skeleton bind space. -
mat4 hx_bindShapeMatrixInverse
: The matrix transforming from skeleton space to object space. -
vec3 hx_skinningMatrices[HX_MAX_SKELETON_JOINTS * 3]
: Only ifHX_USE_SKINNING
is defined andHX_USE_SKINNING_TEXTURE
is not. An array containing all the skeleton joint poses. Every 3 elements form a single affine matrix. -
float hx_morphWeights[8]
: An array containing all the morph weights. Only ifHX_USE_NORMAL_MORPHING
is defined. These relate tovertex_attribute
shx_morphPosition0
tohx_morphPosition7
andhx_morphNormal0
tohx_morphNormal3
.
Similarly, Helix also provides a bunch of default textures for your convenience.
-
hx_dither2D
: a 2D texture used for dithering. Every pixel contains(cos(randomAngle), sin(randomAngle), randomValue, 1.0)
so it can be used for random offsetting and random rotations. -
hx_normalDepthBuffer
: the normal depth buffer of the opaque elements in the scene. The depth value encoded here is linear as opposed to the gpu's depth buffer values. -
hx_backbuffer
: The texture containing the rendered opaque objects. If used in a material, it will be considered a transparent material. Using this has a performance impact, especially on mobile devices. -
hx_ssao
: The texture containing the SSAO occlusion. -
hx_shadowMap
: the shadow map. -
hx_skinningTexture
: the texture containing the skeleton pose. Every row represents a row in the matrix, every column a joint. Only ifHX_USE_SKINNING
andHX_USE_SKINNING_TEXTURE
are both defined.
-
type saturate(type value)
: Synonymous toclamp(value, 0.0, 1.0)
-
vec4 hx_floatToRGBA8(float value)
: Encodes afloat
value from 0.0 - 1.0 in avec4
. -
float hx_RGBA8ToFloat(vec4 rgba)
: Decodes avec4
to a float value (0.0 - 1.0). -
vec2 hx_floatToRG8(float value)
: Encodes afloat
value from 0.0 - 1.0 in avec2
. -
float hx_RG8ToFloat(vec4 rgba)
: Decodes avec2
to a float value (0.0 - 1.0). -
vec2 hx_encodeNormal(vec3 normal)
: Encodes avec3
normal in avec2
-
vec3 hx_decodeNormal(vec4 data)
: Decodes the normal from ahx_normalDepthBuffer
sample. -
float hx_decodeLinearDepth(vec4 data)
: Decodes the depth from ahx_normalDepthBuffer
sample. -
float hx_log10(float val)
: Calculates the log base 10. -
vec4/vec3 hx_gammaToLinear(vec4/vec3 color)
: Converts from gamma color space to linear space. -
vec4/vec3 hx_linearToGamma(vec4/vec3 color)
: Converts from linear color space to gamma space. -
vec3 hx_getFrustumVector(vec2 position, mat4 unprojectionMatrix)
: Uses an inverse projection matrix to calculate the "absolute" frustum vector. This is essentially a vector going from near plane to far plane on the top-right edge of the frustum. This is used for interpolation with symmetric frusta. -
vec3 hx_getLinearDepthViewVector(vec2 position, mat4 unprojectionMatrix)
: This returns the view vector for a given screen position (normalized -1.0 to 1.0), scaled so the Y component is 1. This means that if we calculate the Y ("into screen") coordinate, we can scale it with the vector to obtain the view position. -
vec3 hx_fresnel(vec3 normalSpecularReflectance, vec3 lightDir, vec3 halfVector)
: Calculates the Fresnel factor. -
vec3 hx_fresnelProbe(vec3 normalSpecularReflectance, vec3 lightDir, vec3 normal, float roughness)
: Calculates the Fresnel factor for light probes, taking into account geometrical attenuation due to roughness. -
float hx_luminance(vec4/vec3 color)
: calculates the luminance of a color -
float hx_linearStep(float lower, float upper, float x)
: A linear (ie: non-smooth) variant ofsmoothstep
-
vec4 hx_sampleDefaultDither(sampler2D ditherTexture, vec2 uv)
: Samples the default dither texture. -
vec3 hx_intersectCubeMap(vec3 rayOrigin, vec3 cubeCenter, vec3 rayDir, float cubeSize)
: Allows "correcting" a reflection vector for "sized" cube maps. -
mat4 hx_getSkinningMatrix(v)
: Returns the skinning matrix for the current vertex. Requiresvertex_attribute
shx_jointWeights
andhx_jointIndices
to be provided. Thev
parameter is due to a bug in IE and Edge with#define
: simply pass in 0.