-
Notifications
You must be signed in to change notification settings - Fork 50
Notes
Shader files are written in OpenGL Shader Language (GLSL), version 1.10. The Book of Shaders is a good beginners guide to the shader language.
Each shader has a map
function which is executed once per voxel:
- It receives the location of the voxel as its only parameter
- Should return a float between
0.0
and255.0
representing the voxel colour in the palette.
For example, the following shader will fill the entire volume with voxels coloured from palette index 1
.
// xs_begin
// author : '@lachlanmcdonald'
// xs_end
float map(vec3 v) {
return 1.0;
}
- Shaders must contain the header (
xs_begin
andxs_end
), even if there are no parameters. -
author
is optional. Whilst there is no standard for it value, a URL or Twitter handle is customary. - As shaders return a
float
, the value is rounded-up. For instance,0.4999
will result in a voxel palette of1.0
. - Return values are clamped between
0.0
and255.0
, so it is safe to return a value outside of this range.
Note that the shader itself uses the word arg (short for argument) when referring to the shader inputs. However, to avoid confusion between a shader argument and a function argument, the former is referred to instead as a parameter throughout this documentation.
- All parameters are passed through to the shader as a
float
. - The order that the arg tags are defined is the order that they appear within the UI.
- In previous versions of MagicaVoxel, there was a limit to the number of parameters which could be passed to a shader. In current versions, this limit no longer applies.
Key | Description |
---|---|
name |
The label of the parameter as it appears within the UI. |
var |
Exposes the parameter as a variable. For instance, a var of m_size will expose the variable m_size throughout the shader. Any valid GLSL variable name is accepted, it is conventional to use snake-case and prefix the variable name with m_
|
range |
The minimum and maximum value accepted by the field, separated by a space. For instance, '0 100' would enforce a minimum and maximum value of 0 and 100 , respectively. If the user enters a value outside of this range, it will be clamped. |
value |
The default value of the parameter |
step |
The number to increase or decrease the value as the user scrubs the field. Users can still manually enter numbers which are not dividable by the step
|
precision |
The number of fractional digits allowed when entering a number. A value of 0 only accepts whole numbers. A value of 2 would allow numbers as small as 0.01
|
The following keys are deprecated but included for backward compatibility:
Key | Description |
---|---|
id |
The index of the parameter, used to populate i_args . Replaced by var . |
decimal |
Used to indicate whether the field should accept a whole (0 ) or fractional number (1 ). Regardless of this value, the field is always exposed to the shader as a float . This was replaced by the precision key to fix an issue where the field only accepted 1 significant digit, making it impossible to provide values smaller than 0.1 . |
Some hardware will not allow the extension of exiting functions with a different signature. For maximum portability, a function's name should never match an existing function.
For instance, the following code (which attempts to create a new version of mod
to work with only int
s) will not work on all systems:
int mod(int a, int b) {
return int(mod(float(a), float(b)));
}
Instead, it should be given a different name:
int imod(int a, int b) {
return int(mod(float(a), float(b)));
}
Some hardware cannot coercion between int
and float
, and instead require an explicit command. You should always explicitly cast from int
to float
and vice versa.
i.e.
float a = 1.0;
int b = int(a * 2.0); // will work on all hardware
int b = a * 2.0; // will not work on all hardware
float c = 2 * 6; // will not work on all hardware
float c = float(2 * 6); // will work on all hardware
Similarly, a function must always return the expected return type (the value is not coerced):
float a() {
return float(1); // will work
return 1; // will not work
}
The map
function is passed the centre-point of the voxel. So, a voxel as position 0
, 0
, 0
will be passed to the map
function as vec3(0.5, 0.5, 0.5)
.
If this is undesirable, you can floor the entire vec3
in one operation:
vec3 v = floor(v);
Which is the same as below but more concise:
v.x = floor(v.x);
v.y = floor(v.y);
v.z = floor(v.z);
voxel()
always refers to the original model. For example:
float map(vec3 v) {
if (all(equal(v, vec3(0.0, 0.0, 0.0)))) {
return 1.0;
} else {
return voxel(vec3(0.0, 0.0, 0.0));
}
}
Will not result in the entire model being replaced with index 0
. Instead, all voxels will be replaced with the original index at position 0,0,0
.
For example, b()
must be defined before a()
, or it will not be defined when a()
tries to call it.
float b() {
return 1.0;
}
float a() {
return b();
}
float map(vec3 v) {
return a();
}
There are platform-dependant limits on the size of arrays. When an array size exceeds this limit, MagicaVoxel will not display an error but no voxels will be added by the shader. However, in some circumstances random voxels will appear.
It is best not to initialise arrays with more than 255 elements.
voxel()
for retrieving a colour index will return 0.0
when addressing beyond the volume size. Therefore, it is not necessary to check whether the x
, y
or z
co-ordinates will be out-of-bounds before calling voxel
.
voxel(500.0, 500.0, 500.0); // 0.0
voxel(-1.0, -1.0, -1.0); // 0.0
no_axis_mode
will be true
when no axis modes are set, false
otherwise.
bool no_axis_mode = all(equal(ivec3(i_axis), ivec3(0)));
bvec3 axis_mode = equal(ivec3(i_axis), ivec3(1));
axis_mode
is a bvec3
indicating which axis mode is set.
For example: axis_mode.x
will be true
if the X-axis mode is set.
float pal_mix(float p) {
float f = floor(mix(0.0, float(i_num_color_sels), p));
return color_sel(f);
}
bool is_sel_color(float p) {
for (int i = 0; i < i_num_color_sels; i += 1) {
if (p == color_sel(float(i))) {
return true;
}
}
return false;
}
By default, shaders are loaded from MagicaVoxel's shader
director. However, you can set a new path by changing the dir_xs_shader
parameter within config/config.txt
and specifying the new path:
i.e.
dir_xs_shader : "/Users/lachlan/magicavoxel-shaders/shader"
In Visual Studio Code, GLSL shader syntax-highlighting can be enabled with the Shader languages support for VS Code extension.
.txt
files are not automatically detected as shaders. The following snippet can be added to the workspace settings to override the associations for .txt
files:
{
"files.associations": {
"**/shader/**/*.txt": "glsl"
}
}
All content in this wiki is licenced under the CC BY-NC-SA 4.0 license. Code snippets are dual-licenced under the MIT License.