Smooth Voxels Manual


by Samuel van Egmond






https://svox.glitch.me

Smooth Voxels 1.2.0

To table of contents ▼
by Samuel van Egmond

Are you a developer and not a designer with crazy (or any) Blender skills? But you would still like to create your own 3D models, for instance for a great little WebXR game? Then Smooth Voxels is for you!

Smooth Voxels allows you to turn voxel models into low poly style or smooth organic looking models, even mixing styles in one model.

Smooth Voxels uses vertex averaging to produce a much smoother appearance than for instance marching cubes, as seen in the 7x7x7(!) voxel apple to the right.

Yeah, I got it, just take me to the Smooth Voxel Playground!

All these models are generated from voxels!

Voxels can be smoothed, randomly displaced or warped for different visual styles and effects, and the materials allow for Physically Based Rendering, having roughness, metalness, opacity and emission. For added reality, you can add textures and normal maps or per vertex ambient occlusion.

Using Smooth Voxels
Want to use your Smooth Voxel models in a WebXR project or a 3D scene or game? Just export them as .gltf or .glb file for direct use in A-frame, Three.js, Babylon.js, PlayCanvas, Unity or Blender.



The Smooth Voxels A-Frame component is fast enough to use in small static scenes.

However, especially with complex models and ambient occlusion, exporting a .glft or .glb model or even an javascript A-Frame component may speed up loading times for your scenes. You'll have to try out what works for your situation.

For a full example using Smooth Voxels .glb models for objects, rooms and hallways, visit infinitemuseum.glitch.me.

About this document
This documentation contains a full description of the different Smooth Voxels Component features. Press the 'Try it out' buttons and play around with the numbers in the examples to better understand their effects.
You can browse the documentation on your desktop, but also in VR, all samples work in the Oculus Browser on Meta Quest (2) and Oculus Go.
At the end of this documentation you will find a full model syntax reference and compression description. If you just need a quick reference use the Cheat sheet.
The simple text based model syntax makes Smooth Voxels one of the easiest ways to procedurally generate 3D models! See the Getting Started with Procedural Generation chapter for more information.
If you want to read this documentation offline, it prints well to PDF.

Table of Contents

To top ▲

Smooth Voxel Playground

To table of contents▲ If you just want to explore and see some examples, head over to the Smooth Voxel Playground!



The playground shows after every render how many materials and how many faces (triangles) were created. For best performance when using models in a game or scene, reducing materials to a minimum (preferably 1) is most important, but reducing face count also helps. Always try to use the smallest model size and fill all interiors (more voxels, but less faces).
The Smooth Voxel Playground allows you to load and save Smooth Voxel Models and convert images into Smooth Voxel models.

Importing MagicaVoxel
The Smooth Voxel Playground also allows you to convert MagicaVoxel .vox models. Everytime you reload your model you keep the SVOX model and material settings which are linked to the palette indexes in your .vox model. The palette index is stored with the color id, e.g. A(123):#0088FF. That way you can edit your model in MagicaVoxel, reloading in the playground to see the result without losing your materials. To start without model and material settings, just clear the editor before loading a new .vox model.
Exporting
Another great option is to export you model to .gltf or .glb, which gives you an immediately usable model for use in WebXR and other 3D environments.
Note that the .gltf and .glb file format does not allow for some of the material types and options for some material types:
Eventhough side = back is not supported by .gltf and .glb materials, Smooth Voxels reverses the faces in its models to give the same result. When needed, materials can of course be recreated and added in your target environment.

Note that the A-Frame javascript export options in the playground do support all material types!

Smooth Voxels Release Notes

To table of contents▲ Smooth Voxels 1.2.0 (April 3, 2022)

New model functionality: New playground functionality:
New examples for 1.2.0: Bug fixes: Known issues: Smooth Voxels 1.1.0 (December 19, 2021)

New model functionality: New playground functionality: Bug fixes:
Smooth Voxels 1.0.0 (December 11, 2021)

This was the first Smooth Voxels release, containing already almost all functionality described in this document.

Getting Started with the SVOX Component

To table of contents▲ Getting started with Smooth Voxels in your own A-Frame project is easy.

Just go to the svox-examples project on glitch.com, include the smoothvoxels.1.2.0.min.js and smoothvoxelworker.1.2.0.js files into your own project and follow the example below. This example shows a runtime generated Smooth Voxels model: Try it out

        
      
Perhaps unexpectedly, workers slow the generation down. However, they do offload most of the work from the main thread keeping the page responsive.

Getting Started without the SVOX Component

To table of contents▲ If you don't want to include the Smooth Voxels library, you've got two other options; You can just export the model from the Smooth Voxel Playground as a .gltf file. and use that directly. For more information see the A-Frame documentation.

Alternatively, you can also export your model directly into a javascript A-Frame component that you can use directly as in the example below, which uses the same model as the previous example: Try it out

      
      
Note that, with or without the Smooth Voxels Component, when using metalness on the standard material, you need to provide an environment map that can be reflected. Adding the below code at the end of the html file between the </body> and the </html> tag will provide a simple environment map:

        
      

Getting Started on your own SVOX Component

To table of contents▲ Sometimes it is better to create your own A-Frame component instead of the Smooth Voxels A-Frame component. Especially when reusing the same Smooth Voxel model multiple times it may be better to create your own A-Frame component, generate the mesh once and reuse it.

The following example shows how to generate a mesh and reuse it many times to create 'infinite' 3D Truchet tiles: Try it out
    
        
      

Getting Started with Procedural Generation

To table of contents▲ Smooth Voxels is one of the easiest ways of getting into procedural 3D model generation.

Just go to the svox-examples project on glitch.com, include the smoothvoxels.1.2.0.min.js and smoothvoxelworker.js files into your own project and follow the example below.

Below an example in which a planet is generated: Try it out


      
      
      

Model basics

To table of contents▲ Let's start with an simple example of a model showing most of the features of the Smooth Voxels component. Try it out
model
size = 5 2 5        // Size X * Y * Z Voxels    
scale = 0.2         // Uniform scale, X Y Z is also supported    
origin = -y         // The origin is at center bottom    
rotation =  0 225 0 // Rotate the vertices     
position =  0 0.1 0 // Reposition the vertices (in world scale) 
resize = model      // Resize the model to original size after deform, warp or scatter

wireframe = false   // Wireframe can be set globally or on a material

// Add ambient occlusion for extra realism
ao = 5 1  

// The red material uses the default flat lightened voxel material
material
  colors = A:#880000 

// The green material is transparent
material lighting = flat, opacity = 0.5
  colors = B:#080 

// The blue material is deformed and metallic  
material lighting = smooth, roughness = 0.3, metalness = 1, deform = 1
  colors = C:#00F

// The yellow material is deformed and clamped 
// For clamping use a material both flat and smooth
material lighting = both, roughness = 0.2, deform = 1, clamp = -z -x +y
  colors = D:#880 

// The voxels are represented in X Y Z order
// I.e. from left to right you see the (two) horizontal voxel layers
voxels = 
-D-C- -----
DD-CC -D-C-
----- ----- 
AA-BB -A-B-
-A-B- -----
The model has the following properties, of which only 'size' is mandatory:

General Transformations
Scale, origin, rotation and position are not 'virtual' transformations but actual transformations that will be performed on the final model. This can be useful when combining multiple models or when you want the 'front' of the model to be one of the corners.
Planar actions
Shaders

Materials

To table of contents▲ Materials allow for standard properties such as roughness, metalness, opacity, or emission, but they also specify how the voxel vertices need to be deformed, scattered, etc. to turn voxels into smooth organic or low poly models. By using different materials in one model, these different styles can be mixed.

Per material one or more colors must be defined. By setting the voxels to different colors, the voxels also get the respective materials for these colors.

A material has the following properties, of which only ''colors' is mandatory:

Lighting Physically Based Rendering properties General Deformations Planar actions Shaders The following example shows most of the features of the most versatile material, the standard material: Try it out
model 
size = 14 6 14, 
scale = 0.0714, 
origin = -y

// The red material rough (roughness 1) and non-metallic (metalness 0)
material deform = 3, side = double
  colors = A:#800

// The green material is more polished and somewhat metallic
material lighting = flat, roughness = 0.5, metalness = 0.5, 
         deform = 3, side = double, colors = B:#080

// The blue material is highly polished and metallic
material lighting = smooth, roughness = 0.2, metalness = 1, 
          deform = 3, side = double,  colors = C:#00F

// The light blue material is semi-transparent
material lighting = smooth, roughness = 0.2, metalness = 1, 
         deform = 3, opacity = 0.6, side = double
         colors = D:#0DF

// The magenta material uses wireframe
material lighting = flat, deform = 3, wireframe = true
  colors = E:#F0F

// The orange material is actually red but emits yellow light
// Ignore fog (e.g. in a scene with black fog for darkness)
material lighting = flat, emissive = #FF0 0.5, fog = false, 
         deform = 3, side = double
  colors = F:#F00

voxels =
6(------EE------)
6(-----EEEE-----)
6(-FF--EEEE--DD-)
6(FFFF--EE--DDDD)
6(FFFFFEEEEDDDDD)
6(-FFFFEEEEDDDD-)
6(---FFFEEDDD---)
6(---AAABBCCC---)
6(-AAAABBBBCCCC-)
6(AAAAABBBBCCCCC)
6(AAAA--BB--CCCC)
6(-AA--BBBB--CC-)
6(-----BBBB-----)
6(------BB------)

Material Types

To table of contents▲ With the standard material and the many Smooth Voxel options we can create almost any material. However, this material can be slower than other options, especially when used on a mobile device with a less powerful GPU. The same way as that it is important to reduce the number of materials and faces to increase performance of your models, it may also help to use other material types.
Always test in your target platform and target devices to see what gives the best results. The actual performance not only depends on the type of the material, but for instance also on the amount and size of textures used.
Smooth Voxels supports the following material types (in order of performance, slower to faster):
The phong, lambert and basic material types use reflectivity instead of rougness and metalness and phong also has shininess. However, for ease of use Smooth Voxel only supports roughness and metalness, and uses those to approximate reflectivity and metalness. Note that .gltf and .glb export do not support reflectivity. Try to export and import in your target environment to see what works and what does not.
Smooth Voxels also supports the following material types for 'special effects':
The following example shows the other material types that exist next to the standard material: Try it out
texture id = glass, cube = false, image = data:image/jpeg;base64,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

model
size = 14 6 14
scale = 0.0714
origin = -y
rotation = 0 180 0
wireframe = false

// The red material is a phong material
material type = phong, lighting = smooth, deform = 3
  colors = A:#800

// The green material is a lambert material
material type = lambert, lighting = smooth, deform = 3
  colors = B:#080

// The blue material is a basic material (unaffected by scene lights, affected by Smooth Voxel lights)
material type = basic, lighting = smooth, deform = 3
  colors = C:#00F

// The light blue material is a toon material
material type = toon, lighting = smooth, deform = 3
  colors = D:#48C

// The glass material is a matcap material using a glass matcap
material type = matcap, matcap = glass, lighting = smooth, deform = 3
  colors = E:#FFF

// The multi colored material is a normal material showing the surface normals
material type = normal, lighting = smooth, deform = 3
  colors = F:#F00

voxels =
6(------EE------)
6(-----EEEE-----)
6(-FF--EEEE--DD-)
6(FFFF--EE--DDDD)
6(FFFFFEEEEDDDDD)
6(-FFFFEEEEDDDD-)
6(---FFFEEDDD---)
6(---AAABBCCC---)
6(-AAAABBBBCCCC-)
6(AAAAABBBBCCCCC)
6(AAAA--BB--CCCC)
6(-AA--BBBB--CC-)
6(-----BBBB-----)
6(------BB------)

Colors

To table of contents▲ Every material must define one or more colors with IDs. E.g. colors = A:#F00 B:#00FF00 Blue:#0000ff

Colors must use hexadecimal notation with 3 (#RGB) or 6 (#RRGGBB) hexadecimal digits and must start with a # character.
Each color must have an Id consisting of one uppercase letter, optionally followed by one or more lower case letters [A-Z][a-z]*.
These color Id's are used in the Voxel matrix where empty voxels are shown by means of '-'.
Multiple materials result in multiple draw calls, but multiple colors per material do not, so limit the number of materials to limit the number of draw calls.
Smooth Voxels will combine materials which are the same except for lighting, ao, fade, deform, warp, scatter, flatten, clamp and skip.

Deform

To table of contents▲ Deform works by averaging linked vertices. Deform has three parameters:

deform = <count> <strength> <damping>.

Without damping a shape becomes more and more deformed, while often shrinking the model parts, when the count is higher.
With damping (e.g. 0.7) this effect is diminished while allowing enough steps for a very smooth deformation.
This shrinking can be counteracted in two ways:

1) Add resize=model to the model to resize the model to the original bounding box. This works only if the entire model is deformed and shrinks, not just a part.
2) Use a high count (e.g. 100), with strength 0.5 and damping -1. The negative value for damping makes alternate steps shrink and grow the model resulting in it staying roughly the same size.
Experiment with the values to get the best result with the lowest count. High counts can result in slow rendering when used on large models.
Some combinations of values can create a feedback loop resulting in your model becoming extremely jagged even with a positive strength. Try to reduce the strength if that is the case.
Try it out
// This material is deformed 
material lighting = flat, deform = 1
  colors = B:#0F0 

// This material is more strongly deformed
material lighting = flat, deform = 4
  colors = C:#00F 

// This 'spiked' material is deformed with a negative strength
material lighting = flat, deform = 1 -1
  colors = D:#880
model
size = 7 3 7
scale = 0.143
origin = -y
rotation = 0 -45 0

wireframe = false

// The red material is not deformed
material lighting = flat
  colors = A:#800

// The green material is deformed
material lighting = flat, metalness = 1, deform = 1
  colors = B:#0F0

// The blue material is more strongly deformed
material lighting = flat, metalness = 1, deform = 4
  colors = C:#00F

// The yellow material is deformed with a negative strength
material lighting = flat, deform = 1 -1
  colors = D:#880

voxels =
DDD-CCC D-D-C-C --D-C--
DDD-CCC ------- --D-C--
DDD-CCC D-D-C-C DDD-CCC
------- ------- -------
AAA-BBB A-A-B-B AAA-BBB
AAA-BBB ------- --A-B--
AAA-BBB A-A-B-B --A-B--

Warp

To table of contents▲ Warp uses a noise function to move each vertex.
Warp has two parameters, deform = <strength> <frequency>.

Strength is used to increase how much each vector is moved.
Frequency (optional, > 0 and <= 1) determines how fast the noise field changes (lower means slower). Try it out
// Use tile to prevent scatter and warp at the edges
// tile = x z

// This material is warped 
material lighting = flat, warp = 1 0.25
  colors = B:#0F0 

// This material is strongly warped
material lighting = flat, warp = 2 0.5
  colors = C:#00F 

// This material is warped with a high frequency
material lighting = flat, warp = 0.5 1
  colors = D:#FF0
model
size = 7 7 7        
scale = 0.143        
origin = -y        
rotation = 0 -45 0 
wireframe = false  

// Use tile to prevent scatter and warp at the edges
// tile = x z

// Warp is different each time (press show repeatedly) 

// The red material is not warped
material lighting = flat, metalness = 1
  colors = A:#F00 

// The green material is warped 
material lighting = flat, metalness = 1, warp = 2 0.125
  colors = B:#0F0 

// The blue material is warped with a higher frequency
material lighting = flat, metalness = 1, warp = 2 0.25
  colors = C:#00F 

// The yellow material is warped only in the x and y directions
material lighting = flat, metalness = 1, warp = 1 0.25, clamp = y
  colors = D:#FF0
  
voxels = 
--D-C-- --D-C-- --D-C-- DDD-CCC --D-C-- --D-C-- --D-C-- 
--D-C-- --D-C-- --D-C-- DDD-CCC --D-C-- --D-C-- --D-C-- 
DDD-CCC DDD-CCC DDD-CCC DDD-CCC DDD-CCC DDD-CCC DDD-CCC 
------- ------- ------- ------- ------- ------- -------
AAA-BBB AAA-BBB AAA-BBB AAA-BBB AAA-BBB AAA-BBB AAA-BBB
--A-B-- --A-B-- --A-B-- AAA-BBB --A-B-- --A-B-- --A-B--
--A-B-- --A-B-- --A-B-- AAA-BBB --A-B-- --A-B-- --A-B--

Scatter

To table of contents▲ Scatter moves the x, y and z of each vertex by a maximum of the provided amount.
Scatter has one parameter, scatter = <strength>.
Strengths over 0.5 (or even smaller when combining with deform or warp) may result in intersecting faces.
Try it out
// Use tile to prevent scatter and warp at the edges
// tile = x z

// This material is scattered 
material lighting = flat, scatter = 0.25
  colors = B:#0F0 

// This material is strongly scattered
material lighting = flat, scatter = 0.5
  colors = C:#00F 

// This material is deformed and scattered
material lighting = flat, deform = 2, scatter = 0.15
  colors = D:#FF0
model
size = 7 3 7        
scale = 0.143        
origin = -y   
rotation = 0 135 0
wireframe = false  

// Use tile to prevent scatter and warp at the edges
// tile = x z

// Scatter is different each time (press show repeatedly) 

// The red material is not scattered
material lighting = flat, metalness = 1
  colors = A:#F00 

// The green material is scattered 
material lighting = flat, metalness = 1, scatter = 0.25
  colors = B:#0F0 

// The blue material is strongly scattered
material lighting = flat, metalness = 1, scatter = 0.5
  colors = C:#00F 

// The yellow material is deformed and scattered
material lighting = flat, metalness = 1, deform = 2, scatter = 0.15
  colors = D:#FF0 

voxels = 
DDD-CCC DDD-CCC DDD-CCC 
DDD-CCC DDD-CCC DDD-CCC 
DDD-CCC DDD-CCC DDD-CCC 
------- ------- -------
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB

Planar definitions

To table of contents▲ Flatten, clamp and skip specify sides of the model or of the material that need to be flattened, clamped or skipped. Aosides specifies sides of the model from which the ambient light is occluded (e.g. a wall or floor). These settings are all done using planar definitions.

Planar definitions are best explained by example:
See the respective chapters for examples of planar definitions to try out.

Flatten and Clamp

To table of contents▲ Flatten and Clamp both fix the vertices to the sides of the model or the material in a certain direction, and work the same for scatter and warp, simply flattening the vertices.

In case of deform, which deforms by averaging linked vertices, clamp also breaks the links with vertices on the clamed sides. Where flatten results in simply flattening the side, clamp therefore results in perpendicular edges.

Flatten and clamp can be set on the model in which case the boundaries of the model are used for -x, +x, -y, +y, -z and +z. When flatten and clamp are used on a material the boundaries of the material are used.

See the chapter on planar definitions on how to specify which sides or planes to skip.
Try it out
// This material is flattened at the top and bottom
material lighting = flat, deform = 3, flatten = -y +y
  colors = B:#080 
  
// This material clamps all vertices in the y direction 
material lighting = flat, deform = 3, scatter = 0.2, clamp = y
  colors = D:#880
model
size = 7 3 7
scale = 0.143
origin = -y
wireframe = false
rotation = 0 -45 0

// The red material is deformed but not flattened or clamped
material lighting = flat, deform = 3
  colors = A:#800

// The green material is flattened at the top and bottom
material lighting = flat, deform = 3, flatten = -y +y
  colors = B:#080

// The blue material is clamped at the top and bottom
material lighting = flat, deform = 3, clamp = -y +y
  colors = C:#00F

// The yellow material clamps all vertices in the y direction
material lighting = flat, deform = 3, scatter = 0.2, clamp = y
  colors = D:#880

voxels =
DDD-CCC --D-CCC --D-CCC
DDD-CCC DDD-CCC --D-CCC
DDD-CCC DDD-CCC DDD-CCC
------- ------- -------
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB

To table of contents▲ Skip is used to not render certain planes or sides. This can be used for models which are not seen from certain angles (e.g. from the bottom). In case of deform, which deforms by averaging linked vertices, clamp also breaks the links with vertices on the clamed sides. Where flatten results in simply flattening the side, clamp results in perpendicular edges.

Skip can be set on the model in which case the boundaries of the model are used for -x, +x, -y, +y, -z and +z. When skip is used on a material the boundaries of the material are used.

See the chapter on planar definitions on how to specify which sides or planes to skip.
Try it out
// This material skips the faces for the top and bottom of the model
material lighting = flat, deform = 3, flatten = -y +y, skip = -y +y
  colors = B:#080 
  
// This material skips all faces in the y plane 
material lighting = flat, deform = 3, scatter = 0.2, skip = y 
  colors = D:#880
model
size = 7 3 7
scale = 0.143
origin = -y
rotation = 0 135 0
wireframe = false

// Let all materials skip the top side of the model
skip = +y

material lighting = flat, deform = 3
  colors = A:#800

material lighting = flat, deform = 3, flatten = -y +y
  colors = B:#080

material lighting = flat, deform = 3, clamp = -y +y
  colors = C:#00F

// Override the default so the yellow material skips the top of all voxels
material lighting = flat, deform = 3, scatter = 0.2, clamp = y, skip = y
  colors = D:#880

voxels =
DDD-CCC --D-CCC --D-CCC
DDD-CCC DDD-CCC --D-CCC
DDD-CCC DDD-CCC DDD-CCC
------- ------- -------
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB
AAA-BBB AAA-BBB AAA-BBB

Fade

To table of contents▲ Fade lets the colors for one material blend into each other when different colored voxels are next to each other. Fade is simply added to the material as fade = true.
Try it out
// This model fades the voxel colors
size = 3
material lighting = flat, fade = true 
  colors = A:#800 B:#840  C:#880
voxels =
--A --- ---
-AB --B ---
ABC -BC --C
model
size = 7 3 7
scale = 0.143
origin = -y
rotation = 0 135 0

// Voxels, not faded
material lighting = flat, deform = 0, fade = false
  colors = A:#800 B:#840  C:#880

// Voxels faded
material lighting = flat, deform = 0, fade = true
  colors = D:#800 E:#840  F:#880

// Flat faded
material lighting = flat, deform = 2, clamp = -x -y -z +x +y +z, fade = true
  colors = G:#800 H:#840  I:#880

// Smooth faded
material lighting = both, deform = 2, clamp = -x -y -z +x +y +z, fade = true
  colors = J:#800 K:#840  L:#880

voxels =
JJJ-GGG --K-H-- --L-I--
JJJ-GGG -KK-HH- --L-I--
JJJ-GGG KKK-HHH LLL-III
------- ------- -------
AAA-DDD BBB-EEE CCC-FFF
AAA-DDD -BB-EE- --C-F--
AAA-DDD --B-E-- --C-F--

Shape

To table of contents▲ Shape allows for some interesting effects by deforming the entire model to fit inside a the specified shape. The possible shapes are: Try it out
// This model is sphere shaped
size = 5
shape = sphere
material lighting = flat, colors = A:#800 B:#080
voxels =
AAAAA A---A A---A A---A AAAAA
AAAAA ----- ----- ----- AAAAA
AAAAA ----- BBBBB ----- AAAAA
AAAAA ----- ----- ----- AAAAA
AAAAA A---A A---A A---A AAAAA
model
size = 5
scale = 0.2
origin = -y
shape = sphere // Also try: cylinder-x, cylinder-y and cylinder-z
material lighting = flat, colors = A:#800 B:#080
voxels =
AAAAA A---A A---A A---A AAAAA
AAAAA ----- ----- ----- AAAAA
AAAAA ----- BBBBB ----- AAAAA
AAAAA ----- ----- ----- AAAAA
AAAAA A---A A---A A---A AAAAA

Lights

To table of contents▲ Lights can be baked into the model vertex colors, but lights do not throw shadows.

Combining Smooth Voxel lights with type basic materials (which are not affected by lights in a scene) gives great lighting with best performance.

Three types of light are supported: Lights are applied after all transformations have been done. Direction and position must therefore be specified in real coordinates.

Lights are applied to all materials, except for materials that specify 'lights = false'.

Point lights can be added to the model for debugging or a visible light by adding a size and optional detail. Detail can be 0 to 3 where 0 is an octahedron, and 1, 2 and 3 spheres with 32, 128 and 512 faces respectively. Try it out
// An ambient filler light, not too bright
light color = #FFF 0.33

// Red, green and blue directional lights (scene coordinates!)
light color = #F44, direction = -1.0  0.0 -1.0
light color = #4F4, direction =  0.0 -1.0  0.0
light color = #44F, direction =  1.0  0.0 -1.0

// An visible point light with limited distance  (scene coordinates!)
light color = #FF0, position = 0.0 0.4 0, distance = 0.5, size = 0.25, detail = 2
model

// An ambient filler light, not too bright
light color = #FFF 0.33

// Red, green and blue directional lights (scene coordinates!)
light color = #F44, direction = -1.0  0.0 -1.0
light color = #4F4, direction =  0.0 -1.0  0.0
light color = #44F, direction =  1.0  0.0 -1.0

// An bright point light with limited distance  (scene coordinates!)
light color = #FF0 2, position = 0.0 0.4 0, distance = 0.5, size = 0.25, detail = 2

model
size = 10 10 10
scale = 0.1
origin = -y
rotation = 0 -45 0

// A basic material, which is not affected by the lights in your scene
material type = basic, lighting = smooth, deform = 20
  colors = A:#FFF

voxels
AAAAAAAAAA 4(AAAAAAAAAA) 4(AAAAAAAAAA) AAAAAAAAAA
AAAAAAAAAA 4(A--------A) 4(A--------A) AAAAAAAAAA
AAAAAAAAAA 4(A--------A) 4(A--------A) AAAAAAAAAA
AAAAAAAAAA 4(A--------A) 4(A--------A) AAAAAAAAAA
AAAAAAAAAA 4(A--------A) 4(A--------A) AAAAAAAAAA
AAAAAAAAAA 4(A--------A) 4(A---------) AAAAAAAAA-
AAAAAAAAAA 4(A--------A) 4(A---------) AAAAAAAA--
AAAAAAAAAA 4(A--------A) 4(A---------) AAAAAAA---
AAAAAAAAAA 4(A--------A) 4(A---------) AAAAAA----
AAAAAAAAAA 4(AAAAAAAAAA) 4(AAAAA-----) AAAAA-----
Lights work per vertex, for point lights this means that their light may look 'blocky' when using few voxels.
Point lights can prevent Smooth Voxels from combining faces, resulting in a larger (and thus slower) mesh.

Ambient Occlusion

To table of contents▲ Ambient Occlusion is calculated by shooting rays out of each vertex to determine the distance to other parts of the model. When other parts are near, the vertex is darkened because the ambient light will be more occluded by the nearby geometry. This can a slow process on large models but greatly improves the realism of your models in some cases providing nice fading shadows.

Ambient Occlusion, ao, has three parameters, ao = <color> <maxDistance> <strength>.
The default color is black, but you can get different effects using other colors. Higher maxDistance (in voxels, default 1) takes further away parts into account. A higher strength (default 1) makes the ambient occlusion stronger, i.e. the occluded parts darker. A negative strength make the occlude parts lighter, which can be used to make bright spots appear behind lamps.

Ambient Occlusion Sides, aoside, is used to specify which sides occlude ambient light. See the chapter on planar definitions on how to specify which sides occlude ambient light.

The default for ao can be set on the model (before the materials) and/or can be overridden per material. Aosides can only be set on the model. Try it out
// This model has strong, slowly fading, ambient occlusion (slower)
ao = 10 2

// This model has normal ambient occlusion 
ao = 5 1

// This model has subtle ambient occlusion only in the corners (faster)
ao = 1 1

// This model has inversed ambient occlusion (as if lit from inside)
ao = 5 -2

// This model has red colored ambient occlusion
ao = #FF0 2 1

// For this model the ambient light from beneath is occluded
ao = 5 1
aosides = -y
model
size = 12 5 6
scale = 0.1
origin = -y
rotation = 0 -20 0

aosides = -y

// The yellow material uses the model settings for black (default) ambient occlusion
ao = 3 1

material lighting = flat
  colors = A:#880

// The black material uses yellow ambient occlusion
material lighting = flat, ao = #880 3 2
  colors = B:#000

voxels =
BBBBB--AAAAA B---B--A---A B---B--A---A B---B--A---A BBBBB--AAAAA
BBBBB--AAAAA ------------ ------------ ------------ B---B--A---A
BBBBB--AAAAA BB-BB--AA-AA BB-BB--AA-AA BB-BB--AA-AA BBBBB--AAAAA
BBBBB--AAAAA BB-BB--AA-AA BB-BB--AA-AA BB-BB--AA-AA BBBBB--AAAAA
BBBBB--AAAAA ------------ ------------ ------------ B---B--A---A
BBBBB--AAAAA B---B--A---A B---B--A---A B---B--A---A BBBBB--AAAAA
Ambient Occlusion can be slow. Higher maximum distances and larger models can sometimes be very slow.
Ambient Occlusion with a negative strength can result in the RGB values going outside of their normal range. In Three.js / A-Frame this works well (even a white surface can have a brighter spot) but does not work in PlayCanvas. This behavior may change in future versions of those frameworks.

Textures

To table of contents▲ Texture can really improve the look and feel of your object by adding details much smaller than the voxels themselves. However, since you cannot define exactly which part of the texture goes where, you can't texture complex models as you could in for instance Blender. Especially since texture wrap around voxels before they are smoothed, the results may vary and require quite some compromises. Textures are great however to create for instance repeating surfaces (planks, rivets, etc.) or small, specific details, like a painting in a frame or an image on a monitor.

Textures can be wrapped around the object or around the voxels. To use a texture you first need to convert an image them to Base64: Supported image types are png, jpg and gif (no animation).
Preferably use power of two texture sizes, i.e. typically 64x64, 128x128, 256x256 or 512x512.

Textures need to be predefined outside of a material using the Base64 string (inclusing mime type):

texture id=<textureid>, image = data:image/<type>;base64,<base64imagedata>

Once defined, the texture can be used in one or more materials as texture, normal map, roughness map or metalness map (typically different textures for each):

Add map = <textureid> to add a texture map to a material.

Add normalmap = <textureid> to add a texture as normal map to a material.

Add transparent = true to the material when using a transparent texture otherwise you won't be able to see through the texture.

Adding alphatest = <float:distance> to a material helps when your texture is opaque with clear holes in it. The material will not be rendered if the opacity is lower than this value.
Transparent materials give rise to occlusion issues where a transparent texture will hide other transparent textures behind it, even in its gaps. This will for instance make a model with a double sided transparent material with gaps look like the other side is missing when looking through the gaps at the front side. Using alphatest will often solve this problem at the cost of smooth alpha transitions.
By default the texture will cover the model on all sides, but the texture for a material can be transformed to stretch or rotate, by specifying width, height and offset in voxels:

maptransform = <width> <height> <xoffset> <yoffset> <rotation>.

The example below uses a map for the sides of the can and a normal map for the pull tap on top: Try it out
// The side of the can
texture id = can, cube = false, image = data:image/png;base64,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

// The normal map for the top of the can
texture id = top, cube = false, image = data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAMAAACdt4HsAAAAqFBMVEWEhP9KjN6EjP+Mc+Zrc+a1jL2MhP+MpeZrhOata96M3rVrpd61rb2MUt6Mrb1Krd5rUt6MhNbmjLVKa96MKZytSt4pjJwpa5xKSt7Oa5xrzpy1jOa1zrWtSpyMSpxrSpxKSpxrzt4pa95rjJzOSpyMKd5rKd6ta5ytKZxrKZyMnJS1reYpjN7Oa95KzpxKrZxKa5wpSpxKzt5rrZwprZxKjJyMa5xKKZzGSRY/AAACH0lEQVRYw+2W3XaiMBSFhUAAAyExgxVEUFutOm3nf+b932xOYgqtq7Q5XPXCc5F4YO2PncUOZnKta332iiI+NbVh/hh5PeVRBErfZ5uqYki5n9bRy5495TgXaWqmQGRZRszPPMfo6Vm/OGrAoaC6+R0jAElo9ItzVxR69DAAogEK9JaQwLBFAWYw3ARdW8LwgAbM+77UPAyAzugrgOHhAXHfageoHJQA8PIuFUs0YEZtdvpUzXEOiM5zXjHYUXXq61AiHSzNxBjn/LwpYpyDMLm8EmxxgCVI/n2xdYAFeR4aoFS3NRI0IAmtxPefOzUGAJ+l6mdsuzGAFN4Ay02nYhSAGICJMERaAx6QgJkBlMQkSAO2OAA1gEmyysRaGcDNKMAky4SAlwqAORJQQnifprZqajYjej9PfFvwEwsI7sjFhR8EoxeifHx9Rc2zgjo/fiFTai10BuKcFcRVrwOYvCQE6wAiCQQ3fRjBpDPQrWgtYASC0yqU/GrTuDpkhJBAHFv7F5PLxMHA95B2gc7ga3IUILfl3dOPDQTh8E3PwYL6u3wHcDtM712S4ZtxBxjpUrkA/jwO39yGyw8B8bd7MvyGUocg5Ld3dEC/DiEiDgRZkMEtArPDIuSKvPV8mfqOp8ycrS5NBKLloHctVslml5FevW9lHaGOuqzisml2u6bZ79tW8inHHrj1EXvDf51OJw7qyMqxED+CAvG1Pnn9B6SGHYnYmA+2AAAAAElFTkSuQmCC

model
size = 10 12 10
shape = cylinder-y
scale = 0.05 0.075 0.05
rotation = 45 -50 0
origin = -y +z

// Position the texture and normal map
material lighting = smooth, roughness = 0.2, metalness = 1, map = can, maptransform = 10 10 0 -0.1
  colors = A:#FFF 
material lighting = flat,   roughness = 0.2, metalness = 1, normalmap = top, maptransform = 8 8 -0.125 0.2
  colors = B:#DEF
material lighting = smooth, roughness = 0.2, metalness = 1, deform = 4
  colors = C:#DEF
material lighting = flat,   roughness = 0.2, metalness = 1
  colors = D:#DEF
  
voxels
CCCCCCCCCC AAAAAAAAAA 8(AAAAAAAAAA) AAAAAAAAAA CCCCCCCCCC
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
C--------C ADDDDDDDDA 8(AAAAAAAAAA) ABBBBBBBBA C--------C
CCCCCCCCCC AAAAAAAAAA 8(AAAAAAAAAA) AAAAAAAAAA CCCCCCCCCC
      
Next to map and normalmap, also other types of maps are supported:

Add roughnessmap = <textureid> to add a texture as roughness map to a material.

Add metalnessmap = <textureid> to add a texture as metalness map to a material.

Add emissivemap = <textureid> to add a texture as emissive map to a material.

Add emissivemap = <textureid> to add a texture as emissive map to a material.

Add matcap = <textureid> to add a matcap texture to a material of type 'matcap'.

Note that a metalness map is multiplied by metalness.
If you don't specify the metalness when using metalnessmap, metalness will be set to 1.
Note that a roughness map is multiplied by roughness.
If you don't specify the roughness when using roughnessmap, roughness will be set to 1.
Note that metalness map and roughness map textures are typically greyscale images. The values use will be 0 for black to 1 for white.
However, when using both together in one material, one texture must be supplied.
The metalness values will be taken from the B channel.
The roughness values will be taken from the G channel.
When using different textures, gltf export will not work correctly!
Note that a emissive map is multiplied by the emissive color and emissive intensity.
If you don't specify emissive when using emissivemap, emissive will be set to white with intensity 1.

Cube Textures

To table of contents▲ Next to using the same image on all sides of the model / voxels it is also possible to use cube textures. Cube textures can only be used to cover the entire model and are therefore not allowed in combination with maptransform.

Cube textures should be twice as wide as they are high, preferably using power of two texture sizes, i.e. typically 512x256, 1024x512 or 2048x1024. Cube textures must have the layout as shown this image:


Cube textures are used the same as normal textures and also need to be predefined outside of a material:

texture id = <textureid>, cube = true, 
        image = data:image/<type>;base64,<base64imagedata>
Many equirectangular images can be found, which can be converted to a cube map using a Cubemap Generator. The resulting images need to be combined using the pattern shown above.

Reflection & Refraction

To table of contents▲ Reflection and refraction can add a lot of realism to your models. Chrome or glass can look quite convincing with the right settings.
Smooth Voxel uses A-Frame and Three.js. The behaviour for reflection and refraction for different material types may vary in other platforms.
Reflection
Reflection works out of the box for the standard material as it takes over the environment map from the playground or your scene. To make standard material reflect, simply use a low roughness (near 0) and set high metalness (near 1).

The following standard material resembles chrome:
material type = standard, lighting = smooth, roughness = 0, metalness = 1,
  colors = A:#DDD
Reflection also works for basic and phong material types, however they do not automatically pick up the enviroment map from the playground or your scene. For basic and phong materials you have to explicitly set the reflectionmap.
In the Playground, simply select 'Add Environment' to add the current environment as a texture to use as a reflectionmap or refractionmap.
The following is very shiny reflective blue ceramic:
// Texture loaded in the Playground with 'Add Environment'
texture id = env, image = data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAA...
      
material type = phong, lighting = smooth, roughness = 0, metalness = 0.05, reflectionmap = env
  colors = A:#579
Refraction
Refraction is what makes glass look like glass and water like water. Simply setting the opacity of a material will get you the transparency, but you material would look more like very thin plastic than like glass.

Refraction works mostly the same as reflection. For all three material types, standard, phong and basic, simply add a refractionmap with a low roughness (near 0) and a high metalness (near 1).

The following is a typical wine bottle glass material:
// Texture loaded in the Playground with 'Add Environment'
texture id = env, image = data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAA...
      
material type = phong, lighting = smooth, roughness = 0, metalness = 1, refractionmap = env 
  colors = A:#080
Refraction & Reflection
If you use the wine bottle glass material above, you will notice that it looks like glass, but it misses an important property. Real glass not only refracts the light, but also reflects it. And even though the material above shows the specular reflections of the lights in the scene, it does not reflect the scene itself.
Three.js supports a better glass material out of the box with the MeshPhysicalMaterial. Due to some problems with reflection in the latest version of Three.js and A-Frame this is not yet supported by Smooth Voxels. However, the alternative described here might also be required for other platforms.
To create realistic glass we need to combine refraction with reflection, but this is not (yet) possible with only one material. The solution is to overlay an reflective material over a refractive material by means of adding a shell.

The following is a more realistic wine bottle glass material, with reflection:
// Texture loaded in the Playground with 'Add Environment'
texture id = env, image = data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/4QBoRXhpZgAATU0AKgAAAA...
      
material type = phong, lighting = smooth, roughness = 0, metalness = 1, refractionmap = env, shell = B 0.01 
  colors = A:#080
material type = phong, lighting = smooth, roughness = 0, metalness = 1, opacity = 0.2, reflectionmap = env 
  colors = B:#080
See the Shell chapter for more information on shells.
The example below shows reflection, refraction and combined refraction & reflection: Try it out
// Texture loaded in the Playground with 'Add Environment'
texture id = env, image = data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEA...

// The red material is reflective.
// Standard material uses the scene environment map, no reflectionmap needed
material type = standard, lighting = both, deform = 5, 
         roughness = 0, metalness = 1
  colors = A:#F66
  
// The blue material is only refractive which looks less realistic
// Refraction works best with phong and basic materials
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, 
         refractionmap = env, refractionratio = 0.95
  colors = B:#88F

// The green material is refractive with a reflective shell (so twice as many faces!)
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, refractionmap = env, 
         refractionratio = 0.95, shell = D 0.01
  colors = C:#4C4
  
// The reflective shell. Phong and basic materials need an explicit reflectionmap
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, opacity = 0.3, 
         reflectionmap = env, refractionratio = 0.95
  colors = D:#FFF
  
voxels
5(--CC-----BB--)
5(-CCCC---BBBB-)
5(CCCCCC-BBBBBB)
5(CCCCCC-BBBBBB)
5(-CCCCC-BBBBB-)
5(--CCCC-BBBB--)
5(-------------)
5(------A------)
5(-----AAA-----)
5(----AAAAA----)
5(---AAAAAAA---)
5(---AAAAAAA---)
5(----AAAAA----)
5(-----AAA-----)
// Texture loaded in the Playground with 'Add Environment'
texture id = env, image = %ENVIRONMENT%

model
size     = 21 13 20
scale    = 0.05 
position = 0 0 0.1 
origin   = -y
clamp = -y

// The red material is reflective.
// Standard material uses the scene environment map, no reflectionmap needed
material type = standard, lighting = both, deform = 5, 
         roughness = 0, metalness = 1
  colors = A:#F66
  
// The blue material is only refractive which looks less realistic
// Refraction works best with phong and basic materials
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, 
         refractionmap = env, refractionratio = 0.95
  colors = B:#88F

// The green material is refractive with a reflective shell (so twice as many faces!)
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, refractionmap = env, 
         refractionratio = 0.95, shell = D 0.01
  colors = C:#4C4
  
// The reflective shell. Phong and basic materials need an explicit reflectionmap 
material type = basic, lighting = both, deform = 5, 
         roughness = 0, metalness = 1, opacity = 0.3, 
         reflectionmap = env, refractionratio = 0.95
  colors = D:#FFF
  
voxels
1(---------------------) 1(---------------------) 5(--CCCCC-------BBBBB--) 2(---------------------) 2(---------------------) 2(---------------------)
1(---------------------) 1(--CCCCC-------BBBBB--) 5(-CCCCCCC-----BBBBBBB-) 2(--CCCCC-------BBBBB--) 2(---------------------) 2(---------------------)
1(---CCC---------BBB---) 1(-CCCCCCC-----BBBBBBB-) 5(CCCCCCCCC---BBBBBBBBB) 2(-CCCCCCC-----BBBBBBB-) 2(---CCC---------BBB---) 2(---------------------)
1(--CCCCC-------BBBBB--) 1(-CCCCCCC-----BBBBBBB-) 5(CCCCCCCCC---BBBBBBBBB) 2(-CCCCCCC-----BBBBBBB-) 2(--CCCCC-------BBBBB--) 2(---CCC---------BBB---)
1(--CCCCC-------BBBBB--) 1(-CCCCCCC-----BBBBBBB-) 5(CCCCCCCCC---BBBBBBBBB) 2(-CCCCCCC-----BBBBBBB-) 2(--CCCCC-------BBBBB--) 2(---CCCC-------BBBB---)
1(--CCCCCC-----BBBBBB--) 1(-CCCCCCC-----BBBBBBB-) 5(CCCCCCCCC---BBBBBBBBB) 2(-CCCCCCC-----BBBBBBB-) 2(--CCCCCC-----BBBBBB--) 2(---CCCCC-----BBBBB---)
1(---CCCCCC---BBBBBB---) 1(-CCCCCCCC---BBBBBBBB-) 5(CCCCCCCCC---BBBBBBBBB) 2(-CCCCCCCC---BBBBBBBB-) 2(---CCCCCC---BBBBBB---) 2(----CCCCC---BBBBB----)
1(-----CCCC---BBBB-----) 1(--CCCCCCC---BBBBBBB--) 5(-CCCCCCCC---BBBBBBBB-) 2(--CCCCCCC---BBBBBBB--) 2(-----CCCC---BBBB-----) 2(-----CCCC---BBBB-----)
1(------CCC---BBB------) 1(------CCC---BBB------) 5(--CCCCCCC---BBBBBBB--) 2(------CCC---BBB------) 2(------CCC---BBB------) 2(------CCC---BBB------)
1(----------A----------) 1(----------A----------) 5(----------A----------) 2(----------A----------) 2(----------A----------) 2(----------A----------)
1(---------AAA---------) 1(---------AAA---------) 5(---------AAA---------) 2(---------AAA---------) 2(---------AAA---------) 2(---------AAA---------)
1(---------AAA---------) 1(---------AAA---------) 5(--------AAAAA--------) 2(---------AAA---------) 2(---------AAA---------) 2(---------AAA---------)
1(---------AAA---------) 1(--------AAAAA--------) 5(-------AAAAAAA-------) 2(--------AAAAA--------) 2(---------AAA---------) 2(---------AAA---------)
1(---------AAA---------) 1(-------AAAAAAA-------) 5(------AAAAAAAAA------) 2(-------AAAAAAA-------) 2(---------AAA---------) 2(---------AAA---------)
1(--------AAAAA--------) 1(-------AAAAAAA-------) 5(------AAAAAAAAA------) 2(-------AAAAAAA-------) 2(--------AAAAA--------) 2(---------AAA---------)
1(--------AAAAA--------) 1(-------AAAAAAA-------) 5(------AAAAAAAAA------) 2(-------AAAAAAA-------) 2(--------AAAAA--------) 2(---------AAA---------)
1(--------AAAAA--------) 1(-------AAAAAAA-------) 5(------AAAAAAAAA------) 2(-------AAAAAAA-------) 2(--------AAAAA--------) 2(---------AAA---------)
1(---------AAA---------) 1(-------AAAAAAA-------) 5(------AAAAAAAAA------) 2(-------AAAAAAA-------) 2(---------AAA---------) 2(---------------------)
1(---------------------) 1(--------AAAAA--------) 5(-------AAAAAAA-------) 2(--------AAAAA--------) 2(---------------------) 2(---------------------)
1(---------------------) 1(---------------------) 5(--------AAAAA--------) 2(---------------------) 2(---------------------) 2(---------------------)

Shell

To table of contents▲ Shell allows for the adding of one or more shells around the model or on certain materials. A shell can be a wireframe cage or a transparent shield, or, by showing only the back side of the shell it becomes an cartoon style outline. Shells can be defined on the model as well as on a material, and are themselves created from defined materials. A shell is formed by repeating the faces of the model but pushing them out and using a different material. Since a shell is modeled from the faces of the model, its material will ignore many of the properties. For example:
Smooth Voxel models combines faces to get smaller meshes with better performance. However, this simplified model is also used for the shell, but when pushing out faces using their normals, gaps may appear in the shell. Often this is not noticible, but when it does happen you can prevent simplification by adding simplify = false.
Be aware, shells double your face count and model size. Especially transparent textured shells can give a significant performance degradation when using the model. Small outlines using the basic material type however, will likely not have a big impact.
Try it out
// Black outline shell material
material type = basic, side = back
  colors = Black:#000

// Wireframe shell material
material wireframe = true
  colors = Wire:#840
  
// Textured shell material  
material type = basic, map = squares, maptransform = 1 1, transparent = true, side = double, alphatest = 0.2
  colors = Squares:#008
  
// The orange material has a wireframe overlay with a black outline
material lighting = flat, deform = 5, scatter = 0.25
  shell = Wire 0.01 Black 0.5
  colors = A:#F80

// The blue material has a textured shell
material type = toon, lighting = smooth, deform = 5
  shell = Squares 0.5
  colors = B:#44F

voxels =
6(AAAA--BBBB)
6(AAAA--BBBB)
6(AAAA--BBBB)
6(AAAA--BBBB)
texture id = squares, cube = false, image = data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsIAAA7CARUoSoAAAAP+SURBVHhe7Z3LcuMwEAOd/f9/3jVSYdaJ9RY5BGbQF19FdJcsH6J8/H3yMGWZHcDH1+cs2tlZriOcP1+fM5g9OhPTtpgVgOW/M2WTGQFY/jrh20QHYPn7hG4UGYDlHydsq6gALP88IZtFBGD51xm+3egALP8+QzccGYDl92PYlqMCsPz+DNl0RACWP47u2/YOwPLH03XjngFYfhzdtu4VgOXH02XzHgFY/jxub383AMufzy0HdwKwfB4uu7gagOXzccnJlQAsn5fTbs4GYPn8nHJ0JgDL1+Gwq6MBWL4eh5wdCcDyddl1txeA5euz6XArAMvPw6rLtQAsPx+LTpcCsPy8vLn9HYDl5+eH49cALL8O365bAJZfj0/naw+BpgiVA3j9m/yyL8moGsCS8JIRVAxgS3S5CKoFcERwqQgqBXBGbJkIqgRwRWiJCCoEcEdk+giyB9BDYOoIMgfQU1zaCLIGMEJYyggyBjBSVLoIsgUQIShVBJkCiBSTJoIsAcwQkiKCDAHMFCEfgXoADAKkI1AOgGl42QhUA2AcXDICxQCYh5aLQC0AhYGlIlAKQGlYmWtVCUDu1vpE4poVAlCU36C/dvYAlOU3qM/AHEAG+Q3as7AGkEl+g/JMjAFklN+gOxtbAJnlN6jOyBRABfkNmrOyBFBJfoPizP738cVh/RVggmh3gNlvCKl6F5q+O8sdoOIraijOzPQVUCkCmrOyPQNUiIDqjIwPgZkjoDsb66+AjBFQnok1AJApAtqzMAcAMkRAfQb2AIByBPTXrhAAUIxA4ppVAgBKEchcq1IAQGFYqbuVWgCAeWAp+UAxAMA4tJx8oBoAYBpcUj5QDgAwDC8rH6gHAGYKkJYPMgQAZoiQlw+yBAAihaSQDzIFACLEpJEPsgUARgpKJR9kDACMEJVOPsgaAOgpLKV8kDkA0ENcWvkgewDgjsDU8kGFAMAVkenlgyoBgDNCS8gHlQIAR8SWkQ+qBQC2BJeSDyoGAJZEl5MPqgYAXoWXlA8qB2CetAD8lpB6fDp/vQM4gjp8u/79FeAI8vPD8dIzgCPIy5vbtYdAR5CPRadrAQBHkIdVl1sBAEegz6bDvQCAI9Bl192RAIAj0OOQs6MBAEegw2FXZwIAjoCfU47OBgAcAS+n3VwJADgCPi45uRoAcAQ8XHZxJwDgCOZzy8HdAIAjmMft7XsEABxBPF027xUAcARxdNu6ZwDAEYyn68a9AwCOYBzdtx0RAHAE/Rmy6agAgCPox7AtRwYAHMF9hm44OgDgCK4zfLuIAIAjOE/IZlEBAEdwnLCtIgMAjmCf0I2iAwCOYJ3wbWYEABzBO1M2mRUAcAT/mbZF++/hpiSPxz/tx5DoiAhOVAAAAABJRU5ErkJggg==

model
size = 14 6 14,
scale = 0.0714,
origin = -y
position = 0 0.05 0

// Shell materials
// ---------------

// Black or white outline shell material
material type = basic, side = back
  colors = Black:#000 White:#FFF

// Normal (multi colored) outline shell material
material type = normal, side = back
  colors = Normal:#FFF

// Wireframe shell material
material wireframe = true
  colors = Wire:#000 Orangewire:#840
  
// Textured shell material  
material type = basic, map = squares, maptransform = 1 1, transparent = true, side = double, alphatest = 0.2
  colors = Squares:#008
  
// Model materials
// ---------------

// The red material has a thin black outline
material type = toon, lighting = smooth, deform = 5
  shell = Black 0.1
  colors = A:#800

// The green material has a double, black and white outline
material type = toon, lighting = smooth, deform = 5
  shell = Black 0.25 White 0.5
  colors = B:#080

// The blue material has a textured shell
material type = toon, lighting = smooth, deform = 5
  shell = Squares 0.5
  colors = C:#44F

// The light blue material has an internal shell
material lighting = smooth, roughness = 0.2, metalness = 1, deform = 5, opacity = 0.6
  shell = Wire -0.5
  colors = D:#0DF

// The magenta material has a multi colored outline
material lighting = smooth, roughness = 0.5, metalness = 1, deform = 5
  shell = Normal 0.5
  colors = E:#F0F

// The orange material has a wireframe overlay with a black outline
material lighting = flat, deform = 5, scatter = 0.25
  shell = Orangewire 0.01 Black 0.5
  colors = F:#F80

voxels =
6(-----EEEE-----)
6(-----EEEE-----)
6(FFFF-EEEE-DDDD)
6(FFFF-EEEE-DDDD)
6(FFFF------DDDD)
6(FFFF------DDDD)
6(--------------)
6(--------------)
6(AAAA------CCCC)
6(AAAA------CCCC)
6(AAAA-BBBB-CCCC)
6(AAAA-BBBB-CCCC)
6(-----BBBB-----)
6(-----BBBB-----)

Shaders

To table of contents▲ To see how vertex data can be used in a shader, see the shader example.

In custom shaders it is often useful to add extra data to each vertex to determine how the shader should handle this vertex / face. This can be done by adding a data attribute to the model and materials.

The model determines the names of the attributes that are passed to the shader and the default value(s) of the attributes in case they are not set on a material:
data = [ <attributename> <float:default> [<float:default>] [<float:default>] [<float:default>] ]+
The number of default values determines the data type of the attributes for the shader: Per material these defaults can be overridden with specific values by a data attribute repeating the names and number of values in the same order as in the model.
Each voxel has its own vertices in the final geometry. When two voxels touch and have different vertex data that changes the behavior of the vertex shader, these voxels may no longer touch in you scene.
Note that even though these extra attributes are exported in .gltf and .glb and generated code, not all applications are able to handle them.
SmoothVoxel models are by default simplified, i.e. faces are combined to reduce the model memory usage and increase performance. When using vertex shaders it is often unwanted that the model is simplified, It is usually better to have all vertices separate so they can individually be displaced for a smoother effect. To stop SmoothVoxels from simplifying the model or a certain material set simplify on the model and/or material:
simplify = { true | false }

Model syntax

To table of contents▲ Smooth Voxel models are text based and describe the materials, general transformations and of course the voxels themselves.

The model consists of multiple settings of the form <name> = <value>. Each setting can be defined on a separate line, or multiple on one line, separated by comma's. Only the 'voxels' setting is allowed to be split over multiple lines.

Single line comments start with // and are allowed on separate lines or at the end of a line.

The syntax of a model is consists of a number of larger blocks:
[ <texture-definitions> ]
[ <light-definitions> ]
<model-definition>
<material-definitions>
<voxel-matrix>


Lights

The syntax for light definitions is:
// Ambient lights
light color = <color> [ <float:strength> ]

//Directional lights
light color = <color> [ <float:strength> ]
[, direction = <float:x> <float:y> <float:z>]

// Point lights
light color = <color> [ <float:strength> ]
[, position = <float:x> <float:y> <float:z>] [, distance = <float> ]
[, size = <float>] [, detail = 0 | 1 | 2 | 3]


Textures

Textures for map(s), normalmap(s), roughnessmap(s), metalnessmap(s), emissivemap(s) and matcap(s) are defined as follows:
[ texture id = <texturename>, [ cube = true | false, ] image = data:image/<type>;base64,<base64imagedata> ]+


Model settings

The general settings for the model definition are:
// Start model with 'model' keyword
model      

size   =  { <int> | <int:x-size> <int:y-size> <int:z-size> }

// Transformation parameters (vertices are actually transformed)
[ origin  = <planar-definition> ]
[ scale = <float> | <float:x-scale> <float:y-scale> <float:z-scale> ]
[ resize = model | bounds ]
[ rotation = <float:x-degrees> <float:y-degrees> <float:z-degrees> ]
[ position = <float:x-position> <float:y-position> <float:z-position> ]

// Global planar actions
[ flatten = <planar-definition> ]
[ clamp   = <planar-definition> ]
[ skip    = <planar-definition> ]
[ tile    = <planar-definition> ]

// Global material settings
[ wireframe = true | false ]

// Global Ambient Occulusion Parameters
[ ao = [<color>] [ <float:maxDistance> ] [ <float:strength> ] ]
[ aosides = <planar-definition> ]

// Define names and defaults for vertex data attributes (float, vec2, vec3 or vec4) for custom shaders
[ data = [ <attributename> <float:default> [<float:default>] [<float:default>] [<float:default>] ]+ ]

// Determines whether faces are combined to simplify the model (default = true) turn off when using vertex shaders
[ simplify = true | false ]

// Shell parameter
[ shell   = { <colorId> <float:distance> }+ | none ] // ColorId determines the material

<material>+

voxels
<voxel-matrix>


Planar definitions

Planar definitions are used to specify which planes or sides are used for that feature:
<planar-definition> = [-x] [x] [+x] [-y] [y] [+y] [-z] [z] [+z]


Materials

Multiple materials can be defined per model and multiple colors per material:
<material> =

// A material must start with the keyword 'material'
material <material parameters>

// How the lighting reflects on the surface
lighting = { flat | smooth | both }

// The type of material
type = { standard | phong | lambert | basic | toon | matcap | normal }

// Physically Based Rendering parameters
[ roughness = <float> ] // For non-standard material types converted into reflectivity
[ metalness = <float> ] // For non-standard material types ignored
[ opacity   = <float> ] // 0.0 is fully transparent, 1.0 is fully opaque
[ emissive  = [ <color> ] [ <float:intensity> ] ]

// Transparent texture parameters
[ transparent = true | false ] // Set when using transparent textures
[ alphatest = <float> ] // Solves many transparent texture issues (0.0 - 1.0)

// Extra parameters
[ wireframe = true | false ]
[ fade      = true | false ] // Whether voxel colors will fade with the next voxel
[ fog       = true | false ] // Whether the material is affected by fog
[ lights    = true | false ] // Whether the material is affected by Smooth Voxel lights
[ side      = front | back | double ]

// Ambient Occlusion parameters
[ ao = [ <color> ] [ <float:maxDistance> ] [ <float:strength> ] ]

// Vertex altering parameters
[ deform  = <int:count> [ <float:strength>  [ <float:damping> ] ] ]
[ warp    = <float:strength> [ <float:frequency> ] ]
[ scatter = <float:strength> ]

// Planar altering parameters
[ flatten = <planar-definition> ]
[ clamp   = <planar-definition> ]
[ skip    = <planar-definition> ]

// Texture parameters
[ map           = <textureid> ]
[ normalmap     = <textureid> ]
[ roughnessmap  = <textureid> ]
[ metalnessmap  = <textureid> ]
[ emissivemap   = <textureid> ]
[ maptransform  = <width> <height> <xoffset> <yoffset> <rotation> ]
[ reflectionmap = <textureid> ]
[ refractionmap = <textureid> ]
[ refractionratio = <float> ]

// Overrule the model default for vertex data attributes (names, order and number of values must exactly match the model)
[ data = [ <attributename> <float:default> [<float:default>] [<float:default>] [<float:default>] ]+ ]

// Determines whether faces are combined to simplify the model
[ simplify = true | false ]

// Shell parameter
[ shell   = { <colorId> <float:distance> }+ | none ] // Shell(s) for this material, or 'none' for no shell

// All materials must have at least one color
colors = <material-colors>
Colors

Colors must use hexadecimal notation with 3 (#RGB) or 6 (#RRGGBB) hexadecimal digits and must start with a # character:
<color> = { #[0-9a-fA-F]{3} | #[0-9a-fA-F]{6} }
Every material must define one or more colors with IDs:
<material-colors> = { <color-id>:<color> }+     // E.g. A:#123 or Ab:#0088FF
<color-id> = [A-Z][a-z]*
Each color must have an Id consisting of one uppercase letter, optionally followed by one or more lower case letters [A-Z][a-z]*. These color Id's are used in the Voxel matrix where empty voxels are shown by means of '-'.

When loading MagicaVoxel models, the palette index of each color is preserved:
<color-id + pallette-index> = [A-Z][a-z]*([0-9]+)     // E.g. A(1):#123 or Ab(123):#0088FF  
Color Id's are not preserved when reading and writing a model but are determined depending on the voxel count (i.e. the most used color will be Id 'A').
The syntax allows for newlines between attributes for the model or materials, but values must always appear behind the '<name> ='' on the same line. This is normally not a problem since most values are relatively short. The material colors form the exception since a material can have many colors. In that case the line continuation character _ can be used to allow for line breaks between colors as in this example:
size = 16 1 1
scale = 0.1
material lighting = flat
colors = A:#000000 B:#111111 C:#222222 D:#333333 _
         E:#444444 F:#555555 G:#666666 H:#777777 _
         I:#888888 J:#999999 K:#AAAAAA L:#BBBBBB _
         M:#CCCCCC N:#DDDDDD O:#EEEEEE P:#FFFFFF
voxels
ABCDEFGHIJKLMNOP


Voxel matrix

The voxel matrix consists of one color Id for each voxel or '-' for a missing voxel.
The voxel matrix is ordered x, y then z.
All whitespace in the voxel matrix is ignored allowing for a more human readable format.
The Smooth Voxels component renders internal spaces (without voxels) as well, resulting in extra, possibly never seen, faces. This not only creates larger models, but also will cost performance while viewing these models. Always fill models so they are not hollow for best performance. Preferably fill them with the same material and color as the outside.

Voxel matrix compression

To table of contents▲ Compressed voxel matrices can reduce the size of large voxel models to a few percent of the original model.

The voxel matrix can be compressed using recursive run length encoding:
An integer count indicates the next color or empty voxel must be repeated the indicated number of times.
Groups can be created by means of a count followed by a group between ( and ).

The following definitions are all valid and all create the same red 'T' character: Try it out
// Human readable form, from left to right showing the vertical layers
size = 3 4 2
material lighting = flat, colors = A:#F00
voxels = 
-A- -A- -A- AAA
-A- -A- -A- AAA

// No whitespace
size = 3 4 2
material lighting = flat, colors = A:#F00
voxels = -A--A--A-AAA-A--A--A-AAA

// Simple run-length encoding
size = 3 4 2
material lighting = flat, colors = A:#F00
voxels = -A2-A2-A-3A-A2-A2-A-3A

// Run-length encoded groups
size = 3 4 2
material lighting = flat, colors = A:#F00
voxels = -2(A2-)A-3A-2(A2-)A-3A

// Recursive run-length encoded groups
size = 3 4 2
material lighting = flat, colors = A:#F00
voxels = 2(-2(A2-)A-3A)
      
// Multi character color id's
size = 3 4 2
material lighting = flat, colors = Red:#F00
voxels = 2(-2(Red2-)Red-3Red)
model
size = 3 4 2
scale = 0.2
origin = -y
rotation = 0 -20 0

// Uncomment each version separately

// Human readible form, from left to right showing the vertical layers
material lighting = flat, colors = A:#F00
voxels = 
-A- -A- -A- AAA
-A- -A- -A- AAA

// No whitespace
// material lighting = flat, colors = A:#F00
// voxels = -A--A--A-AAA-A--A--A-AAA

// Simple run-length encoding
// material lighting = flat, colors = A:#F00
// voxels = -A2-A2-A-3A-A2-A2-A-3A

// Run-length encoded groups
// material lighting = flat, colors = A:#F00
// voxels = -2(A2-)A-3A-2(A2-)A-3A

// Recursive run-length encoded groups
// material lighting = flat, colors = A:#F00
// voxels = 2(-2(A2-)A-3A)
      
// Multi character color id's
// material lighting = flat, colors = Red:#F00
// voxels = 2(-2(Red2-)Red-3Red)

Even though Smooth Voxels does a very good job of compressing the voxel matrix, its algorithm does not always find the absolute optimum. The red T in the example above for instance,
is compressed to 2(-2(A2-)A-3A) but could be compressed even more to 2(3(-A-)3A).

Compression efficiency

Compared for instance to Magicavoxel files, uncompressed Smooth Voxels models are often of similar or even a few times larger in size. The reason for this is that the Smooth Voxels syntax does not only store the voxels, but also the empty spaces, creating relatively big files for large sparse models. However, the equivalent compressed Smooth Voxels models are typically much smaller down to often only several percent. Especially for filled models (without internal empty spaces) the models are usually smaller even uncompressed and much smaller compressed.

Below the compression comparison between Magicavoxel and Smooth Voxels using the Teapot.vox model and a flood filled version of that same model:

Model Magicavoxel SVOX SVOX compressed
Teapot.vox 143,083 bytes 622,896 bytes (435%) 36,619 bytes (25.6%)
TeapotFilled.vox 888,989 bytes 622,896 bytes (70,1%) 22,516 bytes (2,5%)
Sample Teapot model converted by ephtracy from the Utah Teapot.
https://github.com/ephtracy/voxel-model/blob/master/vox/scan/teapot.vox

About Smooth Voxels and me

To table of contents▲ I started with A-Frame almost 4 years ago and was immediately hooked. This was a technology that brought back the times when someone in their attic could create a game in a couple of weeks. And better yet, it just worked. It worked in the browser, on my Oculus Go, Mobile Phone VR Headset and later on my Quest 2. But soon I went through my personal hype cycle:

   😕 This is probably difficult...
   😊 Oh wow, this is easy!
   😁 Let's go metaverse!
   😒 Wait, what? Only primitives?
   😖 Well this sucks.
   😐 But..., it has so much potential.

Seeing the work others had done I realized I was not alone in this. Some of the 'VR experiences' looked very simple, with only primitives or a few Sketchfab models. This is perhaps to be expected, as most people involved with WebXR are developers, not 3D designers. And since there are many, many more software engineers than 3D artists in the world, this is not likely to change.

Since I had done some 3D programming in the past, I tried out some things, generating terrain and planets (... I know). Yet after all that work I only had a terrain, but what about trees, clouds, rocks, pickup items, etc.?

The first version of the Smooth Voxels A-Frame Component started in the fall of 2019 just to try out some ideas I had about generating 3D models. I worked on it off and on in my spare time, sometimes not for months, sometimes until deep in the night, suffering from extreme feature creep. 😏

Even though I've been active in the field of software development for a long time, it has been many years since I developed professionally myself. And since this is just a hobby project I was more interested in trying to figure stuff out for myself than code hygiene, reusing existing libraries, or figuring out browserify and github (thank you glitch.com!).

That being said, I've always had the idea I should make Smooth Voxels open source so I kept the user documentation up to date with lots of examples, created a playground so non-developers could also use it and I did quite a bit of code cleanup to get it ready to go out into the world.

So, I hope you enjoy playing around with the Playground and am looking forward to seeing your models turn up in your next (Web)XR game!

If you have a question, want to report a bug, and please let me know where I can see your Smooth Voxel Creations(!), feel free to mail me. (Depending on my work load, my responsiveness may vary.)

Get smoothing those voxels!

Samuel van Egmond

visitor badge