Cogs.Core
NoiseSampler.cpp
1#include "NoiseSampler.h"
2
3#include <iostream>
4#include <cstdlib>
5#include <cmath>
6#include <algorithm>
7
8#include <glm/vec2.hpp>
9#include <glm/vec4.hpp>
10
11#include <random>
12
13namespace
14{
15 char grad2D[4][2] =
16 {
17 { 1, 1 },
18 { -1, 1 },
19 { 1, -1 },
20 { -1, -1 }
21 };
22}
23
24namespace Cogs
25{
26 namespace Core
27 {
28 NoiseSampler::NoiseSampler(unsigned int seed)
29 {
30 //if (permutationTable.empty()) {
31
32 // Create permuation table
33 std::minstd_rand eng(seed);
34 std::uniform_int_distribution<int> dist(0, RAND_MAX);
35
36 permutationTable.resize(256);
37 for (unsigned i = 0; i < 256; i++) {
38 permutationTable[i] = static_cast<uint8_t>(i);
39 }
40 for (int i = 255; i > 0; i--) {
41 int j = dist(eng) / ((RAND_MAX / i) + 1); // use higher-order bits of rand
42 std::swap(permutationTable[j], permutationTable[i]);
43 }
44
45 // create gradient table
46 gradient2DTable.resize(256 * 2);
47 for (int i = 0; i < 256; i++) {
48 float theta = dist(eng) * (float((2.0f * glm::pi<float>()) / float(RAND_MAX))) - 1.f;
49 gradient2DTable[2 * i + 0] = std::cos(theta);
50 gradient2DTable[2 * i + 1] = std::sin(theta);
51 }
52 //}
53 }
54
55 float NoiseSampler::gradNoise2D(glm::vec2 t, glm::ivec2 period)
56 {
57 glm::vec2 ti = glm::floor(t); // integer part
58
59 glm::ivec2 tl(int(ti.x) % period.x, int(ti.y) % period.y);
60 glm::ivec2 tq((tl.x + 1) % period.x, (tl.y + 1) % period.y);
61
62 glm::vec2 tf = t - ti; // fractional part
63 glm::vec2 sf = glm::vec2(1.f) - tf;
64
65 // we do a reparameterization of the fractional part, to improve smoothness
66 // over lattice lines. In Perlin's original 1984 paper, the
67 // reparameterization was
68 //
69 // phi = 3t^2-2t^3, phi(0) = 0, phi(1) = 1
70 // phi' = 6t - 6t^2, phi'(0) = 0, phi'(1) = 0,
71 // phi'' = 6 - 12t, phi''(0) = 6, phi''(1) = -6
72 //
73 // which has nonzero second-order derivatives at 0 and 1. This becomes
74 // visible in specular highlights, and in "Improving Noise", Perlin suggests
75 // to use
76 //
77 // phi = 6t^5 - 15t^4 + 10t^3, phi(0) = 0, phi(1) = 1
78 // phi' = 30t^4 - 60t^3 + 30t^2, phi'(0) = 0, phi'(1) = 0,
79 // phi'' = 120t^3 - 180t^2 + 60t, phi'(0) = 0, phi'(1) = 0,
80 //
81 // which indeed has zero second-order derivatives.
82 glm::vec2 phi_t = tf*tf*tf*(6.f*tf*tf - 15.f*tf + 10.f);
83 glm::vec2 phi_s = glm::vec2(1.f) - phi_t;
84
85 int l0x = permutationTable[tl.x & 0xff];
86 int l00 = (permutationTable[(l0x + tl.y)&0xff]);
87 int l01 = (permutationTable[(l0x + tq.y)&0xff]);
88 int l1x = permutationTable[tq.x & 0xff];
89 int l10 = (permutationTable[(l1x + tl.y)&0xff]);
90 int l11 = (permutationTable[(l1x + tq.y)&0xff]);
91
92#if 1
93 float vy0 = (grad2D[l00&0x3][0] * tf.x + grad2D[l00&0x3][1] * tf.y)*phi_s.x
94 + (grad2D[l10&0x3][0] * sf.x + grad2D[l10&0x3][1] * tf.y)*phi_t.x;
95 float vy1 = (grad2D[l01&0x3][0] * tf.x + grad2D[l01&0x3][1] * sf.y)*phi_s.x
96 + (grad2D[l11&0x3][0] * sf.x + grad2D[l11&0x3][1] * sf.y)*phi_t.x;
97#else
98 float vy0 = (gradient2DTable[2 * l00 + 0] * tf.x + gradient2DTable[2 * l00 + 1] * tf.y)*phi_s.x
99 + (gradient2DTable[2 * l10 + 0] * sf.x + gradient2DTable[2 * l10 + 1] * tf.y)*phi_t.x;
100
101 float vy1 = (gradient2DTable[2 * l01 + 0] * tf.x + gradient2DTable[2 * l01 + 1] * sf.y)*phi_s.x
102 + (gradient2DTable[2 * l11 + 0] * sf.x + gradient2DTable[2 * l11 + 1] * sf.y)*phi_t.x;
103#endif
104
105 return vy0*phi_s.y + vy1*phi_t.y;
106 }
107
108 void NoiseSampler::gradNoise2D(float* dst, float frequency, int w, int h)
109 {
110 glm::ivec2 period(w/frequency, h/frequency);
111
112 float a = 1.f / std::max(w, h);
113 for (int j = 0; j < h; j++) {
114 float v = a*static_cast<float>(j);
115 for (int i = 0; i < w; i++) {
116 float u = a* static_cast<float>(i);
117 dst[j*w + i] = gradNoise2D(frequency*glm::vec2(u+0.5f, v+0.5f), period);
118 }
119 }
120 }
121
122 void NoiseSampler::gradNoise2DSum(float* dst, float frequency_min, float frequency_max, int w, int h)
123 {
124 std::vector<glm::vec4> param; // shift u, shift v, frequency, weight
125 std::vector<glm::ivec2> periods;
126 for (float f = frequency_min; f < frequency_max; f = f * 2) {
127 param.push_back(glm::vec4(1.f / f, 1.f / f, f, 2.f*frequency_min / f));
128 periods.push_back(glm::ivec2(w / f, h / f));
129 }
130
131 float a = 1.f / std::max(w, h);
132
133 for (int j = 0; j < h; j++) {
134 float v = a*static_cast<float>(j);
135 for (int i = 0; i < w; i++) {
136 float u = a*static_cast<float>(i);
137
138 float sum = 0.f;
139 for (size_t k = 0; k < periods.size(); k++) {
140 sum += param[k].w*gradNoise2D(param[k].z*glm::vec2(u + 0.5f, v + 0.5f), periods[k]);
141 }
142 dst[j*w + i] = sum;
143 }
144 }
145 }
146
147 void NoiseSampler::gradNoise2DTurbulence(float* dst, float frequency_min, float frequency_max, int w, int h)
148 {
149 std::vector<glm::vec4> param; // shift u, shift v, frequency, weight
150 std::vector<glm::ivec2> periods;
151 for (float f = frequency_min; f < frequency_max; f = f * 2) {
152 param.push_back(glm::vec4(1.f / f, 1.f / f, f, 2.f*frequency_min / f));
153 periods.push_back(glm::ivec2(w / f, h / f));
154 }
155
156
157 float a = 1.f / std::max(w, h);
158
159 for (int j = 0; j < h; j++) {
160 float v = a*static_cast<float>(j);
161 for (int i = 0; i < w; i++) {
162 float u = a*static_cast<float>(i);
163
164 float sum = 0.f;
165 for (size_t k = 0; k < periods.size(); k++) {
166 sum += std::abs(param[k].w*gradNoise2D(param[k].z*glm::vec2(u+0.5f, v+0.5f), periods[k]));
167 }
168 dst[j*w + i] = sum - 1.f;
169 }
170 }
171 }
172 } // namespace Core
173} // namespace Cogs
Contains all Cogs related functionality.
Definition: FieldSetter.h:23