Perlin Noise algorithm does not seem to produce gradient noise - c++

I am attempting to implement Perlin Noise in c++.
Firstly, the problem (I think) is that the output is not what I expect. Currently I simply use the generated Perlin Noise values in a greyscaled image, and this is the results I get:
However, from my understanding, it's supposed to look more along the lines of:
That is, the noise I am producing currently seems to be more along the lines of "standard" irregular noise.
This is the Perlin Noise Algorithm I have implemented so far:
float perlinNoise2D(float x, float y)
{
// Find grid cell coordinates
int x0 = (x > 0.0f ? static_cast<int>(x) : (static_cast<int>(x) - 1));
int x1 = x0 + 1;
int y0 = (y > 0.0f ? static_cast<int>(y) : (static_cast<int>(y) - 1));
int y1 = y0 + 1;
float s = calculateInfluence(x0, y0, x, y);
float t = calculateInfluence(x1, y0, x, y);
float u = calculateInfluence(x0, y1, x, y);
float v = calculateInfluence(x1, y1, x, y);
// Local position in the grid cell
float localPosX = 3 * ((x - (float)x0) * (x - (float)x0)) - 2 * ((x - (float)x0) * (x - (float)x0) * (x - (float)x0));
float localPosY = 3 * ((y - (float)y0) * (y - (float)y0)) - 2 * ((y - (float)y0) * (y - (float)y0) * (y - (float)y0));
float a = s + localPosX * (t - s);
float b = u + localPosX * (v - u);
return lerp(a, b, localPosY);
}
The function calculateInfluence has the job of generating the random gradient vector and distance vector for one of the corner points of the current grid cell and returning the dot product of these. It is implemented as:
float calculateInfluence(int xGrid, int yGrid, float x, float y)
{
// Calculate gradient vector
float gradientXComponent = dist(rdEngine);
float gradientYComponent = dist(rdEngine);
// Normalize gradient vector
float magnitude = sqrt( pow(gradientXComponent, 2) + pow(gradientYComponent, 2) );
gradientXComponent = gradientXComponent / magnitude;
gradientYComponent = gradientYComponent / magnitude;
magnitude = sqrt(pow(gradientXComponent, 2) + pow(gradientYComponent, 2));
// Calculate distance vectors
float dx = x - (float)xGrid;
float dy = y - (float)yGrid;
// Compute dot product
return (dx * gradientXComponent + dy * gradientYComponent);
}
Here, dist is a random number generator from C++11:
std::mt19937 rdEngine(1);
std::normal_distribution<float> dist(0.0f, 1.0f);
And lerp is simply implemented as:
float lerp(float v0, float v1, float t)
{
return ( 1.0f - t ) * v0 + t * v1;
}
To implement the algorithm, I primarily made use of the following two resources:
Perlin Noise FAQ
Perlin Noise Pseudo Code
It's difficult for me to pinpoint exactly where I seem to be messing up. It could be that I am generating the gradient vectors incorrectly, as I'm not quite sure what type of distribution they should have. I have tried with a uniform distribution, however this seemed to generate repeating patterns in the texture!
Likewise, it could be that I am averaging the influence values incorrectly. It has been a bit difficult to discern exactly how it should be done from from the Perlin Noise FAQ article.
Does anyone have any hints as to what might be wrong with the code? :)

It seems like you are only generating a single octave of Perlin Noise. To get a result like the one shown, you need to generate multiple octaves and add them together. In a series of octaves, each octave should have a grid cell size double that of the last.
To generate multi-octave noise, use something similar to this:
float multiOctavePerlinNoise2D(float x, float y, int octaves)
{
float v = 0.0f;
float scale = 1.0f;
float weight = 1.0f;
float weightTotal = 0.0f;
for(int i = 0; i < octaves; i++)
{
v += perlinNoise2D(x * scale, y * scale) * weight;
weightTotal += weight;
// "ever-increasing frequencies and ever-decreasing amplitudes"
// (or conversely decreasing freqs and increasing amplitudes)
scale *= 0.5f;
weight *= 2.0f;
}
return v / weightTotal;
}
For extra randomness you could use a differently seeded random generator for each octave. Also, the weights given to each octave can be varied to adjust the aesthetic quality of the noise. If the weight variable is not adjusted each iteration, then the example above is "pink noise" (each doubling of frequency carries the same weight).
Also, you need to use a random number generator that returns the same value each time for a given xGrid, yGrid pair.

Related

Calculating Angle/Curvature?

I'm trying to apply the Gauss-Bonnet theorem to my C++ OpenGL application and compute the value of the interior angle at vertex Vi in the neighboring triangle Fi in my mesh.
I did some searching before making this post, and I know that to do this for a 2D model, one could use the function below to get the angles:
void angles(double points[][2], double angles[], int npoints){
for(int i = 0; i < npoints; i++){
int last = (i - 1 + npoints) % npoints;
int next = (i + 1) % npoints;
double x1 = points[i][0] - points[last][0];
double y1 = points[i][1] - points[last][1];
double x2 = points[next][0] - points[i][0];
double y2 = points[next][1] - points[i][1];
double theta1 = atan2(y1, x1)*180/3.1415926358979323;
double theta2 = atan2(y2, x2)*180/3.1415926358979323;
angles[i] = (180 + theta1 - theta2 + 360);
while(angles[i]>360)angles[i]-=360;
} }
But how can I find the angles with a 3D mesh (x, y, and z) vertices?
The analogous concept in 3D is called Gaussian curvature. The situation is much more complicated than 2D, and there is no single good way of calculating or estimating the Gaussian curvature for a mesh. There's a survey paper that may give you some ideas.

Efficient way to get the angle between two vectors in a single plane?

If I know for a fact that the x and z values of the vectors will be identical,
therefore im only concerned in measuring the 'vertical' angle of from the differences in the y plane, is there a more efficient method to do this compared to computing the dot product?
My current code using the dot product method is as follows:
float a_mag = a.magnitude();
float b_mag = b.magnitude();
float ab_dot = a.dot(b);
float c = ab_dot / (a_mag * b_mag);
// clamp d to from going beyond +/- 1 as acos(+1/-1) results in infinity
if (c > 1.0f) {
c = 1.0;
} else if (c < -1.0) {
c = -1.0;
}
return acos(c);
I would love to be able to get rid of these square roots
Suppose that your two vectors live at u = (x, y1, z) and v = (x, y2, z), and you're interested in the planar angle between the two along the plane spanned by the two vectors. You'd have to compute the dot product and the magnitude, but you can save a few operations:
u.v = x.x + y1.y2 + z.z
u^2 = x.x + y1.y1 + z.z
v^2 = x.x + y2.y2 + z.z
So we should precompute:
float xz = x*x + z*z, y11 = y1*y1, y12 = y1*y2, y22 = y2*y2;
float cosangle = (xz + y12) / sqrt((xz + y11) * (xz + y22));
float angle = acos(cosangle);
If the values of x and z are unchanged, then the calculation is very easy: just use basic trigonometry.
Let the points be (x, y1, z) and (x, y2, z). You can find out the angle a vector makes with the ZX-plane. Let the angles be t1 and t2 respectively. Then:
w = sqrt(x^2 + z^2)
tan(t1) = y1 / w
So t1 = atan(y1 / w)
Similarly t2 = atan(y2 / w)
The angle is (t2 - t1)
There's one pitfall: When both x and z are zero, the tans are undefined... but such a trivial case can easily be handled separately.
Unfortunately, there seems to be no way to avoid the square root.

Using Perlin noise to create lightning?

Actually I am having several questions related to the subject given in the topic title.
I am already using Perlin functions to create lightning in my application, but I am not totally happy about my implementation.
The following questions are based on the initial and the improved Perlin noise implementations.
To simplify the issue, let's assume I am creating a simple 2D lightning by modulating the height of a horizontal line consisting of N nodes at these nodes using a 1D Perlin function.
As far as I have understood, two subsequent values passed to the Perlin function must differ by at least one, or the resulting two values will be identical. That is because with the simple Perlin implementation, the Random function works with an int argument, and in the improved implementation values are mapped to [0..255] and are then used as index into an array containing the values [0..255] in a random distribution. Is that right?
How do I achieve that the first and the last offset value (i.e. for nodes 0 and N-1) returned by the Perlin function is always 0 (zero)? Right now I am modulation a sine function (0 .. Pi) with my Perlin function to achieve that, but that's not really what I want. Just setting them to zero is not what I want, since I want a nice lightning path w/o jaggies at its ends.
How do I vary the Perlin function (so that I would get two different paths I could use as animation start and end frames for the lightning)? I could of course add a fixed random offset per path calculation to each node value, or use a differently setup permutation table for improved Perlin noise, but are there better options?
That depends on how you implement it and sample from it. Using multiple octaves helps counter integers quite a bit.
The octaves and additional interpolation/sampling done for each provides much of the noise in perlin noise. In theory, you should not need to use different integer positions; you should be able to sample at any point and it will be similar (but not always identical) to nearby values.
I would suggest using the perlin as a multiplier instead of simply additive, and use a curve over the course of the lightning. For example, having perlin in the range [-1.5, 1.5] and a normal curve over the lightning (0 at both ends, 1 in the center), lightning + (perlin * curve) will keep your ends points still. Depending on how you've implemented your perlin noise generator, you may need something like:
lightning.x += ((perlin(lightning.y, octaves) * 2.0) - 0.5) * curve(lightning.y);
if perlin returns [0,1] or
lightning.x += (perlin(lightning.y, octaves) / 128.0) * curve(lightning.y);
if it returns [0, 255]. Assuming lightning.x started with a given value, perhaps 0, that would give a somewhat jagged line that still met the original start and end points.
Add a dimension to the noise for every dimension you add to the lightning. If you're modifying the lightning in one dimension (horizontal jagged), you need 1D perlin noise. If you want to animate it, you need 2D. If you wanted lightning that was jagged on two axis and animated, you'd need 3D noise, and so on.
After reading peachykeen's answer and doing some (more) own research in the internet, I have found the following solution to work for me.
With my implementation of Perlin noise, using a value range of [0.0 .. 1.0] for the lightning path nodes work best, passing the value (double) M / (double) N for node M to the Perlin noise function.
To have a noise function F' return the same value for node 0 and node N-1, the following formula can be applied: F'(M) = ((M - N) * F(N) + N * F (N - M)) / M. In order to have the lightning path offsets begin and end with 0, you simply need to subtract F'(0) from all lightning path offsets after having computed the path.
To randomize the lightning path, before computing the offsets for each path node, a random offset R can be computed and added to the values passed to the noise function, so that a node's offset O = F'(N+R). To animate a lightning, two lightning paths need to be computed (start and end frame), and then each path vertex has to be lerped between its start and end position. Once the end frame has been reached, the end frame becomes the start frame and a new end frame is computed. For a 3D path, for each path node N two offset vectors can be computed that are perpendicular to the path at node N and each other, and can be scaled with two 1D Perlin noise values to lerp the node position from start to end frame position. That may be cheaper than doing 3D Perlin noise and works quite well in my application.
Here is my implementation of standard 1D Perlin noise as a reference (some stuff is virtual because I am using this as base for improved Perlin noise, allowing to use standard or improved Perlin noise in a strategy pattern application. The code has been simplified somewhat as well to make it more concise for publishing it here):
Header file:
#ifndef __PERLIN_H
#define __PERLIN_H
class CPerlin {
private:
int m_randomize;
protected:
double m_amplitude;
double m_persistence;
int m_octaves;
public:
virtual void Setup (double amplitude, double persistence, int octaves, int randomize = -1);
double ComputeNoise (double x);
protected:
double LinearInterpolate (double a, double b, double x);
double CosineInterpolate (double a, double b, double x);
double CubicInterpolate (double v0, double v1, double v2, double v3, double x);
double Noise (int v);
double SmoothedNoise (int x);
virtual double InterpolatedNoise (double x);
};
#endif //__PERLIN_H
Implementation:
#include <math.h>
#include <stdlib.h>
#include "perlin.h"
#define INTERPOLATION_METHOD 1
#ifndef Pi
# define Pi 3.141592653589793240
#endif
inline double CPerlin::Noise (int n) {
n = (n << 13) ^ n;
return 1.0 - ((n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0;
}
double CPerlin::LinearInterpolate (double a, double b, double x) {
return a * (1.0 - x) + b * x;
}
double CPerlin::CosineInterpolate (double a, double b, double x) {
double f = (1.0 - cos (x * Pi)) * 0.5;
return a * (1.0 - f) + b * f;
}
double CPerlin::CubicInterpolate (double v0, double v1, double v2, double v3, double x) {
double p = (v3 - v2) - (v0 - v1);
double x2 = x * x;
return v1 + (v2 - v0) * x + (v0 - v1 - p) * x2 + p * x2 * x;
}
double CPerlin::SmoothedNoise (int v) {
return Noise (v) / 2 + Noise (v-1) / 4 + Noise (v+1) / 4;
}
int FastFloor (double v) { return (int) ((v < 0) ? v - 1 : v; }
double CPerlin::InterpolatedNoise (double v) {
int i = FastFloor (v);
double v1 = SmoothedNoise (i);
double v2 = SmoothedNoise (i + 1);
#if INTERPOLATION_METHOD == 2
double v0 = SmoothedNoise (i - 1);
double v3 = SmoothedNoise (i + 2);
return CubicInterpolate (v0, v1, v2, v3, v - i);
#elif INTERPOLATION_METHOD == 1
return CosineInterpolate (v1, v2, v - i);
#else
return LinearInterpolate (v1, v2, v - i);
#endif
}
double CPerlin::ComputeNoise (double v) {
double total = 0, amplitude = m_amplitude, frequency = 1.0;
v += m_randomize;
for (int i = 0; i < m_octaves; i++) {
total += InterpolatedNoise (v * frequency) * amplitude;
frequency *= 2.0;
amplitude *= m_persistence;
}
return total;
}
void CPerlin::Setup (double amplitude, double persistence, int octaves, int randomize) {
m_amplitude = (amplitude > 0.0) ? amplitude : 1.0;
m_persistence = (persistence > 0.0) ? persistence : 2.0 / 3.0;
m_octaves = (octaves > 0) ? octaves : 6;
m_randomize = (randomize < 0) ? (rand () * rand ()) & 0xFFFF : randomize;
}

UV mapping for a dome?

I am trying to understand how can I change UV mapping of a dome, I need a different texture map projection than this one coded below:
protected final void createDome(final float radius) {
int lats=16;
int longs=16;
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textures2x4[0].getTextureID());
int i, j;
int halfLats = lats / 2;
for(i = 0; i <= halfLats; i++)
{
double lat0 = MathUtils.PI * (-0.5 + (double) (i - 1) / lats);
double z0 = Math.sin(lat0)* radius;
double zr0 = Math.cos(lat0)* radius;
double lat1 = MathUtils.PI * (-0.5 + (double) i / lats);
double z1 = Math.sin(lat1)* radius;
double zr1 = Math.cos(lat1)* radius;
GL11.glBegin(GL11.GL_QUAD_STRIP);
for(j = 0; j <= longs; j++)
{
double lng = 2 * MathUtils.PI * (double) (j - 1) / longs;
double x = Math.cos(lng);
double y = Math.sin(lng);
double s1, s2, t;
s1 = ((double) i) / halfLats;
s2 = ((double) i + 1) / halfLats;
t = ((double) j) / longs;
// HERE: I don't know how to calculate the UV mapping
GL11.glTexCoord2d(s1, t);
GL11.glNormal3d(x * zr0, y * zr0, z0);
GL11.glVertex3d(x * zr0, y * zr0, z0);
GL11.glTexCoord2d(s2, t);
GL11.glNormal3d(x * zr1, y * zr1, z1);
GL11.glVertex3d(x * zr1, y * zr1, z1);
}
GL11.glEnd();
}
}
I linked the output image and the original map. Pratically I need a UV mapping which places the Artic at the zenith/top of the dome, and the Antartic streched on the bottom side of the dome... the Artic/Antartic map is only used to figure out what I mean, my need it's not to fit a globe emisphere
Output image http://img831.imageshack.us/img831/3481/lwjgl.png
Source map http://img203.imageshack.us/img203/4930/earthc.png
Take a look at this function calls (disclaimer: untested - I haven't used LWJGL, but the concept should be identical):
GL11.glMatrixMode(GL11.GL_TEXTURE);
GL11.glRotate(90, 0, 0, 1); // (1) Here you transform texture space
GL11.glMatrixMode(GL11.GL_MODELVIEW);
// and so on
Basically, you need to rotate texture on object. And that's the way you do it - transform texture projection matrix. The line (1) rotates texture 90 degrees along Z axis (perpendicular to texture plane). It's Z axis, because the last argument is 1. Last three arguments denote X, Y and Z respectively (I'll leave the whole explanation for later if you're interested).
The best You can do is to grasp all the basic stuff (projection, texture space, normal vectors, triangulation, continuity, particle systems and a lot more) is to download some trial version of a 3d package and play with it. I learned a lot just out of playing with 3D Studio Max (trial version available, and many more for free). If you have some free time and will to learn something new I strongly advise to look into it. In the end, if You're really interested in 3D graphics You'll end up using one any way - be it 3d package or game engine level editor.
EDIT: After more reading I recognized my own code... Basically you could only swap some of the coordinates to reflect symmetrically along diagonal. You might end up upside down, but that can also be fixed with additional tweaking (or transforming the view axis). Here is my untested guess:
// tweaked to get pole right
s1 = ((double) j) / longs;
s2 = ((double) j + 1) / longs;
t = ((double) i) / halfLats;
Try swapping s1 with s2 if it's not right.

Creating a linear gradient in 2D array

I have a 2D bitmap-like array of let's say 500*500 values. I'm trying to create a linear gradient on the array, so the resulting bitmap would look something like this (in grayscale):
(source: showandtell-graphics.com)
The input would be the array to fill, two points (like the starting and ending point for the Gradient tool in Photoshop/GIMP) and the range of values which would be used.
My current best result is this:
alt text http://img222.imageshack.us/img222/1733/gradientfe3.png
...which is nowhere near what I would like to achieve. It looks more like a radial gradient.
What is the simplest way to create such a gradient? I'm going to implement it in C++, but I would like some general algorithm.
This is really a math question, so it might be debatable whether it really "belongs" on Stack Overflow, but anyway: you need to project the coordinates of each point in the image onto the axis of your gradient and use that coordinate to determine the color.
Mathematically, what I mean is:
Say your starting point is (x1, y1) and your ending point is (x2, y2)
Compute A = (x2 - x1) and B = (y2 - y1)
Calculate C1 = A * x1 + B * y1 for the starting point and C2 = A * x2 + B * y2 for the ending point (C2 should be larger than C1)
For each point in the image, calculate C = A * x + B * y
If C <= C1, use the starting color; if C >= C2, use the ending color; otherwise, use a weighted average:
(start_color * (C2 - C) + end_color * (C - C1))/(C2 - C1)
I did some quick tests to check that this basically worked.
In your example image, it looks like you have a radial gradient. Here's my impromtu math explanation for the steps you'll need. Sorry for the math, the other answers are better in terms of implementation.
Define a linear function (like y = x + 1) with the domain (i.e. x) being from the colour you want to start with to the colour your want to end with. You can think of this in terms of a range the within Ox0 to OxFFFFFF (for 24 bit colour). If you want to handle things like brightness, you'll have to do some tricks with the range (i.e. the y value).
Next you need to map a vector across the matrix you have, as this defines the direction that the colours will change in. Also, the colour values defined by your linear function will be assigned at each point along the vector. The start and end point of the vector also define the min and max of the domain in 1. You can think of the vector as one line of your gradient.
For each cell in the matrix, colours can be assigned a value from the vector where a perpendicular line from the cell intersects the vector. See the diagram below where c is the position of the cell and . is the the point of intersection. If you pretend that the colour at . is Red, then that's what you'll assign to the cell.
|
c
|
|
Vect:____.______________
|
|
I'll just post my solution.
int ColourAt( int x, int y )
{
float imageX = (float)x / (float)BUFFER_WIDTH;
float imageY = (float)y / (float)BUFFER_WIDTH;
float xS = xStart / (float)BUFFER_WIDTH;
float yS = yStart / (float)BUFFER_WIDTH;
float xE = xEnd / (float)BUFFER_WIDTH;
float yE = yEnd / (float)BUFFER_WIDTH;
float xD = xE - xS;
float yD = yE - yS;
float mod = 1.0f / ( xD * xD + yD * yD );
float gradPos = ( ( imageX - xS ) * xD + ( imageY - yS ) * yD ) * mod;
float mag = gradPos > 0 ? gradPos < 1.0f ? gradPos : 1.0f : 0.0f;
int colour = (int)( 255 * mag );
colour |= ( colour << 16 ) + ( colour << 8 );
return colour;
}
For speed ups, cache the derived "direction" values (hint: premultiply by the mag).
There are two parts to this problem.
Given two colors A and B and some percentage p, determine what color lies p 'percent of the way' from A to B.
Given a point on a plane, find the orthogonal projection of that point onto a given line.
The given line in part 2 is your gradient line. Given any point P, project it onto the gradient line. Let's say its projection is R. Then figure out how far R is from the starting point of your gradient segment, as a percentage of the length of the gradient segment. Use this percentage in your function from part 1 above. That's the color P should be.
Note that, contrary to what other people have said, you can't just view your colors as regular numbers in your function from part 1. That will almost certainly not do what you want. What you do depends on the color space you are using. If you want an RGB gradient, then you have to look at the red, green, and blue color components separately.
For example, if you want a color "halfway between" pure red and blue, then in hex notation you are dealing with
ff 00 00
and
00 00 ff
Probably the color you want is something like
80 00 80
which is a nice purple color. You have to average out each color component separately. If you try to just average the hex numbers 0xff0000 and 0x0000ff directly, you get 0x7F807F, which is a medium gray. I'm guessing this explains at least part of the problem with your picture above.
Alternatively if you are in the HSV color space, you may want to adjust the hue component only, and leave the others as they are.
void Image::fillGradient(const SColor& colorA, const SColor& colorB,
const Point2i& from, const Point2i& to)
{
Point2f dir = to - from;
if(to == from)
dir.x = width - 1; // horizontal gradient
dir *= 1.0f / dir.lengthQ2(); // 1.0 / (dir.x * dir.x + dir.y * dir.y)
float default_kx = float(-from.x) * dir.x;
float kx = default_kx;
float ky = float(-from.y) * dir.y;
uint8_t* cur_pixel = base; // array of rgba pixels
for(int32_t h = 0; h < height; h++)
{
for(int32_t w = 0; w < width; w++)
{
float k = std::clamp(kx + ky, 0.0f, 1.0f);
*(cur_pixel++) = colorA.r * (1.0 - k) + colorB.r * k;
*(cur_pixel++) = colorA.g * (1.0 - k) + colorB.g * k;
*(cur_pixel++) = colorA.b * (1.0 - k) + colorB.b * k;
*(cur_pixel++) = colorA.a * (1.0 - k) + colorB.a * k;
kx += dir.x;
}
kx = default_kx;
ky += dir.y;
}
}