Materials & Shaders
ShaderMaterial is a declarative wrapper around Minecraft's RenderPipeline. It removes repetitive pipeline state setup — shader identifiers, vertex layout, draw mode, blending, depth state, culling, bias, samplers, uniform blocks — while leaving the underlying pipeline available for advanced use.
Java materials
ShaderMaterial material = ShaderMaterial.builder(Identifier.of("my_mod", "rune_burst"))
.shaders(Identifier.of("my_mod", "core/rune_burst"))
.geometry(VertexFormats.POSITION_TEXTURE_COLOR, VertexFormat.DrawMode.TRIANGLES)
.transparency(ShaderMaterial.Transparency.ADDITIVE)
.depth(DepthTestFunction.LEQUAL_DEPTH_TEST, false)
.cull(false)
.build();
| Builder method | Meaning |
|---|---|
shaders(shared) / shaders(vertex, fragment) | Shader source identifiers |
geometry(format, drawMode) | Vertex format and primitive type |
stage(RenderStage) | Preferred render stage |
transparency(Transparency) | OPAQUE, ALPHA, PREMULTIPLIED_ALPHA, or ADDITIVE |
depth(function, writeDepth) | Depth test and depth-write flag |
cull(boolean) | Back-face culling |
depthBias(scale, constant) | Polygon offset for decals |
sampler(name) | Declare a texture sampler |
uniformBlock(name) | Declare a std140 uniform block |
shaderToyInputs() | Add ShaderToy-style uniforms (see Advanced rendering) |
irisProgram(IrisProgramHint) | Let Iris map this pipeline to a pack program |
shaderPackPolicy(ShaderPackPolicy) | Behavior when a shader pack is active |
material.precompile() compiles the pipeline eagerly (call it in prepare() so failures surface at startup, not first draw). material.shouldRender() respects the shader-pack policy.
Shader sources live at:
assets/my_mod/shaders/core/rune_burst.vsh
assets/my_mod/shaders/core/rune_burst.fsh
Shader-pack policies
Materials preserve their own vertex and fragment shaders by default. Behavior under an active Iris pack is explicit:
| Policy | Behavior |
|---|---|
PRESERVE_CUSTOM (default) | Keep the procedural shaders; draw in Nebulon's post-pack compatibility window |
INHERIT_SHADER_PACK | Let Iris map/replace the pipeline with a pack program; requires an IrisProgramHint |
DISABLE_WHEN_ACTIVE | Skip the material entirely while a pack is active |
.shaderPackPolicy(ShaderPackPolicy.PRESERVE_CUSTOM) // the default
.irisProgram(IrisProgramHint.ENTITIES_TRANSLUCENT) // selects INHERIT_SHADER_PACK
.shaderPackPolicy(ShaderPackPolicy.DISABLE_WHEN_ACTIVE) // neither path looks right
irisProgram(...) is not a render-order hintCalling it selects INHERIT_SHADER_PACK: Iris replaces your fragment shader with the pack's program. Use it only for geometry meant to inherit pack shading, and override the renderer's shaderPackStages() to return stages() so the geometry stays inside Iris's world pipeline.
Available IrisProgramHint values: NONE, BASIC, TEXTURED, TERRAIN, TRANSLUCENT, SKY_BASIC, SKY_TEXTURED, ENTITIES, ENTITIES_TRANSLUCENT, PARTICLES, PARTICLES_TRANSLUCENT, BEACON_BEAM, LINES.
An IrisProgramHint never creates a required Iris dependency — all Iris access is reflective and degrades gracefully.
Reloadable JSON materials
Definitions under assets/<namespace>/nebulon/materials/<path>.json are resource-pack overrideable and addressed as <namespace>:<path>:
{
"shader": "my_mod:core/rune",
"vertex_format": "position_texture_color",
"draw_mode": "triangles",
"blend": "additive",
"depth_test": "lequal",
"depth_write": false,
"cull": false,
"stage": "before_translucent",
"shader_pack": "preserve_custom",
"uniform_blocks": ["RuneData"],
"samplers": ["NoiseSampler"]
}
Notable keys and values:
| Key | Values |
|---|---|
shader (or vertex + fragment) | Shader identifier(s) |
vertex_format | position, position_color, position_color_normal, position_texture, position_texture_color, position_texture_color_normal |
blend / transparency | e.g. alpha, additive |
depth_test | none, less, lequal / less_or_equal, equal, greater |
depth_bias_scale, depth_bias_constant | Polygon offset |
stage | e.g. before_translucent |
shader_pack | preserve_custom, etc. |
iris_program | An IrisProgramHint name |
Using a reloadable material
ReloadableMaterial handle = NebulonMaterials.get(Identifier.of("my_mod", "rune"));
ShaderMaterial current = handle.material();
long revision = handle.revision();
On F3+T or resource-pack changes, replacements are staged first. A renderer calls applyPending() on the render thread; only a candidate whose GPU pipeline compiles successfully increments revision() and replaces material(). Invalid JSON or GLSL is logged while the handle retains its last working material. pending() and lastError() expose reload state for development tools. If a definition disappears from the active packs, its handle becomes unavailable rather than serving stale content.