Shaders

WARNING

The shaders on this page are incompatible with Render Dragon. That means that they will not work on Windows and Console devices past 1.16.200, nor other devices past 1.18.30!

Overview

Shaders are divided into 2 folders: glsl and hlsl. For shaders to work on every device, you need to code shaders in both languages. For testing on Windows, hlsl is enough. When rewriting shaders from one language to another, there are few things to change, like HLSL float3 is vec3 in GLSL. Mapping between those languages can be found here

Materials

Vertex, fragments, and sometimes geometry shaders are combined with some options as materials and are required for custom shaders. To create new material, you need to create a file, which matches the name of the .material file in the vanilla resource pack. For example: materials/particles.material. Materials support inheritance by adding parent material after a colon. For example: entity_alpha:entity_base

Common material definition fields

Field nameDescriptionExample valueNotes
vertexShaderPath to the shader relative to hlsl/glsl folderFor HLSL shader, .hlsl suffix is added.
fragmentShaderPath to the shader relative to hlsl/glsl folderFor HLSL shader, .hlsl suffix is added.
vertexFieldsAn array of fields passed to vertex shaderIt's better to copy this field from vanilla material.
variantsAn array of objects, which define variants of the materialIt's better to copy this field from vanilla material.
+definesAn array of #define directives to add to the shader sourceUseful for reusing shader, but changing some minor setting.
+statesAn array of states to enable["Blending", "DisableAlphaWrite", "DisableDepthWrite"]For OpenGL implementation, this is equivalent to glEnable call.
-definesAn array of #defines directives to remove from inherited +defines
+samplerStatesAn array of objects, defining how texture at certain index is treated{ "samplerIndex": 0, "textureFilter": "Point" }textureFilter specifies how to sample the texture and textureWrap specifies the behavior, when accessing outside of the texture dimensions.
msaaSupportMultisample anti-aliasing supportBoth
blendSrcSpecifies how the color source blending factors are computedOneFor OpenGL implementation, this is equivalent to glBlendFunc call.
blendDstSpecifies how the color destination blending factors are computedOneFor OpenGL implementation, this is equivalent to glBlendFunc call.

Example:

Copy
json
{
	"materials": {
		"version": "1.0.0",
		"particle_debug": {
			"vertexShader": "shaders/particle_generic.vertex",
			"fragmentShader": "shaders/particle_debug.fragment",

			"vertexFields": [
				{ "field": "Position" },
				{ "field": "Color" },
				{ "field": "UV0" }
			],

			"+samplerStates": [
				{
					"samplerIndex": 0,
					"textureFilter": "Point"
				}
			],

			"msaaSupport": "Both"
		}
	}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

For all the details about material files and possible field values, check material file JSON schema.

Troubleshooting

Shader doesn’t change

Every time there is a change in the shader, you need to restart Minecraft to recompile the shader completely.

Compilation error

When there is a shader compilation error, a line number is usually specified where the error occurred. You need to check a few lines above the one set in error because Minecraft adds #define directives before compilation.

Couldn’t find constant buffer named: $Globals

I couldn’t accurately find the actual cause of this error, but it seems to be somehow connected to global variables. Removing them (initializing them in the main function or changing them to #define directives) seems to fix the problem.

Tips and tricks

Passing variables to the shader

You can pass variables to the shader from a particle or an entity by changing entity color. Input color is clamped to <0.0, 1.0>. To pass more significant values, you need to divide by max value (or at least some considerable number).

Using time in shader

TIME variable is a number of seconds as float and is global for all shaders. For time-based on particle lifetime, you need to pass this:

Copy
json
"minecraft:particle_appearance_tinting": {
    "color": ["variable.particle_age/variable.particle_lifetime", 0, 0, 1]
}
1
2
3

Then in the shader, use PSInput.color.r as time, where 0.0 is particle birth and 1.0 is particle death.

Camera direction towards the entity

For entity shaders, you can make the shader dependent on the camera direction towards the entity.

  • Add to PS_Input in vertex and fragment shader new field
Copy
float3 viewDir: POSITION;
1
  • After that, add to vertex shader this line
Copy
PSInput.viewDir = normalize((mul(WORLD, mul(BONES[VSInput.boneId], float4(VSInput.position, 1)))).xyz);
1
  • In the fragment shader, use PSInput.viewDir to make changes depending on camera rotation

Debugging values

The easiest way to debug a value is to turn it into color and render it like this.

Copy
PSOutput.color = float4(PSInput.uv, 0., 1.);
1

This should create a red-green gradient, showing that the values of uv are between <0, 0> and <1, 1>.

You can use the debug shader I wrote based on this shader. Right now, this shader will display values of the color passed to the shader. To display another value, change line 70 in hlsl shader to

Copy
int ascii = getFloatCharacter( cellIndex, <float4 vector here> );
1

GLSL version of debugging shader may crash Minecraft, use only for debugging.

Download debug shader

Contributors

SirLich