Shaders

Note: This documentation is for the old 0.2.0 version of A-Frame. Check out the documentation for the current 1.6.0 version

Shaders (in A-Frame) are responsible for creating the material for the base material component. A-Frame ships with a couple of built-in shading models: standard and flat.

We can register custom shaders to implement different visual effects and materials. We have available the materials provided by three.js, including THREE.ShaderMaterial. With THREE.ShaderMaterial, we can provide our own GLSL vertex and fragment shaders (small programs that run on the GPU), and we can define a schema for their uniforms and attributes just as we would with component schemas. The shader’s schema will extend the base material component’s schema, and as a result we can pass values from markup directly to the shader.

Built-in Shading Models

A-Frame ships with a couple of built-in shading models.

Standard Shading Model

The standard shading model is the default shader for the material component. It is a physically-based shading model that uses THREE.MeshStandardMaterial under the hood. The standard shading model can be explicitly specified by setting the shader to be standard in the material component (i.e., material="shader: standard").

Properties

Property Description Default Value
color Base diffuse color. #fff
height Height of video (in pixels), if defining a video texture. 360
envMap Environment cubemap texture for reflections. Can be a selector to or a comma-separated list of URLs. None
fog Whether or not material is affected by fog. true
metalness How metallic the material is from 0 to 1. 0.5
repeat How many times a texture (defined by src) repeats in the X and Y direction. 1 1
roughness How rough the material is from 0 to 1. A rougher material will scatter reflected light in more directions than a smooth material. 0.5
width Width of video (in pixels), if defining a video texture. 640
src Image or video texture map. Can either be a selector to an <img> or <video>, or an inline URL. None

Physically-Based Shading

Physically based shading is a recent trend in rendering systems where materials behave realistically to lighting conditions. Appearance is a result of the interaction between the incoming light and the properties of the material.

To achieve realism, the diffuse color (can be supplied through the base material component), metalness, roughness properties of the material must accurately be controlled, often based on real-world material studies. Some people have compiled charts of realistic values for different kinds of materials that can be used as a starting point.

For example, for a tree bark material, as an estimation, we might set:

<a-entity geometry="primitive: cylinder"
material="src: treebark.png; color: #696969; roughness: 1; metalness: 0">
</a-entity>

For basic scenes, physically based materials are generally not needed. In that case, we want to specify flat shading for better performance.

Environment Maps

The envMap property defines what environment the material reflects. This also works together with plain textures. Though unlike regular texture maps, the envMap property takes a cubemap, six images put together to form a cube. The cubemap is wrapped around the entity and applied as a texture. Note that cubemaps can not yet be defined for regular textures.

If shader is set to standard, which it is by default, the clarity of the reflection depends on the metalness, and roughness properties.

<a-scene>
<a-assets>
<a-cubemap id="sky">
<img src="right.png">
<img src="left.png">
<img src="top.png">
<img src="bottom.png">
<img src="front.png">
<img src="back.png">
</a-cubemap>
</a-assets>

<a-entity geometry="primitive: box" material="envMap: #sky; roughness: 0"></a-entity>
</a-scene>

Flat Shading Model

Since materials default to physically based shading, materials will factor in light when we might not want them to. To specify flat shading, useful for displaying media like images or videos, we can define the shader property to be flat. The flat shading model can be specified by setting the shader to be flat in the material component:

<a-entity geometry="primitive: plane" material="src: #cat-image; shader: flat">
</a-entity>

Properties

Property Description Default Value
color Base diffuse color. #fff
fog Whether or not material is affected by fog. true
height Height of video (in pixels), if defining a video texture. 360
repeat How many times a texture (defined by src) repeats in the X and Y direction. 1 1
src Image or video texture map. Can either be a selector to an <img> or <video>, or an inline URL. None
width Width of video (in pixels), if defining a video texture. 640

Registering a Custom Shader

We register a shader with AFRAME.registerShader, passing in a schema. We can then either pass vertex and fragment shaders for use with THREE.ShaderMaterial, or we can override the A-Frame shader initialization and pass in our own material.

Schema

Shader schemas are similar to component schemas. The schema will extend the base material component schema. If we are creating a custom shader by passing in vertex and fragment shaders, we can specify uniforms and attributes:

AFRAME.registerShader('hello-world-shader', {
schema: {
sunPosition: { type: 'vec3', is: 'uniform' },
time: { type: 'time', is: 'uniform' }
}
});

Shader Uniform and Attribute Types

Type Description
color Built-in convenience (vec3) uniform type. Will take colors in multiple formats and automatically convert them to vec3 format (e.g., ‘red’ -> THREE.Vector3(1, 0, 0))
number Maps to GLSL float.
time Built-in convenience (float) uniform type. If specified, the material component will continuously update the shader with the global scene time.
vec2 Maps to GLSL vec2.
vec3 Maps to GLSL vec3.
vec4 Maps to GLSL vec4.

Standard Component Properties

If we just want to create a custom material THREE.Material, we can define component properties as usual:

AFRAME.registerShader('custom-material', {
schema: {
emissive: { default: '#000' },
wireframe: { default: false }
}
});

Creating a Custom Shader

Under the hood, custom shaders use THREE.ShaderMaterial. We register a shader with AFRAME.registerShader while defining a schema, a vertex shader, and a fragment shader:

AFRAME.registerShader('hello-world-shader', {
schema: {
color: { type: 'vec3', default: '0.5 0.5 0.5', is: 'uniform' }
},

vertexShader: [
'void main() {',
' gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix * gl_Vertex;',
'}'
].join('\n'),

fragmentShader: [
'uniform vec3 color;'
'void main() {'
' gl_FragColor = vec4(color, 1.0);',
'}'
].join('\n')
});

To use the shader, we set the material component’s shader property to the name of a registered shader. Then we pass the define shader uniforms and attributes as properties:

<a-entity geometry="primitive: box"
material="shader: hello-world-shader; color: 0.3 0.3 0.3"></a-entity>

Creating a Custom Material

By default, shaders will try to create a THREE.ShaderMaterial. We can override the init and update handlers of the shader and setting this.material to work with our own custom material:

AFRAME.registerShader('line-dashed', {
schema: {
dashSize: { default: 3 },
lineWidth: { default: 1 }
},

/**
* `init` used to initialize material. Called once.
*/
init: function (data) {
this.material = new THREE.LineDashedMaterial(data);
},

/**
* `update` used to update the material. Called on initialization and when data updates.
*/
update: function (data) {
this.material.dashsize = data.dashsize;
this.material.linewidth = data.linewidth;
}
});

Shader API Reference

Like components, shaders have a schema and lifecycle handlers.

Property Description
schema Defines properties, uniforms, attributes that the shader will use to extend the material component.
init Lifecycle handler called once during shader initialization. Used to create the material.
update Lifecycle handler called once during shader initialization and when data is updated. Used to update the material or shader.

Additional Resources