Я пишу простую веб-игру, которая требует от меня создания случайных «зон» в верхнем мире, состоящих из нескольких тысяч плиток ландшафта (вероятно, между 100x100 и 500x500).Большинство онлайн-советов предлагают начать с создания шума Перлина и использовать его в качестве карты высот, затем другой экземпляр для влажности, другой для температуры и т. Д., А затем назначить значения рельефа на основе их комбинации.
Я бы предпочел не полагаться на установку каких-либо других языков или программ для этого.Тем не менее, похоже, что нет встроенной функции для генерации карты шума perlin с CFML напрямую.Какой самый простой способ сделать это с минимальными внешними зависимостями?
Есть ли какой-нибудь "perlinNoise" java-метод, который я могу использовать для создания массива, с которым я затем смогу работать в CFML?Существует ли исходный код cfscript / cfml или cfc, доступный онлайн для реализации функции perlin (я не знаю, смогу ли я сам перевести что-то с другого языка)?Или же самый простой способ - установить и использовать что-то вроде ImageMagick для генерации / чтения файла изображения через cfexecute?
Что я пробовал
Сначала я попытался преобразоватькод C ++, показанный в википедии.Это было бы легко, если бы я когда-либо работал с C ++ в своей жизни.К сожалению у меня нет.Я дошел до этого:
<cffunction name="lerp" access="public" output="no" returntype="numeric" description="Function to linearly interpolate between a0 and a1">
<cfargument name="a0" type="numeric" required="yes">
<cfargument name="a1" type="numeric" required="yes">
<cfargument name="weight" type="numeric" required="yes">
<cfset returnVal = (1.0 - weight) * a0 + weight * a1>
<cfreturn returnVal>
</cffunction>
<cffunction name="dotGridGradient" access="public" output="no" returntype="numeric" description="Computes the dot product of the distance and gradient vectors.">
<cfargument name="ix" type="numeric" required="yes">
<cfargument name="iy" type="numeric" required="yes">
<cfargument name="x" type="numeric" required="yes">
<cfargument name="y" type="numeric" required="yes">
<!--- Precomputed (or otherwise) gradient vectors at each grid node --->
<!--- <cfset test = Gradient[IYMAX][IXMAX][2]> --->
<!--- Compute the distance vector --->
<cfset dx = x - ix>
<cfset dy = y - iy>
<!--- Compute the dot-product --->
<cfset returnVal= (dx*Gradient[iy][ix][0] + dy*Gradient[iy][ix][1])>
<cfreturn returnVal>
</cffunction>
<cffunction name="perlin" access="public" output="no" returntype="numeric" description="Compute Perlin noise at coordinates x, y">
<cfargument name="x" type="numeric" required="yes">
<cfargument name="y" type="numeric" required="yes">
<!--- Determine grid cell coordinates --->
<cfset x1 = int(x) + 1>
<cfset y1 = int(y) + 1>
<!--- Determine interpolation weights --->
<!--- Could also use higher order polynomial/s-curve here --->
<cfset sx = x - x0>
<cfset sy = y - y0>
<!--- Interpolate between grid point gradients --->
float n0, n1, ix0, ix1, value;
<cfset n0 = dotGridGradient(x0, y0, x, y)>
<cfset n1 = dotGridGradient(x1, y0, x, y)>
<cfset ix0 = lerp(n0, n1, sx)>
<cfset n0 = dotGridGradient(x0, y1, x, y)>
<cfset n1 = dotGridGradient(x1, y1, x, y)>
<cfset ix1 = lerp(n0, n1, sx)>
<cfset returnVal= lerp(ix0, ix1, sy)>
<cfreturn returnVal>
</cffunction>
Однако на самом деле работает только функция lerp.Я понятия не имел, что означает «градиент».Я предполагаю, что это математическая функция, но я не уверен, как реализовать это здесь.Мои поиски в Google продолжают давать мне другой код, а также некоторые объяснения, которые не являются интуитивно понятными для меня.
В этот момент использование IM становилось все более привлекательным.Это кажется более мощным, и я просто избегал этого, поскольку выяснить это и иметь еще одну вещь для установки на каждом шаге сервера казалось более трудоемким, чем иметь что-то в коде.Поскольку кодовый подход оказался более сложным, чем я ожидал, я взял перерыв, чтобы попытаться сфокусироваться на IM.
Для этого я начал с создания засеянного плазмы или фрактального холста, который отлично работал.Затем я попробовал много разных способов извлечения информации для каждого пикселя с ограниченным успехом:
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert -size 500x500 -seed #seed# plasma:fractal -blur #blur# -shade 120x45 -auto-level #imgRoot#/temp/#fname#.png" />
<cfloop from="1" to="20" index="x">
<cfloop from="1" to="20" index="y">
<!--- <cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert '#imgRoot#/temp/#fname#.png[1x1+#x#+#y#]' #imgRoot#/temp/temp.png" /> --->
<!--- Works; takes 27s for 400 pixels. Will take hours for full size maps.
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="identify -verbose #imgRoot#/temp/#fname#.png[1x1+#x#+#y#]" />
<cfset imgResult = ListFirst(ListLast(imgResult, "gray("), "%")>
--->
<!--- Returns blank; probably because of u.r not being defined in a grayscale image?
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert #imgRoot#/temp/#fname#.png[1x1+#x#+#y#] -format ""%[fx:floor(255*u)]"" info" />
--->
<!--- Errors with some decode delegate error
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert #imgRoot#/temp/#fname#.png: -format '%[pixel:p{#x#,#y#}]' info" /> --->
<!--- Errors with some decode delegate error
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert #imgRoot#/temp/#fname#.png: -crop 1x1+#x#+#y# -depth 8 txt" />
--->
<!--- Returns the same value for every pixel
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert -verbose #imgRoot#/temp/#fname#.png[1x1+#x#+#y#] txt" />
--->
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="identify -verbose #imgRoot#/temp/#fname#.png[1x1+#x#+#y#]" />
<cfset imgResult = ListFirst(ListLast(imgResult, "gray("), "%")>
<cfset returnVal[x][y] = imgResult>
</cfloop>
</cfloop>
Так что мой лучший метод до сих пор - 27-й, чтобы получить данные для 400 пикселей, и это ничего не делать с этими данными.Если мне нужно обработать изображение с разрешением 160 тыс. Пикселей (400х400) в реальном сценарии, это сработает примерно за 3 часа разгрузки моего процессора.Итак, если мне нужно 3 карты (для высоты, влажности и температуры), это ... нецелесообразно.