|
| 1 | +/* |
| 2 | + * Copyright LWJGL. All rights reserved. |
| 3 | + * License terms: http://lwjgl.org/license.php |
| 4 | + */ |
| 5 | +#version 330 core |
| 6 | + |
| 7 | +#define PI 3.14159265359 |
| 8 | +#define TWO_PI 6.28318530718 |
| 9 | +#define ONE_OVER_PI (1.0 / PI) |
| 10 | +#define ONE_OVER_2PI (1.0 / TWO_PI) |
| 11 | + |
| 12 | +/* |
| 13 | + * Define the type of a random variable/vector which is used to compute |
| 14 | + * spatial values (positions, directions, angles, etc.). |
| 15 | + * Actually this should have been a typedef, but we can't do this in |
| 16 | + * GLSL. |
| 17 | + */ |
| 18 | +#define spatialrand vec2 |
| 19 | + |
| 20 | +/** |
| 21 | + * Compute an arbitrary unit vector orthogonal to the unit vector 'v' |
| 22 | + * which is either of the three base coordinate axes (or the negation of |
| 23 | + * either of them). |
| 24 | + * |
| 25 | + * http://lolengine.net/blog/2013/09/21/picking-orthogonal-vector-combing-coconuts |
| 26 | + * |
| 27 | + * @param v the unit base vector to compute an orthogonal unit vector |
| 28 | + * from |
| 29 | + * @returns the unit vector orthogonal to 'v' |
| 30 | + */ |
| 31 | +vec3 orthoBase(vec3 v) { |
| 32 | + return abs(v.x) > abs(v.z) ? vec3(-v.y, v.x, 0.0) : vec3(0.0, -v.z, v.y); |
| 33 | +} |
| 34 | + |
| 35 | +/** |
| 36 | + * Compute an arbitrary unit vector orthogonal to any vector 'v'. |
| 37 | + * |
| 38 | + * @param v the vector to compute an orthogonal unit vector from |
| 39 | + * @returns the unit vector orthogonal to 'v' |
| 40 | + */ |
| 41 | +vec3 ortho(vec3 v) { |
| 42 | + return normalize(cross(v, orthoBase(v))); |
| 43 | +} |
| 44 | + |
| 45 | +/** |
| 46 | + * http://amindforeverprogramming.blogspot.de/2013/07/random-floats-in-glsl-330.html?showComment=1507064059398#c5427444543794991219 |
| 47 | + */ |
| 48 | +uint hash3(uint x, uint y, uint z) { |
| 49 | + x += x >> 11; |
| 50 | + x ^= x << 7; |
| 51 | + x += y; |
| 52 | + x ^= x << 3; |
| 53 | + x += z ^ (x >> 14); |
| 54 | + x ^= x << 6; |
| 55 | + x += x >> 15; |
| 56 | + x ^= x << 5; |
| 57 | + x += x >> 12; |
| 58 | + x ^= x << 9; |
| 59 | + return x; |
| 60 | +} |
| 61 | + |
| 62 | +/** |
| 63 | + * Generate a floating-point pseudo-random number in [0, 1). |
| 64 | + * |
| 65 | + * With Monte Carlo sampling we need random numbers to generate random |
| 66 | + * vectors for shooting rays. |
| 67 | + * This function takes a vector of three arbitrary floating-point |
| 68 | + * numbers and based on those numbers returns a pseudo-random number in |
| 69 | + * the range [0..1). |
| 70 | + * Since in GLSL we have no other state/entropy than what we input as |
| 71 | + * uniforms or compute from varying variables (e.g. the invocation id of |
| 72 | + * the current work item), any "pseudo-random" number will necessarily |
| 73 | + * only be some kind of hash over the input. |
| 74 | + * This function however has very very good properties exhibiting no |
| 75 | + * patterns in the distribution of the random numbers, no matter the |
| 76 | + * magnitude of the input values. |
| 77 | + * |
| 78 | + * http://amindforeverprogramming.blogspot.de/2013/07/random-floats-in-glsl-330.html |
| 79 | + * |
| 80 | + * @param f some input vector of numbers to generate a single |
| 81 | + * pseudo-random number from |
| 82 | + * @returns a single pseudo-random number in [0, 1) |
| 83 | + */ |
| 84 | +float random(vec3 f) { |
| 85 | + uint mantissaMask = 0x007FFFFFu; |
| 86 | + uint one = 0x3F800000u; |
| 87 | + uvec3 u = floatBitsToUint(f); |
| 88 | + uint h = hash3(u.x, u.y, u.z); |
| 89 | + return uintBitsToFloat((h & mantissaMask) | one) - 1.0; |
| 90 | +} |
| 91 | + |
| 92 | +/** |
| 93 | + * Transform the given vector 'v' from its local frame into the |
| 94 | + * orthonormal basis with +Z = z. |
| 95 | + * |
| 96 | + * @param v the vector to transform |
| 97 | + * @param z the direction to transform +Z into |
| 98 | + * @returns the vector in the new basis |
| 99 | + */ |
| 100 | +vec3 around(vec3 v, vec3 z) { |
| 101 | + vec3 t = ortho(z), b = cross(z, t); |
| 102 | + return t * v.x + b * v.y + z * v.z; |
| 103 | +} |
| 104 | + |
| 105 | +/** |
| 106 | + * Compute the cartesian coordinates from the values typically computed |
| 107 | + * when generating sample directions/angles for isotropic BRDFs. |
| 108 | + * |
| 109 | + * @param rp this is a pseudo-random number in [0, 1) representing the |
| 110 | + * angle `phi` to rotate around the principal vector. For |
| 111 | + * isotropic BRDFs where `phi` has the same probability of |
| 112 | + * being chosen, this will always be a random number in [0, 1) |
| 113 | + * that can directly be used from rand.x given to all sample |
| 114 | + * direction generation functions |
| 115 | + * @param c this is the cosine of theta, the angle in [0, PI/2) giving |
| 116 | + * the angle between the principal vector and the one to |
| 117 | + * generate. Being the cosine of an angle in [0, PI/2) the |
| 118 | + * value 'c' is itself in the range [0, 1) |
| 119 | + * @returns the cartesian direction vector |
| 120 | + */ |
| 121 | +vec3 isotropic(float rp, float c) { |
| 122 | + float p = TWO_PI * rp, s = sqrt(1.0 - c*c); |
| 123 | + return vec3(cos(p) * s, sin(p) * s, c); |
| 124 | +} |
| 125 | + |
| 126 | +/** |
| 127 | + * Generate a uniformly distributed random vector on the hemisphere |
| 128 | + * around the given normal vector 'n'. |
| 129 | + * |
| 130 | + * http://mathworld.wolfram.com/SpherePointPicking.html |
| 131 | + * |
| 132 | + * @param n the normal vector determining the direction of the |
| 133 | + * hemisphere |
| 134 | + * @param rand a vector of two floating-point pseudo-random numbers |
| 135 | + * @returns the random hemisphere vector plus its probability density |
| 136 | + * value |
| 137 | + */ |
| 138 | +vec4 randomHemispherePoint(vec3 n, spatialrand rand) { |
| 139 | + return vec4(around(isotropic(rand.x, rand.y), n), ONE_OVER_2PI); |
| 140 | +} |
| 141 | +/** |
| 142 | + * Compute the probability density value of randomHemispherePoint() |
| 143 | + * generating the vector 'v'. |
| 144 | + * |
| 145 | + * @param n the normal vector determining the direction of the |
| 146 | + * hemisphere |
| 147 | + * @param v the v vector as returned by randomHemispherePoint() |
| 148 | + * @returns pdf(v) for the uniform hemisphere distribution |
| 149 | + */ |
| 150 | +float hemisphereProbability(vec3 n, vec3 v) { |
| 151 | + return dot(v, n) <= 0.0 ? 0.0 : ONE_OVER_2PI; |
| 152 | +} |
| 153 | + |
| 154 | +/** |
| 155 | + * Generate a cosine-weighted random vector on the hemisphere around the |
| 156 | + * given normal vector 'n'. |
| 157 | + * |
| 158 | + * The probability density of any vector is directly proportional to the |
| 159 | + * cosine of the angle between that vector and the given normal 'n'. |
| 160 | + * |
| 161 | + * http://www.rorydriscoll.com/2009/01/07/better-sampling/ |
| 162 | + * |
| 163 | + * @param n the normal vector determining the direction of the |
| 164 | + * hemisphere |
| 165 | + * @param rand a vector of two floating-point pseudo-random numbers |
| 166 | + * @returns the cosine-weighted random hemisphere vector plus its |
| 167 | + * probability density value |
| 168 | + */ |
| 169 | +vec4 randomCosineWeightedHemispherePoint(vec3 n, spatialrand rand) { |
| 170 | + float c = sqrt(rand.y); |
| 171 | + return vec4(around(isotropic(rand.x, c), n), c * ONE_OVER_PI); |
| 172 | +} |
| 173 | + |
| 174 | +/** |
| 175 | + * Generate Phong-weighted random vector around the given reflection |
| 176 | + * vector 'r'. |
| 177 | + * Since the Phong BRDF has higher values when the outgoing vector is |
| 178 | + * close to the perfect reflection vector of the incoming vector across |
| 179 | + * the normal, we generate directions primarily around that reflection |
| 180 | + * vector. |
| 181 | + * |
| 182 | + * http://blog.tobias-franke.eu/2014/03/30/notes_on_importance_sampling.html |
| 183 | + * |
| 184 | + * @param r the direction of perfect reflection |
| 185 | + * @param a the power to raise the cosine term to in the Phong model |
| 186 | + * @param rand a vector of two pseudo-random numbers |
| 187 | + * @returns the Phong-weighted random vector |
| 188 | + */ |
| 189 | +vec4 randomPhongWeightedHemispherePoint(vec3 r, float a, spatialrand rand) { |
| 190 | + float ai = 1.0 / (a + 1.0), pr = (a + 1.0) * pow(rand.y, a * ai) * ONE_OVER_2PI; |
| 191 | + return vec4(around(isotropic(rand.x, pow(rand.y, ai)), r), pr); |
| 192 | +} |
| 193 | + |
| 194 | +/** |
| 195 | + * Generate a random vector uniformly distributed over the disk with 'd' |
| 196 | + * pointing at its center and with the radius 'r' at a distance of 'd'. |
| 197 | + * |
| 198 | + * https://en.wikipedia.org/wiki/Solid_angle#Cone,_spherical_cap,_hemisphere |
| 199 | + * https://en.wikipedia.org/wiki/Angular_diameter#Formula |
| 200 | + * |
| 201 | + * @param n the normalize direction vector towards the disk's center |
| 202 | + * @param d the distance to the disk |
| 203 | + * @param r the radius of the disk |
| 204 | + * @param rand a vector of two pseudo-random numbers |
| 205 | + * @returns the random disk sample vector |
| 206 | + */ |
| 207 | +vec4 randomDiskPoint(vec3 n, float d, float r, spatialrand rand) { |
| 208 | + float D = r/d, c = inversesqrt(1 + D * D), pr = ONE_OVER_2PI / (1.0 - c); |
| 209 | + return vec4(around(isotropic(rand.x, 1.0 - rand.y * (1.0 - c)), n), pr); |
| 210 | +} |
| 211 | +/** |
| 212 | + * Compute the probability density value of randomDiskPoint() |
| 213 | + * generating the z vector 'z'. |
| 214 | + * |
| 215 | + * https://en.wikipedia.org/wiki/Solid_angle#Cone,_spherical_cap,_hemisphere |
| 216 | + * https://en.wikipedia.org/wiki/Angular_diameter#Formula |
| 217 | + * |
| 218 | + * @param the normalize direction vector towards the disk's center |
| 219 | + * @param d the distance to the disk |
| 220 | + * @param r the radius of the disk |
| 221 | + * @param v the vector as returned by randomDiskPoint() |
| 222 | + * @returns pdf(z) for the disk distribution |
| 223 | + */ |
| 224 | +float diskProbability(vec3 n, float d, float r, vec3 v) { |
| 225 | + float D = r/d, c = inversesqrt(1 + D * D), pr = ONE_OVER_2PI / (1.0 - c); |
| 226 | + return dot(n, v) <= c ? 0.0 : pr; |
| 227 | +} |
0 commit comments