Skip to content

渲染管线

Behemiron 提供统一的客户端渲染管线,支持渲染命令、渲染通道和并行渲染。

概念导图

渲染命令

RenderCommand 接口

所有渲染操作通过命令模式封装:

kotlin
interface RenderCommand {
    /** 渲染类型(用于分组和批处理) */
    val renderType: RenderType?

    /** 排序键(透明物体排序,值越大越远) */
    val sortKey: Float

    /** 是否透明 */
    val isTransparent: Boolean

    /** 执行渲染 */
    fun execute(context: RenderContext)
}

RenderContext

渲染上下文包含当前帧的渲染信息:

kotlin
data class RenderContext(
    val poseStack: PoseStack,           // 变换矩阵栈
    val bufferSource: MultiBufferSource, // 缓冲区源
    val partialTick: Float,             // 插值因子(0-1)
    val cameraX: Double,                // 相机位置
    val cameraY: Double,
    val cameraZ: Double,
    val frameTimeMs: Long,              // 帧时间(毫秒)
    val gameTime: Long,                 // 游戏时间(ticks)
    val graphics: GuiGraphics?,         // GUI 渲染上下文
    val camera: Camera?                 // 相机实例
)

使用示例

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()
    }
}

渲染通道

RenderPass

渲染通道表示管线中的一个阶段:

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()

TransparentRenderPass

专门处理透明物体的通道,自动按距离排序:

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

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

TypedRenderPass

类型化通道接口,用于只处理特定类型命令:

kotlin
class MyTypedPass : RenderPass("my_pass"), TypedRenderPass {

    override fun canHandle(command: RenderCommand): Boolean {
        return command is MySpecialCommand
    }
}

通道统计

kotlin
val stats = pass.getStats()
println("通道: ${stats.name}")
println("总命令数: ${stats.totalCommands}")
println("分组命令: ${stats.groupedCommands}")
println("RenderType 数: ${stats.renderTypeCount}")

并行渲染

IRenderable 接口

支持并行顶点生成的渲染对象:

kotlin
interface IRenderable {
    /**
     * 渲染到顶点缓冲区
     *
     * 注意:此方法可能在工作线程调用
     * - 不要修改 OpenGL 状态
     * - 不要访问非线程安全的共享资源
     */
    fun renderToBuffer(buffer: VertexConsumer, camera: Camera, partialTick: Float)

    /** 是否已移除 */
    val isRemoved: Boolean

    /** 到相机的距离平方(用于排序) */
    fun getDistanceSq(camX: Double, camY: Double, camZ: Double): Double
}

ParallelRenderType

并行渲染类型,定义渲染生命周期:

kotlin
abstract class ParallelRenderType {

    /** 是否启用并行渲染 */
    open fun isParallel(): Boolean = false

    /** 排序模式 */
    open fun getSortMode(): SortMode = SortMode.NONE

    /** 准备渲染状态(主线程) */
    open fun prepareState() {}

    /** 开始缓冲区(可能在工作线程) */
    open fun beginBuffer(buffer: BufferBuilder) {
        buffer.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
    }

    /** 结束缓冲区并上传(主线程) */
    open fun endBuffer(buffer: BufferBuilder) {
        BufferUploader.drawWithShader(buffer.end())
    }

    /** 释放渲染状态(主线程) */
    open fun releaseState() {}
}

SortMode

kotlin
enum class SortMode {
    NONE,           // 不排序
    BACK_TO_FRONT   // 从远到近(透明物体)
}

ParallelExecutor

并行渲染执行器,使用 ForkJoinPool 并行生成顶点:

kotlin
val executor = ParallelExecutor.getInstance()

// 执行并行渲染
executor.execute(
    renderables = particles,      // IRenderable 列表
    config = myRenderConfig,      // IRenderConfig 实现
    camera = camera,
    partialTick = partialTick,
    defaultBuilder = bufferBuilder
)

// 批量执行多个渲染配置
executor.executeBatch(
    batches = listOf(
        particles to particleConfig,
        trails to trailConfig
    ),
    camera = camera,
    partialTick = partialTick,
    defaultBuilder = bufferBuilder
)

// 获取并行度
val threads = executor.getParallelism()

并行阈值

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

着色器注册表

ShaderRegistry

统一的着色器管理:

kotlin
// 获取着色器
val shader = ShaderRegistry.get("particle", "my_plugin")

// 获取失败时返回默认着色器
val shader = ShaderRegistry.getOrDefault("round_box", "my_plugin")

内置着色器

UIparticlefastBlitspriteBlitroundBoxprogressRoundBoxframeRoundBoxroundLinesdfRectroundpanelBg

视频videoNV12videoYUV420P

特效hdrParticlespriteHdrParticlebrightPassdownSamplingupSamplingbloomFinalScatterPass

外部着色器

Behemiron/photon/shader/ 目录加载:

Behemiron/
└── photon/
    └── shader/
        └── my_shader.json

JSON 格式(Minecraft 标准):

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
// 清理指定所有者的缓存
ShaderRegistry.clearExternalCache("my_plugin")

// 清理所有缓存
ShaderRegistry.clearExternalCache(null)

// 获取缓存数量
val count = ShaderRegistry.getExternalCachedCount("my_plugin")

状态检查

kotlin
// 检查 UI 着色器是否就绪
if (ShaderRegistry.isUILoaded()) {
    // UI 渲染可用
}

// 检查 Bloom 着色器是否就绪
if (ShaderRegistry.isBloomShadersReady()) {
    // Bloom 特效可用
}