Skip to main content
Minecraft Community Edition features a highly optimized rendering pipeline designed for console hardware, with platform-specific optimizations for PlayStation 3 SPUs, Xbox rendering, and modern platforms.

Rendering Pipeline Overview

┌────────────────────────────────────────────────┐
│           GameRenderer::render()               │
│  ┌──────────────────────────────────────────┐  │
│  │ 1. setupCamera() - View/Projection       │  │
│  │ 2. Clear screen, setup fog               │  │
│  │ 3. LevelRenderer::render() - World       │  │
│  │ 4. EntityRenderer - Entities             │  │
│  │ 5. renderSnowAndRain() - Weather         │  │
│  │ 6. renderItemInHand() - First person     │  │
│  │ 7. Gui::render() - HUD/GUI               │  │
│  └──────────────────────────────────────────┘  │
└────────────────────────────────────────────────┘

Core Rendering Classes

GameRenderer

The main renderer that orchestrates the entire rendering pipeline (GameRenderer.h:15):
class GameRenderer
{
private:
    Minecraft *mc;
    float renderDistance;
    ItemInHandRenderer *itemInHandRenderer;
    
    // Camera control
    SmoothFloat smoothTurnX;
    SmoothFloat smoothTurnY;
    SmoothFloat smoothDistance;  // Third-person distance
    
    // Lighting
    static const int NUM_LIGHT_TEXTURES = 4;
    int lightTexture[NUM_LIGHT_TEXTURES];
    intArray lightPixels[NUM_LIGHT_TEXTURES];
    
    // FOV per player (split-screen)
    float fov[4];
    float oFov[4];
    
public:
    GameRenderer(Minecraft *mc);
    
    void tick(bool bFirst);
    void render(float a, bool bFirst);
    void setupCamera(float a, int eye);
    void updateLightTexture(float a);
};

LevelRenderer

Manages world rendering including chunks, entities, and tile entities (LevelRenderer.h:35):
class LevelRenderer : public LevelListener
{
public:
    static const int CHUNK_SIZE = 16;
    static const int CHUNK_Y_COUNT = Level::maxBuildHeight / CHUNK_SIZE;
    
    // Platform-specific command buffer limits
#if defined _XBOX_ONE
    static const int MAX_COMMANDBUFFER_ALLOCATIONS = 512 * 1024 * 1024;
#elif defined __ORBIS__  // PS4
    static const int MAX_COMMANDBUFFER_ALLOCATIONS = 448 * 1024 * 1024;
#elif defined __PS3__
    static const int MAX_COMMANDBUFFER_ALLOCATIONS = 110 * 1024 * 1024;
#endif
    
private:
    // Per-player data (split-screen)
    MultiPlayerLevel *level[4];
    ClipChunkArray chunks[4];
    TileRenderer *tileRenderer[4];
    double xOld[4], yOld[4], zOld[4];
    
    Minecraft *mc;
    Textures *textures;
    
    // Chunk rendering
    int xChunks, yChunks, zChunks;
    OffsettedRenderList renderLists[4];
    
    // Entity tracking
    int totalEntities, renderedEntities, culledEntities;
    
public:
    int render(shared_ptr<Mob> player, int layer, double alpha, bool updateChunks);
    void renderEntities(Vec3 *cam, Culler *culler, float a);
    void tick();
    bool updateDirtyChunks();
};
LevelRenderer maintains separate state for each split-screen player, allowing up to 4 simultaneous viewports.

Chunk Rendering System

Chunk Structure

The world is divided into 16×16×16 render chunks:
class Chunk
{
    int x, y, z;              // Chunk position
    bool dirty;               // Needs rebuild
    bool isEmpty[2];          // Empty layers
    bool compiled;            // Has display lists
    int refCount;             // Split-screen references
    
    // Display lists for rendering
    OffsettedRenderList *lists;
    
    void rebuild(TileRenderer *renderer);
    void render(int layer);
};

Chunk Flags

Global flags track chunk state across all viewports:
// LevelRenderer.h:244
static const int CHUNK_FLAG_COMPILED    = 0x01;  // Has display list
static const int CHUNK_FLAG_DIRTY       = 0x02;  // Needs rebuild
static const int CHUNK_FLAG_EMPTY0      = 0x04;  // Layer 0 empty
static const int CHUNK_FLAG_EMPTY1      = 0x08;  // Layer 1 empty
static const int CHUNK_FLAG_NOTSKYLIT   = 0x10;  // No sky light
static const int CHUNK_FLAG_REF_MASK    = 0x07;  // Reference count
static const int CHUNK_FLAG_REF_SHIFT   = 5;

unsigned char *globalChunkFlags;  // One byte per chunk

Chunk Rebuild

Chunks are rebuilt on background threads:
#ifdef _LARGE_WORLDS
static const int MAX_CONCURRENT_CHUNK_REBUILDS = 4;
static const int MAX_CHUNK_REBUILD_THREADS = 3;
static Chunk permaChunk[MAX_CONCURRENT_CHUNK_REBUILDS];
static C4JThread *rebuildThreads[MAX_CHUNK_REBUILD_THREADS];

int LevelRenderer::rebuildChunkThreadProc(LPVOID lpParam) {
    while (true) {
        // Wait for chunk to rebuild
        WaitForSingleObject(s_activationEventA[threadIdx], INFINITE);
        
        // Rebuild chunk geometry
        Chunk *chunk = &permaChunk[threadIdx];
        chunk->rebuild(tileRenderer);
        
        // Signal completion
        SetEvent(s_rebuildCompleteEvents->events[threadIdx]);
    }
}
#endif

Dirty Chunk Management

XLockFreeStack<int> dirtyChunksLockFreeStack;
bool dirtyChunkPresent;

void LevelRenderer::setDirty(int x0, int y0, int z0, int x1, int y1, int z1, Level *level) {
    // Mark all chunks in region as dirty
    for (int cx = x0 >> 4; cx <= x1 >> 4; cx++) {
        for (int cy = y0 >> 4; cy <= y1 >> 4; cy++) {
            for (int cz = z0 >> 4; cz <= z1 >> 4; cz++) {
                int idx = getGlobalIndexForChunk(cx, cy, cz, level);
                setGlobalChunkFlag(idx, CHUNK_FLAG_DIRTY);
                dirtyChunksLockFreeStack.push(idx);
            }
        }
    }
    dirtyChunkPresent = true;
}

bool LevelRenderer::updateDirtyChunks() {
    int rebuiltCount = 0;
    const int MAX_REBUILDS_PER_FRAME = 2;
    
    while (rebuiltCount < MAX_REBUILDS_PER_FRAME) {
        int idx;
        if (!dirtyChunksLockFreeStack.pop(idx)) {
            break;  // No more dirty chunks
        }
        
        // Get chunk and rebuild
        Chunk *chunk = getChunkByGlobalIndex(idx);
        if (chunk && chunk->dirty) {
            chunk->rebuild(tileRenderer);
            rebuiltCount++;
        }
    }
    
    return rebuiltCount > 0;
}

Platform-Specific Rendering

PlayStation 3 SPU Tasks

PS3 uses Synergistic Processing Units (SPUs) for parallel rendering tasks:
#ifdef __PS3__
C4JSpursJobQueue::Port* m_jobPort_CullSPU;
C4JSpursJobQueue::Port* m_jobPort_FindNearestChunk;
bool m_bSPUCullStarted[4];

void LevelRenderer::cull_SPU(int playerIndex, Culler *culler, float a) {
    // Prepare SPU job data
    CullJobData jobData;
    jobData.chunks = chunks[playerIndex];
    jobData.culler = culler;
    jobData.playerPos = level[playerIndex]->player->getPos();
    
    // Submit to SPU
    C4JSpursJobQueue::submitJob(m_jobPort_CullSPU, &jobData, sizeof(jobData));
    m_bSPUCullStarted[playerIndex] = true;
}

void LevelRenderer::waitForCull_SPU() {
    // Wait for all SPU jobs to complete
    for (int i = 0; i < 4; i++) {
        if (m_bSPUCullStarted[i]) {
            C4JSpursJobQueue::waitForJob(m_jobPort_CullSPU);
            m_bSPUCullStarted[i] = false;
        }
    }
}
#endif
PS3 SPU tasks include:
  • LevelRenderer_cull: Frustum culling
  • LevelRenderer_zSort: Transparent sorting
  • LevelRenderer_FindNearestChunk: Nearest chunk selection
  • GameRenderer_updateLightTexture: Light map generation
  • ChunkUpdate/TileRenderer_SPU: Chunk mesh generation
SPU tasks run on the PS3’s Cell architecture, offloading work from the main PPU thread. Each SPU has 256KB of local memory.

Rendering Layers

Geometry is rendered in multiple passes:
enum RenderLayer {
    LAYER_OPAQUE = 0,      // Solid blocks
    LAYER_ALPHA_TEST = 1,  // Cutout textures (leaves, grass)
    LAYER_TRANSLUCENT = 2  // Water, ice, stained glass
};

int LevelRenderer::render(shared_ptr<Mob> player, int layer, double alpha, bool updateChunks) {
    if (updateChunks) {
        updateDirtyChunks();
    }
    
    // Cull chunks to view frustum
    cull(culler, alpha);
    
    // Render visible chunks for this layer
    return renderChunks(0, chunks.size(), layer, alpha);
}

Texture Management

The Textures class (Textures.h:201) manages all texture loading and binding:
class Textures
{
public:
    static bool MIPMAP;
    static C4JRender::eTextureFormat TEXTURE_FORMAT;
    
private:
    unordered_map<wstring, int> idMap;
    unordered_map<int, BufferedImage*> loadedImages;
    
    // Dynamic textures
    unordered_map<wstring, HttpTexture*> httpTextures;  // Downloaded skins
    unordered_map<wstring, MemTexture*> memTextures;    // Runtime textures
    
    // Texture atlases
    PreStitchedTextureMap *terrain;
    PreStitchedTextureMap *items;
    
    TexturePackRepository *skins;
    Options *options;
    
public:
    void bindTexture(const wstring &resourceName);
    void bindTexture(int resourceId);
    
    int loadTexture(TEXTURE_NAME texId, const wstring& resourceName);
    void replaceTexture(intArray rawPixels, int w, int h, int id);
    
    void tick(bool updateTextures, bool tickDynamics);
    void reloadAll();
};

Texture Atlas

Blocks and items are stitched into texture atlases:
class PreStitchedTextureMap
{
    vector<Icon*> icons;  // Individual texture regions
    int atlasWidth, atlasHeight;
    int textureId;
    
public:
    Icon* registerIcon(const wstring& name);
    void stitch();  // Combine all icons into atlas
};

class Icon
{
public:
    float u0, v0;  // Top-left UV
    float u1, v1;  // Bottom-right UV
    int width, height;
};

Texture Formats

Platform-specific texture compression:
namespace C4JRender {
    enum eTextureFormat {
        TEXTURE_FORMAT_RxGyBzAw,    // Standard RGBA
        TEXTURE_FORMAT_DXT1,         // BC1 compression (no alpha)
        TEXTURE_FORMAT_DXT5,         // BC3 compression (with alpha)
        TEXTURE_FORMAT_BC7           // Modern compression (Xbox One/PS4)
    };
}

void Textures::setTextureFormat(const wstring& resourceName) {
#if defined _XBOX_ONE || defined __ORBIS__
    TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_BC7;
#elif defined __PS3__ || defined _XBOX
    TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_DXT5;
#else
    TEXTURE_FORMAT = C4JRender::TEXTURE_FORMAT_RxGyBzAw;
#endif
}

Lighting System

Light Texture

Dynamic lighting uses a generated light texture:
void GameRenderer::updateLightTexture(float a) {
    Level *level = mc->level;
    
    for (int y = 0; y < 16; y++) {        // Sky light
        for (int x = 0; x < 16; x++) {    // Block light
            // Calculate lighting value
            float skyBrightness = level->getSkyBrightness(1.0f);
            float blockBright = x / 15.0f;
            float skyBright = y / 15.0f * skyBrightness;
            
            // Apply night vision, underwater tinting, etc.
            float brightness = max(blockBright, skyBright);
            
            // Convert to RGB
            int r = (int)(brightness * 255);
            int g = (int)(brightness * 255);
            int b = (int)(brightness * 255);
            
            lightPixels[0][y * 16 + x] = 0xFF000000 | (r << 16) | (g << 8) | b;
        }
    }
    
    // Upload to GPU
    textures->replaceTexture(lightPixels[0], 16, 16, lightTexture[0]);
}
The light texture is a 16×16 lookup table indexed by (blockLight, skyLight) to get the final RGB color.

Smooth Lighting

Ambient occlusion for smooth lighting:
static bool useAmbientOcclusion() {
    return Minecraft::getInstance()->options->ambientOcclusion;
}

// In TileRenderer:
float getLightColor(Level *level, int x, int y, int z) {
    if (!useAmbientOcclusion()) {
        return level->getBrightness(x, y, z);
    }
    
    // Sample neighboring blocks
    float total = 0;
    int count = 0;
    
    for (int dx = -1; dx <= 1; dx++) {
        for (int dy = -1; dy <= 1; dy++) {
            for (int dz = -1; dz <= 1; dz++) {
                total += level->getBrightness(x+dx, y+dy, z+dz);
                count++;
            }
        }
    }
    
    return total / count;
}

Entity Rendering

Entities use the EntityRenderer hierarchy:
class EntityRenderer
{
public:
    virtual void render(shared_ptr<Entity> entity, double x, double y, double z,
                       float yRot, float partialTick) = 0;
};

class MobRenderer : public EntityRenderer
{
    HumanoidModel *model;
    
    virtual void render(shared_ptr<Entity> entity, ...) {
        Mob *mob = (Mob*)entity.get();
        
        // Setup transforms
        glPushMatrix();
        glTranslatef(x, y, z);
        glRotatef(yRot, 0, 1, 0);
        
        // Bind texture
        bindTexture(mob->getTexture());
        
        // Render model
        model->render(mob, partialTick);
        
        glPopMatrix();
    }
};

Sky and Weather Rendering

Sky Dome

void LevelRenderer::renderSky(float alpha) {
    glDisable(GL_TEXTURE_2D);
    
    // Sky color based on time of day
    Vec3 skyColor = level->getSkyColor(alpha);
    glColor3f(skyColor.x, skyColor.y, skyColor.z);
    
    // Render sky hemisphere
    glCallList(skyList);
    
    // Sun and moon
    glEnable(GL_TEXTURE_2D);
    float celestialAngle = level->getSunAngle(alpha);
    
    glPushMatrix();
    glRotatef(celestialAngle * 360.0f, 1, 0, 0);
    
    textures->bindTexture(TN_TERRAIN_SUN);
    renderCelestialBody(30.0f);  // Sun
    
    glRotatef(180, 1, 0, 0);
    textures->bindTexture(TN_TERRAIN_MOON_PHASES);
    renderCelestialBody(20.0f);  // Moon
    
    glPopMatrix();
}

Weather Effects

void GameRenderer::renderSnowAndRain(float a) {
    Level *level = mc->level;
    if (level->getRainLevel(a) <= 0) return;
    
    // Particle positions around player
    Vec3 playerPos = mc->player->getPos(a);
    
    for (int i = 0; i < 1000; i++) {
        int x = (int)(playerPos.x + rainXa[i] * 10);
        int z = (int)(playerPos.z + rainZa[i] * 10);
        int y = level->getHeightValue(x, z);
        
        Biome *biome = level->getBiome(x, z);
        
        if (biome->getRainfall() > 0) {
            // Render rain or snow particle
            if (biome->getSnowAccumulation(x, y, z)) {
                renderSnowParticle(x, y, z, a);
            } else {
                renderRainParticle(x, y, z, a);
            }
        }
    }
}

Performance Optimizations

Only render chunks visible in the camera frustum:
void LevelRenderer::cull(Culler *culler, float a) {
    for (Chunk *chunk : chunks) {
        if (culler->isVisible(chunk->getBoundingBox())) {
            chunk->visible = true;
        } else {
            chunk->visible = false;
            occludedChunks++;
        }
    }
}
Skip rendering chunks completely hidden by opaque geometry.
Reduce geometry complexity for distant chunks on some platforms.
Pre-compiled chunk geometry for efficient rendering:
class OffsettedRenderList {
    GLuint displayList;
    
    void render() {
        glCallList(displayList);
    }
};

Best Practices

Minimize State Changes

Batch rendering by texture and render state to reduce GPU overhead.

Use Chunk Flags

Check CHUNK_FLAG_EMPTY before rendering to skip air chunks.

Limit Chunk Rebuilds

Rebuild max 2 chunks per frame to maintain 60 FPS.

Offload to Threads

Use background threads (or SPUs on PS3) for chunk building.

Overview

High-level architecture and components

Client-Server

Client-server separation and management

Build docs developers (and LLMs) love