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 = 0x 01 ; // Has display list
static const int CHUNK_FLAG_DIRTY = 0x 02 ; // Needs rebuild
static const int CHUNK_FLAG_EMPTY0 = 0x 04 ; // Layer 0 empty
static const int CHUNK_FLAG_EMPTY1 = 0x 08 ; // Layer 1 empty
static const int CHUNK_FLAG_NOTSKYLIT = 0x 10 ; // No sky light
static const int CHUNK_FLAG_REF_MASK = 0x 07 ; // 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 ;
}
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.0 f );
float blockBright = x / 15.0 f ;
float skyBright = y / 15.0 f * 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] = 0x FF000000 | (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.0 f , 1 , 0 , 0 );
textures -> bindTexture (TN_TERRAIN_SUN);
renderCelestialBody ( 30.0 f ); // Sun
glRotatef ( 180 , 1 , 0 , 0 );
textures -> bindTexture (TN_TERRAIN_MOON_PHASES);
renderCelestialBody ( 20.0 f ); // 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);
}
}
}
}
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