NeoForge-1.20.4Mod开发教程之方块实体渲染

本篇教程参考为Flandre芙兰的NeoForge1.20.4Mod开发的视频,这篇教程是我边学边总结的,与其说是教程更像我的学习笔记,如有不清楚的地方,大家可以在评论区提问。在我力所能及的范围内,我都会解答的

 

方块实体渲染(Block Entity Rendering / BER)*就是:当一个方块的外观*不能只靠静态的烘焙模型(JSON/OBJ 等)表示时,游戏会给这个方块配一个 BlockEntity,并在客户端用 BlockEntityRenderer 每帧(或按需)执行自定义绘制逻辑——比如箱子盖子开合、熔炉燃烧动画、带动态内容的展示框、会旋转/浮动的物体、根据方块实体数据改变显示内容等。Forge 文档明确指出:BER 用于渲染“无法用静态 baked model 表达”的方块,并且它要求方块拥有 BlockEntity。

 

我们在之前的RubyFrame基础上修改

RubyFrame类

public class RubyFrame extends BaseEntityBlock implements SimpleWaterloggedBlock {
     public RubyFrame() {
         super(Properties.ofFullCopy(Blocks.STONE).strength(5).noOcclusion());
         this.registerDefaultState(this.stateDefinition.any().setValue(BlockStateProperties.WATERLOGGED, false));
    }
     public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
     public static final VoxelShape SHAPE1 = Block.box(0,0,0,16,2,16);
     public static final VoxelShape SHAPE2 = Block.box(0,14,0,16,16,16);
     public static final VoxelShape SHAPE3 = Block.box(0,2,0,2,14,2);
     public static final VoxelShape SHAPE4 = Block.box(0,2,16,2,14,16);
     public static final VoxelShape SHAPE5 = Block.box(14,2,0,16,14,2);
     public static final VoxelShape SHAPE6 = Block.box(14,2,14,16,14,16);
     public static final VoxelShape ALL_SHAPE = Shapes.or(SHAPE1, SHAPE2, SHAPE3, SHAPE4, SHAPE5, SHAPE6);
 ​
     @Override
     public  VoxelShape getShape( BlockState state,  BlockGetter reader,  BlockPos pos,  CollisionContext collisionContext) {
         return ALL_SHAPE;
    }
 ​
     @Override
     protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
         builder.add(WATERLOGGED);
    }
 ​
     @Override
     public BlockState getStateForPlacement(BlockPlaceContext ctx) {
         BlockPos blockpos = ctx.getClickedPos();
         FluidState fluidstate = ctx.getLevel().getFluidState(blockpos);
         return this.defaultBlockState().setValue(WATERLOGGED, fluidstate.getType() == Fluids.WATER);
    }
 ​
     @Override
     public BlockState updateShape(BlockState state,
                                   Direction facing,
                                   BlockState facingState,
                                   LevelAccessor level,
                                   BlockPos currentPos,
                                   BlockPos facingPos) {
         if (state.getValue(WATERLOGGED)) {
             level.scheduleTick(currentPos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
        }
         return super.updateShape(state, facing, facingState, level, currentPos, facingPos);
    }
 ​
     @Override
     public  FluidState getFluidState(BlockState state) {
         return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
    }
 ​
     protected boolean isPathfindable(BlockState state,PathComputationType type) {
         return switch (type) {
             case LAND -> false;
             case WATER -> state.getFluidState().is(FluidTags.WATER);
             case AIR -> false;
             default -> false;
        };
    }
 //新增方法
     @Override
     protected MapCodec<? extends BaseEntityBlock> codec() {
         return null;
    }
 ​
     @Override
     public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
         return new RubyFrameBlockEntity(pos, state);
    }
 ​
     @Override
     public RenderShape getRenderShape(BlockState state) {
         return RenderShape.MODEL;
    }
 }

 

RubyFrameBlockEntity类

public class RubyFrameBlockEntity extends BlockEntity {
     public RubyFrameBlockEntity(BlockPos pos, BlockState blockState) {
         super(ModBlockEntities.RUBY_FRAME_BLOCK_ENTITY.get(), pos, blockState);
    }
 }

 

创建RubyFrameBlockEntityRender类

这里渲染部分

public class RubyFrameBlockEntityRender implements BlockEntityRenderer<RubyFrameBlockEntity> {
     public RubyFrameBlockEntityRender(BlockEntityRendererProvider.Context context) {}
     @Override
     public void render(RubyFrameBlockEntity blockEntity, float partialTick, PoseStack poseStack, MultiBufferSource buffer, int packedLight, int packedOverlay) {//每帧渲染被调用的入口
         poseStack.pushPose();//保存当前的矩阵状态
         poseStack.translate(1.0D, 0.0D, 0.0D);//把渲染位置平移到方块坐标系的右侧 1 格
         BlockRenderDispatcher blockRenderDispatcher = Minecraft.getInstance().getBlockRenderer();
         BlockState blockState = Blocks.CHEST.defaultBlockState();//获取箱子的默认状态
         blockRenderDispatcher.renderSingleBlock(blockState, poseStack, buffer, packedLight,packedOverlay);
         poseStack.popPose();//恢复push之前的矩阵状态
 ​
         poseStack.pushPose();
         poseStack.translate(0.0D, 1.0D, 0.0D);//把渲染位置平移到上面 1 格
         ItemRenderer itemRenderer = Minecraft.getInstance().getItemRenderer();
         ItemStack itemStack = new ItemStack(Items.DIAMOND);
         BakedModel bakedModel = itemRenderer.getModel(itemStack,blockEntity.getLevel(),null,0);//获取物品模型,数字为随机种子,用于模型随机变体;0表示固定
         itemRenderer.render(itemStack, ItemDisplayContext.FIXED,true,poseStack,buffer,packedLight,packedOverlay,bakedModel);
         poseStack.popPose();
 ​
    }
 }

 

参数说明

render 方法参数

•RubyFrameBlockEntity blockEntity:当前正在渲染的方块实体实例,可用来拿位置、状态、世界等数据

float partialTick:渲染插值用的部分刻(tick)进度,0~1,用于平滑动画

PoseStack poseStack:矩阵栈,用来做平移/旋转/缩放,影响当前渲染坐标系

MultiBufferSource buffer:渲染缓冲区,所有顶点会写到这里

int packedLight:打包光照值(含方块光和天空光),影响亮度

int packedOverlay:打包叠加值,用于破坏动画或发光效果等叠加层

 

renderSingleBlock 参数

blockRenderDispatcher.renderSingleBlock(blockState, poseStack, buffer, packedLight, packedOverlay);

BlockState blockState:要渲染的方块状态,这里是 Blocks.CHEST.defaultBlockState()

PoseStack poseStack:当前矩阵栈(已平移过)

MultiBufferSource buffer:当前缓冲区

int packedLight:光照值

int packedOverlay:叠加值

 

getModel 参数

BakedModel bakedModel = itemRenderer.getModel(itemStack, blockEntity.getLevel(), null, 0);

ItemStack itemStack:要渲染的物品堆栈,这里是 Items.DIAMOND

Level level:当前世界,用于让模型根据世界环境变化(比如渲染变种)。传 blockEntity.getLevel()

LivingEntity entity:用于根据实体状态决定模型变化,这里传 null(无实体上下文)

int seed:随机种子,用于模型随机变体;0 表示固定

 

itemRenderer.render 参数

itemRenderer.render(itemStack, ItemDisplayContext.FIXED, true, poseStack, buffer, packedLight, packedOverlay, bakedModel); ItemStack itemStack:要渲染的物品堆栈

ItemDisplayContext.FIXED:渲染上下文,决定缩放和朝向,FIXED 类似展示框

boolean leftHanded:是否以左手方式渲染。这里传 true,会影响朝向

PoseStack poseStack:矩阵栈 MultiBufferSource buffer:缓冲区

int packedLight:光照值 int packedOverlay:叠加值

BakedModel bakedModel:最终要渲染的物品模型

 

ItemDisplayContext的其他取值

THIRD_PERSON_RIGHT_HAND第三人称右手持物

THIRD_PERSON_LEFT_HAND:第三人称左手持物

FIRST_PERSON_RIGHT_HAND:第一人称右手持物

FIRST_PERSON_LEFT_HAND:第一人称左手持物

HEAD:头盔槽位(头部)

GUI:物品栏/快捷栏等 GUI 展示

GROUND:掉落在地面的物品

FIXED:物品展示框等固定展示

NONE:代码侧的兜底上下文,通常不用于JSON display里

补充:在更高版本(例如 1.21.11+ 的映射)中,枚举里出现了ON_SHELF。是否能用取决于你当前 NeoForge/Minecraft 版本

 

类介绍

BlockRenderDispatcher类

字段

BlockModelShaper blockModelShaper:用来根据 BlockState 找到对应的 BakedModel

ModelBlockRenderer modelRenderer:实际把方块模型“tesselate/渲染”成顶点的核心渲染器

BlockEntityWithoutLevelRenderer blockEntityRenderer:用于渲染 ENTITYBLOCK_ANIMATED 这种依赖 BEWLR 的方块(例如箱子、床等自定义渲染)

LiquidBlockRenderer liquidBlockRenderer:专门渲染液体方块(流体)

RandomSource random:内部用来产生稳定随机,用于模型随机变体

BlockColors blockColors:方块染色器,用于草、树叶等根据环境决定颜色的方块

构造器

BlockRenderDispatcher(BlockModelShaper blockModelShaper, BlockEntityWithoutLevelRenderer blockEntityRenderer, BlockColors blockColors):注入模型查找器、BEWLR 和颜色系统;并初始化 modelRenderer 和 liquidBlockRenderer

方法 1.模型和渲染器获取

getBlockModelShaper():返回 BlockModelShaper

getModelRenderer():返回 ModelBlockRenderer

getBlockModel(BlockState state):用 blockModelShaper 获取 BakedModel

getLiquidBlockRenderer():返回 LiquidBlockRenderer

2.破坏贴图渲染

renderBreakingTexture(...)(两个重载):渲染方块破坏动画的贴图层 核心流程: BlockState → BakedModel → modelData → tesselateBlock(…) 只对 RenderShape.MODEL 生效

3.批量世界渲染

renderBatched(...)(两个重载):在世界里批量渲染方块(chunk 渲染路径)

4.液体渲染

renderLiquid(...):优先让流体自身扩展渲染(IClientFluidTypeExtensions),否则 fallback 到 LiquidBlockRenderer。同样有异常捕获与 CrashReport

5.单个方块渲染

renderSingleBlock(...)(两个重载):用于在 GUI、物品、方块实体渲染等场景里“独立渲染”一个方块

6.资源重载回调

onResourceManagerReload(ResourceManager resourceManager):资源重载时调用,主要让LiquidBlockRenderer重新抓取贴图

 

 

ItemRenderer类

字段

Minecraft minecraft:客户端实例,提供全局资源、设置等

ItemModelShaper itemModelShaper:物品模型映射表:Item → ModelResourceLocation → BakedModel

TextureManager textureManager:纹理管理器,用于绑定贴图

ItemColors itemColors:物品染色器,给可染色物品(如药水、草莓酱类)提供颜色

BlockEntityWithoutLevelRenderer blockEntityRenderer:用于特殊物品的自定义渲染(如箱子、床等)

构造器

ItemRenderer(Minecraft minecraft, TextureManager textureManager, ModelManager modelManager, ItemColors itemColors, BlockEntityWithoutLevelRenderer blockEntityRenderer)

初始化字段,创建 itemModelShaper,并为所有 Item 注册默认的 inventory 模型

方法

1.模型映射

getItemModelShaper():返回 itemModelShaper

2.渲染入口

render(ItemStack itemStack, ItemDisplayContext displayContext, boolean leftHand, PoseStack poseStack, MultiBufferSource buffer, int combinedLight, int combinedOverlay, BakedModel p_model):核心渲染流程

核心流程:

根据显示上下文选择三叉戟/望远镜特殊模型

应用相机变换(handleCameraTransforms

如果不是自定义渲染器,则走模型渲染流程

若需要“附魔发光(glint)”则选择不同的 VertexConsumer

3.渲染模型列表

renderModelLists(BakedModel model, ItemStack stack, int combinedLight, int combinedOverlay, PoseStack poseStack, VertexConsumer buffer):遍历 6 个面 + 无方向面,把所有 BakedQuad交给renderQuadList

4.渲染 Quad

renderQuadList(PoseStack poseStack, VertexConsumer buffer, List<BakedQuad> quads, ItemStack itemStack, int combinedLight, int combinedOverlay):把quads写入缓冲区,并应用物品染色

5.模型获取

getModel(ItemStack stack, @Nullable Level level, @Nullable LivingEntity entity, int seed):根据 ItemStack + 世界/实体 + 随机种子,拿到最终BakedModel 

处理三叉戟/望远镜特殊模型并应用overrides

6.静态渲染封装

renderStatic(...)(两个重载):提供更简化的入口,内部还是走 getModel → render

7.资源重载

onResourceManagerReload(ResourceManager resourceManager):资源重载时刷新itemModelShaper缓存

8.获取自定义渲染器

getBlockEntityRenderer():返回blockEntityRenderer

与发光/附魔相关的工具方法

getArmorFoilBuffer

getCompassFoilBuffer / getCompassFoilBufferDirect

getFoilBuffer / getFoilBufferDirect

这些方法决定是否使用带 glint 的 render type,以及在 GUI/一人称时做缩放处理

© 版权声明
THE END
喜欢就支持一下吧
点赞4赞赏 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容