Skip to main content

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 methodMeaning
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:

PolicyBehavior
PRESERVE_CUSTOM (default)Keep the procedural shaders; draw in Nebulon's post-pack compatibility window
INHERIT_SHADER_PACKLet Iris map/replace the pipeline with a pack program; requires an IrisProgramHint
DISABLE_WHEN_ACTIVESkip 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 hint

Calling 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:

KeyValues
shader (or vertex + fragment)Shader identifier(s)
vertex_formatposition, position_color, position_color_normal, position_texture, position_texture_color, position_texture_color_normal
blend / transparencye.g. alpha, additive
depth_testnone, less, lequal / less_or_equal, equal, greater
depth_bias_scale, depth_bias_constantPolygon offset
stagee.g. before_translucent
shader_packpreserve_custom, etc.
iris_programAn 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.