版权声明:本文为博主原创文章,欢迎读者转载学习,转载请注明文章出处。 https://blog.csdn.net/qq_37338983/article/details/78556125
前几篇一直在讲一些基础性的内容,之前的介绍一直都是理论上的解释,理论总要与实际相结合,才能有所收获,知识也才能得到有效的应用。下面,就从一个实例开始讲解,这个实例绘制了一个彩色立方体,且实现了键盘与鼠标的交互,shift+鼠标左键或中建是旋转立方体,shift+鼠标右键是平移立方体,shift+鼠标滚轮缩放立方体,其效果图如下:
接下来就详细地附上程序和注释。
1 HTML文档
其中,MV.js与cuon-matrix.js是相关地矩阵运算库,button用于添加底部地交互按。
interactiveCube.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>webgl-interactive-cube</title>
<script type="text/javascript" src="libs/initShader.js"></script>
<script type="text/javascript" src="libs/webgl-utils.js"></script>
<script type="text/javascript" src="libs/MV.js"></script>
<script type="text/javascript" src="libs/cuon-matrix.js"></script>
<script type="text/javascript" src="js/cube/interactiveCube.js"></script>
</head>
<body>
<canvas id="WebGL-mouseCube" width="1000" height="680"></canvas>
<br/>
<p><strong>鼠标操作: “shift+左键和中键旋转” “shift+右键平移” “shift+滚轮缩放”</strong></p>
<button id ="xRotate">绕x轴旋转</button>
<button id ="yRotate">绕y轴旋转</button>
<button id ="zRotate">绕z轴旋转</button>
<button id ="sRotate">开始/停止旋转</button>
</body>
</html>
2 interactiveCube.js文件
/**
* Created by wjh on 2017/10/31.
*/
//定义变量
//用于绘制模型
var canvas, gl;
var numVertices = 36;
var points = [], colors = [];
//用于HTML按钮交互
var xAixs = 0;
var yAixs = 1;
var zAixs = 2;
var axis = 0;
var theta = [0,0,0];
var currentAngle = [0.0, 0.0];
var rotate = false;
var thetaLoc;
//鼠标键盘交互
var isShiftDown = false;//用于shift+鼠标键交互
var vRotateMatrix, rotateMatrix, u_rotateMatrix;//旋转
var vTranslateMatrix, translateMatrix, u_translateMatrix, Tx = 0, Ty = 0;//平移
var vScalingMatrix, scalingMatrix, u_scalingMatrix, factor = 0, Sx = 1, Sy = 1, Sz = 1;//缩放
window.onload = function cube (){
canvas = document.getElementById('WebGL-mouseCube');
// gl = WebGLUtils.setupWebGL(canvas);
gl = canvas.getContext('experimental-webgl',{antialias:true});
colorCube();
if(!gl){
console.log('浏览器不支持WebGL');
}
//设置视口大小
gl.viewport(0,0,canvas.width,canvas.height);
//清除canvas
gl.clearColor(0, 0, 0, 1.0);
//消除隐藏面
gl.enable(gl.DEPTH_TEST);
//初始化着色器
var program = initShaders(gl,"shader/cube/mouseCubeVshader.glsl","shader/cube/fragmentShader.glsl");
gl.useProgram(program);//将着色器程序设置为有效
//颜色数据
var cBuffer = gl.createBuffer();//创建缓冲区对象
gl.bindBuffer(gl.ARRAY_BUFFER,cBuffer);//绑定对象
gl.bufferData(gl.ARRAY_BUFFER,flatten(colors),gl.STATIC_DRAW);//向缓冲区对象写入数据
var vColor = gl.getAttribLocation(program, 'vColor');//获取着色器中的Attribute变量
gl.vertexAttribPointer(vColor, 4, gl.FLOAT,false,0,0);//将缓冲区对象分配给attribute变量
gl.enableVertexAttribArray(vColor);//建立attribute变量与缓冲区之间的连接
//顶点数据
var vBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vBuffer);
gl.bufferData(gl.ARRAY_BUFFER,flatten(points),gl.STATIC_DRAW);
var vPosition = gl.getAttribLocation(program, 'vPosition');
gl.vertexAttribPointer(vPosition, 4, gl.FLOAT, false,0,0);
gl.enableVertexAttribArray(vPosition);
//旋转变换矩阵
vRotateMatrix = gl.getUniformLocation(program,'vRotateMatrix');
rotateMatrix = new Matrix4();
rotateMatrix.setPerspective(45,canvas.width/canvas.height,1.0,10000);
rotateMatrix.lookAt(1.0,1.0,2.0, 0.0,0.0,0.0, 0.0,1.0,0.0);
initMouseControl(canvas,currentAngle);
//平移矩阵
vTranslateMatrix = gl.getUniformLocation(program, 'vTranslateMatrix');
translateMatrix = new Matrix4();
//缩放矩阵
vScalingMatrix = gl.getUniformLocation(program,'vScalingMatrix');
scalingMatrix = new Matrix4();
thetaLoc = gl.getUniformLocation(program, 'theta');
//添加交互按钮的函数功能
document.getElementById('xRotate').onclick = function (){
axis = xAixs;
};//绕x轴
document.getElementById('yRotate').onclick = function (){
axis = yAixs;
};//绕y轴
document.getElementById('zRotate').onclick = function (){
axis = zAixs;
};//绕z轴
document.getElementById('sRotate').onclick = function (){
rotate = !rotate;
};//控制是否旋转
//添加键盘监听事件
document.addEventListener('keydown',onDocumentKeyDown,false);
document.addEventListener('keyup',onDocumentKeyUp,false);
//绘制渲染
render();
};
//立方体与的顶点索引和面
function colorCube(){
quad( 1, 0, 3, 2 );
quad( 2, 3, 7, 6 );
quad( 3, 0, 4, 7 );
quad( 6, 5, 1, 2 );
quad( 4, 5, 6, 7 );
quad( 5, 4, 0, 1 );
}
function quad(a, b, c, d)
{
var vertices = [
vec4( -0.25, -0.25, 0.25, 1.0 ),
vec4( -0.25, 0.25, 0.25, 1.0 ),
vec4( 0.25, 0.25, 0.25, 1.0 ),
vec4( 0.25, -0.25, 0.25, 1.0 ),
vec4( -0.25, -0.25, -0.25, 1.0 ),
vec4( -0.25, 0.25, -0.25, 1.0 ),
vec4( 0.25, 0.25, -0.25, 1.0 ),
vec4( 0.25, -0.25, -0.25, 1.0 )
];
var vertexColors = [
[ 0.0, 0.0, 0.0, 1.0 ], // black
[ 1.0, 0.0, 0.0, 1.0 ], // red
[ 1.0, 1.0, 0.0, 1.0 ], // yellow
[ 0.0, 1.0, 0.0, 1.0 ], // green
[ 0.0, 0.0, 1.0, 1.0 ], // blue
[ 1.0, 0.0, 1.0, 1.0 ], // magenta
[ 0.0, 1.0, 1.0, 1.0 ], // cyan
[ 1.0, 1.0, 1.0, 1.0 ] // white
];
var indices = [a,b,c,a,c,d];
for (var i=0; i<indices.length;++i){
points.push(vertices[indices[i]]);
colors.push(vertexColors[a]);
}
}
//键盘监听事件响应函数
function onDocumentKeyDown(event){
switch(event.keyCode){
case 16:
isShiftDown = true;
break;
}
}
function onDocumentKeyUp(event){
switch(event.keyCode){
case 16:
isShiftDown = false;
break;
}
}
//在canvas上添加鼠标交互
function initMouseControl(canvas,currentAngle){
var dragging1 = false, dragging2 = false, dragging3 = false;
var lastX = -1, lastY = -1;
canvas.onmousedown = function (event) {//按下鼠标触发监听事件
var x = event.clientX, y = event.clientY;
switch (event.button) {
case 0:
case 1: //鼠标左键及中键
var rect1 = event.target.getBoundingClientRect();
if (rect1.left <= x && x < rect1.right && rect1.top <= y && y < rect1.bottom) {
lastX = x;
lastY = y;
dragging1 = true;
dragging3 = true;
}
break;
case 2: //鼠标右键
var rect2 = event.target.getBoundingClientRect();
if (rect2.left <= x && x < rect2.right && rect2.top <= y && y < rect2.bottom) {
lastX = x;
lastY = y;
dragging2 = true;
canvas.oncontextmenu = function () {//在canvas里屏蔽浏览器右键菜单,不兼容火狐
return false;
}
}
break;
}
};
//滚轮缩放操作
canvas.onmousewheel = function (event) {
if (isShiftDown){
console.log(event.wheelDelta);
factor = event.wheelDelta / 1200;
Sx += factor / 5;//x方向的缩放范围
Sy += factor / 5;//y方向的缩放范围
Sz += factor / 5;//z方向的缩放范围
}
};
//松开鼠标
canvas.onmouseup = function (event) {
switch (event.button) {
case 0:
case 1:
dragging1 = false;
dragging3 = false;
break;
case 2:
dragging2 = false;
}
};
//移动鼠标
canvas.onmousemove = function (event) {//鼠标移动监听
var x = event.clientX, y = event.clientY;
//旋转
if ((dragging1 || dragging3) && isShiftDown) {
var factor1 = 200 / canvas.height;//旋转速度
var dx1 = factor1 * (x - lastX);
var dy1 = factor1 * (y - lastY);
//限制x轴旋转范围
currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy1, 90), -90);
currentAngle[1] = currentAngle[1] + dx1;
}
//平移
if (isShiftDown && dragging2) {
var factor2 = 2 / canvas.height;//平移速度
var dx2 = factor2 * (x - lastX);
var dy2 = factor2 * (y - lastY);
//限制平移范围
Tx = Math.max(Math.min(Tx + dx2, 500), -500);
Ty = Math.max(Math.min(Ty - dy2, 300), -300);
}
//更新上一个位置为开始位置
lastX = x;
lastY = y;
};
}
//绘制函数
function render(){
//旋转矩阵
u_rotateMatrix = new Matrix4();
u_rotateMatrix.set(rotateMatrix);
u_rotateMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0);
u_rotateMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0);
gl.uniformMatrix4fv(vRotateMatrix, false, u_rotateMatrix.elements);
//平移矩阵
u_translateMatrix = new Matrix4();
u_translateMatrix.set(translateMatrix);
u_translateMatrix.translate(Tx, 0.0, 0.0);
u_translateMatrix.translate(0.0, Ty, 0.0);
gl.uniformMatrix4fv(vTranslateMatrix, false, u_translateMatrix.elements);
//缩放矩阵,这里的if主要是为了限制
// 模型的缩放范围保持正常范围
if(Sx>=0&&Sy>=0&&Sz>=0) {
u_scalingMatrix = new Matrix4();
u_scalingMatrix.set(scalingMatrix);
u_scalingMatrix.scale(Sx, Sy, Sz);
gl.uniformMatrix4fv(vScalingMatrix, false, u_scalingMatrix.elements);
}
gl.clear(gl.COLOR_BUFFER_BIT|gl.DEPTH_BUFFER_BIT);
if(rotate)theta[axis] += 0.8;//绕坐标轴运动的旋转角
gl.uniform3fv(thetaLoc, theta);
gl.drawArrays(gl.TRIANGLES, 0, numVertices);//绘制图形
requestAnimFrame( render );//动画
}
3 着色器文件代码
(1).顶点着色器mouseCubeVshader.glsl
attribute vec4 vPosition;
attribute vec4 vColor;
varying vec4 fColor;
uniform mat4 vRotateMatrix;
uniform mat4 vTranslateMatrix;
uniform mat4 vScalingMatrix;
uniform vec3 theta;
void main() {
vec3 angles = radians( theta );
vec3 c = cos( angles );
vec3 s = sin( angles );
mat4 rx = mat4(1.0, 0.0, 0.0, 0.0,
0.0, c.x, s.x, 0.0,
0.0, -s.x, c.x, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 ry = mat4(c.y, 0.0, -s.y, 0.0,
0.0, 1.0, 0.0, 0.0,
s.y, 0.0, c.y, 0.0,
0.0, 0.0, 0.0, 1.0
);
mat4 rz = mat4(c.z, s.z, 0.0, 0.0,
-s.z, c.z, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
);
fColor = vColor;
gl_Position = vScalingMatrix * vTranslateMatrix * vRotateMatrix * rz * ry * rx * vPosition;
// gl_Position.x = -gl_Position.x;
// gl_Position.y = -gl_Position.y;
// gl_Position.z = -gl_Position.z;
}
(2) 片元着色器fragmentShader.glsl
precision mediump float;
varying vec4 fColor;
void main() {
gl_FragColor = fColor;
}
以上正式一个可交互地旋转立方体地完整代码实例。