首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

VC++ 火焰動態仿真實戰:從物理建模到 DirectDraw 硬體加速渲染

     編輯:VC 教學

仿真流程總覽

電腦仿真標準流程:實體對象 → 物理特性提煉 → 數學模型 → 仿真演算法 → 程式實現 → 視覺化輸出。本文以火焰為例,展示如何在有限 CPU 資源下,用近似模型 + 隨機擾動 + DirectDraw 硬體加速 達到逼真即時效果。

一、簡單近似模型設計

物理觀察與簡化假設

  • 火源:二值位圖標記,高亮素 = 火源,低亮素 = 非火源。
  • 熱擴散:像素新值 = 自身與上下左右四鄰素平均值 (P(x,y)+P(x,y-1)+P(x,y+1)+P(x-1,y)+P(x+1,y))/5。雖非嚴格拉普拉斯算子,但運算極快且視覺差異極小。
  • 冷卻衰減:每幀固定減去常數 c,模擬溫度下降。
  • 對流上升:整個火焰緩衝區向上捲動 1~2 像素,模擬熱空氣上升。

偽代碼核心迴圈

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;  // 交換緩衝
}

二、改進模型:非均勻冷卻與種子點

1. 預計算冷卻貼圖

固定衰減值導致火焰過於規則。預先生成 coolMap[W*H]:隨機灑點 → 高斯模糊 → 獲得空間變化的冷卻係數。仿真時 p = max(0, avg - coolMap[i]),並同步向上捲動 coolMap 以配合對流。

2. 種子點機制維持火源活力

預先生成 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));
// 種子點注入 (略)

三、DirectDraw 硬體加速顯示

純 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();

實驗結果與結論

  • 在 Pentium III 級硬體上即可達 60+ FPS 全螢幕火焰。
  • 演算法複雜度 O(W×H) 與火焰幾何複雜度無關,易擴展至煙霧、水波、爆炸等流體現象。
  • 現代開發建議改用 Direct3D 11/12 Compute Shader 或 OpenGL 在 GPU 上並行執行相同細胞自動機邏輯,效能提升百倍以上。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved