2

我正在使用粒子沉积来尝试在程序上创建一些类似火山的山脉,但我从中得到的只是金字塔状的结构。是否有人熟悉该算法,可能能够阐明我可能做错了什么。我现在把每个粒子都放在同一个地方。如果我不这样做,它们就会散布在非常薄的一层而不是任何类型的山上。

void TerrainClass::ParticalDeposition(int loops){
float height = 0.0;




//for(int k= 0; k <10; k++){

    int dropX = mCurrentX = rand()%(m_terrainWidth-80) + 40;
    int dropY = mCurrentZ = rand()%(m_terrainHeight-80) + 40;
    int radius = 15;
    float angle = 0;
    int tempthing = 0;
    loops = 360;

    for(int i = 0; i < loops; i++){



        mCurrentX = dropX + radius * cos(angle);
        mCurrentZ = dropY + radius * sin(angle);

        /*f(i%loops/5 == 0){
            dropX -= radius * cos(angle);
            dropY += radius * sin(angle);
            angle+= 0.005;
            mCurrentX = dropX;
            mCurrentZ = dropY;
        }*/

        angle += 360/loops;


        //dropX += rand()%5;
        //dropY += rand()%5;

        //for(int j = 0; j < loops; j++){



            float newY = 0;

            newY = (1 - (2.0f/loops)*i); 

            if(newY < 0.0f){
                newY = 0.0f;
            }



            DepositParticle(newY);
        //}
    }
//}
}

void TerrainClass::DepositParticle(float heightIncrease){

bool posFound = false;

m_lowerList.clear();

while(posFound == false){
    int offset = 10;
    int jitter;

    if(Stable(0.5f)){
        m_heightMap[(m_terrainHeight*mCurrentZ)+mCurrentX].y += heightIncrease;
        posFound = true;
    }else{
        if(!m_lowerList.empty()){

            int element = rand()%m_lowerList.size();

            int lowerIndex = m_lowerList.at(element);

            MoveTo(lowerIndex);

        }
    }
} 
}

bool TerrainClass::Stable(float deltaHeight){

int index[9];
float height[9];

index[0] = ((m_terrainHeight*mCurrentZ)+mCurrentX);                                                                     //the current index
index[1] = ValidIndex((m_terrainHeight*mCurrentZ)+mCurrentX+1)     ? (m_terrainHeight*mCurrentZ)+mCurrentX+1    : -1;   // if the index to the right is valid index set index[] to index else set index[] to -1
index[2] = ValidIndex((m_terrainHeight*mCurrentZ)+mCurrentX-1)     ? (m_terrainHeight*mCurrentZ)+mCurrentX-1    : -1;   //to the left
index[3] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX)   ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX  : -1;   // above
index[4] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX)   ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX  : -1;   // bellow
index[5] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX+1) ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX+1: -1;   // above to the right
index[6] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX+1) ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX+1: -1;   // below to the right
index[7] = ValidIndex((m_terrainHeight*(mCurrentZ+1))+mCurrentX-1) ? (m_terrainHeight*(mCurrentZ+1))+mCurrentX-1: -1;   // above to the left
index[8] = ValidIndex((m_terrainHeight*(mCurrentZ-1))+mCurrentX-1) ? (m_terrainHeight*(mCurrentZ-1))+mCurrentX-1: -1;   // above to the right

for ( int i = 0; i < 9; i++){
    height[i] = (index[i] != -1) ? m_heightMap[index[i]].y : -1;
}

m_lowerList.clear();

for(int i = 1; i < 9; i++){
    if(height[i] != -1){
        if(height[i] < height[0] - deltaHeight){
            m_lowerList.push_back(index[i]);
        }
    }
}

return m_lowerList.empty();
}

bool TerrainClass::ValidIndex(int index){
return (index > 0 && index < m_terrainWidth*m_terrainHeight) ?  true : false;
}

void TerrainClass::MoveTo(int index){
mCurrentX = index%m_terrainWidth;
mCurrentZ = index/m_terrainHeight;
}

这就是所有使用的代码。

4

2 回答 2

8

你应该看看这两篇论文:

GPU 上的快速水力侵蚀模拟和可视化

GPU 上的快速水力和热力侵蚀(先阅读第一个,第二个在其上展开)

不要被“在 GPU 上”吓到,算法在 CPU 上运行得很好(虽然速度较慢)。算法本身并不进行粒子沉降(但你也不;)) - 它们反而将粒子聚合成几层矢量场。

关于这个算法的一个重要的事情是它会侵蚀已经存在的高度图——例如用perlin 噪声生成的高度图。如果初始高度场完全平坦(或者即使高度变化不足),它也会失败。

我自己实现了这个算法并且大部分都取得了成功(还有更多的工作要做,算法很难平衡以给出普遍的好结果) - 见下图。

请注意,第二篇论文中的热风化组件的柏林噪声可能对您来说已经足够了(并且可能会为您省去很多麻烦)。

您还可以在我的项目中找到该算法的基于 C++ CPU 的实现(特别是这个文件,注意 GPL 许可证!)及其在我论文第 24-29 页的简化描述。

侵蚀示例

于 2013-05-16T17:52:25.607 回答
1

如果您希望粒子不扩散到单层中,您的粒子将需要在其物理模型中具有一些表面摩擦和/或粘性(或类似)。这是在更新粒子模拟时在代码的碰撞检测和碰撞响应部分执行的。

一种简单的方法是使粒子粘在一起(相互吸引)。粒子也需要有一个大小,这样它们就不会简单地收敛到完美重叠。如果你想让它们相互吸引,那么你需要测试粒子之间的距离。您可能会从查看一些使用粒子的 DirectX SDK 示例中受益,特别是(双关语!)在 NVidia GPU 计算 SDK 中有一个很棒的演示(Simon Green?),它在 CUDA 中实现了粘性粒子。它包括一份描述他们所做工作的自述文件。如果您不打算进行大量粒子计数,您可以看到粒子如何相互作用并忽略所有 CUDA/GPU 的东西。

另请注意,一旦您使用粒子间力,那么您将检查大约 0.5*n^2 个粒子组合(对)......因此您可能需要使用简单的空间分区方案或类似的方法来将力限制在附近只有粒子组。祝你好运!

于 2013-05-16T03:10:46.290 回答