
經典的實時火焰效果基於「調色盤索引遞減」與「鄰域平均模糊」兩大支柱:
WORD palette[256];
ZeroMemory(palette, sizeof(palette));
for (int i = 0; i < 64; ++i) {
// 0-63: 黑 -> 深紅 (僅紅分量漸增)
palette[i] = (i/2) << 10; // R 分量在 bit 10-14
// 64-127: 深紅 -> 亮黃 (紅滿,綠漸增)
palette[i+64] = 0x7C00 | ((i/2) << 5 & 0x3E0); // G 分量在 bit 5-9
// 128-191: 亮黃 -> 亮白 (紅綠滿,藍漸增)
palette[i+128] = 0x7C00 | 0x3E0 | (i/2 & 0x1F); // B 分量在 bit 0-4
// 192-255: 全白
palette[i+191] = 0xFFFF;
}
註:0x7C00 = 紅滿 (31<<10),0x3E0 = 綠滿 (31<<5)。此調色盤可直接用於 16-bit 顯示模式;若為 24/32-bit 則需擴展為 RGBQUAD 陣列。
// 緩衝區:索引值,char 足夠 (0-255)
char fireBuf[320][220]; // 假設 320x200 可見區,底部留 2 行作燃料區
// 每幀:注入燃料 (底部第 2 行)
for (int x = 0; x < 320; ++x) {
int j = rand() * 320 / RAND_MAX;
fireBuf[218][j] = (rand() / (float)RAND_MAX > 0.5f) ? 255 : 0; // 隨機亮/暗顆粒
}
// 從上往下渲染 (y=0 為頂部)
for (int y = 0; y < 218; ++y) {
for (int x = 0; x < 320; ++x) {
// 簡單衰減:取正下方像素索引 -1
fireBuf[y][x] = (fireBuf[y+1][x] > 0) ? fireBuf[y+1][x] - 1 : 0;
}
}
// 可選:3x3 平均模糊
// for (y=1; y<217; ++y) for (x=1; x<319; ++x)
// fireBuf[y][x] = (fireBuf[y-1][x-1]+fireBuf[y-1][x]+fireBuf[y-1][x+1]
// +fireBuf[y][x-1]+fireBuf[y][x]+fireBuf[y][x+1]
// +fireBuf[y+1][x-1]+fireBuf[y+1][x]+fireBuf[y+1][x+1]) / 9;
// 映射到顯存 (伪代碼)
for (y=0; y<200; ++y) for (x=0; x<320; ++x)
screen[y][x] = palette[fireBuf[y][x]];
火焰是「從下往上」渲染(y 遞增),光牆則是「從左往右」或「從右往左」水平傳播。只需將雙重循環的遍歷順序改為水平方向:
// 光牆:從左往右 (x 遞增)
for (int x = 1; x < 319; ++x) {
for (int y = 0; y < 200; ++y) {
fireBuf[y][x] = (fireBuf[y][x-1] > 0) ? fireBuf[y][x-1] - 1 : 0;
}
}
// 燃料注入在最左列 (x=0)
for (int y = 0; y < 200; ++y) {
if (rand()/(float)RAND_MAX > 0.5f) fireBuf[y][0] = 255;
}
同理,垂直光牆(從上往下)只需將火焰循環的 y 方向反轉:for (int y = height-2; y > 0; --y)。
LightWall/
├── Palette.cpp/h // 調色盤生成、載入、保存
├── Buffer.cpp/h // 索引緩衝區分配、燃料注入、傳播、模糊
├── Renderer.cpp/h // 將索引緩衝區 Blit 到屏幕 (GDI/DirectDraw/OpenGL 紋理)
├── Main.cpp // 消息循環、定時器驅動幀更新
└── Resource/ // 預設調色盤圖片、圖示
本文旨在傳遞「索引動畫」的核心思想:將色彩運算與幾何演化分離。掌握此法,可輕鬆實作火焰、光牆、水波、電弧、魔法光環等諸多經典特效,並在現代引擎中作為 Shader 的靈感原型。