案例实现功能如下:
- 鼠标左右键移动旋转
- 滑动滑轮实现缩放
首先物体的旋转是通过对模型矩阵的旋转计算modelMatrix.rotate(angle, x, y, z)
这个方法可以让物体绕某个轴进行旋转多少度,进而变换顶点坐标,normalMatrix、mvpMatrix...
都是可以进行一些列的矩阵变换的,计算过后在清空颜色缓存以及深度缓存,重新绘制页面;
因此使用鼠标去控制物体就是通过监听鼠标的各种事件来进行矩阵变换即可
基本步骤如下:
- 首先鼠标左右键按下,获取一个点,并记录下来 如
v(x,y)
- 当鼠标开始移动的时候计算此时需要旋转的角度
- 通过矩阵转换来实现旋转
- 控制缩放可以控制相机和物体的平移变换
1、创建一个立方体
着色器部分
// 顶点着色器
var v_shader_source = '' +
'attribute vec4 a_Position;' +
'attribute vec4 a_Normal;' +
'uniform mat4 u_MvpMatrix;' +
'uniform mat4 u_ModelMatrix;' +// 模型矩阵
'uniform mat4 u_NormalMatrix;' +// 法向量旋转矩阵
'varying vec4 v_Color;' +
'varying vec3 v_Normal;' +
'varying vec3 v_Position;' +
'void main(){' +
' vec4 color = vec4(1.0, 1.0, 1.0, 1.0);' + // 物体颜色
' gl_Position = u_MvpMatrix * a_Position;' +
' v_Position = vec3(u_ModelMatrix * a_Position);' +
' v_Normal = normalize(vec3(u_NormalMatrix * a_Normal));' +
' v_Color = color;' +
'}';
// 片元着色器
var f_shader_source = '' +
'precision mediump float;' +
'uniform vec3 u_LightColor;' + // 灯光颜色
'uniform vec3 u_LightPosition;' + // 灯光位置
'uniform vec3 u_AmbientLight;' + // 环境光颜色
'varying vec3 v_Normal;' +
'varying vec3 v_Position;' +
'varying vec4 v_Color;' +
'void main(){' +
// 对法线进行归一化,因为其内插之后长度不一定是1.0
' vec3 normal = normalize(v_Normal);' +
// 计算光线方向并归一化
' vec3 lightDirection = normalize(u_LightPosition - v_Position);' +
// 计算光线方向和法向量的点积
' float nDotL = max(dot(lightDirection, normal), 0.0);' +
// 计算漫反射和环境反射以及最终的颜色值
' vec3 diffuse = u_LightColor * v_Color.rgb * nDotL;' +
' vec3 ambient = u_AmbientLight * v_Color.rgb;' +
' gl_FragColor = vec4(diffuse + ambient, v_Color.a);' +
'}';
2、添加鼠标事件监听
1、旋转处理
// 添加鼠标事件监听处理
function initEventHandles(domElement, currentAngle) {
var dragging = false;
var lastX = -1;
var lastY = -1;
// 添加事件监听的dom元素
domElement = (domElement !== undefined) ? domElement : document;
// 角度默认值位0.0
currentAngle = (currentAngle !== undefined) ? currentAngle : [0.0, 0.0];
// 鼠标按下事件
domElement.onmousedown = function (event) {
event.preventDefault();
// 鼠标点击位置
var x = event.clientX;
var y = event.clientY;
lastX = x;
lastY = y;
dragging = true;
}
domElement.onmouseleave = function (event) {
event.preventDefault();
dragging = false;
}
// 鼠标抬起事件
domElement.onmouseup = function (event) {
event.preventDefault();
dragging = false;
}
// 鼠标移动事件
domElement.onmousemove = function (event) {
event.preventDefault();
var x = event.clientX, y = event.clientY;
if (dragging) {
// 旋转比例--速度
var factor = 100 / domElement.height;
var dx = factor * (x - lastX);
var dy = factor * (y - lastY);
// 限制 x轴的旋转角度 -90 --- 90
currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90.0), -90.0)
currentAngle[1] = currentAngle[1] + dx;
}
lastX = x, lastY = y;
}
}
上面的代码是处理旋转
2、缩放处理
// 滑轮缩放处理
canvas.addEventListener('wheel', onMouseWheel, false);
function onMouseWheel(event) {
vpMatrix.translate(0.0, 0.0, -event.deltaY * 0.004);
mvpMatrix.set(vpMatrix).multiply(modelMatrix);
gl.uniformMatrix4fv(u_MvpMatrix, false, mvpMatrix.elements);
renderer();
}
3、重复绘制方法
// 重新绘制
function renderer() {
// 设置灯光的逆转置矩阵,让modelMatrix 转成为modelMatrix 的逆矩阵
normalMatrix.setInverseOf(modelMatrix);
normalMatrix.transpose();
gl.uniformMatrix4fv(u_NormalMatrix, false, normalMatrix.elements);
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0);
}
3、示例链接
具体案例代码所在仓库如下
https://gitee.com/ithanmang/webgl-notes