canvas = {
const pixels = imageData.data;
const rgbColor = hexToRGB(hexColor);
const cx = canvasWidth / 2, cy = canvasHeight / 2;
const radius2 = radius * radius;
const negLight = light.map(x => -x);
let i = 0;
for (let y = 0; y < canvasHeight; y++) {
for (let x = 0; x < canvasWidth; x++, i += 4) {
const dx = x - cx;
const dy = y - cy;
const d2 = dx*dx + dy*dy;
if (d2 <= radius2) {
const normal = normalize([
Math.sin(dx / radius * Math.PI/2),
Math.cos(dy / radius * Math.PI/2 + Math.PI/2),
Math.sqrt(radius2 - dx*dx - dy*dy) / radius
]);
const nDotL = dot(normal, negLight);
let diffuse = Math.max(0, nDotL) * diffuseStrength;
diffuse += ambientStrength;
let specular = 0;
if (nDotL > 0) {
const lightReflect = reflect(light, normal);
const eye = [0, 0, radius * 20];
const eyeToSurface = normalize(sub(eye, normal.map(x => x * radius)));
specular = Math.pow(Math.max(0, dot(eyeToSurface, lightReflect)), shiny);
specular *= specularStrength;
specular *= 255;
}
pixels[i + 0] = rgbColor[0] * diffuse + specular;
pixels[i + 1] = rgbColor[1] * diffuse + specular;
pixels[i + 2] = rgbColor[2] * diffuse + specular;
pixels[i + 3] = 255;
}
else {
pixels[i + 0] = 0;
pixels[i + 1] = 0;
pixels[i + 2] = 0;
pixels[i + 3] = 255;
}
}
}
ctx.putImageData(imageData, 0, 0);
return ctx.canvas;
}