当前位置:   article > 正文

二维火焰实现的算法_d3d 火焰

d3d 火焰

1. 创建火焰纹理

   D3DXCreateTexture(m_pd3dDevice, m_iTextureSize, m_iTextureSize, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &m_pTexture) ;

 

2. 创建火焰颜色调色板

   在渲染火焰之前 必须有一个颜色调色板来说明火焰值是如何映射到RGBA颜色的

   1. 可以把调色板直接嵌入到程序代码中

   2. 也可以使用调色板文件 这是一个1024字节的文件

   加载调色板的代码 read (hFile, &m_Palette[0], 256*4) ;

   保存调色板的代码 save (hFile, &m_Palette[0], 256*4) ;

   3. 还可以从图形文件中提取调色板

   LPDIRECT3DTEXTURE8 pTexture ;

   D3DXCreateTextureFromFileEx (m_pd3dDevice, strBMPfile, 0, 0, 1, 0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED,

   0, 0, 0, NULL, m_Palette, &pTexture) ;

   SAFE_RELEASE (pTexture) ;

   这个函数完成了许多工作 但是我们只关心的是把图像使用的调色板放到变量m_Palette中

 

3. 处理火焰

  现在可以进入帧循环渲染火焰了

   // 循环遍历所有的火焰值

   for (int y=0; y < m_iTextureSize; y++) {
     for (int x=0; x < m_iTextureSize-0; x++) {
      // 它们将立即存放这个火焰值到它的左边、右边、上边和下边的火焰值中
      unsigned char firevalue_left, firevalue_right, firevalue_bottom, firevalue_top;
      int finalfirevalue;

      // 必须考录围绕水平轴的绕接(而不是垂直轴)
      // 因此需要计算x/y +- 1, 并且先临时存储起来
      int xplus1, xminus1, yplus1, yminus1;
      xplus1 = x+1; if (xplus1 >= m_iTextureSize) xplus1=0;
      xminus1= x-1; if (xminus1 < 0) xminus1 = m_iTextureSize-1;
      yplus1 = y+1; if (yplus1 >= m_iTextureSize) yplus1=m_iTextureSize-1;
      yminus1= y-1; if (yminus1 < 0) yminus1 = 0;
     
      // 现在可以获取的相邻像素的火焰值
      firevalue_right = m_pActiveBuffer[(y*m_iTextureSize)+xplus1];
      firevalue_left  = m_pActiveBuffer[(y*m_iTextureSize)+xminus1];
      firevalue_bottom= m_pActiveBuffer[((yplus1)*m_iTextureSize)+x];
      firevalue_top   = m_pActiveBuffer[((yminus1)*m_iTextureSize)+x];

      // 现在是最重要的部分——计算新的火焰值
      finalfirevalue = (firevalue_left+firevalue_right+firevalue_top+firevalue_bottom)/4;

      // 减去一个特定的值来模拟火焰的“冷却”效果
      // 这里需要应用冷却贴图
      finalfirevalue -= m_iCoolAmount;

      // 保证减去冷却量后的结果不会小于0
      if (finalfirevalue < 0) finalfirevalue = 0;

      // 在中间结果暂存数组中存放火焰值, 位置在它初始行的上面一行
      // 这样来模拟火焰上升的效果
      m_pScratchBuffer[((yminus1)*m_iTextureSize)+x] = finalfirevalue;

   }

 

   为火焰添加数字燃料来让它持续燃烧

   // 处理整个2×1块

   for (int x=0; x < m_iTextureSize; x+=2) {
    // 只对最下面一行添加燃料
    int y=m_iTextureSize-1;
   
    // 确定这个特定点是需要增加燃料还是减少, 方法是加上一个(-31, 31)之间的数
    int fuel = m_pActiveBuffer[(y*m_iTextureSize)+x] + (rand() % 64) - 32;

    // 结果必须在0~255之间
    if (fuel > 255) fuel = 255;
    if (fuel < 0) fuel = 0;

    // 把新燃料值用于相邻的两个像素
    // 这有助于减少火焰可能出现的“抖动”
    m_pScratchBuffer[(y*m_iTextureSize)+x] = (unsigned char)fuel;
    m_pScratchBuffer[(y*m_iTextureSize)+x+1] = (unsigned char)fuel;
  }

 

4. 在纹理上添加火焰

   1. 调用IDirect3DTexture8接口的LockRect方法锁定纹理并返回两个关键信息

   2. 使用从LockRect中获取的指针来操作纹理元素

   3. 操作完成后解锁纹理

  HRESULT hr;
  // 锁定纹理
  D3DLOCKED_RECT lockedrect;
  ::ZeroMemory(&lockedrect, sizeof(lockedrect));
 
  if (FAILED(hr = m_pTexture->LockRect(0, &lockedrect, NULL, 0))) return(false);
 
  // 现在纹理表面已经被锁定, 我们可以使用间距来遍历纹理表面
  unsigned char *pSurfBits = static_cast<unsigned char *>(lockedrect.pBits);
 
  int index=0;
 
  for (int y=0; y < m_iTextureSize; y++) {
    for (int x=0; x < m_iTextureSize; x++) {
      // 这个位置的火焰值确定了纹理元素的颜色
      pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peBlue; // blue
      pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peGreen; // green
      pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peRed; // red
      pSurfBits[index++] = m_Palette[m_pActiveBuffer[(y*m_iTextureSize)+x]].peFlags; // alpha
    }
    // 下一行
    index += lockedrect.Pitch - (m_iTextureSize*4);
  }

  // 解锁纹理表面
  if (FAILED(hr = m_pTexture->UnlockRect(0))) return(false);

 

5. 用火焰纹理填充视口

   要使用正交矩阵

   D3DXMATRIX matProject;
   D3DXMatrixOrthoLH (&matProject, 640.0f, 480.0f, 1.0f, 1000.0f) ;
   Device->SetTransform (D3DTS_PROJECTION, &matProject) ;

 

 

 

 

摘自 《Directx特效游戏程序设计》

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/IT小白/article/detail/883838
推荐阅读
相关标签
  

闽ICP备14008679号