我正在编写一个简单的基于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>因此,到目前为止,我最好的方法是要求27s来提取400个像素的数据,而这并没有对这些数据做任何事情。如果我需要处理一个160 K像素图像(400x400)在现实世界的场景,这就可以计算出大约3个小时与我的处理器挂钩。所以假设我需要3张地图(海拔,湿度和温度),那是不实际的。
发布于 2018-12-14 15:20:13
就效率而言,我还没有找到一个我完全满意的解决方案,但是我已经没有时间去解决这个问题并继续前进了。我可能会在未来回来优化,但目前,我有一个解决方案,虽然缓慢的工作。
根据Mark在https://stackoverflow.com/a/26629083/762721的出色回答,我发现,令人惊讶的是,解决我的问题的最有效的方法是用图像魔术生成分形,使用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分钟内在250 K像素图像(500x500)上运行这个程序,这几乎比我直接获取像素信息的速度快了40倍。我认为优化和验证都有很大的空间来避免错误,一旦我收紧了它,我会回来更新这个答案。
现在,使用它生成3500x500图像,解析信息,并将其写入数据库,可以在30分钟内完成。这虽然不是最优的,但却是实用的。
https://stackoverflow.com/questions/53730471
复制相似问题