current page Overview
Minecraft core shaders

Give the shader a signal it can actually see.

Core shaders do not get a nice item name or model ID to inspect. This tiny pack shows the practical workaround: carry an identifying value in render data, detect it in item.fsh, then branch into a different result.

6files
1marker color
1special branch
01

What the example proves

The custom item uses a solid teal texture: RGB(19, 211, 173). The item fragment shader samples that texture normally. When it sees that exact RGB value, it writes white instead.

marker pixel19 / 211 / 173
special output1.0 / 1.0 / 1.0

Everything else stays on the normal path. That makes this a clean first test: the special case is obvious, and the vanilla path still exists right next to it.

What this is not

This is not model-name lookup. The shader is reacting to a visible signal in render data. If another texture reaches the same pipeline with the same color, those pixels can match too.

02

What the shader actually sees

By the time item.fsh runs, Minecraft has already picked the item model and turned it into render inputs. The shader gets UVs, vertex color, distance values and textures. It does not get a string like example:shader_block.

01Item stackThe item carries its model component.
02Model resolveMinecraft chooses geometry and texture.
03Render dataUVs, colors and texture samples enter the pipeline.
04Core shaderGLSL can only inspect what arrived there.

That is why a signal matters. The hard part is often not writing the effect itself. The hard part is giving the shared shader a reliable clue that one render should be treated differently.

03

The marker signal

This sample uses the most obvious version possible: the whole texture is the marker. It is visually blunt, but great for learning, because it makes the branch impossible to miss.

marker branch
vec4 color = texture(Sampler0, texCoord0);
ivec3 rgb = ivec3(round(color.rgb * 255.0));

if (all(equal(rgb, ivec3(19, 211, 173)))) {
    fragColor = vec4(1.0, 1.0, 1.0, color.a);
    return;
}

Texture samples arrive as normalized floats. Multiplying by 255.0 and rounding turns them back into easy integer RGB values for comparison.

Later on

In a real pack, you usually hide the signal on a reserved UV patch, a hidden face, a vertex-color pattern or another channel that ordinary textures are unlikely to hit by accident.

04

Pack structure

The sample namespace is deliberately example so the files stay generic and easy to reuse.

example/ ├── pack.mcmeta ├── pack.png └── assets/ ├── example/ │ ├── items/ │ │ └── shader_block.json │ ├── models/item/ │ │ └── shader_block.json │ └── textures/item/ │ └── shader_block.png └── minecraft/ └── shaders/core/ └── item.fsh

Item definition

assets/example/items/shader_block.json
{
  "model": {
    "type": "minecraft:model",
    "model": "example:item/shader_block"
  }
}

Model

assets/example/models/item/shader_block.json
{
  "parent": "minecraft:block/cube_all",
  "textures": {
    "all": "example:item/shader_block"
  }
}

Pack metadata

pack.mcmeta
{
  "pack": {
    "min_format": [
      84,
      0
    ],
    "max_format": [
      84,
      0
    ],
    "description": "shader test"
  }
}
05

The whole shader

The special branch lives near the top. Matching pixels return early. Everything else keeps the ordinary cutout, tint and fog path.

assets/minecraft/shaders/core/item.fsh
#version 330

#moj_import <minecraft:fog.glsl>
#moj_import <minecraft:dynamictransforms.glsl>

uniform sampler2D Sampler0;

in float sphericalVertexDistance;
in float cylindricalVertexDistance;
in vec4 vertexColor;
in vec2 texCoord0;

out vec4 fragColor;

void main() {
    vec4 color = texture(Sampler0, texCoord0);
    ivec3 rgb = ivec3(round(color.rgb * 255.0));

    if (all(equal(rgb, ivec3(19, 211, 173)))) {
        fragColor = vec4(1.0, 1.0, 1.0, color.a);
        return;
    }

#ifdef ALPHA_CUTOUT
    if (color.a < ALPHA_CUTOUT) {
        discard;
    }
#endif

    color *= vertexColor * ColorModulator;
    fragColor = apply_fog(color, sphericalVertexDistance, cylindricalVertexDistance, FogEnvironmentalStart, FogEnvironmentalEnd, FogRenderDistanceStart, FogRenderDistanceEnd, FogColor);
}

Once this version makes sense, replacing the white output with a real effect is the easy part.

06

Run the example

  1. Put example.zip in your resource-pack folder and enable it.
  2. Join a world where you can use commands.
  3. Give yourself the custom item using the command below.
  4. If the shader branch is working, the cube renders white.
give command
/give @s minecraft:stone[minecraft:item_model='example:shader_block']
example.zippack made for 26.1.2
download
07

Take it further

Change the output first

Leave the marker condition alone and change only the special output. That confirms your edit is reaching the correct branch.

first edit
fragColor = vec4(0.45, 0.9, 1.0, color.a);

Hide the signal

The next real step is moving the marker off the visible surface. Use hidden geometry, a reserved UV patch or another channel the player does not see.

Add more identities

Once one signal is reliable, several values can select several effects. At that point you are building a small protocol between the model data and the core shader.

Remember

item.fsh is shared. Conditions that are too broad can affect ordinary items as well.

08

Troubleshooting

The cube is teal

The model and texture are probably loading, but the branch is not producing white. Recheck the RGB values and make sure the correct item.fsh loaded.

The item is still normal stone

Debug the model before the shader. Check the item model component, namespace, item definition and model path.

Other items change too

Another texture matched your condition. Move toward a more deliberate marker pattern as soon as the idea clicks.

An edit breaks rendering

Return to the last working shader and change one thing at a time. Keep identification, math and output edits separate while debugging.