主题
渲染管线
客户端统一渲染管线,支持渲染命令、渲染通道、并行渲染和着色器注册。
前置知识
需要了解 资源管理 和 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
| 属性/方法 | 类型 | 说明 |
|---|---|---|
renderType | RenderType? | 渲染类型(用于分组和批处理) |
sortKey | Float | 排序键(透明物体排序,值越大越远) |
isTransparent | Boolean | 是否透明 |
execute(context) | (RenderContext) -> Unit | 执行渲染 |
RenderContext
| 属性 | 类型 | 说明 |
|---|---|---|
poseStack | PoseStack | 变换矩阵栈 |
bufferSource | MultiBufferSource | 缓冲区源 |
partialTick | Float | 插值因子(0~1) |
cameraX | Double | 相机 X 坐标 |
cameraY | Double | 相机 Y 坐标 |
cameraZ | Double | 相机 Z 坐标 |
frameTimeMs | Long | 帧时间(毫秒) |
gameTime | Long | 游戏时间(ticks) |
graphics | GuiGraphics? | GUI 渲染上下文 |
camera | Camera? | 相机实例 |
RenderPass
| 方法 | 参数 | 说明 |
|---|---|---|
| 构造函数 | name: String, priority: Int, enabled: Boolean | 创建渲染通道 |
collect(command) | RenderCommand | 收集渲染命令 |
prepare() | — | 准备(排序等) |
execute(context) | RenderContext | 执行所有已收集的命令 |
clear() | — | 清空命令列表 |
getStats() | — | 获取通道统计信息 |
RenderPass 统计 (Stats):
| 属性 | 类型 | 说明 |
|---|---|---|
name | String | 通道名称 |
totalCommands | Int | 总命令数 |
groupedCommands | Int | 分组命令数 |
renderTypeCount | Int | RenderType 数量 |
TransparentRenderPass
继承 RenderPass,自动按距离从远到近排序。
| 参数 | 类型 | 说明 |
|---|---|---|
name | String | 通道名称 |
priority | Int | 优先级(越小越先执行) |
IRenderable(并行渲染接口)
| 属性/方法 | 类型 | 说明 |
|---|---|---|
renderToBuffer(buffer, camera, partialTick) | — | 渲染到顶点缓冲区(可能在工作线程调用) |
isRemoved | Boolean | 是否已移除 |
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, String | ShaderInstance? | 获取着色器 |
getOrDefault(name, owner) | String, String | ShaderInstance | 获取着色器,失败返回默认 |
clearExternalCache(owner) | String? | — | 清理缓存(null 清理全部) |
getExternalCachedCount(owner) | String? | Int | 获取缓存数量 |
isUILoaded() | — | Boolean | UI 着色器是否就绪 |
isBloomShadersReady() | — | Boolean | Bloom 着色器是否就绪 |
内置着色器:
| 分类 | 着色器名称 |
|---|---|
| UI | particle、fastBlit、spriteBlit、roundBox、progressRoundBox、frameRoundBox、roundLine、sdfRect、round、panelBg |
| 视频 | videoNV12、videoYUV420P |
| 特效 | hdrParticle、spriteHdrParticle、brightPass、downSampling、upSampling、bloomFinalScatterPass |
渲染通道
不透明通道
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()后再使用对应着色器。