Scratchapixel 2.0
Sign in
This project contains the following files (right-click files you'd like to download):
noise.cpp
//[header] // A simple program to demonstrate the concept of value noise //[/header] //[compile] // Download the noise.cpp file to a folder. // Open a shell/terminal, and run the following command where the file is saved: // // c++ -o noise noise.cpp -std=c++11 -O3 // // Run with: ./noise. Open the file ./noise.ppm in Photoshop or any program // reading PPM files. //[/compile] //[ignore] // Copyright (C) 2012 www.scratchapixel.com // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. //[/ignore] // if you use windows uncomment these two lines //#include "stdafx.h" //#define _USE_MATH_DEFINES #include <cmath> #include <cstdio> #include <random> #include <functional> #include <iostream> #include <fstream> template<typename T> class Vec2 { public: Vec2() : x(T(0)), y(T(0)) {} Vec2(T xx, T yy) : x(xx), y(yy) {} Vec2 operator * (const T &r) const { return Vec2(x * r, y * r); } Vec2& operator *= (const T &r) { x *= r, y *= r; return *this; } T x, y; }; typedef Vec2<float> Vec2f; // [comment] // Linear interpolation // [/comment] template<typename T = float> inline T lerp(const T &lo, const T &hi, const T &t) { return lo * (1 - t) + hi * t; } // [comment] // The smoothstep function // [/comment] inline float smoothstep(const float &t) { return t * t * (3 - 2 * t); } class ValueNoise { public: ValueNoise(unsigned seed = 2016) { std::mt19937 gen(seed); std::uniform_real_distribution<float> distrFloat; auto randFloat = std::bind(distrFloat, gen); // create an array of random values and initialize permutation table for (unsigned k = 0; k < kMaxTableSize; ++k) { r[k] = randFloat(); permutationTable[k] = k; } // shuffle values of the permutation table std::uniform_int_distribution<unsigned> distrUInt; auto randUInt = std::bind(distrUInt, gen); for (unsigned k = 0; k < kMaxTableSize; ++k) { unsigned i = randUInt() & kMaxTableSizeMask; std::swap(permutationTable[k], permutationTable[i]); permutationTable[k + kMaxTableSize] = permutationTable[k]; } } float eval(Vec2f &p) const { int xi = std::floor(p.x); int yi = std::floor(p.y); float tx = p.x - xi; float ty = p.y - yi; int rx0 = xi & kMaxTableSizeMask; int rx1 = (rx0 + 1) & kMaxTableSizeMask; int ry0 = yi & kMaxTableSizeMask; int ry1 = (ry0 + 1) & kMaxTableSizeMask; // random values at the corners of the cell using permutation table const float & c00 = r[permutationTable[permutationTable[rx0] + ry0]]; const float & c10 = r[permutationTable[permutationTable[rx1] + ry0]]; const float & c01 = r[permutationTable[permutationTable[rx0] + ry1]]; const float & c11 = r[permutationTable[permutationTable[rx1] + ry1]]; // 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 = lerp(c00, c10, sx); float nx1 = lerp(c01, c11, sx); // linearly interpolate the nx0/nx1 along they y axis return lerp(nx0, nx1, sy); } static const unsigned kMaxTableSize = 256; static const unsigned kMaxTableSizeMask = kMaxTableSize - 1; float r[kMaxTableSize]; unsigned permutationTable[kMaxTableSize * 2]; }; int main(int argc, char **argv) { unsigned imageWidth = 512; unsigned imageHeight = 512; float *noiseMap = new float[imageWidth * imageHeight]{ 0 }; #if 0 // [comment] // Generate white noise // [/comment] unsigned seed = 2016; std::mt19937 gen(seed); std::uniform_real_distribution<float> distr; auto dice = std::bind(distr, gen); // std::function<float()> for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { // generate a float in the range [0:1] noiseMap[j * imageWidth + i] = dice(); } } #elif 0 // [comment] // Generate value noise // [/comment] ValueNoise noise; float frequency = 0.05f; for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { // generate a float in the range [0:1] noiseMap[j * imageWidth + i] = noise.eval(Vec2f(i, j) * frequency); } } #elif 0 // [comment] // Generate fractal pattern // [/comment] ValueNoise noise; float frequency = 0.02f; float frequencyMult = 1.8; float amplitudeMult = 0.35; unsigned numLayers = 5; float maxNoiseVal = 0; for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { Vec2f pNoise = Vec2f(i, j) * frequency; float amplitude = 1; for (unsigned l = 0; l < numLayers; ++l) { noiseMap[j * imageWidth + i] += noise.eval(pNoise) * amplitude; pNoise *= frequencyMult; amplitude *= amplitudeMult; } if (noiseMap[j * imageWidth + i] > maxNoiseVal) maxNoiseVal = noiseMap[j * imageWidth + i]; } } for (unsigned i = 0; i < imageWidth * imageHeight; ++i) noiseMap[i] /= maxNoiseVal; #elif 0 // [comment] // Generate turbulence pattern // [/comment] ValueNoise noise; float frequency = 0.02f; float frequencyMult = 1.8; float amplitudeMult = 0.35; unsigned numLayers = 5; float maxNoiseVal = 0; for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { Vec2f pNoise = Vec2f(i, j) * frequency; float amplitude = 1; for (unsigned l = 0; l < numLayers; ++l) { noiseMap[j * imageWidth + i] += std::fabs(2 * noise.eval(pNoise) - 1) * amplitude; pNoise *= frequencyMult; amplitude *= amplitudeMult; } if (noiseMap[j * imageWidth + i] > maxNoiseVal) maxNoiseVal = noiseMap[j * imageWidth + i]; } } for (unsigned i = 0; i < imageWidth * imageHeight; ++i) noiseMap[i] /= maxNoiseVal; #elif 0 // [comment] // Generate marble pattern // [/comment] ValueNoise noise; float frequency = 0.02f; float frequencyMult = 1.8; float amplitudeMult = 0.35; unsigned numLayers = 5; for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { Vec2f pNoise = Vec2f(i, j) * frequency; float amplitude = 1; float noiseValue = 0; // compute some fractal noise for (unsigned l = 0; l < numLayers; ++l) { noiseValue += noise.eval(pNoise) * amplitude; pNoise *= frequencyMult; amplitude *= amplitudeMult; } // we "displace" the value i used in the sin() expression by noiseValue * 100 noiseMap[j * imageWidth + i] = (sin((i + noiseValue * 100) * 2 * M_PI / 200.f) + 1) / 2.f; } } #else 1 // [comment] // Generate wood pattern // [/comment] ValueNoise noise; float frequency = 0.01f; for (unsigned j = 0; j < imageHeight; ++j) { for (unsigned i = 0; i < imageWidth; ++i) { float g = noise.eval(Vec2f(i, j) * frequency) * 10; noiseMap[j * imageWidth + i] = g - (int)g; } } #endif // output noise map to PPM std::ofstream ofs; ofs.open("./noise.ppm", std::ios::out | std::ios::binary); ofs << "P6\n" << imageWidth << " " << imageHeight << "\n255\n"; for (unsigned k = 0; k < imageWidth * imageHeight; ++k) { unsigned char n = static_cast<unsigned char>(noiseMap[k] * 255); ofs << n << n << n; } ofs.close(); delete[] noiseMap; return 0; }