Hi,
Long time On/Off lurker here and now I'm here with a question that seems to be so common that I just can't find an answer.
Light appears to rotate around the left sphere even thou it should be fixed like on the right sphere.
It seems not to be a shader problem, which I don't have knowhow (yet) to fix, but rather the time when I should send my normals to vertex shader?
Here is the main code form the webpage. I guess the problem lies in drawSphere functions.
<html>
<head>
<title>WEBGL</title>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1">
<link href='css/smoothness/jquery-ui-1.8.13.custom.css' type='text/css' rel='stylesheet'/>
<script type="text/javascript" src="js/gl/glMatrix-0.9.5.min.js"></script>
<script type="text/javascript" src="js/jquery-1.5.1.min.js"></script>
<script type="text/javascript" src="js/jquery-ui-1.8.13.custom.min.js"></script>
<script type="text/javascript" src="js/gl/webgl-utils.js"></script>
<script type="text/javascript" src="js/gl/webgl-debug.js"></script>
<script type="text/javascript">
var gl;
var shaderProgram = null;
var x = 0;
var y = 0;
var z = 0;
var angle = 0;
var mvMatrix = mat4.create();
var pMatrix = mat4.create();
var nMatrix = mat4.create();
var distance = -200;
var mesh = null;
var mvMatrixStack = [];
</script>
<script id="shader-vs" type="x-shader/x-vertex">
attribute vec3 aVertexPosition;
attribute vec3 aVertexNormal;
uniform mat4 uMVMatrix;
uniform mat4 uPMatrix;
uniform mat4 uNMatrix;
uniform vec3 uLightPosition;
varying vec3 vNormal;
varying vec3 vLightRay;
varying vec3 vEyeVec;
void main(void) {
//Transformed vertex position
vec4 vertex = uMVMatrix * vec4(aVertexPosition, 1.0);
//Transformed normal position
vNormal = vec3(uNMatrix * vec4(aVertexNormal, 1.0));
//Transformed light position
vec4 light = uMVMatrix * vec4(uLightPosition,1.0);
//Light position
vLightRay = vertex.xyz-light.xyz;
//Vector Eye
vEyeVec = -vec3(vertex.xyz);
//Final vertex position
gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition, 1.0);
}
</script>
<script id="shader-fs" type="x-shader/x-fragment">
#ifdef GL_ES
precision highp float;
#endif
uniform vec4 uLightAmbient;
uniform vec4 uLightDiffuse;
uniform vec4 uLightSpecular;
uniform vec4 uMaterialAmbient;
uniform vec4 uMaterialDiffuse;
uniform vec4 uMaterialSpecular;
uniform float uShininess;
varying vec3 vNormal;
varying vec3 vLightRay;
varying vec3 vEyeVec;
void main(void)
{
vec3 L = normalize(vLightRay);
vec3 N = normalize(vNormal);
//Lambert's cosine law
float lambertTerm = dot(N,-L);
//Ambient Term
vec4 Ia = uLightAmbient * uMaterialAmbient;
//Diffuse Term
vec4 Id = vec4(0.0,0.0,0.0,1.0);
//Specular Term
vec4 Is = vec4(0.0,0.0,0.0,1.0);
if(lambertTerm > 0.0)
{
Id = uLightDiffuse * uMaterialDiffuse * lambertTerm;
vec3 E = normalize(vEyeVec);
vec3 R = reflect(L, N);
float specular = pow( max(dot(R, E), 0.0), uShininess);
Is = uLightSpecular * uMaterialSpecular * specular;
}
//Final color
vec4 finalColor = Ia + Id + Is;
finalColor.a = 1.0;
gl_FragColor = finalColor;
}
</script>
<script type="text/javascript">
function createShaderProgram() {
var fragmentShader = getShader(gl, "shader-fs");
var vertexShader = getShader(gl, "shader-vs");
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Could not initialise shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.aVertexPosition = gl.getAttribLocation(shaderProgram, "aVertexPosition");
shaderProgram.aVertexNormal = gl.getAttribLocation(shaderProgram, "aVertexNormal");
shaderProgram.uPMatrix = gl.getUniformLocation(shaderProgram, "uPMatrix");
shaderProgram.uMVMatrix = gl.getUniformLocation(shaderProgram, "uMVMatrix");
shaderProgram.uNMatrix = gl.getUniformLocation(shaderProgram, "uNMatrix");
shaderProgram.uMaterialAmbient = gl.getUniformLocation(shaderProgram, "uMaterialAmbient");
shaderProgram.uMaterialDiffuse = gl.getUniformLocation(shaderProgram, "uMaterialDiffuse");
shaderProgram.uMaterialSpecular = gl.getUniformLocation(shaderProgram, "uMaterialSpecular");
shaderProgram.uShininess = gl.getUniformLocation(shaderProgram, "uShininess");
shaderProgram.uLightPosition = gl.getUniformLocation(shaderProgram, "uLightPosition");
shaderProgram.uLightAmbient = gl.getUniformLocation(shaderProgram, "uLightAmbient");
shaderProgram.uLightDiffuse = gl.getUniformLocation(shaderProgram, "uLightDiffuse");
shaderProgram.uLightSpecular = gl.getUniformLocation(shaderProgram, "uLightSpecular");
//Light uniforms
gl.uniform3fv(shaderProgram.uLightPosition, [25.0, 2.0, -15]);
gl.uniform4f(shaderProgram.uLightAmbient, 1.0, 1.0, 1.0, 1.0);
gl.uniform4f(shaderProgram.uLightDiffuse, 1.0, 1.0, 1.0, 1.0);
gl.uniform4f(shaderProgram.uLightSpecular, 1.0, 1.0, 1.0, 1.0);
//Object Uniforms
gl.uniform4f(shaderProgram.uMaterialAmbient, 0.1, 0.1, 0.1, 1.0);
gl.uniform4f(shaderProgram.uMaterialDiffuse, 0.5, 0.8, 0.1, 1.0);
gl.uniform4f(shaderProgram.uMaterialSpecular, 0.6, 0.6, 0.6, 1.0);
gl.uniform1f(shaderProgram.uShininess, 200.0);
}
function initShaders() {
createShaderProgram("shader-vs", "shader-fs");
}
function getShader(gl, id) {
var shaderScript = document.getElementById(id);
if (!shaderScript) {
return null;
}
var str = "";
var k = shaderScript.firstChild;
while (k) {
if (k.nodeType == 3) {
str += k.textContent;
}
k = k.nextSibling;
}
var shader;
if (shaderScript.type == "x-shader/x-fragment") {
shader = gl.createShader(gl.FRAGMENT_SHADER);
} else if (shaderScript.type == "x-shader/x-vertex") {
shader = gl.createShader(gl.VERTEX_SHADER);
} else {
return null;
}
gl.shaderSource(shader, str);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert(gl.getShaderInfoLog(shader));
return null;
}
return shader;
}
function init(canvas) {
initGL(canvas);
initShaders();
mesh = new Model('sphere');
tick();
}
function mvPushMatrix() {
var copy = mat4.create();
mat4.set(mvMatrix, copy);
mvMatrixStack.push(copy);
}
function mvPopMatrix() {
if (mvMatrixStack.length == 0) {
throw "Invalid popMatrix!";
}
mvMatrix = mvMatrixStack.pop();
}
function uploadModelViewMatrixToShader() {
gl.uniformMatrix4fv(shaderProgram.uMVMatrix, false, mvMatrix);
}
function uploadProjectionMatrixToShader() {
gl.uniformMatrix4fv(shaderProgram.uPMatrix, false, pMatrix);
}
function uploadNormalMatrixToShader() {
mat4.set(mvMatrix, nMatrix);
mat4.inverse(nMatrix);
mat4.transpose(nMatrix);
gl.uniformMatrix4fv(shaderProgram.uNMatrix, false, nMatrix);
}
function drawStaticSphere() {
mvPushMatrix();
uploadModelViewMatrixToShader();
gl.uniform4fv(shaderProgram.uMaterialAmbient, mesh.ambient);
gl.uniform4fv(shaderProgram.uMaterialDiffuse, mesh.diffuse);
gl.uniform4fv(shaderProgram.uMaterialSpecular, mesh.specular);
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.normalPositionBuffer);
gl.vertexAttribPointer(shaderProgram.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(shaderProgram.aVertexNormal);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indexPositionBuffer);
gl.drawElements(gl.TRIANGLES, mesh.indexPositionBuffer.numItems, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
mvPopMatrix();
}
function drawSphere() {
mvPushMatrix();
mat4.rotate(mvMatrix, degToRad(angle), [1, 0, 0]);
mat4.translate(mvMatrix, [2, 0.0, 0.0], mvMatrix);
uploadModelViewMatrixToShader();
gl.uniform4fv(shaderProgram.uMaterialAmbient, mesh.ambient);
gl.uniform4fv(shaderProgram.uMaterialDiffuse, mesh.diffuse);
gl.uniform4fv(shaderProgram.uMaterialSpecular, mesh.specular);
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.vertexPositionBuffer);
gl.vertexAttribPointer(shaderProgram.aVertexPosition, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
gl.bindBuffer(gl.ARRAY_BUFFER, mesh.normalPositionBuffer);
gl.vertexAttribPointer(shaderProgram.aVertexNormal, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(shaderProgram.aVertexNormal);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, mesh.indexPositionBuffer);
gl.drawElements(gl.TRIANGLES, mesh.indexPositionBuffer.numItems, gl.UNSIGNED_SHORT, 0);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
mvPopMatrix();
}
function drawScene() {
gl.clearColor(0.3, 0.3, 0.3, 1.0);
gl.clearDepth(100.0);
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
mat4.perspective(60, gl.viewportWidth / gl.viewportHeight, 0.1, 1000.0, pMatrix);
gl.enableVertexAttribArray(shaderProgram.aVertexPosition);
gl.enableVertexAttribArray(shaderProgram.aVertexNormal);
mat4.identity(mvMatrix);
mat4.lookAt([0, 0, -10], [0, 0, 0], [0, 1, 0], mvMatrix);
uploadModelViewMatrixToShader();
uploadProjectionMatrixToShader();
uploadNormalMatrixToShader();
drawStaticSphere();
drawSphere();
}
var lastTime = 0;
var angle = 0;
var animateFlag = false;
function animate() {
var timeNow = new Date().getTime();
if (lastTime != 0) {
var elapsed = timeNow - lastTime;
if (true) angle += (90 * elapsed) / 5000.0;
}
lastTime = timeNow;
}
function tick() {
requestAnimFrame(function () {
tick()
});
drawScene();
animate();
}
function initGL(canvas) {
try {
//gl = canvas.getContext("webgl");
gl = WebGLDebugUtils.makeDebugContext(canvas.getContext("webgl"));
//gl = WebGLDebugUtils.makeDebugContext(gl, undefined, logGLCall);
gl.viewportWidth = canvas.width;
gl.viewportHeight = canvas.height;
} catch (e) {
}
if (!gl) {
alert("Could not initialise WebGL, sorry :-(");
}
}
function degToRad(degrees) {
return degrees * Math.PI / 180;
}
function logGLCall(functionName, args) {
console.log("gl." + functionName + "(" +
WebGLDebugUtils.glFunctionArgsToString(functionName, args) + ")");
}
function webGLStart() {
var canvas = document.getElementById("canvas");
init(canvas);
}
</script>
<script type="text/javascript" src="js/Texture.js"></script>
<script type="text/javascript" src="js/Model.js"></script>
</head>
<body onload="webGLStart();">
<canvas id="canvas" style="border: none;" width="480" height="400"></canvas>
<div id="bottom">
<table style="padding=0px">
<tr>
<td>light X:</td>
<td id='slider-x-value' width='30px'>25</td>
<td width="150px">
<div id="slider-x"/>
</td>
</tr>
<tr>
<td>light Y:</td>
<td id="slider-y-value" width="30px">2</td>
<td width="150px">
<div id="slider-y"/>
</td>
</tr>
<tr>
<td>light Z:</td>
<td id="slider-z-value" width="30px">-15</td>
<td width="150px">
<div id="slider-z"/>
</td>
</tr>
</table>
</div>
<script>
$('#slider-x').slider({value: 25.0, min: -100, max: 100, step: 0.1, slide: updateLightPosition, change: updateLightPosition});
$('#slider-y').slider({value: 2.0, min: -100, max: 100, step: 0.1, slide: updateLightPosition, change: updateLightPosition});
$('#slider-z').slider({value: -15, min: -100, max: 100, step: 0.1, slide: updateLightPosition, change: updateLightPosition});
function updateLightPosition() {
var x = $('#slider-x').slider("value");
var y = $('#slider-y').slider("value");
var z = $('#slider-z').slider("value");
gl.uniform3fv(shaderProgram.uLightPosition, [x, y, z]);
$('#slider-x-value').html(x);
$('#slider-y-value').html(y);
$('#slider-z-value').html(z);
}
</script>
</body>
</html>