Skip to content

Instantly share code, notes, and snippets.

@Erkaman
Last active April 22, 2018 11:02
Show Gist options
  • Save Erkaman/a62d5b920164a716d22bf63f260374cc to your computer and use it in GitHub Desktop.
Save Erkaman/a62d5b920164a716d22bf63f260374cc to your computer and use it in GitHub Desktop.
Program that provides empirical evidence that the clamped cosine lobes 0.5cos^2 and (5/6)cos^4 form a partition of unity over the unit sphere. This is related to the ambient dice paper: http://www.ppsloan.org/publications/AmbientDice.pdf
#include <stdio.h>
#include <math.h>
float max(float x, float y) {
return x > y ? x : y;
}
class vec3 {
public:
float x;
float y;
float z;
vec3(const float x_, const float y_, const float z_):
x(x_),
y(y_),
z(z_){
}
vec3() {
x = 0.0f;
y = 0.0f;
z = 0.0f;
}
friend vec3 operator*(const float s, const vec3& a) {
return vec3(s * a.x, s * a.y, s * a.z);
}
static float dot(const vec3& a, const vec3& b) {
return a.x * b.x + a.y * b.y + a.z * b.z;
}
};
const float PI = 3.14;
const float EPSILON = 1e-5f;
const float THETA_SPACING = 0.01f;
const float PHI_SPACING = 0.01f;
const float BIG_PHI = 0.618034f;
const float RCP_ICO_LEN = 1.0 / sqrt(1 + BIG_PHI * BIG_PHI);
// these are all the vertex groups from the ambient dice paper.
// they are all the vertices on the icosahedron defined in the paper.
// and the vectors are all normalized, by multiplying by RCP_ICO_LEN
const int N_ICO_VERTICES = 12;
vec3 ico_vertices[N_ICO_VERTICES] = {
// Group A.
RCP_ICO_LEN * vec3(+1.0f, +BIG_PHI, +0.0f),
RCP_ICO_LEN * vec3(+1.0f, -BIG_PHI, +0.0f),
RCP_ICO_LEN * vec3(-1.0f, +BIG_PHI, +0.0f),
RCP_ICO_LEN * vec3(-1.0f, -BIG_PHI, +0.0f),
// Group B
RCP_ICO_LEN * vec3(0.0f, +1.0, +BIG_PHI),
RCP_ICO_LEN * vec3(0.0f, +1.0, -BIG_PHI),
RCP_ICO_LEN * vec3(0.0f, -1.0, +BIG_PHI),
RCP_ICO_LEN * vec3(0.0f, -1.0, -BIG_PHI),
// Group C
RCP_ICO_LEN * vec3(+BIG_PHI, 0.0f, +1.0f),
RCP_ICO_LEN * vec3(+BIG_PHI, 0.0f, -1.0f),
RCP_ICO_LEN * vec3(-BIG_PHI, 0.0f, +1.0f),
RCP_ICO_LEN * vec3(-BIG_PHI, 0.0f, -1.0f),
};
int main() {
// we sample a whole bunch of units vectors on the unit sphere.
for(float theta = 0.0f; theta <= +2.0 * PI; theta += THETA_SPACING) {
for(float phi = 0.0f; phi <= +PI; phi += PHI_SPACING) {
const vec3 sample_v(sin(theta) * cos(phi), sin(theta) * sin(phi),cos(theta));
float sum = 0.0f;
/*
Every vertex on the icosahedron will define a clamped
squared cosine lobe: cos^2.
These lobes form a partition of unity over the unit sphere, the paper claims.
If we project some unit vector on all the lobes, then the sums of all those
projections should be 1, if the partition of unity property is true.
And if we compute this sum for many, many vertices on the unit sphere,
and this sum is always 1, then we know with great certainty that the lobes form
a partiton of unity.
*/
for(int iv = 0; iv < N_ICO_VERTICES; ++iv) {
const vec3 ico_v = ico_vertices[iv];
const float cos_theta = max(0.0f, vec3::dot(ico_v, sample_v));
const float cos_theta2 = cos_theta * cos_theta;
sum += 0.5f * cos_theta2;
}
if(fabs(sum - 1.0) > EPSILON) {
// negative result. it is not a partition of unity.
printf("0.5cos^2 IS NOT a partition of unity!\n");
printf("test failed for sample %f %f %f!\n", sample_v.x, sample_v.y, sample_v.z);
printf("sum was %f instead of 1.0!\n", sum);
return 1;
}
}
}
printf("0.5cos^2 IS a partition of unity!\n");
// now let's also check for (5/6)cos^4
for(float theta = 0.0f; theta <= +2.0 * PI; theta += THETA_SPACING) {
for(float phi = 0.0f; phi <= +PI; phi += PHI_SPACING) {
const vec3 sample_v(sin(theta) * cos(phi), sin(theta) * sin(phi),cos(theta));
float sum = 0.0f;
for(int iv = 0; iv < N_ICO_VERTICES; ++iv) {
const vec3 ico_v = ico_vertices[iv];
const float cos_theta = max(0.0f, vec3::dot(ico_v, sample_v));
const float cos_theta4 = cos_theta * cos_theta * cos_theta * cos_theta;
sum += (5.0f / 6.0f) * cos_theta4;
}
if(fabs(sum - 1.0) > EPSILON) {
// negative result. it is not partition of unity.
printf("(5.0/6.0)cos^4 IS NOT a partition of unity!\n");
printf("test failed for sample %f %f %f!\n", sample_v.x, sample_v.y, sample_v.z);
printf("sum was %f instead of 1.0!\n", sum);
return 1;
}
}
}
printf("(5.0/6.0)cos^4 IS a partition of unity!\n");
}
@dodoleon
Copy link

hello, I am reading the ambient dice paper, but dont know how to use partition of unity, and don't know how to use ambient dice to integral diffuse effect. Could you give me some formula to help me understand these questions, Thank you.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment