-- f: Noise function (2-dimensional)
-- x,y: Coordinate
-- w,h: Dimensions of output image/texture
function noiseTileBlend(f, x, y, w, h)
return (f:get(x+w,y+h)*(w-x)*(h-y) +
f:get(x,y+h)*(x)*(h-y) +
f:get(x,y)*x*y +
f:get(x+w,y)*(w-x)*y)/(w*h)
end
-- Function to map a noise function to a buffer
-- f: Noise function (2-dimensional)
-- buf: 2D array data type
function mapFunctionSeamless(f, buf)
local mw,mh=buf:getWidth(), buf:getHeight()
local x,y
for x=0,mw- 1, 1 do
for y=0,mh- 1, 1 do
buf:set(x,y,noiseTileBlend(f,x,y,mw,mh))
end
end
end
-- f: Noise function (4-dimensional)
-- x,y: Coordinate
-- w,h: Array dimensions
function noiseTileCircular(f,x,y,w,h)
-- Calculate point on circle on X/Y plane corresponding
to s, the distance along the image X axis
local nx=cos(s)
local ny=sin(s)
-- Calculate point on circle on Z/W plane corresponding
to t, the distance along the image Y axis
local nz=cos(t)
local nw=sin(t)
are the most averaged, while samples toward the
edges, and especially the corners, are the least
averaged. This may have serious side effects,
especially for textures that will be tiled many
times over, resulting in highly visible repeating
patterns. The alteration to the fundamental
characteristics of the function is another highly
undesirable side effect of this method, and it may
render it unsuitable for many applications.
Multi-dimensional Function
Mapping
¶ The final technique is an algorithm that
maps a N*2-dimensional noise function to a
N-dimensional buffer in such a manner that
the seamless tiling effect occurs as a result of
a mathematical domain transformation, rather
than a blend of samples. The easiest way to
understand this is to start with the idea of using
a two-dimensional noise function to obtain a
looping one-dimensional buffer of noise. We
could use any of the blending methods above,
altered for one-dimensional functions, in order
to blend multiple samples and create the tiling
noise. Or we could imagine starting with a
two-dimensional function and tracing a circle
through it at some location, as seen in Figure 3.
We start at Point A and evaluate points
spaced evenly around the circumference, one
point per location in the one-dimensional buffer.
By the time we near the end of the buffer, we
are drawing near to Point A once more. Thus,
the final buffer will tile seamlessly with itself,
without any blending or interpolation being
performed. We can use the parametric form of
a circle with unit radius in order to perform the
domain transformation as below.
x=cos(t)
y=sin(t)
return f:get(nx,ny,nz,nw)
end
Each region is the same size as the final
output image. A point is sampled within each
region: all points correspond relative to one
another, and a final mapping is performed by
interpolating or blending these values. The
interpolating factors are calculated based on the
location of the given point relative to the region's
boundaries such that, when the location being
sampled is nearer the left edge of the image,
the point samples from the right-hand regions
gain greater weight; however as the sampling
draws near the right edge, the point samples
from the left-hand regions gain greater weight.
Correspondingly, when a point is near the top
edge, it gains greater contribution from the
bottom point samples, and as it nears the
bottom edge it gains greater contributions from
the top point samples. When the final blends are
performed, the final output will seamlessly tile
due to the way the interpolation is performed.
The most common algorithm used to perform
this blend is shown in Listing 1.
-- f: Noise function (2-dimensional)
-- x: Coordinate
-- w: Length of array
function noiseTile1DCircular(f, x, w)
local t=x/w* 2*pi
local nx=cos(t)
local ny=sin(t)
return f:get(nx,ny)
end
To extend the idea to two-dimensional noise for
textures, we need to extend the circle-tracing
www.gdmag.com 9