当前位置: 首页 > news >正文

我的世界Java版1.21.4的Fabric模组开发教程(十五)方块实体渲染器

这是适用于Minecraft Java版1.21.4的Fabric模组开发系列教程专栏第十五章——方块实体渲染器。想要阅读其他内容,请查看或订阅上面的专栏。

当简单的方块实体不能满足方块的视觉需求时,方块实体渲染器(Block Entity Renderers) 便可以为方块渲染自定义内容。例如,我们可将文字、图像或图形等内容渲染在方块上,或者说以方块为基准,将内容渲染在方块周围。渲染的内容虽然依赖方块实体对应的渲染器,但这与方块模型无关,即使方块模型出现问题,渲染在方块上的内容依然可以正常显示,且始终渲染在方块模型上层。

本章将承接上一章节的内容。将光源方块(light_block) 的光照等级数字垂直地渲染在方块正上方。相关内容可以参考我的世界Java版1.21.4的Fabric模组开发教程(十四)方块实体。

完成上一章节中创建方块实体的步骤后,继续完成下列步骤来借助方块实体渲染器渲染内容:

  • 配置客户端项目(可选);
  • 创建方块实体渲染器类;
  • 注册自定义方块实体渲染器;
  • 渲染文字;

当然,要确保已经掌握了创建方块实体的前置知识。

配置客户端项目(可选)

这是本教程首次开始在客户端项目中编写代码,因此从本章开始,仅仅在服务端完成编码已经不能满足部分模组开发的需求,尤其涉及到实体、游戏画面和GUI等内容的渲染时;

通常,所有关于渲染的逻辑代码都应当编写在客户端模块中。如果项目没有将客户端和服务端分离,需要先配置客户端项目才能开始创建方块实体渲染器类;

如果项目已经将客户端与服务端分离成两个模块,则可以跳过本节。

1.首先,需要创建com/example/test/client目录,用于存放所有客户端文件;
在这里插入图片描述
2.然后在client文件夹中创建TestClient.java,类名可以为当前模组Id+“Client”。使其继承ClientModInitializer类,作为当前项目客户端的入口点类,同时实现onInitializeClient()方法。

public class TestClient implements ClientModInitializer {@Overridepublic void onInitializeClient() {}
}

创建方块实体渲染器类

想要开始渲染内容,首先需要创建一个方块实体渲染器类,然后在其中添加渲染内容的逻辑。一般,创建方块实体渲染器类需要继承BlockEntityRenderer接口,且在此之前需要完成方块和方块实体类的创建;

在开始前,需要掌握大量有关渲染的API用法。


方块实体渲染器接口BlockEntityRenderer

BlockEntityRenderer接口用于创建指定方块的方块实体对应的渲染器。一般,在创建指定方块实体的渲染器类时,需要实现此接口并使用方块实体类作为此接口的泛型,其中必须实现的方法为render()

void render(T entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay);

其中的参数包括:

  • T entity:方块实体对象。重写方法时会自动填充实现BlockEntityRenderer接口时使用的泛型对象;
  • float tickDelta:时间差值。一般指的是介于两帧(tick)之间的差值,用于完成动画的平滑过渡;
  • MatrixStack matrices:矩阵堆栈对象。用于控制渲染内容的位移、旋转和缩放等操作;
  • VertexConsumerProvider vertexConsumers:顶点消耗提供器对象;
  • int light:光照值;
  • int overlay:覆盖纹理效果;

稍后,我们将渲染逻辑添加到此方法中。

矩阵堆栈类MatrixStack

MatrixStack类指的是一种数据结构为栈的变换矩阵,用于控制渲染的内容在3D空间中的平移、缩放或旋转。当然,以栈的形式存储的数据结构严格遵循“先进后出(FILO)/后进先出(LIFO)”的基本原则。因此,渲染的基本过程应当为“压入→变换→渲染→弹出”。

MatrixStack类对象中方法的使用贯穿整个渲染过程,这些方法包括但不限于:

  • push():将当前矩阵的副本,也就是即将变换的矩阵压入堆栈;
  • pop():丢弃当前矩阵,从堆栈弹出变换完成的矩阵;
  • peek():获取当前堆栈最顶层的矩阵;
  • multiply(Quaternionf quaternion):应用旋转变换。需要传递一个Quaternionf对象;
  • scale(float x, float y, float z):应用缩放变换。分别传递X、Y、Z轴扩大的倍数;
  • translate(double x, double y, double z):应用平移变换。分别传递X、Y、Z轴的偏移量。

四元数类Quaternionf

Quaternionf类用于表示并处理渲染内容的3D旋转,是调用MatrixStack对象的multiply()方法时必须传递的参数之一。不过,在调用multiply()方法时,我们一般不会直接使用Quaternionf类来创建它的对象。由Minecraft API提供的RotationAxis接口提前封装了一些Quaternionf对象,这些对象可供应用旋转变换时使用;

确切地说,四元数类Quaternionf不属于Minecraft API。

旋转轴函数式接口RotationAxis

RotationAxis类是在1.21.4版本中添加的工具类,用于简化常见的轴向旋转操作。尽管接口被@FunctionalInterface注解所修饰,在本章中暂时没有出现关于此接口函数表达式的相关用法。

X、Y、Z三个基本轴的常量已经在接口中声明并完成了初始化,分别为:

  • NEGATIVE_X:指定绕X轴负方向旋转;
  • POSITIVE_X:指定绕X轴正方向旋转;
  • NEGATIVE_Y:指定绕Y轴负方向旋转;
  • POSITIVE_Y:指定绕Y轴正方向旋转;
  • NEGATIVE_Z:指定绕Z轴负方向旋转;
  • POSITIVE_Z:指定绕Z轴正方向旋转;

想要完成旋转,还需要调用:

  • rotation(float rad):指定旋转的弧度制数值,返回Quaternionf对象;
  • rotationDegrees(float deg):指定旋转的角度制数值,是接口中最常用的方法,返回Quaternionf对象。

顶点数据供应器接口VertexConsumerProvider

VertexConsumerProvider接口用于管理顶点数据渲染,并为不同渲染层提供对应的VertexConsumer,其对象声明在render()方法的参数列表中,是渲染过程中需要使用到的参数之一。VertexConsumer接口旨在构建和提交顶点数据,它可以将模型的几何数据(顶点颜色、纹理坐标、覆盖光效、法线向量等)转换为GPU可处理的格式,并最终渲染到屏幕上;

这属于渲染系统的底层API,一般情况不会直接使用。

方块实体渲染器工厂上下文内部类BlockEntityRendererFactory.Context

BlockEntityRendererFactory.Context内部类用于构建方块实体渲染器的上下文环境,其中提供了渲染方块实体所需的各种依赖组件和工具。一般作为自定义方块实体渲染器类构造方法的参数。

其中声明的方法大多数是为了获取渲染过程中使用到的工具,包括:

  • getTextRenderer():获取文本渲染器对象;
  • getRenderDispatcher():获取渲染调度器对象;
  • getRenderManager():获取渲染管理器对象;

为了渲染文字,在本章中使用到了getTextRenderer()方法来获取文本渲染器。

文本渲染器类TextRenderer

TextRenderer类用于在游戏界面中渲染文本,是渲染文本时必须用到的API。其中封装了用于处理文本渲染、渲染长度计算和颜色设置的方法。

要使用这些方法,首先需要获取TextRenderer的对象。通常,获取TextRenderer对象的方法有两种。一是通过客户端类MinecraftClient的实例:

MinecraftClient.getInstance().textRenderer;

不过这是旧版的写法;

二是在自定义方块实体渲染器类中声明TextRenderer对象,然后在构造方法中通过BlockEntityRendererFactory.Context对象的getTextRenderer()方法对其进行初始化:

private final TextRenderer textRenderer;public XXXBlockEntityRenderer(BlockEntityRendererFactory.Context context) {textRenderer = context.getTextRenderer();
}

这是最新的写法,也是本章中用到的方法;

如果已经获取到TextRenderer的对象,就可以开始配置文字相关设置,然后渲染文字。使用到的方法包括:

  • getWidth(String text):用于获取文字渲染后的长度,传递一个字符串作为参数,即要渲染的文字;
  • draw(...):用于渲染的核心方法。其中的参数按顺序分别代表:
    • String text:指定要渲染的文字;
    • float x:指定文本基线的X坐标;
    • float y:指定文本基线的Y坐标;
    • int color:指定文本颜色,需要传递一个16进制RGB颜色码;
    • boolean shadow: 指定是否渲染阴影;
    • Matrix4f matrix:指定变换矩阵,用于应用平移、缩放或旋转操作。一般的写法为matrices.peek().getPositionMatrix(),即通过MatrixStack对象的peek()方法获取MatrixStack对象的栈顶矩阵条目,然后在调用getPositionMatrix()方法获取变换后的矩阵;
    • VertexConsumerProvider vertexConsumers:指定顶点数据提供器,一般使用render()方法中提供的参数;
    • TextRenderer.TextLayerType layerType:指定文本渲染层类型。需要传递内部枚举类TextRenderer.TextLayerType中的枚举常量,包括:
      • NORMAL:正常模式。大多数文本的渲染模式,如GUI文本、物品名称等;
      • SEE_THROUGH:穿透模式。文本可以穿过其他物体始终可见;
      • POLYGON_OFFSET:混合偏移模式。旨在解决文本与方块表面深度冲突,即Z-Fighting问题;
    • int backgroundColor:指定背景色,数值可以为0,代表透明色;
    • int light:指定光照值。一般使用render()方法中提供的参数。

渲染系统类RenderSystem

RenderSystem类用于控制图形渲染状态(如颜色混合、深度测试、纹理绑定等),它提供了对OpenGL底层渲染的封装,是Minecraft底层渲染控制中枢。在生产环境中,RenderSystem类可用于创建自定义渲染逻辑;

其中声明的方法大多为静态方法,例如:

  • enableDepthTest():启用深度测试;
  • disableCull():禁用背面剔除;
  • setShaderColor(...):设置着色器颜色;

如果一切都已准备就绪,便可以开始创建自定义方块实体渲染器类。

1.在客户端项目(或client目录)中创建rendering文件夹,用于存放渲染相关文件。然后创建LightBlockEntityRenderer.java,作为“光源方块”的方块实体渲染器类;

使其继承BlockEntityRenderer<T extends BlockEntity>类,并重写render()方法;

public class LightBlockEntityRenderer implements BlockEntityRenderer<LightBlockEntity> {@Overridepublic void render(LightBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {}
}

这里的LightBlockEntity类是“光源方块”的方块实体类,已经在上一章节创建完毕;

2.声明一个私有成员常量textRenderer,类型为TextRenderer,用于渲染文本;

private final TextRenderer textRenderer;

3.声明构造方法,调用BlockEntityRendererFactory.Context对象的getTextRenderer()方法完成对textRenderer的初始化;

public LightBlockEntityRenderer(BlockEntityRendererFactory.Context context) {textRenderer = context.getTextRenderer();
}

注册自定义方块实体渲染器

与方块、物品、魔咒效果等相同,在使用自定义方块实体渲染器前,需要将其在游戏注册表中注册。


方块实体渲染器工厂类BlockEntityRendererFactories

BlockEntityRendererFactories类专门用于注册方块实体渲染器,是相当实用的工具类,它主要简化了方块实体渲染器与方块实体类型的绑定过程。

想要注册方块实体渲染器,应当调用register()方法;

public static <T extends BlockEntity> void register(BlockEntityType<? extends T> type, BlockEntityRendererFactory<T> factory) {FACTORIES.put(type, factory);
}

需要传递两个参数:

  • BlockEntityType<? extends T> type:指定方块实体对象。一般使用注册方块实体时返回的方块实体对象;
  • BlockEntityRendererFactory<T> factory:指定方块实体渲染器工厂对象。一般传递自定义方块实体渲染器类的表达式来创建其对象,即LightBlockEntityRenderer::new

在客户端入口点类中的onInitializeClient()方法中注册方块实体渲染器;

@Override
public void onInitializeClient() {BlockEntityRendererFactories.register(ModBlockEntities.LIGHT_BLOCK_ENTITY, LightBlockEntityRenderer::new);
}

调用BlockEntityRendererFactories类的静态方法register()完成方块实体渲染器的注册,方法中按顺序传递“光源方块”的方块实体对象ModBlockEntities.LIGHT_BLOCK_ENTITY以及自定义方块实体渲染器类的表达式LightBlockEntityRenderer::new创建的BlockEntityRendererFactory对象。

渲染文字

下面,我们将光照强度数值渲染在“光源方块”正上方。期间可能会随时启动游戏进行测试,以确保文本最终处于正确的位置。所有逻辑都将添加到自定义方块实体渲染器类的render()方法中。

1.首先,通过方块实体类获取需要渲染的文字;

String text = String.valueOf(entity.getLevels());

通过方块实体对象entitygetLevels()方法获取方块的光照强度,然后将其转换为字符串并保存到text中;

2.可以先将数字直接渲染到方块周围,然后在调整其大小和位置;

textRenderer.draw(text, 1f, 1f, 0xffffff, false, matrices.peek().getPositionMatrix(),  vertexConsumers, TextRenderer.TextLayerType.NORMAL, 0, light);

调用文本渲染器对象的draw()方法,其中的参数按顺序分别设置了要渲染的文本为text、文本基线的X坐标为1f、Y坐标为1f、文本颜色0xffffff(白色)、不显示文字阴影、变换矩阵为当前矩阵堆栈的栈顶条目的位置矩阵matrices.peek().getPositionMatrix()、顶点数据提供器对象、文本渲染层模式为TextLayerType.NORMAL、背景色为0(透明)以及光照值为light

先以上面的数值设置渲染文本,启动游戏后数值可以按需逐步修改;

如果现在启动游戏,放置方块后,会看到渲染已经生效,不过文本大小非常夸张,位置也完全不符合需求;
在这里插入图片描述
3.现在,我们使用MatrixStack对象调整文字的大小和位置,过程中要严格遵循“压入变换矩阵→应用变换→渲染→弹出变换矩阵”的处理逻辑。

  • 压入变换矩阵
    调用push()方法将当前矩阵的副本压入矩阵堆栈。
    matrices.push();
    
  • 应用缩放变换
    调用scale()方法缩小矩阵大小;
    matrices.scale(1 / 20f, 1 / 20f, 1 / 20f);
    
    将当前矩阵缩小20倍,可以看到文本已经缩小。
    在这里插入图片描述
  • 应用位置变换
    调用translate()方法调整文本的位置;
    matrices.translate(0.5, 1.5, 0.5);
    
    X、Y、Z轴的位移量分别为0.5、1.5和0.5。可以看到文本位置已经在方块的上方,但位置还不够居中,稍后我们调整draw()方法中的参数来解决此问题;
    在这里插入图片描述
    不过,右键方块修改光照值,会发现数字处于完全倒置的状态。
  • 应用旋转变换
    调用multiply()方法旋转文本;
    matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180));
    
    使其沿X轴正半轴以角度制旋转180度,可以看到文本大小和朝向目前显示正确。
    在这里插入图片描述
  • 弹出变换矩阵
    调用pop()方法,丢弃当前矩阵,弹出变换完毕的矩阵。
    matrices.pop();
    

4.微调draw()方法中的文本基线的X和Y坐标。

为了让光照强度无论为一位数还是两位数都处于居中状态,可以调用文本渲染器对象的getWidth()方法来动态获取文本渲染后的长度。声明width变量并调用getWidth()方法对其初始化;

String text = String.valueOf(entity.getLevels());
float width = textRenderer.getWidth(text);

方法传递一个字符串,即渲染的文本。将返回的长度存储在width变量中。最后,调整draw()方法中的X和Y参数;

textRenderer.draw(text, -width / 2, -4f,...);

将X设为-width / 2可以使文本始终在X方向上处于居中状态,然后将Y设为-4f可以使其底部距离方块更远;

现在,文本的大小、位置、朝向等近乎完美。
在这里插入图片描述
5.此时移动到文本的相反方向,会发现文本不显示。这是因为Minecraft的渲染系统默认启用了背面剔除(Back-face Culling) 以减少渲染工作量,从而提高渲染效率。
请添加图片描述
我们可以在压入变换矩阵前通过RenderSystem类关闭“背面剔除”功能;

RenderSystem.disableCull();

调用静态方法disableCull()关闭“背面剔除”,启动游戏可以发现玩家处于文本的相反朝向时,文本始终显示;
在这里插入图片描述
现在,文本的渲染已经通过方块实体渲染器完成,所有功能均已实现。render()方法中的代码应当如下:

@Override
public void render(LightBlockEntity entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertexConsumers, int light, int overlay) {RenderSystem.disableCull();matrices.push();matrices.scale(1 / 20f, 1 / 20f, 1 / 20f);matrices.translate(0.5, 1.5, 0.5);matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(180));String text = String.valueOf(entity.getLevels());float width = textRenderer.getWidth(text);textRenderer.draw(text,-width / 2, -4f,0xffffff,false,matrices.peek().getPositionMatrix(),vertexConsumers,TextRenderer.TextLayerType.NORMAL,0,light);matrices.pop();
}

本章小结

本章详细阐述了通过方块实体渲染器在方块周围渲染内容的过程,也首次在客户端项目中编写代码。本文篇幅较长,难度较大,需要有方块和方块实体创建的前置知识来完成学习。另外,还需要了解众多关于渲染的API用法来减少编码过程中的障碍。感谢各位的阅读,有兴趣可以订阅此专栏!

http://www.lryc.cn/news/584288.html

相关文章:

  • 北京一家IPO业绩持续性存疑,关联交易频繁独立性堪忧
  • iOS 抓包详细教程:从零搭建、操作到实战调试的全流程指南
  • C++ -- STL -- vector
  • 北斗舞动在线监测装置:电力安全的“智慧守护者”
  • 大健康IP如何借“合规创新”抢占行业新风口|创客匠人
  • 基于Python的程序员数据分析与可视化系统的设计与实现
  • linxu内核的signal fault和arm内核的flault
  • 网络综合实验
  • Flowable21条件事件------------持续更新中
  • 【LeetCode100】--- 2.字母异位词分组【复习回顾】
  • 【LeetCode 热题 100】148. 排序链表——(解法二)分治
  • 数据结构与算法之美:广义表
  • ThinkSound V2版 - 一键给无声视频配音,为AI视频生成匹配音效 支持50系显卡 一键整合包下载
  • LeetCode 1652. 拆炸弹
  • 二分查找篇——寻找旋转排序数组中的最小值【LeetCode】
  • 节点小宝:手机图片备份至电脑功能实测体验
  • 机器学习12——支持向量机中
  • Ubuntu 20.04 下**安装 FFmpeg 5.1
  • Lua嵌入式爬虫实现步骤
  • Redis性能基准测试
  • 观众信息设置与统计(视频高级分析与统计功能)
  • Windows下VScode配置FFmpeg开发环境保姆级教程
  • vue中token的使用与统计实践
  • 机器学习11——支持向量机上
  • 快速合并多个CAD图形为单一PDF文档的方法
  • 机器学习之逻辑回归和k-means算法(六)
  • 机器学习:反向神经元传播公式推导
  • C#基础:Winform桌面开发中窗体之间的数据传递
  • 机器学习13——支持向量机下
  • Linux - firewall 防火墙