Skip to main content
LiquidBounce features a sophisticated rendering system built on OpenGL 4.5+, using render pipelines for ESP, tracers, overlays, and custom UI effects.

Architecture

The rendering system consists of:
  1. Render Pipelines - Define how geometry is rendered
  2. Render Environments - Context for 2D/3D rendering
  3. Shaders - GLSL programs for GPU processing
  4. Render Targets - Offscreen buffers for effects

Render Pipelines

Location: render/ClientRenderPipelines.kt:37 Pipelines define the complete rendering state:
object ClientRenderPipelines {
    
    // Basic primitives
    val Lines = newPipeline("lines") {
        forWorldRender()
        relativePosColorSnippet(VertexFormat.Mode.LINES)
    }
    
    val Quads = newPipeline("quads") {
        forWorldRender()
        relativePosColorSnippet(VertexFormat.Mode.QUADS)
    }
    
    val Triangles = newPipeline("triangles") {
        forWorldRender()
        relativePosColorSnippet(VertexFormat.Mode.TRIANGLES)
    }
}

Creating Custom Pipelines

val MyCustomPipeline = newPipeline("my_custom") {
    withVertexShader("path/to/vertex")
    withFragmentShader("path/to/fragment")
    withVertexFormat(DefaultVertexFormat.POSITION_COLOR, VertexFormat.Mode.TRIANGLES)
    withBlend(BlendFunction.TRANSLUCENT)
    withCull(false)
    withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST)
}

World Rendering

Location: render/RenderShortcuts.kt:83

WorldRenderEnvironment

Provides context for 3D world rendering:
renderEnvironmentForWorld(matrixStack) {
    // Draw in world space
    withPositionRelativeToCamera(targetPos) {
        drawBox(
            box = AABB(0.0, 0.0, 0.0, 1.0, 1.0, 1.0),
            faceColor = Color4b(255, 0, 0, 100),
            outlineColor = Color4b(255, 255, 255, 255)
        )
    }
}

Drawing Primitives

Lines:
fun WorldRenderEnvironment.drawLine(p1: Vec3f, p2: Vec3f, argb: Int) =
    drawCustomMesh(ClientRenderPipelines.Lines) { pose ->
        addVertex(pose, p1).setColor(argb)
        addVertex(pose, p2).setColor(argb)
    }
Boxes (render/RenderShortcuts.kt:331):
fun WorldRenderEnvironment.drawBox(
    box: AABB,
    faceColor: Color4b? = Color4b.TRANSPARENT,
    outlineColor: Color4b? = Color4b.TRANSPARENT,
    faceVertices: Int = -1,
    outlineVertices: Int = -1
)
Circles (render/RenderShortcuts.kt:519):
fun WorldRenderEnvironment.drawCircle(
    radius: Float,
    color: Color4b
)

fun WorldRenderEnvironment.drawCircleOutline(
    radius: Float,
    color: Color4b,
    noDepthTest: Boolean = true
)
Gradient Effects (render/RenderShortcuts.kt:428):
fun WorldRenderEnvironment.drawGradientCircle(
    outerRadius: Float,
    innerRadius: Float,
    outerColor: Color4b,
    innerColor: Color4b,
    innerOffset: Vector3fc = Vector3f(),
    noDepthTest: Boolean = true
)

Coordinate Systems

World Space:
// Render at absolute world position
withPositionRelativeToCamera(blockPos) {
    drawBox(FULL_BOX, faceColor = Color4b.RED)
}
Camera-Relative:
// Render relative to camera
withPositionRelativeToCamera {
    translate(Vec3(0.0, 0.0, -5.0))  // 5 blocks in front
    drawCircle(1.0f, Color4b.WHITE)
}

Custom Mesh Rendering

Location: render/RenderShortcuts.kt:182
fun WorldRenderEnvironment.drawCustomMesh(
    pipeline: RenderPipeline,
    textures: Map<String, AbstractTexture> = emptyMap(),
    uniforms: Map<String, GpuBufferSlice> = emptyMap(),
    drawer: VertexConsumer.(PoseStack.Pose) -> Unit
)

Example: Custom Textured Quad

val myTexture = /* ... */

drawCustomMeshTextured(myTexture) { pose ->
    addVertex(pose, -0.5f, -0.5f, 0f).setUv(0f, 0f).setColor(0xFFFFFFFF.toInt())
    addVertex(pose, -0.5f, 0.5f, 0f).setUv(0f, 1f).setColor(0xFFFFFFFF.toInt())
    addVertex(pose, 0.5f, 0.5f, 0f).setUv(1f, 1f).setColor(0xFFFFFFFF.toInt())
    addVertex(pose, 0.5f, -0.5f, 0f).setUv(1f, 0f).setColor(0xFFFFFFFF.toInt())
}

Shader System

Location: render/ClientShaders.kt Custom shaders extend rendering capabilities:
object ClientShaders {
    object Vertex {
        val PosRelativeToCamera = identifier("core/pos_relative_to_camera")
        val PosColorRelativeToCamera = identifier("core/poscolor_relative_to_camera")
    }
    
    object Fragment {
        val BgraPosTex = identifier("core/bgra_pos_tex")
        val PosRelativeToCamera = identifier("core/pos_relative_to_camera")
    }
}

Uniform Buffers

Location: render/ClientUniformDefine.kt
object ClientUniformDefine {
    val GUI_BLUR = UniformDefine("GuiBlur", 16)
    val ROUNDED_RECT = UniformDefine("RoundedRect", 12)
    val DISTANCE_FADE = UniformDefine("DistanceFade", 8)
}

class UniformDefine(val uboName: String, val size: Int) {
    fun createSingleBuffer(): GpuBufferSlice
}

Using Uniforms

val blurUniform = ClientUniformDefine.GUI_BLUR.createSingleBuffer()
blurUniform.writeStd140 {
    putFloat(blurRadius)
    putFloat(alphaStart)
    putFloat(alphaEnd)
}

drawCustomMesh(
    pipeline = MyPipeline,
    uniforms = mapOf("GuiBlur" to blurUniform)
) { pose ->
    // Render with uniform
}

Advanced Effects

Blur Effect

Location: render/engine/BlurEffectRenderer.kt:37
object BlurEffectRenderer : EventListener {
    val overlayRenderTargetHolder = LazyRenderTargetHolder(
        "LiquidBounce BlurOverlay",
        useDepth = true
    )
    
    fun shouldDrawBlur(): Boolean
    fun blitBlurOverlay()
}
The blur effect uses a two-pass Gaussian blur:
  1. Render UI to overlay buffer
  2. Blur overlay buffer
  3. Composite blur with main buffer

Outline Shader

Location: render/engine/OutlineShaderRenderer.kt Renders entity outlines using stencil buffer:
object OutlineShaderRenderer {
    fun beginOutlines()
    fun renderOutlines(color: Color4b)
    fun endOutlines()
}

Distance Fade

Location: render/utils/DistanceFadeUniformValueGroup.kt Fade rendering based on distance:
class DistanceFadeUniformValueGroup : ValueGroup() {
    val fadeStart by float("FadeStart", 50.0f, 0.0f..200.0f)
    val fadeEnd by float("FadeEnd", 100.0f, 0.0f..200.0f)
    
    fun updateIfDirty()
    fun bindUniform(pass: RenderPass)
}

Render Targets

Location: render/engine/LazyRenderTargetHolder.kt Offscreen rendering for effects:
class LazyRenderTargetHolder(
    val name: String,
    val useDepth: Boolean = false
) {
    val raw: RenderTarget?
        get() = /* lazy initialization */
    
    fun resize(width: Int, height: Int)
    fun clear()
}

Example: Custom Post-Processing

val customBuffer = LazyRenderTargetHolder("MyEffect")

// Render to custom buffer
customBuffer.raw!!.bindWrite(true)
drawScene()
customBuffer.raw!!.unbindWrite()

// Apply effect
mc.mainRenderTarget.createRenderPass({ "Custom Effect" }).use { pass ->
    pass.setPipeline(MyEffectPipeline)
    pass.bindTexture("Input", customBuffer.raw!!.colorTextureView, sampler)
    pass.draw(0, 3)
}

Performance Optimization

Batching

Batch rendering for efficiency:
renderEnvironmentForWorld(matrixStack, mode = DrawMode.BATCH) {
    // All draws batched together
    for (entity in entities) {
        drawBox(entity.boundingBox, Color4b.RED)
    }
    // Single GPU call
}

AMD Vega Workaround

Location: render/RenderShortcuts.kt:57
val HAS_AMD_VEGA_APU = (GL11C.glGetString(GL11C.GL_RENDERER)
    ?.startsWith("AMD Radeon(TM) RX Vega") ?: false)

inline fun WorldRenderEnvironment.longLines(draw: WorldRenderEnvironment.() -> Unit) {
    if (HAS_AMD_VEGA_APU) GL11C.glDisable(GL11C.GL_LINE_SMOOTH)
    try {
        draw()
    } finally {
        if (HAS_AMD_VEGA_APU) GL11C.glEnable(GL11C.GL_LINE_SMOOTH)
    }
}
AMD Vega GPU Bug: Newer AMD Vega iGPU drivers (2023+) fail to smooth long lines. The longLines helper disables line smoothing as a workaround.

2D Rendering

Location: render/Render2D.kt For GUI and HUD elements:
object Render2D {
    fun drawRect(x: Float, y: Float, width: Float, height: Float, color: Color4b)
    fun drawRoundedRect(x: Float, y: Float, width: Float, height: Float, radius: Float, color: Color4b)
    fun drawGradientRect(x: Float, y: Float, width: Float, height: Float, startColor: Color4b, endColor: Color4b)
    fun drawTexture(texture: AbstractTexture, x: Float, y: Float, width: Float, height: Float)
}

Font Rendering

Location: render/FontManager.kt
object FontManager {
    fun getFont(name: String, size: Int): FontRenderer
}

interface FontRenderer {
    fun drawString(text: String, x: Float, y: Float, color: Color4b)
    fun getStringWidth(text: String): Float
    fun getFontHeight(): Float
}

Best Practices

Minimize State Changes

// Good - same pipeline
renderEnvironmentForWorld(matrices) {
    for (box in boxes) {
        drawBox(box, Color4b.RED)  // Batched
    }
}

// Bad - pipeline switches
renderEnvironmentForWorld(matrices) {
    drawBox(box1, Color4b.RED)
    drawCircle(1.0f, Color4b.BLUE)  // Pipeline switch!
    drawBox(box2, Color4b.RED)  // Pipeline switch!
}

Use Color4b

Prefer Color4b over integer ARGB:
// Good - type-safe
val color = Color4b(255, 0, 0, 128)

// Bad - error-prone
val color = 0x80FF0000.toInt()

Camera-Relative Rendering

Always render relative to camera for large coordinates:
// Good - prevents floating point errors
withPositionRelativeToCamera(farAwayPos) {
    drawBox(FULL_BOX, Color4b.RED)
}

// Bad - precision issues at large coordinates
translate(farAwayPos)
drawBox(FULL_BOX, Color4b.RED)

Build docs developers (and LLMs) love