cosineDirection I get. Just a random direction in the hemisphere with the pole "nor".
coneDirection should return a random direction within a cone. I faked this by using either a flat disk or hemisphere pushed in the direction of the normal but there must be a better way. If I get the polar coordinates and "jitter" them randomly will that be uniform??
For a cone direction on a hemisphere, I use the following in Fragmentarium in sample e.g. a sun-like light source:
vec3 getSample(vec3 dir, float extent) {
// Create orthogonal vector (fails for z,y = 0)
vec3 o1 = normalize(vec3(0., -dir.z, dir.y));
vec3 o2 = normalize(cross(dir, o1));
// Convert to spherical coords aligned to dir
vec2 r = getUniformRandomVec2();
r.x=r.x*2.*PI;
r.y=1.0-r.y*extent;
float oneminus = sqrt(1.0-r.y*r.y);
return cos(r.x)*oneminus*o1+sin(r.x)*oneminus*o2+r.y*dir;
}
Here ‘extent’ is the size of the light source we sample. It is given as ’1-cos(angle)’, so 0 means a point-like light source (sharp shadows) and 1 means a full hemisphere light source (no shadows).
It is formular 34 in
http://people.cs.kuleuven.be/~philip.dutre/GI/TotalCompendium.pdf, just adapted to a coordinate system aligned with the 'dir' direction.
But you shouldn't use the above formula for glossy specular reflectance. I can see that IQ suggests it, but it is really a hack - you are effectively sampling using a box distribution function, whereas the phong reflection uses a cosine-power distribution. You need to sample the full hemisphere and weight the samples according to dot(reflectedVector, sampleDirection)^Power. Since this is slow, you will want to use importance sampling, and sample according to the cosinus-distribution. Notice, that you should not multiply by the samples by the dot-product-power term if you do this.
Here is a cosine-power distribution sampling function:
vec3 getSampleBiased(vec3 dir, float power) {
// create orthogonal vector (fails for z,y = 0)
vec3 o1 = normalize( vec3(0., -dir.z, dir.y));
vec3 o2 = normalize(cross(dir, o1));
// Convert to spherical coords aligned to dir;
vec2 r = rand(viewCoord*(float(backbufferCounter)+1.0));
if (Stratify) {r*=0.1; r+= cx;}
r.x=r.x*2.*PI;
r.y = 1.0-r.y;
// This should be cosine^n weighted.
// See, e.g. http://people.cs.kuleuven.be/~philip.dutre/GI/TotalCompendium.pdf
// Item 36
r.y=pow(r.y,1.0/(power+1.0));
float oneminus = sqrt(1.0-r.y*r.y);
vec3 sdir = cos(r.x)*oneminus*o1+
sin(r.x)*oneminus*o2+
r.y*dir;
return sdir;
}
Btw, I think that IQ's cosineDirection means a direction chosen according to the power-1 cosinus distribution (because the diffuse light has a cos(normal, sampleDirection) weight), and not a random direction.
The code above is part of the 'Theory/Convolution.frag' example in Fragmentarium. This fragment can be used to derive precalculated specular and diffuse light maps for IBL lightning. I plan to write a blog entry with more details of this soon.