Skip to content

Commit aa57aae

Browse files
committed
Add multiple importance sampling tutorial/demo
1 parent e9f095a commit aa57aae

File tree

8 files changed

+1559
-26
lines changed

8 files changed

+1559
-26
lines changed

res/org/lwjgl/demo/opengl/raytracing/tutorial3/random.glsl

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -136,16 +136,15 @@ vec3 isotropic(float rp, float c) {
136136
* value
137137
*/
138138
vec4 randomHemispherePoint(vec3 n, spatialrand rand) {
139-
float c = rand.y;
140-
return vec4(around(isotropic(rand.x, c), n), ONE_OVER_2PI);
139+
return vec4(around(isotropic(rand.x, rand.y), n), ONE_OVER_2PI);
141140
}
142141

143142
/**
144-
* Generate a cosine-weighted random vector on the hemisphere around
145-
* the given normal vector 'n'.
143+
* Generate a cosine-weighted random vector on the hemisphere around the
144+
* given normal vector 'n'.
146145
*
147-
* The probability density of any vector is directly proportional to
148-
* the cosine of the angle between that vector and the given normal 'n'.
146+
* The probability density of any vector is directly proportional to the
147+
* cosine of the angle between that vector and the given normal 'n'.
149148
*
150149
* http://www.rorydriscoll.com/2009/01/07/better-sampling/
151150
*
@@ -163,10 +162,10 @@ vec4 randomCosineWeightedHemispherePoint(vec3 n, spatialrand rand) {
163162
/**
164163
* Generate Phong-weighted random vector around the given reflection
165164
* vector 'r'.
166-
* Since the Phong BRDF has higher values when the outgoing vector
167-
* is close to the perfect reflection vector of the incoming vector
168-
* across the normal, we generate directions primarily around that
169-
* reflection vector.
165+
* Since the Phong BRDF has higher values when the outgoing vector is
166+
* close to the perfect reflection vector of the incoming vector across
167+
* the normal, we generate directions primarily around that reflection
168+
* vector.
170169
*
171170
* http://blog.tobias-franke.eu/2014/03/30/notes_on_importance_sampling.html
172171
*
@@ -176,6 +175,6 @@ vec4 randomCosineWeightedHemispherePoint(vec3 n, spatialrand rand) {
176175
* @returns the Phong-weighted random vector
177176
*/
178177
vec4 randomPhongWeightedHemispherePoint(vec3 r, float a, spatialrand rand) {
179-
float ai = 1.0 / (a + 1.0), c = pow(rand.y, ai);
180-
return vec4(around(isotropic(rand.x, c), r), (a + 1.0) * pow(rand.y, a * ai) * ONE_OVER_2PI);
178+
float ai = 1.0 / (a + 1.0), pr = (a + 1.0) * pow(rand.y, a * ai) * ONE_OVER_2PI;
179+
return vec4(around(isotropic(rand.x, pow(rand.y, ai)), r), pr);
181180
}

res/org/lwjgl/demo/opengl/raytracing/tutorial3/raytracing.glsl

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ ivec2 px;
118118
* box 'b' and return the values of the parameter 't' at which the ray
119119
* enters and exists the box, called (tNear, tFar). If there is no
120120
* intersection then tNear > tFar or tFar < 0.
121+
*
122+
* @param origin the ray's origin position
123+
* @param dir the ray's direction vector
124+
* @param b the box to test
125+
* @returns (tNear, tFar)
121126
*/
122127
vec2 intersectBox(vec3 origin, vec3 dir, const box b) {
123128
vec3 tMin = (b.min - origin) / dir;
@@ -422,7 +427,7 @@ vec3 trace(vec3 origin, vec3 dir) {
422427
att *= brdf(b, s.xyz, -dir, normal);
423428
}
424429
/*
425-
* Use the newly generate direction vector as the next one to
430+
* Use the newly generated direction vector as the next one to
426431
* follow.
427432
*/
428433
dir = s.xyz;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: http://lwjgl.org/license.php
4+
*/
5+
/* The texture we are going to sample */
6+
uniform sampler2D tex;
7+
8+
/* This comes interpolated from the vertex shader */
9+
in vec2 texcoord;
10+
11+
out vec4 color;
12+
13+
void main(void) {
14+
/* Well, simply sample the texture */
15+
color = texture2D(tex, texcoord);
16+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/*
2+
* Copyright LWJGL. All rights reserved.
3+
* License terms: http://lwjgl.org/license.php
4+
*/
5+
/* The position of the vertex as two-dimensional vector */
6+
in vec2 vertex;
7+
8+
/* Write interpolated texture coordinate to fragment shader */
9+
out vec2 texcoord;
10+
11+
void main(void) {
12+
gl_Position = vec4(vertex, 0.0, 1.0);
13+
/*
14+
* Compute texture coordinate by simply
15+
* interval-mapping from [-1..+1] to [0..1]
16+
*/
17+
texcoord = vertex * 0.5 + vec2(0.5, 0.5);
18+
}
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
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

Comments
 (0)