https://docs.fabricmc.net/zh_cn/1.21/develop/rendering/basic-concepts

Render triangles in Hud

跟fabric文档一样使用 HudRenderCallback 事件:

HudRenderCallback.EVENT.register((drawContext, tickDeltaManager) -> {
	// Get the transformation matrix from the matrix stack, alongside the tessellator instance and a new buffer builder.
	MatrixStack matrixStack = drawContext.getMatrices();
	Matrix4f transformationMatrix = matrixStack.peek().getPositionMatrix();
	Tessellator tessellator = Tessellator.getInstance();
 
	// Begin a triangle strip buffer using the POSITION_COLOR vertex format.
	BufferBuilder buffer = tessellator.begin(VertexFormat.DrawMode.TRIANGLE_STRIP, VertexFormats.POSITION_COLOR);
 
	// Write our vertices, Z doesn't really matter since it's on the HUD.
	buffer.vertex(transformationMatrix, 20, 20, 5).color(0xFF414141);
	buffer.vertex(transformationMatrix, 5, 40, 5).color(0xFF000000);
	buffer.vertex(transformationMatrix, 35, 40, 5).color(0xFF000000);
	buffer.vertex(transformationMatrix, 20, 60, 5).color(0xFF414141);
 
	// We'll get to this bit in the next section.
	RenderSystem.setShader(GameRenderer::getPositionColorProgram);
	RenderSystem.setShaderColor(1.0F, 1.0F, 1.0F, 1.0F);
 
	// Draw the buffer onto the screen.
	BufferRenderer.drawWithGlobalProgram(buffer.end());
});

代码的结果同样查看文档。

Entity ESP

interpolate

if(entity instanceof PlayerEntity || entity instanceof AnimalEntity){
		IEntity e = (IEntity) entity; // 把 entity 转成 Mixin 后的IEntity
		Box box = e.getDimensions().getBoxAt(new Vec3d(
						interpolate(entity.lastRenderX, entity.getX(), mc.getRenderTickCounter().getTickDelta(true)),
						interpolate(entity.lastRenderY, entity.getY(), mc.getRenderTickCounter().getTickDelta(true)),
						interpolate(entity.lastRenderZ, entity.getZ(), mc.getRenderTickCounter().getTickDelta(true)))).expand(0,0.1,0);
		
		draw3DBox(matrixStack, box,Color.CYAN);
}
public static void draw3DBox(MatrixStack matrixStack, Box box, Color color){
	// negate(): Creates a vector with the same length but with the opposite direction.
	box = box.offset(mc.gameRenderer.getCamera().getPos().negate());
	
	Matrix4f matrix = matrixStack.peek().getPositionMatrix();
	Tessellator tessellator = Tessellator.getInstance();
	
	BufferBuilder bufferBuilder = tessellator.begin(VertexFormat.DrawMode.DEBUG_LINES, VertexFormats.POSITION);
	RenderSystem.setShaderColor(color.getRed() / 255f, color.getGreen() / 255f, color.getBlue() / 255f, 0.8f);
	RenderSystem.setShader(ShaderProgramKeys.POSITION);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float)box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.minY, (float) box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.minY, (float) box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.minZ);
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.maxX, (float) box.maxY, (float) box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
	
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.maxZ);
	bufferBuilder.vertex(matrix, (float) box.minX, (float) box.maxY, (float) box.minZ);
	
	// Draw the buffer onto the screen.
	BufferRenderer.drawWithGlobalProgram(bufferBuilder.end());
	
	
}
// 最好在绘制前后包一层 `RenderSystem.enable/disableXXX`  确保深度测试 / blend / 矩阵恢复
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.disableDepthTest();
RenderSystem.depthMask(false);
 
// 渲染代码 ...
 
// 恢复渲染状态
RenderSystem.enableDepthTest();
RenderSystem.disableBlend();
RenderSystem.depthMask(true);

另一种实现方式

override fun onRender3D(event: Render3DEvent) {
	val target = mc.crosshairTarget
	if (target is BlockHitResult) {
			val shape = mc.world
					?.getBlockState(target.blockPos)
					?.getOutlineShape(mc.world, target.blockPos)
 
			if (shape?.isEmpty == true) return
 
			var box = shape?.boundingBox
			box = box?.offset(target.blockPos)
			if (box != null) {
					RenderUtil.drawBox(event.matrix,box,Color.CYAN,1.0)
			}
	}
}
fun drawBox(stack: MatrixStack, box: Box, c: Color, lineWidth: Double) {
	val minX = (box.minX - mc.entityRenderDispatcher.camera.pos.getX())
	val minY = (box.minY - mc.entityRenderDispatcher.camera.pos.getY())
	val minZ = (box.minZ - mc.entityRenderDispatcher.camera.pos.getZ())
	val maxX = (box.maxX - mc.entityRenderDispatcher.camera.pos.getX())
	val maxY = (box.maxY - mc.entityRenderDispatcher.camera.pos.getY())
	val maxZ = (box.maxZ - mc.entityRenderDispatcher.camera.pos.getZ())
 
	val bufferBuilder = Tessellator.getInstance()
			.begin(VertexFormat.DrawMode.LINES, VertexFormats.POSITION_COLOR_NORMAL)
 
	VertexRendering.drawBox(
			stack,
			bufferBuilder,
			minX,
			minY,
			minZ,
			maxX,
			maxY,
			maxZ,
			c.red / 255f,
			c.green / 255f,
			c.blue / 255f,
			c.alpha / 255f
	)
 
	Layers.getGlobalLines(lineWidth).draw(bufferBuilder.end())
}
object Layers {
    private val GLOBAL_QUADS: RenderLayer
    private var GLOBAL_LINES: Function<Double, RenderLayer>
 
    fun getGlobalLines(width: Double): RenderLayer {
        return GLOBAL_LINES.apply(width)
    }
 
    private fun builder(): RenderLayer.MultiPhaseParameters.Builder {
        return RenderLayer.MultiPhaseParameters.builder()
    }
 
    private fun empty(): RenderLayer.MultiPhaseParameters {
        return builder().build(false)
    }
 
    init {
        GLOBAL_QUADS = RenderLayer.of("global_fill", 156, GLOBAL_QUADS_PIPELINE, empty())
 
         GLOBAL_LINES = Util.memoize { l: Double ->
             val width = LineWidth(OptionalDouble.of(l))
             RenderLayer.of(
                 "global_lines",
                 156,
                 GLOBAL_LINES_PIPELINE,
                 builder().lineWidth(width).build(false)
             )
         }
    }
}