我正在编写一个简单的基于Web的游戏,需要我创建一个随机的世界“区域”,该区域由几千个地形图块组成(可能在100x100和500x500之间)。在线上的大多数建议建议,我首先生成Perlin噪声并将其用作高度图,然后再生成另一个实例的湿度,温度的实例,依此类推,然后根据这些实例的组合分配地形值。
我宁愿不依赖安装任何其他语言或程序来执行此操作。但是,似乎没有任何内置函数可以直接使用CFML生成Perlin噪声图。最小的外部依赖关系最简单的方法是什么?
是否可以使用一些“ 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个像素的数据,而对该数据不做任何操作。如果我需要在现实世界中处理160k像素的图像(400x400),则大约需要3个小时来固定我的处理器。因此,假设我需要3张地图(用于海拔,湿度和温度),那是不切实际的。
答案 0 :(得分:0)
我一直无法找到一个我对效率完全满意的解决方案,但是我已经没有时间来解决这个问题并继续前进。将来我可能会回来进行优化,但就目前而言,我有一个可行的解决方案,尽管运行缓慢。
在https://stackoverflow.com/a/26629083/762721中,Mark Mark Setchell的出色答案是,我发现,令人惊讶的是,解决我的问题的最有效方法是使用Image Magic生成分形,使用IM将所有颜色信息写到文件,然后使用Lucee读取该文件并解析每一行以获取亮度信息。这是我正在使用的代码:
<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" />
<cfexecute name="#ImageMagick#\magick.exe"
variable="imgResult"
timeout="60"
arguments="convert #imgRoot#/temp/#fname#.png -depth 8 #imgRoot#/temp/test.txt" />
<cfset myfile = FileOpen("#imgRoot#/temp/test.txt", "read")>
<cfloop condition="NOT FileisEOF(myfile)">
<cfset thisLine = FileReadLine(myfile)>
<cfset x = listFirst(thisLine, ",")>
<cfset y = listGetAt(thisLine, 2, ",")>
<cfset y = listFirst(y, ":")>
<cfif isNumeric(x) and isNumeric(y)>
<cfset thisStart = FindNoCase("gray(", thisLine)>
<cfif thisStart is not 0>
<cfset thisVal = Mid(thisLine, thisStart+5, 99999)>
<cfset thisVal = listFirst(thisVal, ")")>
<cfset returnVal[x+1][y+1] = "#thisVal#">
</cfif>
</cfif>
</cfloop>
<cfset FileClose(myfile)>
我能够在7.1分钟内在25万像素的图像(500x500)上运行此图像,这使其比直接获取像素信息的速度快40倍。我认为优化和验证都有很多空间可以避免错误,一旦我将其收紧了一点,我会回来并更新此答案。
目前,使用它生成3 500x500图像,解析信息并将其写入数据库的过程仅需30分钟。哪个虽然次优,但很实用。