├── LICENSE ├── README.md ├── gradient_noise.cpp └── gradient_noise.hpp /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Wes 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gradient Noise 2 | ## Gradient noise with a templated number of dimensions 3 | 4 | [Animation of 3D gradient noise](http://i.imgur.com/bvJ1E7h.mp4) 5 | 6 | [Image of 2D gradient noise](http://i.imgur.com/RO3qT7k.png) 7 | 8 | [Image of 1D gradient noise](http://i.imgur.com/iClvq3j.png) 9 | 10 | ### How it works 11 | Gradient noise is generated by cubically interpolating pseudorandom nodes. The nodes have n-dimensional integer positions, but noise values are generated from n-dimensional floating-point positions. Generating gradient noise from a position starts with getting the `unit_position` and the `unit_offset`. The `unit_position` is a copy of the position, but all the axis are floored and casted to an integer. The `unit_offset` is the offset of the `position` from the `unit_position`. Next, the nodes are generated. Nodes are pseudorandom floating-point values between -1 and 1 which are associated with an n-dimensional position. To get a unique pseudorandom number from an n-dimensional position, a `std::seed_seq` is used. The `std::seed_seq` is initialized with each of the axis positions. After the nodes are generated, they are interpolated one dimension at a time using the cubic interpolation function. For n dimensions, 4^n nodes are required, and 4^(n - 1) cubic interpolations are required. The value returned from the final cubic interpolation is the final noise value. 12 | 13 | ### Why it's useful 14 | Gradient noise is useful for a wide variety of applications including procedural content generation, simulations, and graphical effects. 15 | 16 | ### How to use it 17 | To generate gradient noise, create a `gradient_noise` object, then use the `operator()` method to generate noise values. Returned noise values are between -1 and 1, but due to how cubic interpolation works, values slightly outside of this range are possible. 18 | `gradient_noise` takes two template parameters. The first template parameter, `float_type`, determines the return type and parameter type. The second template parameter, `dimension_count`, determines the number of dimensions. `gradient_noise` is declared inside the `gnd` namespace and can optionally be initialized with a seed. Construction might look like this: `gnd::gradient_noise my_gradient_noise(my_seed)`. If no seed is provided, a default seed is used. After creation, `gradient_noise` can be reseeded using the `seed` method. *Example code below.* 19 | ```cpp 20 | // Create a 3D gradient noise engine using 42 as a seed 21 | gnd::gradient_noise gradientNoise3d(42); 22 | 23 | // Print a gradient noise value using (123, 555, 777) as a position 24 | std::cout << gradientNoise3d({123.0f, 555.0f, 777.0f}) << std::endl; 25 | 26 | // Reseed the gradient noise engine using 9001 as a new seed 27 | gradientNoise3d.seed(9001); 28 | 29 | // Print a new gradient noise value using the same position as last time 30 | std::cout << gradientNoise3d({123.0f, 555.0f, 777.0f}) << std::endl; 31 | 32 | // Create a 4D gradient noise engine using 1337 as a seed 33 | gnd::gradient_noise gradientNoise4d(1337); 34 | 35 | // Print a gradient noise value using (123, 555, 777, 999) as a position 36 | std::cout << gradientNoise4d({123.0, 555.0, 777.0, 999.0}) << std::endl; 37 | ``` 38 | 39 | ### Notes 40 | `gnd::gradient_noise` is designed to be consistent with `std::default_random_engine`. They both use `operator()` overloads to generate values, they can both be reseeded with a `seed` method, and they can both be constructed with seeds optionally. `gnd::gradient_noise` uses `std::default_random_engine::result_type` as the seed type and `std::default_random_engine::default_seed` as the default seed. 41 | -------------------------------------------------------------------------------- /gradient_noise.cpp: -------------------------------------------------------------------------------- 1 | #include "gradient_noise.hpp" 2 | 3 | // Everything is defined in gradient_noise.hpp 4 | -------------------------------------------------------------------------------- /gradient_noise.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | // "Generic N-Dimensional" 6 | namespace gnd{ 7 | /* float_type is the type of the noise values and the noise parameters 8 | dimension_count is the number of dimensions the noise has */ 9 | template 10 | class gradient_noise{ 11 | public: 12 | // The type of the engine 13 | typedef std::default_random_engine engine_type; 14 | 15 | // The type of the distribution 16 | typedef std::uniform_real_distribution distribution_type; 17 | 18 | // The type of the seed of the engine and the noise 19 | typedef engine_type::result_type seed_type; 20 | 21 | // The number of random numbers discarded after a new engine seed 22 | static constexpr unsigned int discard_count = 1; 23 | 24 | // Construct gradient noise with an optional seed 25 | gradient_noise(engine_type::result_type seed = engine_type::default_seed): m_seed(seed){} 26 | 27 | // Seed the gradient noise 28 | void seed(seed_type val = engine_type::default_seed){m_seed = val;} 29 | 30 | // Return a noise value from an n-dimensional position 31 | auto operator()(std::array position){ 32 | // The unit position 33 | std::array unit_position; 34 | 35 | // The unit position offset 36 | std::array offset_position = position; 37 | 38 | // For every dimension 39 | for(std::size_t dimension = 0; dimension < dimension_count; dimension++){ 40 | // The unit position is the same as the position, but it's the integer floor of all axis positions 41 | unit_position[dimension] = (int)floor(position[dimension]); 42 | 43 | // The offset position is the position within the node 'cell' 44 | offset_position[dimension] -= unit_position[dimension]; 45 | } 46 | 47 | // The psuedorandom nodes to be interpolated 48 | std::array nodes; 49 | 50 | // For every node 51 | for(std::size_t node = 0; node < nodes.size(); node++){ 52 | // The node's position is seed_type because it's used as a seed 53 | std::array node_position; 54 | 55 | // Use the private seed as an extra dimension 56 | node_position[dimension_count] = m_seed * 2; 57 | 58 | // For every dimension 59 | for(std::size_t dimension = 0; dimension < dimension_count; dimension++){ 60 | // Get the n-dimensional position of the node 61 | node_position[dimension] = (node / (int)pow(4, (double)dimension)) % 4 + unit_position[dimension]; 62 | } 63 | 64 | // Make a seed sequence from the node position 65 | std::seed_seq seq(node_position.begin(), node_position.end()); 66 | 67 | // An array with only one value lol 68 | std::array node_seed; 69 | 70 | // Get the node seed 71 | seq.generate(node_seed.begin(), node_seed.end()); 72 | 73 | // Seed the engine with the final node seed 74 | engine_type engine(node_seed[0]); 75 | 76 | // Escape from zero-land 77 | engine.discard(discard_count); 78 | 79 | // Get node value 80 | nodes[node] = distribution_type(-1.0, 1.0)(engine); 81 | } 82 | 83 | // For every dimension 84 | for(std::size_t dimension = 0; dimension < dimension_count; dimension++){ 85 | const auto interpolated_node_count = (nodes.size() / 4) / (int)pow(4, (double)dimension); 86 | for(std::size_t interpolated_node = 0; interpolated_node < interpolated_node_count; interpolated_node++){ 87 | // The node that will be overwritten with the interpolated node (every fourth node is overwritter) 88 | auto node = interpolated_node * 4; 89 | 90 | // Overwrite every 4th node with an interpolation of the 3 proceding nodes including the overwritten node 91 | nodes[interpolated_node] = cerp(nodes[node], nodes[node + 1], nodes[node + 2], nodes[node + 3], offset_position[dimension]); 92 | } 93 | } 94 | 95 | // The final noise value 96 | return nodes[0]; 97 | } 98 | 99 | // Return a cubic interpolation of y 100 | static constexpr auto cerp(float_type y0, float_type y1, float_type y2, float_type y3, float_type mu){ 101 | float_type a0 = y3 - y2 - y0 + y1, a1 = y0 - y1 - a0, a2 = y2 - y0, a3 = y1, mu2 = mu * mu; 102 | return a0 * mu * mu2 + a1 * mu2 + a2 * mu + a3; 103 | } 104 | private: 105 | // The seed of the gradient noise 106 | seed_type m_seed; 107 | }; 108 | }; 109 | --------------------------------------------------------------------------------