<script language="javascript" type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/0.7.3/p5.js"></script>     
body {margin:0px; padding:0px; overflow: hidden}
let roughness = 0.0;

function setup() {
  createCanvas(windowWidth, windowHeight);
}

function draw() {
  const SampleNum = 128;
  background(235, 255, 160);
  const radius = min(width, height) * 0.4;
  const cx = width / 2;
  const cy = height / 2;
  
  let nSamples = 0;
  
  noStroke();
  fill(0);
  for (let i = 0; i < SampleNum; i++) {
    const Xi = Hammersley(i, SampleNum);
    const N = createVector(0, 0, 1);
    const sampleVec = ImportanceSampleGGX(Xi, N, roughness);
    
    circle(sampleVec.x * radius + cx, cy - sampleVec.y * radius, 2);
    nSamples ++;
  }
  
  
  drawLabel(8, 32, "Roughness " + roughness.toFixed(3), LEFT);
  drawLabel(8, 56, "Number of samples " + nSamples, LEFT);
  
  roughness += 0.001;
  if (roughness > 1.0) {roughness = 0;}
}

function ImportanceSampleGGX(Xi, N, roughness)
{
    const a = roughness*roughness;
  
    const phi = 2.0 * PI * Xi[0];
  
    const cosTheta = sqrt((1.0 - Xi[1]) / (1.0 + (a*a - 1.0) * Xi[1]));
    const sinTheta = sqrt(1.0 - cosTheta*cosTheta);
  
    // from spherical coordinates to cartesian coordinates
    const H = createVector();
    H.x = cos(phi) * sinTheta;
    H.y = sin(phi) * sinTheta;
    H.z = cosTheta;
  
    // from tangent-space vector to world-space sample vector
    const up        = abs(N.z) < 0.999 ? createVector(0.0, 0.0, 1.0) : createVector(1.0, 0.0, 0.0);
    const tangent   = up.copy().cross(N).normalize();//normalize(cross(up, N));
  
  const bitangent = N.copy().cross(tangent);//cross(N, tangent);
  
    const v0 = p5.Vector.mult(tangent, H.x);
    const v1 = p5.Vector.mult(bitangent, H.y);
    const v2 = p5.Vector.mult(N, H.z);
  
    const sampleVec = p5.Vector.add(v0, p5.Vector.add(v1, v2));
    return sampleVec;
}  

function VanDerCorput(n, base)
{
    let invBase = 1.0 / base;
    let denom   = 1.0;
    let result  = 0.0;

    for (let i = 0; i < 32; ++i)
    {
        if (n > 0)
        {
            denom   = float(n) % 2.0;
            result += denom * invBase;
            invBase = invBase / 2.0;
            n       = floor(n / 2.0);
        }
    }

    return result;
}

function Hammersley(i, N) {
    return [i / N, VanDerCorput(i, 2)];
}  

function drawLabel(x, y, label, align = CENTER) {
  push();
  strokeWeight(0);
  textFont("monospace");
  textSize(14);
  textAlign(align);
  if (align == LEFT) {x += 6;}
  if (align == RIGHT) {x -= 6;}
  text(label, x, y);
  pop();
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.