Skip to content

Your First Renderer

This tutorial walks you through creating your first custom entity renderer from start to finish. We’ll build a simple glowing orb entity that demonstrates all the core concepts you need to understand.

Let’s start with a simple entity class:

// Example: Simple glowing orb entity
public class GlowingOrbEntity extends Entity {
private float glowIntensity = 1.0f;
private Color glowColor = Color.WHITE;
public GlowingOrbEntity(EntityType<? extends GlowingOrbEntity> entityType, Level level) {
super(entityType, level);
}
@Override
public void tick() {
super.tick();
// Simple pulsing animation
this.glowIntensity = 0.8f + 0.2f * Mth.sin(this.tickCount * 0.1f);
// Randomly change color occasionally
if (this.level.isClientSide && this.tickCount % 60 == 0) {
float r = this.random.nextFloat();
float g = this.random.nextFloat();
float b = this.random.nextFloat();
this.glowColor = new Color(r, g, b);
}
}
public float getGlowIntensity() {
return glowIntensity;
}
public Color getGlowColor() {
return glowColor;
}
}
// Example: Entity registration
public class MyModEntities {
public static final String MOD_ID = "firstrenderer";
public static final EntityType<GlowingOrbEntity> GLOWING_ORB =
Registry.register(Registries.ENTITY_TYPE,
new ResourceLocation(MOD_ID, "glowing_orb"),
EntityType.Builder.of(GlowingOrbEntity::new, MobCategory.MISC)
.sized(0.5f, 0.5f)
.clientTrackingRange(32)
.fireImmune()
.build("glowing_orb"));
public static void register() {
// Entity registration happens in static initialization
}
}

For this simple entity, we’ll use a basic model:

// Example: Simple orb model
public class GlowingOrbModel extends EntityModel<GlowingOrbEntity> {
public static final ModelLayerLocation LAYER_LOCATION =
new ModelLayerLocation(new ResourceLocation(MyModEntities.MOD_ID, "glowing_orb"), "main");
private final ModelPart orb;
public GlowingOrbModel(ModelPart root) {
this.orb = root.getChild("orb");
}
public static LayerDefinition createBodyLayer() {
MeshDefinition meshDefinition = new MeshDefinition();
PartDefinition partDefinition = meshDefinition.getRoot();
// Create a simple sphere-like orb
partDefinition.addOrReplaceChild("orb",
CubeListBuilder.create()
.texOffs(0, 0)
.addBox(-0.25f, -0.25f, -0.25f, 0.5f, 0.5f, 0.5f),
PartPose.ZERO);
return LayerDefinition.create(meshDefinition, 16, 16);
}
@Override
public void setupAnim(GlowingOrbEntity entity, float limbSwing, float limbSwingAmount,
float ageInTicks, float netHeadYaw, float headPitch) {
// Simple floating animation
float bobAmount = Mth.sin(ageInTicks * 0.1f) * 0.1f;
this.orb.y = bobAmount;
// Slow rotation
this.orb.yRot = ageInTicks * 0.05f;
}
@Override
public void renderToBuffer(PoseStack poseStack, VertexConsumer consumer,
int packedLight, int packedOverlay, float red,
float green, float blue, float alpha) {
this.orb.render(poseStack, consumer, packedLight, packedOverlay, red, green, blue, alpha);
}
}

This is the main rendering logic:

// Example: Simple glowing orb renderer
public class GlowingOrbRenderer extends EntityRenderer<GlowingOrbEntity> {
private static final ResourceLocation GLOWING_ORB_TEXTURE =
new ResourceLocation(MyModEntities.MOD_ID, "textures/entity/glowing_orb.png");
private final GlowingOrbModel model;
public GlowingOrbRenderer(EntityRendererProvider.Context context) {
super(context);
this.model = new GlowingOrbModel(context.bakeLayer(GlowingOrbModel.LAYER_LOCATION));
}
@Override
public void render(GlowingOrbEntity entity, float entityYaw, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
poseStack.pushPose();
// Position the entity at eye level
poseStack.translate(0.0, entity.getBbHeight() * 0.5, 0.0);
// Apply rotation to face player
poseStack.mulPose(Axis.YP.rotationDegrees(-entityYaw));
// Get entity properties for rendering
float glowIntensity = entity.getGlowIntensity();
Color glowColor = entity.getGlowColor();
// Render main orb with entity color and glow intensity
this.model.renderToBuffer(poseStack,
bufferSource.getBuffer(RenderType.entityCutout(getTextureLocation(entity))),
packedLight, OverlayTexture.NO_OVERLAY,
glowColor.getRed() / 255.0f * glowIntensity,
glowColor.getGreen() / 255.0f * glowIntensity,
glowColor.getBlue() / 255.0f * glowIntensity,
1.0f);
// Add glow effect with additive blending
renderGlowEffect(entity, partialTick, poseStack, bufferSource);
poseStack.popPose();
}
private void renderGlowEffect(GlowingOrbEntity entity, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource) {
// Create a translucent render type for glow
VertexConsumer glowConsumer = bufferSource.getBuffer(RenderType.entityTranslucent(getTextureLocation(entity)));
// Scale up slightly for glow effect
poseStack.scale(1.5f, 1.5f, 1.5f);
// Use full brightness for glow
int glowLight = 0xF000F0;
// Render semi-transparent glow layer
this.model.renderToBuffer(poseStack, glowConsumer, glowLight, OverlayTexture.NO_OVERLAY,
1.0f, 1.0f, 1.0f, entity.getGlowIntensity() * 0.3f);
}
@Override
public ResourceLocation getTextureLocation(GlowingOrbEntity entity) {
return GLOWING_ORB_TEXTURE;
}
// Make the entity always fully bright
@Override
protected int getBlockLightLevel(GlowingOrbEntity entity, BlockPos blockPos) {
return 15;
}
}
// Example: Register model layer and renderer
public class MyModRenderers {
public static void registerModelLayers() {
EntityModelLayerRegistry.registerModelLayer(GlowingOrbModel.LAYER_LOCATION, GlowingOrbModel::createBodyLayer);
}
public static void registerEntityRenderers() {
EntityRendererRegistry.register(MyModEntities.GLOWING_ORB, GlowingOrbRenderer::new);
}
}

Step 6: Create Client and Server Entry Points

Section titled “Step 6: Create Client and Server Entry Points”
// Example: Main mod class
public class FirstRendererMod implements ModInitializer {
public static final String MOD_ID = "firstrenderer";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);
@Override
public void onInitialize() {
LOGGER.info("Initializing First Renderer Mod");
// Register entities
MyModEntities.register();
}
}
// Example: Client mod class
public class FirstRendererModClient implements ClientModInitializer {
@Override
public void onInitializeClient() {
FirstRendererMod.LOGGER.info("Initializing client side");
// Register renderers and model layers
MyModRenderers.registerModelLayers();
MyModRenderers.registerEntityRenderers();
}
}

Create the following resource files:

src/main/resources/assets/firstrenderer/textures/entity/glowing_orb.png

  • 16x16 pixel texture for the orb
  • Can be white for full color control, or have a base pattern

src/main/resources/assets/firstrenderer/models/entity/glowing_orb.json

{
"texturewidth": 16,
"textureheight": 16,
"elements": [
{
"from": [-0.25, -0.25, -0.25],
"to": [0.25, 0.25, 0.25],
"faces": {
"north": {"uv": [0, 0, 8, 8], "texture": "#0"},
"east": {"uv": [8, 0, 16, 8], "texture": "#0"},
"south": {"uv": [0, 8, 8, 16], "texture": "#0"},
"west": {"uv": [8, 8, 16, 16], "texture": "#0"},
"up": {"uv": [0, 0, 8, 8], "texture": "#0"},
"down": {"uv": [8, 0, 16, 8], "texture": "#0"}
}
}
],
"display": {
"head": {
"translation": [0, 0, 0],
"scale": [0.5, 0.5, 0.5]
}
}
}

src/main/resources/fabric.mod.json

{
"schemaVersion": 1,
"id": "firstrenderer",
"version": "1.0.0",
"name": "First Renderer Example",
"description": "Example mod with custom entity renderer",
"authors": ["YourName"],
"license": "MIT",
"environment": "*",
"entrypoints": {
"main": [
"com.example.firstrenderer.FirstRendererMod"
],
"client": [
"com.example.firstrenderer.FirstRendererModClient"
]
},
"depends": {
"fabricloader": ">=0.14.0",
"fabric-api": "*",
"minecraft": "~1.26.1",
"java": ">=17"
}
}

Use a command or event to spawn your entity:

// Example: Command to spawn entity (simplified)
public class SpawnOrbCommand {
public static int register(CommandDispatcher<CommandSourceStack> dispatcher) {
return Commands.literal("spawnorb")
.requires(source -> source.hasPermission(2))
.executes(context -> {
ServerPlayer player = context.getSource().getPlayerOrException();
GlowingOrbEntity orb = new GlowingOrbEntity(MyModEntities.GLOWING_ORB, player.level);
orb.moveTo(player.position());
player.level.addFreshEntity(orb);
return 1;
})
.register(dispatcher);
}
}
// Example: Debug renderer
public class GlowingOrbRenderer extends EntityRenderer<GlowingOrbEntity> {
private static boolean DEBUG_MODE = false;
@Override
public void render(GlowingOrbEntity entity, float entityYaw, float partialTick,
PoseStack poseStack, MultiBufferSource bufferSource,
int packedLight) {
if (DEBUG_MODE) {
// Render bounding box for debugging
renderDebugBounds(entity, poseStack, bufferSource);
}
// Normal rendering...
}
private void renderDebugBounds(GlowingOrbEntity entity, PoseStack poseStack,
MultiBufferSource bufferSource) {
VertexConsumer debugConsumer = bufferSource.getBuffer(RenderType.lines());
AABB bb = entity.getBoundingBox();
// Render wireframe box
// (Implementation omitted for brevity)
}
}
  1. Entity Class: Contains the game logic and data
  2. Model Class: Defines the 3D shape and animations
  3. Renderer Class: Handles the actual drawing logic
  4. Registration: Connects all components together
  1. Minecraft calls your entity’s tick() method for updates
  2. Game calls your renderer’s render() method each frame
  3. Renderer applies transformations, selects render type, and calls model
  4. Model renders vertices to the provided vertex consumer
  5. Blaze3D handles the rest (shader binding, drawing, etc.)
  1. Check Registration: Ensure entity, renderer, and model layer are all registered
  2. Verify Texture: Make sure texture path is correct and file exists
  3. Check Render Type: Ensure you’re using appropriate render type
  1. File Path: Verify texture is in correct assets folder
  2. Resource Location: Ensure MOD_ID matches folder structure
  3. Image Format: Use PNG format with power-of-2 dimensions
  1. Use Partial Ticks: Always use partial ticks for smooth animations
  2. Check Time Values: Verify you’re using correct time units
  3. Transform Order: Apply transformations in correct order

This example demonstrates the complete entity rendering pipeline:

  • ✅ Entity creation and registration
  • ✅ Model definition with animations
  • ✅ Renderer with custom effects
  • ✅ Proper Fabric integration
  • ✅ Texture and resource management
  • ✅ Debugging and troubleshooting

You can build on this foundation to create more complex rendering effects like particles, custom shaders, and advanced animations.