I am trying to generate a procedural terrain using perlin noise. Before, I was just creating a 1000 * 1000 vertice terrain so I just had a simple function that would fill the height map with the noise values.
However now I am trying to generate a terrain that generates chunks as the camera moves around and I don't think this method is suitable since each chunk would be generated with it's own random heightmap and chunks will not look consistant with eachother. Instead I am using this header file to generate noise which just takes and x and y but whatever values I give it does not seem to generate consistant values and I am not sure why
class Noise {
public:
int seed;
Noise(int seed) {
this->seed = seed;
}
float noise(int x, int y) {
int n = x + y * 57 + seed;
n = ( n << 13 ) ^ n;
float rand = 1 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f;
return rand;
}
float noise(int x, int y, int z) {
int n = x + y + z * 57 + seed;
n = ( n << 13 ) ^ n;
float rand = 1 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0f;
return rand;
}
float smoothNoise(int x, int y) {
float corners = (noise(x+1, y+1) + noise(x+1, y-1) + noise(x-1, y+1) + noise(x-1, y-1)) / 16.0f;
float sides = (noise(x, y+1) + noise(x, y-1) + noise(x-1, y) + noise(x+1, y)) / 8.0f;
float center = noise(x, y) / 4.0f;
return corners + sides + center;
}
float interpolatedNoise(float x, float y) {
int integerX = (int)x;
float fractionalX = x - integerX;
int integerY = (int)y;
float fractionalY = y - integerY;
float v1 = smoothNoise(integerX, integerY);
float v2 = smoothNoise(integerX + 1, integerY);
float v3 = smoothNoise(integerX, integerY + 1);
float v4 = smoothNoise(integerX + 1, integerY + 1);
float i1 = interpolateCosine(v1, v2, fractionalX);
float i2 = interpolateCosine(v3, v4, fractionalX);
return interpolateCosine(i1, i2, fractionalY);
}
// x must be in the range [0,1]
float interpolateLinear(float a, float b, float x) {
return a * (1 - x) + b * x;
}
float interpolateCosine(float a, float b, float x) {
float ft = x * (float)glm::pi<float>();
float f = (1 - ((float)glm::pi<float>())) * 0.5f;
return a * (1 - f) + b * f;
}
};`
I am calling interpolatedNoise() like this:
for(int y=x; x < config->width;x++)
{
for(int y = 0; y < config->height;y++)
{
map[x * config->height + y] = noise.interpolatedNoise((config->width * gridPosX + x) * 0.1f,(config->height * gridPosY + y) * 0.1f)/2.0f + 0.5f; // convert from -1:1 to 0:1
}
}
This is what the terrain looks like using this
but I want the terrain to be smooth. The parameters I pass into the function are divided by some value. The larger I make this value for more random the terrain is with the boxes being smaller and the smaller I make it I just get larger boxes but a more consistant looking terrain.
I am not quite sure how you are calling your Noise constructor, but one problem I see is in the constructor. It should be this->seed = seed;, otherwise you are assigning the argument seed to the value of itself. The seed variable of Noise then would just be a random number, and I feel like that's causing problems. Again, not sure how your callling Noise constructor, but thats my best guess.
Related
just had a quick couple questions as to why my I keep getting an error saying that x and y are not assigned a value at computeBarycentri2d(x, y, t.v) when in the if(!insideTriangle(x, y, t.v)) the values are assigned as an int value. The error I keep receiving for each value is "identifier x is not defined"
The other issue I am running into is that the continue statement in the if(zp >= depth_buf[y * width + x]) wont work giving me the error "a continue statement can only be used in a loop".
Any sort of help on how to fix these errors is greatly appreciated
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
std::array v = t.toVector4();
float trix[3] = {t.v[0][0], t.v[1][0], t.v[2][0] };
float triy[3] = {t.v[0][1], t.v[1][1], t.v[2][1] };
std::pair<float*, float*> xrange = std::minmax_element(trix, trix + 3);
std::pair<float*, float*> yrange = std::minmax_element(triy, triy + 3);
for (int x = std::floor(*xrange.first); x < std::ceil(*xrange.second); ++x)
for(int y = std::floor(*yrange.first); y < std::ceil(*yrange.second); ++y)
if(!insideTriangle(x, y, t.v)) continue;
auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
zp *= Z;
if(zp >= depth_buf[y * width + x]) continue;
depth_buf[y * width + x] = zp;
}
So from the help of #1201ProgramAlarm I found out that all i needed to do was add in brackets to each for loop which got rid of all of my errors!
Here is the code updated:
void rst::rasterizer::rasterize_triangle(const Triangle& t, const std::array<Eigen::Vector3f, 3>& view_pos)
{
std::array v = t.toVector4();
float trix[3] = {t.v[0][0], t.v[1][0], t.v[2][0] };
float triy[3] = {t.v[0][1], t.v[1][1], t.v[2][1] };
std::pair<float*, float*> xrange = std::minmax_element(trix, trix + 3);
std::pair<float*, float*> yrange = std::minmax_element(triy, triy + 3);
for (int x = std::floor(*xrange.first); x < std::ceil(*xrange.second); ++x)
{
for(int y = std::floor(*yrange.first); y < std::ceil(*yrange.second); ++y)
{
if(!insideTriangle(x, y, t.v)) continue;
auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v);
float Z = 1.0 / (alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w());
float zp = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w();
zp *= Z;
if(zp >= depth_buf[y * width + x]) continue;
depth_buf[y * width + x] = zp;
}
}
}
My question is that is it possible to convert a vector which stores samples of original CDF (cumulative density function)...
something like this:
class normal
{
public:
float mean;
float sigma;
float variance = sigma * sigma;
float left_margin = mean - 4 * sigma;
float right_margin = mean + 4 * sigma;
normal():mean(0), sigma(1){}
normal(float m, float s):mean(m), sigma(s){}
float cdf(float x);
float pdf(float x);
};
float normal::pdf(float x)
{
if (x < left_margin || x > right_margin) return 0;
float coefficient = 1 / (float)sqrt(2 * PI * variance);
float x_mean = x - mean;
float result = coefficient * exp(-(x_mean * x_mean) / 2 * variance);
return result;
}
float normal::cdf(float x)
{
if (x <= left_margin) return 0;
if (x >= right_margin) return 1;
float x_mean = x - mean;
float result = (float)(0.5 * (1 + erf((x_mean) / sqrt(2 * variance))));
if (result > 1) return 1;
else return result;
}
std::vector<float> discrete_normal_cdf(normal& X)
{
std::vector<float> vec;
float L = (float)(X.left_margin);
float R = (float)(1.2 * X.right_margin);
while (L <= R)
{
vec.push_back(X.cdf(L));
L = (float)(L + 0.1);
}
std::vector<float> tmp;
// take three samples
tmp.push_back(vec.at(1)); // first non_zero element
tmp.push_back(vec.at(40)); // add element with value of 0.5
tmp.push_back(vec.at(80)); // element with value of 0.99
std::vector<float> cdf_v(5, 0);
for (auto i = 0; i < tmp.size(); i++)
cdf_v.push_back(tmp.at(i));
int l = 0;
while (l < 5)
{
cdf_v.push_back(1);
l++;
}
return cdf_v;
}
In fact what I need is this: if we have a normal
normal n1(5, 1);
take samples of its CDF to piece wise linear CDF:
vector<float> foo = discrete_normal_cdf(n1);
then reconstruct the piecewise linear CDF into normal
normal function(foo)
{
return normal(5, 1);
}
Is this function valid?
I wrote a function which takes a vector as an input
and search all the elements of the vector the for the value of 0.5
and returns the index of that element as the mean of the normal
but it not always true.
normal vec2normal(vector<float>& vec)
{
int mean;
mean = std::find(vec.begin(), vec.end(), 0.5) - vec.begin();
return normal(mean, 1);
}
I have no idea how to do this, so any suggestions will be appreciated
thank you.
I have this sample of code that I try to understand it:
__global__ void
d_boxfilter_rgba_x(unsigned int *od, int w, int h, int r)
{
float scale = 1.0f / (float)((r << 1) + 1);
unsigned int y = blockIdx.x*blockDim.x + threadIdx.x;
if (y < h)
{
float4 t = make_float4(0.0f);
for (int x = -r; x <= r; x++)
{
t += tex2D(rgbaTex, x, y);
}
od[y * w] = rgbaFloatToInt(t * scale);
for (int x = 1; x < w; x++)
{
t += tex2D(rgbaTex, x + r, y);
t -= tex2D(rgbaTex, x - r - 1, y);
od[y * w + x] = rgbaFloatToInt(t * scale);
}
}
}
__global__ void
d_boxfilter_rgba_y(unsigned int *id, unsigned int *od, int w, int h, int r)
{
unsigned int x = blockIdx.x*blockDim.x + threadIdx.x;
id = &id[x];
od = &od[x];
float scale = 1.0f / (float)((r << 1) + 1);
float4 t;
// partea din stanga
t = rgbaIntToFloat(id[0]) * r;
for (int y = 0; y < (r + 1); y++)
{
t += rgbaIntToFloat(id[y*w]);
}
od[0] = rgbaFloatToInt(t * scale);
for (int y = 1; y < (r + 1); y++)
{
t += rgbaIntToFloat(id[(y + r) * w]);
t -= rgbaIntToFloat(id[0]);
od[y * w] = rgbaFloatToInt(t * scale);
}
// main loop
for (int y = (r + 1); y < (h - r); y++)
{
t += rgbaIntToFloat(id[(y + r) * w]);
t -= rgbaIntToFloat(id[((y - r) * w) - w]);
od[y * w] = rgbaFloatToInt(t * scale);
}
// right side
for (int y = h - r; y < h; y++)
{
t += rgbaIntToFloat(id[(h - 1) * w]);
t -= rgbaIntToFloat(id[((y - r) * w) - w]);
od[y * w] = rgbaFloatToInt(t * scale);
}
}
This should be a box filter with CUDA.
From what I have read this should make an average with a given radius.
But in d_boxfilter_rgba_y make something like this:
od[0] = rgbaFloatToInt(t * scale);
I don't understand why is used this scale and why are made all that loops when there should be just one. To calculate the value from -r to +r and divide this by a number of pixels.
Can somebody help me?
To calculate the average of a box with radius 1 (3 values), you do:
(box[0] + box[1] + box[2]) / 3 // which is equal to
(box[0] + box[1] + box[2] * 1/3 // which is equal to your scale factor
The calculation of scale is:
1.0f / (float)((r << 1) + 1); // equal to
1 / ((r * 2) + 1) // equal to
1 / (2r + 1) // 2r because you go to the left and right and +1 for the middle
The two for loops are used, because the "sliding window" optimisation is used. First the first box is calculated:
for (int x = -r; x <= r; x++)
{
t += tex2D(rgbaTex, x, y);
}
And then for each step to the right, the value right of the box is added and the most left value of the box is removed. That way you can calculate the sum of the box with just 2 operations instead of 2*r + 1 operations.
for (int x = 1; x < w; x++)
{
t += tex2D(rgbaTex, x + r, y);
t -= tex2D(rgbaTex, x - r - 1, y);
od[y * w + x] = rgbaFloatToInt(t * scale);
}
}
I am trying to wrap my head around Perlin noise.
This article has helped and I have been trying to recreate the cloud type images that it provides.
My noise code is as follows:
#include "terrain_generator.hpp"
using namespace std;
#define PI 3.1415927;
float noise(int x, int y)
{
int n = x + y * 57;
n = (n<<13) ^ n;
return (1.0 - ( (n * ((n * n * 15731) + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
float cosine_interpolate(float a, float b, float x)
{
float ft = x * PI;
float f = (1 - cos(ft)) * 0.5;
float result = a*(1-f) + b*f;
return result;
}
float smooth_noise_2D(float x, float y)
{
float corners = ( noise(x-1, y-1)+noise(x+1, y-1)+noise(x-1, y+1)+noise(x+1, y+1) ) / 16;
float sides = ( noise(x-1, y) +noise(x+1, y) +noise(x, y-1) +noise(x, y+1) ) / 8;
float center = noise(x, y) / 4;
return corners + sides + center;
}
float interpolated_noise(float x, float y)
{
int x_whole = (int) x;
float x_frac = x - x_whole;
int y_whole = (int) y;
float y_frac = y - y_whole;
float v1 = smooth_noise_2D(x_whole, y_whole);
float v2 = smooth_noise_2D(x_whole, y_whole+1);
float v3 = smooth_noise_2D(x_whole+1, y_whole);
float v4 = smooth_noise_2D(x_whole+1, y_whole+1);
float i1 = cosine_interpolate(v1,v3,x_frac);
float i2 = cosine_interpolate(v2,v4,x_frac);
return cosine_interpolate(i1, i2, y_frac);
}
float perlin_noise_2D(float x, float y)
{
int octaves=5;
float persistence=0.5;
float total = 0;
for(int i=0; i<octaves-1; i++)
{
float frequency = pow(2,i);
float amplitude = pow(persistence,i);
total = total + interpolated_noise(x * frequency, y * frequency) * amplitude;
}
return total;
}
To actually implement the algorithm, I am trying to make the clouds he depicted in the article.
I am using openGL and I am making my own texture and pasting it onto a quad that covers the screen. That is irrelevant though. In the code below, just know that the set pixel function works correctly and that its parameters are (x, y, red, green, blue).
This is essentially my draw loop:
for(int y=0; y<texture_height; y++)
{
for(int x=0; x<texture_width; x++)
{
seed2+=1;
float Val=perlin_noise_2D(x,y);
Val = Val/2.0;
Val = (Val + 1.0) / 2.0;
setPixel(x,y,Val,Val,Val);
}
}
What I get is the following:
How can I manipulate my algorithm to achieve the effect I am looking for? changing the persistence or number of octaves doesn't seem to do much at all.
As your result looks almost like white noise, your samples are probably too far apart within the perlin noise. Try using something smaller than the pixel coordinates to evaluate the noise at.
Something similar to this:
perlin_noise_2D((float)x/texture_width,(float)y/texture_height);
I tried a quick and dirty translation of the code here.
However, my version outputs noise comparable to grey t-shirt material, or heather if it please you:
#include <fstream>
#include "perlin.h"
double Perlin::cos_Interp(double a, double b, double x)
{
ft = x * 3.1415927;
f = (1 - cos(ft)) * .5;
return a * (1 - f) + b * f;
}
double Perlin::noise_2D(double x, double y)
{
/*
int n = (int)x + (int)y * 57;
n = (n << 13) ^ n;
int nn = (n * (n * n * 60493 + 19990303) + 1376312589) & 0x7fffffff;
return 1.0 - ((double)nn / 1073741824.0);
*/
int n = (int)x + (int)y * 57;
n = (n<<13) ^ n;
return ( 1.0 - ( (n * (n * n * 15731 + 789221) + 1376312589) & 0x7fffffff) / 1073741824.0);
}
double Perlin::smooth_2D(double x, double y)
{
corners = ( noise_2D(x - 1, y - 1) + noise_2D(x + 1, y - 1) + noise_2D(x - 1, y + 1) + noise_2D(x + 1, y + 1) ) / 16;
sides = ( noise_2D(x - 1, y) + noise_2D(x + 1, y) + noise_2D(x, y - 1) + noise_2D(x, y + 1) ) / 8;
center = noise_2D(x, y) / 4;
return corners + sides + center;
}
double Perlin::interp(double x, double y)
{
int x_i = int(x);
double x_left = x - x_i;
int y_i = int(y);
double y_left = y - y_i;
double v1 = smooth_2D(x_i, y_i);
double v2 = smooth_2D(x_i + 1, y_i);
double v3 = smooth_2D(x_i, y_i + 1);
double v4 = smooth_2D(x_i + 1, y_i + 1);
double i1 = cos_Interp(v1, v2, x_left);
double i2 = cos_Interp(v3, v4, x_left);
return cos_Interp(i1, i2, y_left);
}
double Perlin::perlin_2D(double x, double y)
{
double total = 0;
double p = .25;
int n = 1;
for(int i = 0; i < n; ++i)
{
double freq = pow(2, i);
double amp = pow(p, i);
total = total + interp(x * freq, y * freq) * amp;
}
return total;
}
int main()
{
Perlin perl;
ofstream ofs("./noise2D.ppm", ios_base::binary);
ofs << "P6\n" << 512 << " " << 512 << "\n255\n";
for(int i = 0; i < 512; ++i)
{
for(int j = 0; j < 512; ++j)
{
double n = perl.perlin_2D(i, j);
n = floor((n + 1.0) / 2.0 * 255);
unsigned char c = n;
ofs << c << c << c;
}
}
ofs.close();
return 0;
}
I don't believe that I strayed too far from the aforementioned site's directions aside from adding in the ppm image generation code, but then again I'll admit to not fully grasping what is going on in the code.
As you'll see by the commented section, I tried two (similar) ways of generating pseudorandom numbers for noise. I also tried different ways of scaling the numbers returned by perlin_2D to RGB color values. These two ways of editing the code have just yielded different looking t-shirt material. So, I'm forced to believe that there's something bigger going on that I am unable to recognize.
Also, I'm compiling with g++ and the c++11 standard.
EDIT: Here's an example: http://imgur.com/Sh17QjK
To convert a double in the range of [-1.0, 1.0] to an integer in range [0, 255]:
n = floor((n + 1.0) / 2.0 * 255.99);
To write it as a binary value to the PPM file:
ofstream ofs("./noise2D.ppm", ios_base::binary);
...
unsigned char c = n;
ofs << c << c << c;
Is this a direct copy of your code? You assigned an integer to what should be the Y fractional value - it's a typo and it will throw the entire noise algorithm off if you don't fix:
double Perlin::interp(double x, double y)
{
int x_i = int(x);
double x_left = x - x_i;
int y_i = int(y);
double y_left = y = y_i; //This Should have a minus, not an "=" like the line above
.....
}
My guess is if you're successfully generating the bitmap with the proper color computation, you're getting vertical bars or something along those lines?
You also need to remember that the Perlin generator usually spits out numbers in the range of -1 to 1 and you need to multiply the resultant value as such:
value * 127 + 128 = {R, G, B}
to get a good grayscale image.