diff --git a/examples/cpp/data/sky.png b/examples/cpp/data/sky.png new file mode 100644 index 0000000000000000000000000000000000000000..587b869486d85ed42389db7a31a33255bb4f9654 Binary files /dev/null and b/examples/cpp/data/sky.png differ diff --git a/examples/cpp/rings.cpp b/examples/cpp/rings.cpp index 78076077a0eb12c7cd6bb0b14344cdab364309c5..d15b7aa9be801d807aea3a367f9137db9f46665b 100644 --- a/examples/cpp/rings.cpp +++ b/examples/cpp/rings.cpp @@ -1,6 +1,8 @@ /* * Three-dimensional pattern in C++ based on the "Rings" Processing example. * + * This version samples colors from an image, rather than using the HSV colorspace. + * * Uses noise functions modulated by sinusoidal rings, which themselves * wander and shift according to some noise functions. * @@ -8,80 +10,124 @@ */ #include <math.h> +#include <time.h> +#include <stdlib.h> #include "lib/color.h" #include "lib/effect.h" #include "lib/noise.h" +#include "lib/texture.h" class Rings : public Effect { public: Rings() - : d(0, 0, 0, 0) {} + : palette("data/sky.png"), + d(0, 0, 0, 0), + threshold(-1.0) + { + srand(time(0)); + seed = fmod(rand() / 1e3, 1e3); + } static const float xyzSpeed = 0.6; static const float xyzScale = 0.08; static const float wSpeed = 0.2; - static const float wRate = 0.001; + static const float wRate = 0.015; static const float ringScale = 1.5; static const float ringScaleRate = 0.01; static const float ringDepth = 0.2; static const float wanderSpeed = 0.04; - static const float wanderSize = 1.8; - static const float hueScale = 5.0; - static const float hueRate = 0.001; + static const float wanderSize = 1.2; + static const float brightnessContrast = 15.0; + static const float colorContrast = 2.0; + static const float targetBrightness = 0.1; + static const float thresholdGain = 0.1; + + // Sample colors along a curved path through a texture + Texture palette; + + // Pseudorandom number seed + float seed; // State variables Vec4 d; + float threshold; // Calculated once per frame - float hue, saturation; float spacing; + float colorParam; + float pixelTotal; + unsigned pixelCount; Vec3 center; virtual void beginFrame(const FrameInfo &f) { - hue = noise2(f.time * hueRate, 20.5) * hueScale; - - saturation = sq(std::min(std::max(0.7f * (0.5f + noise2(f.time * 0.01, 0.5)), 0.0f), 1.0f)); spacing = sq(0.5 + noise2(f.time * ringScaleRate, 1.5)) * ringScale; // Rotate movement in the XZ plane - float angle = noise2(f.time * 0.01, 30.5) * 10.0; - float speed = pow(fabsf(noise2(f.time * 0.01, 40.5)), 2.5) * xyzSpeed; + float angle = noise2(f.time * 0.01, seed + 30.5) * 10.0; + float speed = pow(fabsf(noise2(f.time * 0.01, seed + 40.5)), 2.5) * xyzSpeed; d[0] += cosf(angle) * speed * f.timeDelta; d[2] += sinf(angle) * speed * f.timeDelta; // Random wander along the W axis - d[3] += noise2(f.time * wRate, 3.5) * wSpeed * f.timeDelta; + d[3] += noise2(f.time * wRate, seed + 3.5) * wSpeed * f.timeDelta; // Update center position - center = Vec3(noise2(f.time * wanderSpeed, 50.9), - noise2(f.time * wanderSpeed, 51.4), - noise2(f.time * wanderSpeed, 51.7)) * wanderSize; - } + center = Vec3(noise2(f.time * wanderSpeed, seed + 50.9), + noise2(f.time * wanderSpeed, seed + 51.4), + noise2(f.time * wanderSpeed, seed + 51.7)) * wanderSize; - virtual void debug(const DebugInfo &di) - { - fprintf(stderr, "\t[rings] center = %f, %f, %f\n", center[0], center[1], center[2]); - fprintf(stderr, "\t[rings] d = %f, %f, %f, %f\n", d[0], d[1], d[2], d[3]); - fprintf(stderr, "\t[rings] hue = %f, saturation = %f\n", hue, saturation); + // Wander around the color palette + colorParam = seed + f.time * 0.05f; + + // Reset pixel total accumulators, used for the brightness calc in endFrame + pixelTotal = 0; + pixelCount = 0; } virtual void calculatePixel(Vec3& rgb, const PixelInfo &p) { float dist = len(p.point - center); Vec4 pulse = Vec4(sinf(d[2] + dist * spacing) * ringDepth, 0, 0, 0); - Vec4 s = Vec4(p.point * xyzScale, 0) + d; + Vec4 s = Vec4(p.point * xyzScale, seed) + d; Vec4 chromaOffset = Vec4(0, 0, 0, 10); - float n = fbm_noise4(s + pulse, 4) * 2.5f + 0.1f; - float m = fbm_noise4(s + chromaOffset, 2); + float n = (fbm_noise4(s + pulse, 4) + threshold) * brightnessContrast; + float m = fbm_noise4(s + chromaOffset, 2) * colorContrast; + + rgb = color(colorParam + m, sq(std::max(0.0f, n))); - hsv2rgb(rgb, - hue + 0.5 * m, - saturation, - std::min(0.9f, sq(std::max(0.0f, n))) - ); + // Keep a rough approximate brightness total, for closed-loop feedback + for (unsigned i = 0; i < 3; i++) { + pixelTotal += sq(std::min(1.0f, std::max(0.0f, rgb[i]))); + pixelCount++; + } + } + + virtual void endFrame(const FrameInfo &f) + { + // Adjust threshold in brightness-determining noise function, in order + // to try and keep the average pixel brightness at a particular level. + + if (pixelCount > 0) { + threshold += (targetBrightness - pixelTotal / pixelCount) * thresholdGain; + } + } + + virtual void debug(const DebugInfo &di) + { + fprintf(stderr, "\t[rings] seed = %f\n", seed); + fprintf(stderr, "\t[rings] center = %f, %f, %f\n", center[0], center[1], center[2]); + fprintf(stderr, "\t[rings] d = %f, %f, %f, %f\n", d[0], d[1], d[2], d[3]); + fprintf(stderr, "\t[rings] threshold = %f\n", threshold); + } + + virtual Vec3 color(float parameter, float brightness) + { + // Sample a color from our palette, using a lissajous curve within an image texture + return palette.sample( sinf(parameter) * 0.5f + 0.5f, + sinf(parameter * 0.86f) * 0.5f + 0.5f) * brightness; } };