在Lucee中最简单的产生Perlin噪声的方法是什么?

时间:2018-12-11 18:43:30

标签: coldfusion cfml perlin-noise lucee

我正在编写一个简单的基于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张地图(用于海拔,湿度和温度),那是不切实际的。

1 个答案:

答案 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分钟。哪个虽然次优,但很实用。