Алгоритм сглаживания ландшафта не работает ... он приводит значения к положительной и отрицательной бесконечности с несколькими прогонами - PullRequest
0 голосов
/ 11 июня 2019

Я делаю генератор мира в Java.Алгоритм работает, сначала устанавливая различные позиции с высокими точками (диапазонами) и низкими точками (траншеями), затем он усредняет все это, просматривая каждую плитку, получая высоту всех окружающих плиток, затем усредняя, ​​затем устанавливая все окружающие плиткик новому усредненному значению.Затем он переходит к следующему тайлу и делает то же самое, проходя через оси X и Y.Надежда состоит в том, что он создаст мир, который динамически генерируется позициями диапазонов и траншей.И, посмотрев на простую картину, он действительно достигает этого.

Однако проблемы начинаются с того, что алгоритм запускается более одного раза, что необходимо для сглаживания карты.Ценности начинают стремительно расти, и я не могу понять, почему это происходит.Повторный запуск показывает этот эффект - функция среднего значения должна сдвигать значения к 0, а не к бесконечности.

Хотя местность выглядит немного шатко, при запуске по простому алгоритму шероховатости она выглядит хорошо.(В настоящее время отключено). Я могу сделать шероховатость позже, мне нужно выяснить, почему алгоритм сглаживания не работает.

Я знаю о таких инструментах, как Perlin Noise и Simplex Noise, и это становится все более и более яснымчто мне, возможно, придется начать их использовать, но я хочу выяснить, в чем заключается моя проблема.

Итак, я отследил ошибку до того факта, что ячейки среднего значения перекрываются.Усредняющий тайл 0,0 включает в себя все окружающие тайлы, но, конечно, при перемещении к следующему тайлу, он примет новое значение от 0,0 и включит его в функцию.Но это то, что я хочу.Я хочу, чтобы среднее значение распространялось по всей карте.Я исключил ошибки переполнения и потери, и сама функция среднего значения работает нормально.Интересно, что умножение значений «times» (которое должно быть 9) на 1,1 немного исправляет ошибку, но делает ландшафт слишком плоским.Это, очевидно, просто навязывает проблему.

public static void smoothHeight(int num) { 
        for (int z = 0; z < 1; z++) { //Run through once. Edit to find optimal?
            for (int i = 0; i < toSmooth.size(); i++) { //toSmooth will contain HALF of all values on the map. It will have values on row 0, 2, 4, etc.
                Integer[] pos = toSmooth.get(i);
                int x = pos[0];
                int y = pos[1];
                Tile thisTile = FinalMap[x][y];
                double avgHeightForAll = thisTile.getHeight(); //avgHeightForAll is used to eventually smooth. THIS IS THE VALUE THAT SHOULD GET AVERAGED OUT
                double times = 0;
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) { //Go through tiles around it, and add to avg
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1); //smoothAlgFind ensures that tiles across the map are included. For example, if smoothing at position 0, it will include position 499 in the smoothing, not -1
                        Tile tileToSmooth = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tileToSmooth.isCoastal == true || num == 0) {
                            double smoothTileh = tileToSmooth.getHeight();
                            avgHeightForAll += smoothTileh;
                            times++;    
                        }
                    }
                }
                DecimalFormat df = new DecimalFormat("0.00");
                avgHeightForAll /= (times); //DONT MESS WITH 1.17, REMOVING IT BREAKS EVERYTHING. NO I DONT KNOW WHY. times = 9
                //thisTile.setHeight(avgHeightForAll, 0);
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                if (Math.abs(avgHeightForAll) > 40000) { //if something goes wrong this will trigger.
                    System.out.println((avgHeightForAll * times) + " / " + times + " = " + (avgHeightForAll));
                }
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) {
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tile = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tile.isCoastal == true  || num == 0) {//don't worry about isCoastal, has been disabed for now
                            tile.setHeight(avgHeightForAll, 0); 
                        }
                    }
                }
            }


            for (int i = toSmooth.size() - 1; i >= 0; i--) { //now we go the other way to prevent scerw-y looking generation skewed to one side
                Integer[] pos = toSmooth.get(i);
                int x = pos[0];
                int y = pos[1];
                Tile thisTile = FinalMap[x][y];
                double avgHeightForAll = thisTile.getHeight(); //avgHeightForAll is used to eventually smooth
                double times = 0;
                for (int x1 = x - 1 - p; x1 <= x + 1 + p; x1 += 1) {
                    for (int y1 = y - 1 - p; y1 <= y + 1 + p; y1 += 1) { //Go through tiles around it, and add to avg
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tileToSmooth = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tileToSmooth.isCoastal == true  || num == 0) { //don't worry about isCoastal, has been disabed for now
                            double smoothTileh = tileToSmooth.getHeight();
                            avgHeightForAll += smoothTileh;
                            times++;    
                        }
                    }
                }

                DecimalFormat df = new DecimalFormat("0.00");
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                avgHeightForAll /= (times); //DONT MESS WITH 1.17, REMOVING IT BREAKS EVERYTHING. NO I DONT KNOW WHY.
                //thisTile.setHeight(avgHeightForAll, 0);
                avgHeightForAll = Double.parseDouble(df.format(avgHeightForAll));
                //System.out.println("avgHeightForAll" + avgHeightForAll);
                if (Math.abs(avgHeightForAll) > 40000) { //if something goes wrong this triggers!
                    System.out.println((avgHeightForAll * times) + " / " + times + " = " + (avgHeightForAll));
                }
                for (int x1 = x - 1; x1 <= x + 1; x1 += 1) {
                    for (int y1 = y - 1; y1 <= y + 1; y1 += 1) {
                        Integer[] posToSmooth = smoothAlgFind(new Integer[] {x1, y1}, 1);
                        Tile tile = FinalMap[posToSmooth[0]][posToSmooth[1]];
                        if (tile.isCoastal == true  || num == 0) {
                            tile.setHeight(avgHeightForAll, 0); 
                        }
                    }
                }
            }
        }
        System.out.println("Smoothed");

    }

1 Ответ

0 голосов
/ 19 июня 2019

Ошибка, как вы указали, заключается в использовании предыдущего усредненного значения для уже рассчитанных тайлов. Это искажает среднее.

Идеальная операция сглаживания не изменит ни сумму, ни среднее значение данных.

Правильное решение - скопировать данные hightmap в собственный двумерный массив значений типа double:

double[][] heightmapData = new double[xmax][ymax];
for (int x = 0; x < xmax; x++)
   for (int y = 0; y < ymax; y++)
      heightmapData[x][y] = FinalMap[x][y].getHeight();

Затем вычислите сглаженные значения на основе среднего значения для себя и окружающих плиток, исключая края (любые ребра и фиксированные плитки, такие как isCostal, будут искажать итоги, но перекос будет локализован для окружающих плиток):

for (int x = 1; x < xmax - 1; x++)
   for (int y = 1; y < ymax - 1; y++)
      FinalMap[x][y].setHeight((
                    heightmapData[x - 1][y - 1] +
                    heightmapData[x][y - 1] +
                    heightmapData[x + 1][y - 1] +
                    heightmapData[x][y] +
                    heightmapData[x - 1][y + 1] +
                    heightmapData[x][y + 1] +
                    heightmapData[x + 1][y + 1]) / 9.0);

Используйте Tile.setHeight(), чтобы игнорировать фиксированные обновления плитки. Это облегчит изменение кода позже.

...