1. 四元数旋转
这个小实例实现的是,圆环按照X、Y和Z轴分别旋转一圈,使用的旋转方法是四元数坐标旋转。关于更多的知识,大家可以参考一下 6. 参考资料 中的两篇文章,原实例和源码也是来自那里。
原实例相机只按照X轴进行旋转,并且模型自转。这里我们停用模型自转,但相机需要按照X、Y和Z轴分别旋转一圈,主要思路:
设置一个开关控制旋转轴,相机每绕一个轴旋转一次,则更改旋转轴
2. 代码解析
很简单,过一遍代码思路。初始化开关和每个轴的变量:
var rotateWhich = 0;
const degreeTwo = 720;
const speed = 5;
const xAxis = [1, 0, 0];
const yAxis = [0, 1, 0];
const zAxis = [0, 0, 1];
超过一圈,重置旋转状态和旋转轴:
if ( count > degreeTwo ) {
count = 0;
rotateWhich = ++rotateWhich % 3;
}
var axis = null;
switch ( rotateWhich ) {
case 0:
axis = xAxis;
break;
case 1:
axis = yAxis;
break;
case 2:
axis = zAxis;
break;
default:
console.assert( true, "Error" );
}
3. 附录:项目代码
3.1 HTML
<html lang=""><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>wgld.org WebGL sample 019</title>
<script src="https://wgld.org/j/minMatrixb.js" type="text/javascript"></script>
<script src="script.js" type="text/javascript"></script>
<script id="vs" type="x-shader/x-vertex">
attribute vec3 position;
attribute vec3 normal;
attribute vec4 color;
uniform mat4 mvpMatrix;
uniform mat4 mMatrix;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vPosition = (mMatrix * vec4(position, 1.0)).xyz;
vNormal = normal;
vColor = color;
gl_Position = mvpMatrix * vec4(position, 1.0);
}
</script>
<script id="fs" type="x-shader/x-fragment">
precision mediump float;
uniform mat4 invMatrix;
uniform vec3 lightPosition;
uniform vec3 eyeDirection;
uniform vec4 ambientColor;
varying vec3 vPosition;
varying vec3 vNormal;
varying vec4 vColor;
void main(void){
vec3 lightVec = lightPosition - vPosition;
vec3 invLight = normalize(invMatrix * vec4(lightVec, 0.0)).xyz;
vec3 invEye = normalize(invMatrix * vec4(eyeDirection, 0.0)).xyz;
vec3 halfLE = normalize(invLight + invEye);
float diffuse = clamp(dot(vNormal, invLight), 0.0, 1.0) + 0.2;
float specular = pow(clamp(dot(vNormal, halfLE), 0.0, 1.0), 50.0);
vec4 destColor = vColor * vec4(vec3(diffuse), 1.0) + vec4(vec3(specular), 1.0) + ambientColor;
gl_FragColor = destColor;
}
</script>
</head>
<body>
<canvas id="canvas" width="300" height="300"></canvas>
</body></html>
3.2 Javascript
onload = function(){
var c = document.getElementById('canvas');
c.width = 300;
c.height = 300;
var gl = c.getContext('webgl') || c.getContext('experimental-webgl');
var v_shader = create_shader('vs');
var f_shader = create_shader('fs');
var prg = create_program(v_shader, f_shader);
var attLocation = new Array();
attLocation[0] = gl.getAttribLocation(prg, 'position');
attLocation[1] = gl.getAttribLocation(prg, 'normal');
attLocation[2] = gl.getAttribLocation(prg, 'color');
var attStride = new Array();
attStride[0] = 3;
attStride[1] = 3;
attStride[2] = 4;
var torusData = torus(64, 64, 0.5, 1.5);
var tPosition = create_vbo(torusData.p);
var tNormal = create_vbo(torusData.n);
var tColor = create_vbo(torusData.c);
var tVBOList = [tPosition, tNormal, tColor];
var tIndex = create_ibo(torusData.i);
set_attribute(tVBOList, attLocation, attStride);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, tIndex);
var uniLocation = new Array();
uniLocation[0] = gl.getUniformLocation(prg, 'mvpMatrix');
uniLocation[1] = gl.getUniformLocation(prg, 'mMatrix');
uniLocation[2] = gl.getUniformLocation(prg, 'invMatrix');
uniLocation[3] = gl.getUniformLocation(prg, 'lightPosition');
uniLocation[4] = gl.getUniformLocation(prg, 'eyeDirection');
uniLocation[5] = gl.getUniformLocation(prg, 'ambientColor');
var m = new matIV();
var mMatrix = m.identity(m.create());
var vMatrix = m.identity(m.create());
var pMatrix = m.identity(m.create());
var tmpMatrix = m.identity(m.create());
var mvpMatrix = m.identity(m.create());
var invMatrix = m.identity(m.create());
var q = new qtnIV();
var xQuaternion = q.identity(q.create());
var lightPosition = [15.0, 10.0, 15.0];
var ambientColor = [0.1, 0.1, 0.1, 1.0];
var camPosition = [0.0, 0.0, 10.0];
var camUpDirection = [0.0, 1.0, 0.0];
var count = 0;
gl.enable(gl.DEPTH_TEST);
gl.depthFunc(gl.LEQUAL);
gl.enable(gl.CULL_FACE);
var rotateWhich = 0;
const degreeTwo = 720;
const speed = 5;
const xAxis = [1, 0, 0];
const yAxis = [0, 1, 0];
const zAxis = [0, 0, 1];
(function(){
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clearDepth(1.0);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
count += speed;
if ( count > degreeTwo ) {
count = 0;
rotateWhich = ++rotateWhich % 3;
}
var axis = null;
switch ( rotateWhich ) {
case 0:
axis = xAxis;
break;
case 1:
axis = yAxis;
break;
case 2:
axis = zAxis;
break;
default:
console.assert( true, "Error" );
}
var rad = (count % 180) * Math.PI / 90;
var rad2 = count * Math.PI / 360;
q.rotate(rad2, axis, xQuaternion);
q.toVecIII([0.0, 0.0, 10.0], xQuaternion, camPosition);
q.toVecIII([0.0, 1.0, 0.0], xQuaternion, camUpDirection);
m.lookAt(camPosition, [0, 0, 0], camUpDirection, vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);
m.identity(mMatrix);
m.multiply(tmpMatrix, mMatrix, mvpMatrix);
m.inverse(mMatrix, invMatrix);
gl.uniformMatrix4fv(uniLocation[0], false, mvpMatrix);
gl.uniformMatrix4fv(uniLocation[1], false, mMatrix);
gl.uniformMatrix4fv(uniLocation[2], false, invMatrix);
gl.uniform3fv(uniLocation[3], lightPosition);
gl.uniform3fv(uniLocation[4], camPosition);
gl.uniform4fv(uniLocation[5], ambientColor);
gl.drawElements(gl.TRIANGLES, torusData.i.length, gl.UNSIGNED_SHORT, 0);
gl.flush();
setTimeout(arguments.callee, 1000 / 30);
})();
function create_shader(id){
var shader;
var scriptElement = document.getElementById(id);
if(!scriptElement){return;}
switch(scriptElement.type){
case 'x-shader/x-vertex':
shader = gl.createShader(gl.VERTEX_SHADER);
break;
case 'x-shader/x-fragment':
shader = gl.createShader(gl.FRAGMENT_SHADER);
break;
default :
return;
}
gl.shaderSource(shader, scriptElement.text);
gl.compileShader(shader);
if(gl.getShaderParameter(shader, gl.COMPILE_STATUS)){
return shader;
}else{
alert(gl.getShaderInfoLog(shader));
}
}
function create_program(vs, fs){
var program = gl.createProgram();
gl.attachShader(program, vs);
gl.attachShader(program, fs);
gl.linkProgram(program);
if(gl.getProgramParameter(program, gl.LINK_STATUS)){
gl.useProgram(program);
return program;
}else{
alert(gl.getProgramInfoLog(program));
}
}
function create_vbo(data){
var vbo = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vbo);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(data), gl.STATIC_DRAW);
gl.bindBuffer(gl.ARRAY_BUFFER, null);
return vbo;
}
function set_attribute(vbo, attL, attS){
for(var i in vbo){
gl.bindBuffer(gl.ARRAY_BUFFER, vbo[i]);
gl.enableVertexAttribArray(attL[i]);
gl.vertexAttribPointer(attL[i], attS[i], gl.FLOAT, false, 0, 0);
}
}
function create_ibo(data){
var ibo = gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, ibo);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Int16Array(data), gl.STATIC_DRAW);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
return ibo;
}
};
4. 参考资料
- [WebGL入门]三十二,四元数和minMatrixb.js
- [WebGL入门]三十一,Quaternions(四元数)
5. 免责声明
※ 本文之中如有错误和不准确的地方,欢迎大家指正哒~ ※ 此项目仅用于学习交流,请不要用于任何形式的商用用途,谢谢呢;
|