Skip to content

渲染管线

客户端统一渲染管线,支持渲染命令、渲染通道、并行渲染和着色器注册。

前置知识

需要了解 资源管理 和 Minecraft 的 PoseStack / BufferSource 基本概念。

注意

Mojang于最近宣布了MC将在不久后使用Vulkan后端,渲染管线正在重构以适配VK,同时支持GPU并行计算(比如Compute Shader)

Quick Start

kotlin
class MyRenderCommand(private val x: Double, private val y: Double, private val z: Double) : RenderCommand {
    override val renderType: RenderType? = null
    override val sortKey: Float = 0f
    override val isTransparent: Boolean = false

    override fun execute(context: RenderContext) {
        val pose = context.poseStack
        pose.pushPose()
        pose.translate(x - context.cameraX, y - context.cameraY, z - context.cameraZ)
        // 渲染逻辑 ...
        pose.popPose()
    }
}

API Reference

RenderCommand

属性/方法类型说明
renderTypeRenderType?渲染类型(用于分组和批处理)
sortKeyFloat排序键(透明物体排序,值越大越远)
isTransparentBoolean是否透明
execute(context)(RenderContext) -> Unit执行渲染

RenderContext

属性类型说明
poseStackPoseStack变换矩阵栈
bufferSourceMultiBufferSource缓冲区源
partialTickFloat插值因子(0~1)
cameraXDouble相机 X 坐标
cameraYDouble相机 Y 坐标
cameraZDouble相机 Z 坐标
frameTimeMsLong帧时间(毫秒)
gameTimeLong游戏时间(ticks)
graphicsGuiGraphics?GUI 渲染上下文
cameraCamera?相机实例

RenderPass

方法参数说明
构造函数name: String, priority: Int, enabled: Boolean创建渲染通道
collect(command)RenderCommand收集渲染命令
prepare()准备(排序等)
execute(context)RenderContext执行所有已收集的命令
clear()清空命令列表
getStats()获取通道统计信息

RenderPass 统计 (Stats):

属性类型说明
nameString通道名称
totalCommandsInt总命令数
groupedCommandsInt分组命令数
renderTypeCountIntRenderType 数量

TransparentRenderPass

继承 RenderPass,自动按距离从远到近排序。

参数类型说明
nameString通道名称
priorityInt优先级(越小越先执行)

IRenderable(并行渲染接口)

属性/方法类型说明
renderToBuffer(buffer, camera, partialTick)渲染到顶点缓冲区(可能在工作线程调用)
isRemovedBoolean是否已移除
getDistanceSq(camX, camY, camZ)Double到相机的距离平方(用于排序)

ParallelRenderType

方法返回值说明
isParallel()Boolean是否启用并行渲染(默认 false
getSortMode()SortMode排序模式(NONE / BACK_TO_FRONT
prepareState()准备渲染状态(主线程)
beginBuffer(buffer)开始缓冲区(可能在工作线程)
endBuffer(buffer)结束缓冲区并上传(主线程)
releaseState()释放渲染状态(主线程)

ParallelExecutor

方法参数说明
getInstance()获取单例
execute(renderables, config, camera, partialTick, defaultBuilder)见签名执行并行渲染
executeBatch(batches, camera, partialTick, defaultBuilder)见签名批量执行多个渲染配置
getParallelism()获取并行度(线程数)

ShaderRegistry

方法参数返回值说明
get(name, owner)String, StringShaderInstance?获取着色器
getOrDefault(name, owner)String, StringShaderInstance获取着色器,失败返回默认
clearExternalCache(owner)String?清理缓存(null 清理全部)
getExternalCachedCount(owner)String?Int获取缓存数量
isUILoaded()BooleanUI 着色器是否就绪
isBloomShadersReady()BooleanBloom 着色器是否就绪

内置着色器:

分类着色器名称
UIparticlefastBlitspriteBlitroundBoxprogressRoundBoxframeRoundBoxroundLinesdfRectroundpanelBg
视频videoNV12videoYUV420P
特效hdrParticlespriteHdrParticlebrightPassdownSamplingupSamplingbloomFinalScatterPass

渲染通道

不透明通道

kotlin
val opaquePass = RenderPass(
    name = "opaque",
    priority = 0,
    enabled = true
) { context, commands ->
    for (command in commands) {
        command.execute(context)
    }
}

opaquePass.collect(myCommand)
opaquePass.prepare()
opaquePass.execute(renderContext)
opaquePass.clear()

透明通道

kotlin
val transparentPass = TransparentRenderPass(
    name = "transparent",
    priority = 100
)

// 透明命令自动从远到近排序
transparentPass.collect(transparentCommand)
transparentPass.prepare()  // 执行排序
transparentPass.execute(context)

类型化通道

kotlin
class MyTypedPass : RenderPass("my_pass"), TypedRenderPass {
    override fun canHandle(command: RenderCommand): Boolean {
        return command is MySpecialCommand
    }
}

并行渲染

kotlin
val executor = ParallelExecutor.getInstance()

// 单配置执行
executor.execute(
    renderables = particles,
    config = myRenderConfig,
    camera = camera,
    partialTick = partialTick,
    defaultBuilder = bufferBuilder
)

// 批量执行
executor.executeBatch(
    batches = listOf(
        particles to particleConfig,
        trails to trailConfig
    ),
    camera = camera,
    partialTick = partialTick,
    defaultBuilder = bufferBuilder
)

并行阈值

IRenderable 数量 >= 64 且 IRenderConfig.isParallel() 返回 true 时,才会启用并行渲染。否则使用串行渲染。

外部着色器

Behemiron/photon/shader/ 目录加载,使用 Minecraft 标准 JSON 格式:

json
{
    "blend": {
        "func": "add",
        "srcrgb": "srcalpha",
        "dstrgb": "1-srcalpha"
    },
    "vertex": "my_shader",
    "fragment": "my_shader",
    "attributes": ["Position", "UV0", "Color"],
    "samplers": [{ "name": "Sampler0" }],
    "uniforms": [
        { "name": "ModelViewMat", "type": "matrix4x4", "count": 16 },
        { "name": "ProjMat", "type": "matrix4x4", "count": 16 }
    ]
}

完整示例

kotlin
@Component
class CustomRenderer {

    private val opaquePass = RenderPass("custom_opaque", priority = 50)
    private val transparentPass = TransparentRenderPass("custom_transparent", priority = 150)

    fun onRenderFrame(context: RenderContext) {
        // 收集命令
        opaquePass.collect(MyRenderCommand(10.0, 64.0, 10.0))
        transparentPass.collect(GlowRenderCommand(10.0, 65.0, 10.0))

        // 执行渲染
        opaquePass.prepare()
        opaquePass.execute(context)
        opaquePass.clear()

        transparentPass.prepare()
        transparentPass.execute(context)
        transparentPass.clear()
    }

    // 粒子并行渲染
    fun renderParticles(particles: List<IRenderable>, camera: Camera, partialTick: Float) {
        if (particles.isEmpty()) return

        val executor = ParallelExecutor.getInstance()
        executor.execute(
            renderables = particles,
            config = myParticleConfig,
            camera = camera,
            partialTick = partialTick,
            defaultBuilder = bufferBuilder
        )
    }

    // 使用着色器
    fun renderWithShader() {
        val shader = ShaderRegistry.getOrDefault("roundBox", "my_plugin")
        // 使用 shader 进行渲染 ...
    }
}

注意事项

  • IRenderable.renderToBuffer() 可能在工作线程调用,不要修改 OpenGL 状态,不要访问非线程安全的共享资源。
  • prepareState()releaseState() 只在主线程调用,可安全操作 OpenGL 状态。
  • 透明物体必须使用 TransparentRenderPass 以保证正确的绘制顺序(从远到近)。
  • 检查 ShaderRegistry.isUILoaded() / isBloomShadersReady() 后再使用对应着色器。