Skip to content

Shader Pipeline

The shader pipeline system in Minecraft 26.1 manages compilation, loading, and execution of shader programs. This guide covers how to work with shaders and integrate them into your custom rendering code.

The shader pipeline consists of:

  • Shader compilation and caching managed by ShaderManager
  • Preprocessing system that handles custom directives
  • Pipeline configuration for complete rendering setups
  • Uniform management for shader parameters

The ShaderManager handles the complete shader lifecycle:

net.minecraft.client.renderer.ShaderManager
public class ShaderManager {
private final Map<String, ShaderInstance> shaders = new HashMap<>();
private final GlslPreprocessor preprocessor;
public ShaderInstance getShader(String name) {
return shaders.computeIfAbsent(name, this::loadShader);
}
private ShaderInstance loadShader(String name) {
// Load and compile shader files
// Apply preprocessing
// Cache the result
}
}

The preprocessor handles custom Minecraft-specific directives:

com.mojang.blaze3d.preprocessor.GlslPreprocessor
public class GlslPreprocessor {
public String process(String source, Map<String, String> defines) {
// Process #moj_import directives
// Handle #define substitutions
// Apply version management
return processedSource;
}
}

Vertex shaders transform vertex positions and pass data to fragment shaders:

// Example: Basic vertex shader
#version 330 core
#moj_import <minecraft:common>
in vec3 Position;
in vec4 Color;
in vec2 UV0;
uniform mat4 ModelViewMat;
uniform mat4 ProjMat;
uniform vec3 Light0_Direction;
out vec4 vertexColor;
out vec2 texCoord0;
void main() {
gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
vertexColor = Color;
texCoord0 = UV0;
}

Fragment shaders determine the final color of pixels:

// Example: Basic fragment shader
#version 330 core
#moj_import <minecraft:common>
in vec4 vertexColor;
in vec2 texCoord0;
uniform sampler2D Sampler0;
out vec4 fragColor;
void main() {
vec4 textureColor = texture(Sampler0, texCoord0);
fragColor = vertexColor * textureColor;
}
// Example: Loading and using a custom shader
public class CustomShaderRenderer {
private ShaderInstance customShader;
public void initialize() {
// Load shader from resources
this.customShader = Minecraft.getInstance().getShaderManager()
.getShader("custom_shader");
}
public void render(PoseStack poseStack, MultiBufferSource bufferSource) {
// Set shader uniforms
customShader.safeGetUniform("Time").set((float)Blaze3D.getTime() / 1000.0f);
customShader.safeGetUniform("Color").set(1.0f, 1.0f, 1.0f, 1.0f);
// Bind shader
customShader.apply();
// Render geometry
renderGeometry(poseStack, bufferSource.getBuffer(customRenderType));
// Clear shader
customShader.clear();
}
}

Shaders are configured through JSON files:

custom_shader.json
{
"blend": {
"func": "add",
"src": "src_alpha",
"dst": "one_minus_src_alpha"
},
"vertex": "my_mod:shaders/custom.vert",
"fragment": "my_mod:shaders/custom.frag",
"attributes": [
{
"name": "Position",
"type": "float",
"count": 3
},
{
"name": "Color",
"type": "float",
"count": 4
},
{
"name": "UV0",
"type": "float",
"count": 2
}
],
"samplers": [
{
"name": "Sampler0"
}
],
"uniforms": [
{
"name": "Time",
"type": "float",
"count": 1
},
{
"name": "Color",
"type": "float",
"count": 4
}
]
}

Integrate custom shaders with render types:

// Example: Render type with custom shader
public class CustomRenderTypes {
public static final RenderType CUSTOM_SHADER_TYPE;
static {
CUSTOM_SHADER_TYPE = RenderType.create(
"custom_shader_type",
DefaultVertexFormat.POSITION_COLOR_TEX,
VertexFormat.Mode.QUADS,
256,
RenderType.CompositeState.builder()
.setShaderState(RenderStateShaders.RENDERTYPE_ENTITY_TRANSLUCENT_SHADER)
.setTextureState(RenderStateShaders.RENDERTYPE_ENTITY_TRANSLUCENT_TEXTURE)
.setTransparencyState(RenderStateShards.TRANSLUCENT_TRANSPARENCY)
.setCullState(RenderStateShards.NO_CULL)
.setLightmapState(RenderStateShards.LIGHTMAP)
.setOverlayState(RenderStateShards.OVERLAY)
.createCompositeState(false)
);
}
}

Use pipeline snippets for reusable configurations:

// Example: Custom pipeline snippet
public class CustomPipelineSnippets {
public static final RenderStateShard.ShaderStateShard CUSTOM_SHADER =
new RenderStateShard.ShaderStateShard() {
@Override
public ShaderStateShard createSupplier() {
return () -> Minecraft.getInstance().getShaderManager()
.getShader("my_mod:custom_shader");
}
};
public static RenderType.CompositeState.TransparencyStateShard TRANSLUCENT_BLACK =
RenderStateShards.TransparencyStateShard.of(
RenderStateShards.SourceFactor.ONE,
RenderStateShards.DestFactor.ONE_MINUS_SRC_COLOR
);
}

Animate shader uniforms over time:

// Example: Animated uniforms
public class AnimatedShaderRenderer {
private float animationTime = 0.0f;
public void render(PoseStack poseStack, MultiBufferSource bufferSource, float partialTicks) {
animationTime += partialTicks;
// Set animated uniforms
customShader.safeGetUniform("Time").set(animationTime);
customShader.safeGetUniform("WaveIntensity").set((float)Math.sin(animationTime * 2.0f));
renderGeometry(poseStack, bufferSource);
}
}

Implement multi-pass shader effects:

// Example: Multi-pass rendering
public class MultiPassRenderer {
private final RenderTarget framebuffer;
private final ShaderInstance firstPass;
private final ShaderInstance secondPass;
public void render() {
// First pass: render to framebuffer
framebuffer.bindWrite(true);
RenderSystem.clear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT, true);
firstPass.apply();
renderFirstPassGeometry();
firstPass.clear();
// Second pass: render to screen
Minecraft.getInstance().getMainRenderTarget().bindWrite(true);
secondPass.safeGetUniform("InputTexture").set(framebuffer.getColorTextureId());
secondPass.apply();
renderFullscreenQuad();
secondPass.clear();
}
}

Cache uniform references to avoid repeated lookups:

// Example: Uniform caching
public class OptimizedShaderRenderer {
private final Uniform timeUniform;
private final Uniform colorUniform;
public OptimizedShaderRenderer(ShaderInstance shader) {
this.timeUniform = shader.safeGetUniform("Time");
this.colorUniform = shader.safeGetUniform("Color");
}
public void render(float time, Vec3 color) {
timeUniform.set(time);
colorUniform.set(color.x, color.y, color.z, 1.0f);
renderGeometry();
}
}

Use preprocessor defines for feature toggles:

// Example: Feature toggles
#version 330 core
#moj_import <minecraft:common>
#ifdef ENABLE_LIGHTING
uniform vec3 Light0_Direction;
#endif
in vec3 Position;
in vec4 Color;
out vec4 vertexColor;
void main() {
gl_Position = ProjMat * ModelViewMat * vec4(Position, 1.0);
vertexColor = Color;
#ifdef ENABLE_LIGHTING
float lightFactor = max(0.0, dot(normalize(Normal), Light0_Direction));
vertexColor *= lightFactor;
#endif
}

Problem: Shader fails to compile Solution: Check shader syntax, variable names, and OpenGL version compatibility

Problem: Uniform not found errors Solution: Ensure uniform names match exactly between shader code and configuration

Problem: Textures appear black or missing Solution: Verify texture units are bound correctly and samplers match shader expectations

  1. Use the preprocessor for feature toggles and code reuse
  2. Cache uniform references to improve performance
  3. Group similar effects into unified shaders
  4. Profile shader performance to identify bottlenecks
  5. Use proper resource cleanup for framebuffers and render targets