We have explained most of the techniques used for creating noise in the previous chapter. Creating higher dimensional noise should be from now on much more straightforward task, as they are all based on the same concepts and techniques. Be sure to have mastered and truly understood what we have described in the previous chapter before you move on. If things are not quite clear yet, read it again or drop us an email if you have questions.
我们在前面一篇已经解释了创建一个noise function 所需的大部分技术。创建一个多维的noise function 现在应该不是什么特别困难的事情了(PS:我只流泪不说话T_T...),多维noise函数是建立在相同的思路和技术上的。(PS:只是采样、样本分布方式不一样)确保你完全理解了前一章的所有知识点,如果没有,就再读一次。
Remember that all noise functions return a float but that its input can be a float, a 2D point or a 3D point. The name given to the noise function is related to the dimension of its input value. A 2D noise is therefore a noise function that takes a 2D point as input. A 3D noise takes a 3D point and we even mentioned a 4D noise in the first chapter of this lesson where the fourth dimension accounts in fact for time (it will produce a noise pattern based on a 3D point but animated through time).
记住所有的noise函数都返回一个float , 但是它们的输入可以是 float, 或 2d, 3d 的点。 noise function的名字和它接受的输入参数的维度相关。
一个2D Noise Function 表示它接受2D Point作为输入参数。3DNoise 就接受Vector3咯。我们在序章也提到过有4Dnoise 函数。 第4维是时间。(它基本上是3DNoise,但是会根据时间改变整个结果集的相位)。
PS: 有真正的4D输入,貌似是从数学角度做的多维度定义,感兴趣的同学可以找一下Simplex扩展到4D,5D的情况。
图23:一个4*4的坐标系
If you read the lesson on Interpolation (we suggest you do it now if you have not) you may already have an idea of how things work for 2D noise. For the 2D case
we will distribute random values at the vertex position of a regular 2D grids (figure xx). The 2D version of the noise function will take a 2D point as input. Lets call this point P. Similarly to the 1D version we will need to find the position of that point
on the grid. Like for the 1D case, our 2D grid has a predefined resolution (the grid is square so the resolution is the same along the x and y axis). Lets say 4 (which is a power of two) for the sake of this explanation. We will use the same modulo trick to
remap the position of P on the grid if the point is outside the grid boundaries (if P's coordinates are lower than 0 or greater than 4). We will perform the modulo operation on P's x and y coordinates. That will give us a new coordinates for the point on the
grid (lets call this new point Pnoise).
如果你已经阅读了”插值“相关的课程,你可能已经知道如何处理2DNoise了。
首先我们确保任何x,y的输入都通过取模运算折算到我们规定的4*4周期内。
As you can see on figure 25, the point is surrounded by the four vertices of a cell. We will use the same technique described in the lesson on interpolation to find a value for that point by linearly interpolating the values from the cell corners. To do this, we will first compute tx and ty which are the counterpart of t in our 1D noise.
图25:双线性插值
We can now interpolate the values from the two corners on the left with the values from the two corners on the right using tx. That would give us two values nx0 and nx1 which corresponds to the linear interpolation along the x axis of c01/c10 (nx0) and c01/c11 (nx1). We have now two interpolated values on the lower and upper edge of the cell vertically aligned on Pnoise's x coordinate that we will in turn linearly interpolate using ty. The result of this interpolation along the y axis, is our final noise value for P.
1、在 y = floor( input.y) 时, 对x 做插值 得到nx1。 (a,b可以通过查询原始噪点得到)
2、在 y = ceiling( input.y) 时,对x 做插值, 得到nx2。( a,b可以通过查询原始噪点得到)
3、对y做插值,a = nx1 , b = nx2, 得到最终 noise(x,y)
Here is the code of our simple 2D value noise. The size of the grid is 256 on each side. Note that like for the 1D noise, it is possible to change the remapping function for t. In this version we have chosen the Smoothstep function but you could experiment by ignoring the function (using tx and ty directly), using the Cosine smooth function or any alternative smooth function you know about.
这里有一段代码,不过我们这里用的是256*256的周期。在这里我们使用了 smoothstep插值函数来处理 tx,和ty。作为实验目的,你可以试着不使用平滑函数、consine或者其他你知道的平滑函数。
inline int floor( float x ) { return (int)x - ( x < 0 && (int)x != x ); } inline float Smoothstep( const float & t ) { return t * t * ( 3 - 2 * t ); } /// /// Basic implementation of a 2D value noise /// class Simple2DNoiseA { public: Simple2DNoiseA( unsigned seed = 2011 ) { srand48( seed ); for ( unsigned i = 0; i < kMaxVertices * kMaxVertices; ++i ) { r[ i ] = drand48(); } } /// Evaluate the noise function at position x float eval( const Point2f &pt ) const { int xi = floor( pt.x ); int yi = floor( pt.y ); float tx = pt.x - xi; float ty = pt.y - yi; int rx0 = xi & kMaxVerticesMask; int rx1 = ( rx0 + 1 ) & kMaxVerticesMask; int ry0 = yi & kMaxVerticesMask; int ry1 = ( ry0 + 1 ) & kMaxVerticesMask; /// Random values at the corners of the cell const float & c00 = r[ ry0 * kMaxVerticesMask + rx0 ]; const float & c10 = r[ ry0 * kMaxVerticesMask + rx1 ]; const float & c01 = r[ ry1 * kMaxVerticesMask + rx0 ]; const float & c11 = r[ ry1 * kMaxVerticesMask + rx1 ]; /// Remapping of tx and ty using the Smoothstep function float sx = Smoothstep( tx ); float sy = Smoothstep( ty ); /// Linearly interpolate values along the x axis float nx0 = Mix( c00, c10, sx ); float nx1 = Mix( c01, c11, sx ); /// Linearly interpolate the nx0/nx1 along they y axis return Mix( nx0, nx1, sy ); } static const unsigned kMaxVertices = 256; static const unsigned kMaxVerticesMask = kMaxVertices - 1; float r[ kMaxVertices * kMaxVertices ]; }; void NoiseImage() { Simple2DNoiseA simpleNoise2D; std::ofstream ofs( "./noise2D.ppm" ); unsigned imageWidth = 512, imageHeight = 512; float invImageWidth = 1.f / imageWidth, imvImageHeight = 1.f / imageHeight; ofs << "P6\n" << imageWidth << " " << imageHeight << "\n255\n"; for ( unsigned j = 0; j < imageHeight; ++j ) { for ( unsigned i = 0; i < imageWidth; ++i ) { Point2f pnoise = Point2f( i * invImageWidth, j * imvImageHeight ) * 10; float n = simpleNoise2D.eval( pnoise ); unsigned char c = static_cast<unsigned char>( n * 255 ); ofs << c << c << c; } } ofs.close(); }
If
you you run the code it will produce the following image (left):
如果你运行上述代码,你会生成下面左边边这张图
图26: 左图是我们的noise函数生成的图。右边是Ken Perlin的noise函数生成的图。PerlinNoise的结果更加自然,我们在这里只需要确保自己学会Noise是如何工作的。如果想了解PerlinNoise ,可以查阅Noise Part 2(PS:已经确认没有这个栏目 T_T )
Note that the result is probably not as good as we had hoped for. The resulting noise is quite blocky. Noise from professional applications produces much smoother
results. Remember that this lesson's goal is to teach you the basics concepts used to build a noise function. To improve the results we will need to use more elaborate techniques but it will be hard to study them unless you can build a simple function first.
Don't worry too much about the result of the noise function for now, and just try to grasp how it works, learn and understand its properties. You can still have fun playing around with this code and create some interesting effects/images as we will see in
the next chapter. You will learn about creating a better looking noise in the second lesson.
注意,结果可能并不是我们所期待的那样。noise 集合看起来方块感十足。 专业的应用程序生成的结果会比我们这个平滑的多。不过我们这个课程的目的首先在于告诉你创建一个noise函数所需的全部思路,而结果目前并不是最重要的。后面会学到如何完善我们的结果集。
1、在 y = floor( input.y) 时, 对x 做插值 得到nx1
2、在 y = ceiling( input.y) 时,对x 做插值, 得到nx2
3、对y做插值,a = nx1 , b = nx2, 得到最终 noise(x,y)