Я использую осаждение частиц, чтобы попытаться создать какие-то вулканически похожие горы, но все, что я получаю от этого, — это пирамидоподобные структуры. Кто-нибудь знаком с алгоритмом, который может пролить свет на то, что я могу делать неправильно? Я сбрасываю каждую частицу в одно и то же место в данный момент. Если я не сделаю этого, они рассыпаются в очень тонкий слой, а не в какую-либо гору.
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;
}
Вот и весь использованный код.
Вы должны взглянуть на эти две статьи:
Быстрое моделирование и визуализация гидравлической эрозии на графическом процессоре
Быстрая гидравлическая и термическая эрозия на графическом процессоре (читайте первый первый, второй расширяет его)
Не пугайтесь «на GPU», алгоритмы прекрасно работают на CPU (хотя и медленнее). Алгоритмы не выполняют осаждение частиц как таковое (но вы тоже этого не делаете;)), вместо этого они объединяют частицы в несколько слоев векторных полей.
Одна важная особенность этого алгоритма состоит в том, что он разрушает уже существующие карты высот — например, сгенерированные с помощью перлин шум. Это с треском проваливается, если начальное поле высоты полностью плоское (или даже если оно имеет недостаточное изменение высоты).
Я сам реализовал этот алгоритм и в основном добился успеха (все еще предстоит проделать большую работу, алгоритмы очень трудно сбалансировать, чтобы дать универсально отличные результаты) — см. Изображение ниже.
Обратите внимание, что перлин шум с компонентом Thermal Weathering из второй статьи может быть достаточно для вас (и это может сэкономить вам массу хлопот).
Вы также можете найти реализацию этого алгоритма на основе процессора C ++ в мой проект (в частности, этот файл, обратите внимание на лицензию GPL!) и ее упрощенное описание на страницах 24-29 моего Тезис.
Ваши частицы должны будут иметь некоторое поверхностное трение и / или липкость (или подобное) в своей физической модели, если вы хотите, чтобы они не растекались в один слой. Это выполняется в частях кода при обнаружении столкновений и реагировании на них при обновлении симуляции частиц.
Простой подход — заставить частицы прилипать (притягивать друг друга). Частицы также должны иметь размер, чтобы они не просто сходились к идеально перекрывающимся. Если вы хотите, чтобы они притягивали друг друга, то вам нужно проверить расстояние между частицами.
Вы могли бы получить пользу от просмотра некоторых примеров DirectX SDK, в которых используются частицы, и, в частности (pun arf!), Есть отличная демонстрация (от Simon Green?) В NVidia GPU Computing SDK, которая реализует липкие частицы в CUDA. Он включает в себя документ ReadMe, описывающий, что они сделали. Вы можете увидеть, как частицы взаимодействуют, и игнорировать все, что связано с CUDA / GPU, если вы не собираетесь проводить массовые подсчеты частиц.
Также обратите внимание, что как только вы используете межчастичные силы, вы проверяете примерно 0,5 * n ^ 2 комбинаций (пар) частиц … поэтому вам может потребоваться использовать простую схему пространственного разделения или аналогичную, чтобы ограничить силы рядом только группы частиц.
Удачи!