電腦仿真標準流程:實體對象 → 物理特性提煉 → 數學模型 → 仿真演算法 → 程式實現 → 視覺化輸出。本文以火焰為例,展示如何在有限 CPU 資源下,用近似模型 + 隨機擾動 + DirectDraw 硬體加速 達到逼真即時效果。
(P(x,y)+P(x,y-1)+P(x,y+1)+P(x-1,y)+P(x+1,y))/5。雖非嚴格拉普拉斯算子,但運算極快且視覺差異極小。c,模擬溫度下降。BYTE buffer1[W*H], buffer2[W*H];
while (TRUE) {
for (y=1; y<H-2; y++)
for (x=1; x<W-2; x++) {
n1=buffer1[x+1+y*W]; n2=buffer1[x-1+y*W];
n3=buffer1[x+(y+1)*W]; n4=buffer1[x+(y-1)*W];
p = (n1+n2+n3+n4+buffer1[x+y*W]) / 5; // 四鄰+自身平均
p = (p > c) ? p - c : 0; // 冷卻
buffer2[x + (y-1)*W] = p; // 向上捲動一列 (對流)
}
Copy buffer2 to screen; // 顯示
Copy buffer2 to buffer1; // 交換緩衝
}
固定衰減值導致火焰過於規則。預先生成 coolMap[W*H]:隨機灑點 → 高斯模糊 → 獲得空間變化的冷卻係數。仿真時 p = max(0, avg - coolMap[i]),並同步向上捲動 coolMap 以配合對流。
預先生成 seedMap (隨機分布的高亮點)。每幀以機率 rand() < RAND_MAX/5 在對應位置重設亮度 255,模擬火焰底部持續噴發。
BYTE *p = lightBuf2 + W*2, *pp = coolMap + coolW*2;
BYTE *p1 = lightBuf1 + W*2, *p2 = p1 - W, *p3 = p1 - 1, *p4 = p1 + 1, *p5 = p1 + W;
for (i=0; i<W*(H-4); i++) {
c1 = (*p1 + *p2 + *p3 + *p4 + *p5) / 5;
c2 = *pp;
*p = (c1 > c2) ? c1 - c2 : 0;
++p; ++pp; ++p1; ++p2; ++p3; ++p4; ++p5;
}
// 冷卻圖捲動
memcpy(lightBuf1, lightBuf1 + W*3, W*(H-3));
// 種子點注入 (略)
純 CPU 處理像素再 SetPixel 繪圖極慢。DirectDraw 讓我們直接操作顯存後台緩衝區,再 Flip() 換頁,實現零拷貝、垂直同步的流暢動畫。
DirectDrawCreate(NULL, &lpDD, NULL);
lpDD->SetCooperativeLevel(hwnd, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
lpDD->SetDisplayMode(640, 480, 16);
// 建立 Primary Surface + 1 個 Back Buffer (複雜表面)
DDSURFACEDESC ddsd = {sizeof(ddsd)};
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
ddsd.dwBackBufferCount = 1;
lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
lpDDSPrimary->GetAttachedSurface(&DDSCAPS_BACKBUFFER, &lpDDSBack);
// 1. 清除後台緩衝
lpDDSBack->Blt(NULL, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddbltfx);
// 2. 鎖定後台緩衝取得顯存指標
lpDDSBack->Lock(NULL, &ddsd, 0, NULL);
fire.render((WORD*)ddsd.lpSurface); // 直接寫入 16-bit 顯存
lpDDSBack->Unlock(NULL);
// 3. 換頁 (等待垂直回掃)
while (lpDDSPrimary->Flip(NULL, 0) == DDERR_WASSTILLDRAWING);
if (DDERR_SURFACELOST) lpDDSPrimary->Restore();