MENU
Pointed Light (Per Fragment)
A nearby light source gives rise to pointed light. The direction of the light from a point light source differs at each position.
So the light direction needs to be calculated at each position:
<light direction> = <light source position> - <surface position>
While it is possible to implement the shading in the vertex shader, implementing it in the fragment shader yields a more realistic result.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE html><html><head>
<script src="/shared/webgl-library.js"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec4 p;
attribute vec4 n;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 normalMatrix;
varying vec3 v_Normal;
varying vec3 v_Position;
void main() {
gl_Position = projectionMatrix*viewMatrix*modelMatrix*p;
v_Position = vec3(modelMatrix * p);
v_Normal = normalize(vec3(normalMatrix * n));
}
</script>
<script id="fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision mediump float;
#endif
uniform vec3 lightColor;
uniform vec3 lightPosition;
uniform vec3 ambientLight;
varying vec3 v_Normal;
varying vec3 v_Position;
void main() {
vec4 surfaceColor = vec4(1.0,1.0,1.0,1.0);
vec3 normal = normalize(v_Normal);
vec3 lightDirection = normalize(lightPosition - v_Position);
float nDotL = max(dot(lightDirection, normal), 0.0);
vec3 diffuse = lightColor * surfaceColor.rgb * nDotL;
vec3 ambient = ambientLight * surfaceColor.rgb;
gl_FragColor = vec4(diffuse + ambient, surfaceColor.a);
}
</script>
<script>
function main() {
var canvas = document.getElementById('myCanvas');
var gl = canvas.getContext('webgl');
initShaders(gl, 'vs', 'fs');
var n = initVertexBuffers(gl);
var cvL = gl.getUniformLocation(gl.program, 'lightColor');
var pvL = gl.getUniformLocation(gl.program, 'lightPosition');
var avL = gl.getUniformLocation(gl.program, 'ambientLight');
gl.uniform3f(cvL, 0.8, 0.8, 0.8);
gl.uniform3f(pvL, 3.0, 6.0, 5.0);
gl.uniform3f(avL, 0.1, 0.1, 0.1);
var mmL = gl.getUniformLocation(gl.program, 'modelMatrix');
var vmL = gl.getUniformLocation(gl.program, 'viewMatrix');
var pmL = gl.getUniformLocation(gl.program, 'projectionMatrix');
var mm = new Matrix4();
var vm = new Matrix4();
var pm = new Matrix4();
mm.scale(1, 1.5, 1);
pm.perspective(30, canvas.width/canvas.height, 1, 10);
vm.lookAt(3, 1, 5, 0, 0, 0, 0, 1, 0);
gl.uniformMatrix4fv(mmL, false, new Float32Array(mm.entries));
gl.uniformMatrix4fv(vmL, false, new Float32Array(vm.entries));
gl.uniformMatrix4fv(pmL, false, new Float32Array(pm.entries));
var nmL = gl.getUniformLocation(gl.program, 'normalMatrix');
var nm = new Matrix4(mm.entries);
nm.inverse();
nm.transpose();
gl.uniformMatrix4fv(nmL, false, new Float32Array(nm.entries));
gl.clearColor(0, 0, 0, 1);
gl.enable(gl.DEPTH_TEST);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES,n,gl.UNSIGNED_SHORT,0);
}
function initVertexBuffers(gl) { // Create a sphere
var SPHERE_DIV = 50;
var i, ai, si, ci;
var j, aj, sj, cj;
var p1, p2;
var positions = [];
var indices = [];
for (j = 0; j <= SPHERE_DIV; j++) {
aj = j * Math.PI / SPHERE_DIV;
sj = Math.sin(aj);
cj = Math.cos(aj);
for (i = 0; i <= SPHERE_DIV; i++) {
ai = i * 2 * Math.PI / SPHERE_DIV;
si = Math.sin(ai);
ci = Math.cos(ai);
positions.push(si * sj); // X
positions.push(cj); // Y
positions.push(ci * sj); // Z
}
}
for (j = 0; j < SPHERE_DIV; j++) {
for (i = 0; i < SPHERE_DIV; i++) {
p1 = j * (SPHERE_DIV+1) + i;
p2 = p1 + (SPHERE_DIV+1);
indices.push(p1);
indices.push(p2);
indices.push(p1 + 1);
indices.push(p1 + 1);
indices.push(p2);
indices.push(p2 + 1);
}
}
initArrayBuffer(gl, 'p', new Float32Array(positions), 3);
initArrayBuffer(gl, 'n', new Float32Array(positions), 3);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
var indexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
return indices.length;
}
</script>
</head>
<body onload="main()">
<canvas id="myCanvas" width="500" height="500"></canvas>
</body>
</html>