用鼠标控制立方体的旋转

  • 1. demo效果
  • 2. 实现要点
    • 2.1 注册鼠标事件
      • 2.1.1 注册鼠标事件函数的声明
      • 2.1.2 注册鼠标事件函数的调用
    • 2.2 纹理图片加载
    • 2.3 图形绘制
  • 3. demo代码

1. demo效果


如上图,是一个添加了纹理图片的立方体,这个立方体可以通过鼠标按下后上下左右拖动实现立方体的旋转

2. 实现要点

2.1 注册鼠标事件

2.1.1 注册鼠标事件函数的声明

在这个函数中需要注册鼠标按下事件、鼠标松开事件和鼠标移动事件的处理函数,
鼠标按下事件 的处理函数中,需要获取到鼠标按下的位置并将是否拖拽的标识置为true,鼠标松开事件 处理比较简单,只需要将是否拖拽的标识置为false即可,鼠标移动事件 处理函数中需要获取到当前鼠标的的坐标,分别计算出x轴和y轴需要旋转的角度,赋值给该函数的输出参数currentAngle

function initEventHandlers(canvas, currentAngle) {var dragging = false //默认鼠标拖动不旋转物体var lastX = -1,lastY = -1 //鼠标最后的位置canvas.onmousedown = function (ev) { //注册鼠标按下事件var x = ev.clientX,y = ev.clientY//鼠标在物体上开始拖动var rect = ev.target.getBoundingClientRect()if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {lastX = xlastY = ydragging = true}}//鼠标松开拖动结束canvas.onmouseup = function (ev) {dragging = false}canvas.onmousemove = function (ev) { //注册鼠标移动事件var x = ev.clientX,y = ev.clientYif (dragging) {var factor = 100 / canvas.height //旋转因子var dx = factor * (x - lastX)var dy = factor * (y - lastY)//沿Y轴的旋转角度控制在-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.1.2 注册鼠标事件函数的调用

这里需要注意的是参数currentAngle是一个数组,索引0的值表示绕X轴旋转的角度,索引1的值表示绕Y轴旋转的角度,调用前要先准备这样一个数组,来接收鼠标事件处理后的旋转角度信息,供后面绘图使用

var currentAngle = [0.0, 0.0] //当前旋转的角度[x-axis, y-axis]
initEventHandlers(canvas, currentAngle) //注册鼠标事件

2.2 纹理图片加载

function initTextures(gl) {//创建纹理对象var texture = gl.createTexture()if (!texture) {console.log('创建纹理对象失败')return false}//获取片元着色器uniform变量u_Sampler的存储地址var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler')if (!u_Sampler) {console.log('获取u_Sampler的存储地址失败')return false}var image = new Image()if (!image) {console.log('创建图片对象失败')return false}image.src = './resources/sky_roof.jpg' //设置纹理资源路径//注册图片加载事件的响应函数image.onload = function () {//调用加载纹理函数loadTexture(gl, texture, u_Sampler, image)}return true}function loadTexture(gl, texture, u_Sampler, image) {gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1) //对纹理图像镜像y轴反转gl.activeTexture(gl.TEXTURE0) //激活纹理单元0 gl.bindTexture(gl.TEXTURE_2D, texture) //绑定纹理对象gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) //配置纹理对象参数gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image) //纹理图像分配给纹理对象gl.uniform1i(u_Sampler, 0) //纹理单元编号0传给片元着色器中uniform变量u_Sampler}

2.3 图形绘制

在这里需要注意的是,绘制函数的参数中的currentAngle,它表示鼠标拖拽时计算出的要沿X轴、Y轴旋转的角度,分别存储在下标为0和1的数组中,在该函数中使用时也通过该参数的下标来获取沿X轴、Y轴旋转的角度

var g_MvpMatrix = new Matrix4() //模型视图投影矩阵
function draw(gl, n, viewProjMatrix, u_MvpMatrix, currentAngle) {//计算模型视图投影矩阵 g_MvpMatrix.set(viewProjMatrix) //设置视图投影矩阵 g_MvpMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0) //沿X轴旋转设置矩阵g_MvpMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0) //沿Y轴旋转设置矩阵//模型视图投影矩阵的计算结果传给uniform变量u_MvpMatrixgl.uniformMatrix4fv(u_MvpMatrix, false, g_MvpMatrix.elements)gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0) //绘图
}

3. demo代码

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title></title>
</head><body><!--通过canvas标签创建一个400px*400px大小的画布--><canvas id="webgl" width="400" height="400"></canvas><script type="text/javascript" src="./lib/cuon-matrix.js"></script><script>//顶点着色器var VSHADER_SOURCE = '' +'attribute vec4 a_Position;\n' + //声明attribute变量a_Position,用来存放顶点位置信息'attribute vec2 a_TexCoord;\n' + //声明attribute变量a_TexCoord,用来存放纹理坐标'uniform mat4 u_MvpMatrix;\n' + //声明uniform变量u_MvpMatrix,用来存放模型视图投影组合矩阵'varying vec2 v_TexCoord;\n' + //声明varying变量v_TexCoord,用来向片元着色器传值纹理坐标'void main(){\n' +'  gl_Position = u_MvpMatrix * a_Position;\n' + //将模型视图投影组合矩阵与顶点坐标相乘赋值给顶点着色器内置变量gl_Position'  v_TexCoord = a_TexCoord;\n' + //将纹理坐标传给片元着色器'}\n'//片元着色器var FSHADER_SOURCE = '' +'#ifdef GL_ES\n' +' precision mediump float;\n' + // 设置精度'#endif\n' +'uniform sampler2D u_Sampler;\n' + //声明uniform变量u_Sampler,存放第一个纹理单元编号'varying vec2 v_TexCoord;\n' + //声明varying变量v_TexCoord,用来接收顶点着色器传送的纹理坐标'void main(){\n' +' gl_FragColor = texture2D(u_Sampler,v_TexCoord);\n' + //将texture2D函数抽取纹理单元0纹素颜色赋值给内置变量gl_FragColor'}\n'//初始化着色器函数function initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE) {//创建顶点着色器对象var vertexShader = loadShader(gl, gl.VERTEX_SHADER, VSHADER_SOURCE)//创建片元着色器对象var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, FSHADER_SOURCE)if (!vertexShader || !fragmentShader) {return null}//创建程序对象programvar program = gl.createProgram()if (!gl.createProgram()) {return null}//分配顶点着色器和片元着色器到programgl.attachShader(program, vertexShader)gl.attachShader(program, fragmentShader)//链接programgl.linkProgram(program)//检查程序对象是否连接成功var linked = gl.getProgramParameter(program, gl.LINK_STATUS)if (!linked) {var error = gl.getProgramInfoLog(program)console.log('程序对象连接失败: ' + error)gl.deleteProgram(program)gl.deleteShader(fragmentShader)gl.deleteShader(vertexShader)return null}//使用programgl.useProgram(program)gl.program = program//返回程序program对象return program}function loadShader(gl, type, source) {// 创建顶点着色器对象var shader = gl.createShader(type)if (shader == null) {console.log('创建着色器失败')return null}// 引入着色器源代码gl.shaderSource(shader, source)// 编译着色器gl.compileShader(shader)// 检查顶是否编译成功var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS)if (!compiled) {var error = gl.getShaderInfoLog(shader)console.log('编译着色器失败: ' + error)gl.deleteShader(shader)return null}return shader}function init() {//通过getElementById()方法获取canvas画布var canvas = document.getElementById('webgl')//通过方法getContext()获取WebGL上下文var gl = canvas.getContext('webgl')//初始化着色器if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {console.log('初始化着色器失败')return}// 设置canvas的背景色gl.clearColor(0.0, 0.0, 0.0, 1.0)//初始化顶点坐标和顶点颜色var n = initVertexBuffers(gl)setMatrixAndDraw(gl, n, canvas)}//设置矩阵并绘图function setMatrixAndDraw(gl, n, canvas) {//开启隐藏面消除gl.enable(gl.DEPTH_TEST)//清空颜色和深度缓冲区gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)//获取顶点着色器uniform变量u_MvpMatrix的存储地址var u_MvpMatrix = gl.getUniformLocation(gl.program, 'u_MvpMatrix')if (!u_MvpMatrix) {console.log('获取uniform变量u_MvpMatrix的存储地址失败')return}//创建视图投影矩阵var viewProjMatrix = new Matrix4()viewProjMatrix.setPerspective(30.0, canvas.width / canvas.height, 1.0, 100.0)viewProjMatrix.lookAt(3.0, 3.0, 7.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0)var currentAngle = [0.0, 0.0] //当前旋转的角度[x-axis, y-axis]initEventHandlers(canvas, currentAngle) //注册鼠标事件//设置纹理if (!initTextures(gl)) {console.log('初始化纹理失败')return}var tick = function () { //开始绘图draw(gl, n, viewProjMatrix, u_MvpMatrix, currentAngle)requestAnimationFrame(tick, canvas)}tick()}//初始化顶点坐标和顶点颜色function initVertexBuffers(gl) {var v0 = [1.0, 1.0, 1.0]var v1 = [-1.0, 1.0, 1.0]var v2 = [-1.0, -1.0, 1.0]var v3 = [1.0, -1.0, 1.0]var v4 = [1.0, -1.0, -1.0]var v5 = [1.0, 1.0, -1.0]var v6 = [-1.0, 1.0, -1.0]var v7 = [-1.0, -1.0, -1.0]//顶点var vertices = new Float32Array([...v0, ...v1, ...v2, ...v3, // 前...v0, ...v3, ...v4, ...v5, // 右...v0, ...v5, ...v6, ...v1, // 上...v1, ...v6, ...v7, ...v2, // 左...v7, ...v4, ...v3, ...v2, // 下...v4, ...v7, ...v6, ...v5 // 后])// 纹理var texCoords = new Float32Array([1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v0-v1-v2-v3 前0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 1.0, 1.0, // v0-v3-v4-v5 右1.0, 0.0, 1.0, 1.0, 0.0, 1.0, 0.0, 0.0, // v0-v5-v6-v1 上1.0, 1.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, // v1-v6-v7-v2 左0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0, // v7-v4-v3-v2 下0.0, 0.0, 1.0, 0.0, 1.0, 1.0, 0.0, 1.0 // v4-v7-v6-v5 后])// 绘制的索引var indices = new Uint8Array([0, 1, 2, 0, 2, 3, // 前4, 5, 6, 4, 6, 7, // 右8, 9, 10, 8, 10, 11, // 上12, 13, 14, 12, 14, 15, // 左16, 17, 18, 16, 18, 19, // 下20, 21, 22, 20, 22, 23 // 后])if (!initArrayBuffer(gl, vertices, 3, gl.FLOAT, 'a_Position')) {return -1}if (!initArrayBuffer(gl, texCoords, 2, gl.FLOAT, 'a_TexCoord')) {return -1}//创建缓冲区对象var indexBuffer = gl.createBuffer()//将顶点索引写入缓冲区对象gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer)gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indices, gl.STATIC_DRAW)return indices.length}function initEventHandlers(canvas, currentAngle) {var dragging = false //默认鼠标拖动不旋转物体var lastX = -1,lastY = -1 //鼠标最后的位置canvas.onmousedown = function (ev) { //注册鼠标按下事件var x = ev.clientX,y = ev.clientY//鼠标在物体上开始拖动var rect = ev.target.getBoundingClientRect()if (rect.left <= x && x < rect.right && rect.top <= y && y < rect.bottom) {lastX = xlastY = ydragging = true}}//鼠标松开拖动结束canvas.onmouseup = function (ev) {dragging = false}canvas.onmousemove = function (ev) { //注册鼠标移动事件var x = ev.clientX,y = ev.clientYif (dragging) {var factor = 100 / canvas.height //旋转因子var dx = factor * (x - lastX)var dy = factor * (y - lastY)//沿Y轴的旋转角度控制在-90到90度之间currentAngle[0] = Math.max(Math.min(currentAngle[0] + dy, 90.0), -90.0)currentAngle[1] = currentAngle[1] + dx}lastX = x, lastY = y}}var g_MvpMatrix = new Matrix4() //模型视图投影矩阵 function draw(gl, n, viewProjMatrix, u_MvpMatrix, currentAngle) {//计算模型视图投影矩阵 g_MvpMatrix.set(viewProjMatrix) //设置视图投影矩阵 g_MvpMatrix.rotate(currentAngle[0], 1.0, 0.0, 0.0) //沿X轴旋转设置矩阵g_MvpMatrix.rotate(currentAngle[1], 0.0, 1.0, 0.0) //沿Y轴旋转设置矩阵//模型视图投影矩阵的计算结果传给uniform变量u_MvpMatrixgl.uniformMatrix4fv(u_MvpMatrix, false, g_MvpMatrix.elements)gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)gl.drawElements(gl.TRIANGLES, n, gl.UNSIGNED_BYTE, 0) //绘图}function initArrayBuffer(gl, data, num, type, attribute) {//创建缓冲区对象var buffer = gl.createBuffer()//将顶点坐标和顶点颜色信息写入缓冲区对象gl.bindBuffer(gl.ARRAY_BUFFER, buffer)gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)//获取顶点着色器attribute变量存储地址, 分配缓存并开启var a_Attribute = gl.getAttribLocation(gl.program, attribute)gl.vertexAttribPointer(a_Attribute, num, type, false, 0, 0)gl.enableVertexAttribArray(a_Attribute)return true}function initTextures(gl) {//创建纹理对象var texture = gl.createTexture()if (!texture) {console.log('创建纹理对象失败')return false}//获取片元着色器uniform变量u_Sampler的存储地址var u_Sampler = gl.getUniformLocation(gl.program, 'u_Sampler')if (!u_Sampler) {console.log('获取u_Sampler的存储地址失败')return false}var image = new Image()if (!image) {console.log('创建图片对象失败')return false}image.src = './resources/sky_roof.jpg' //设置纹理资源路径//注册图片加载事件的响应函数image.onload = function () {//调用加载纹理函数loadTexture(gl, texture, u_Sampler, image)}return true}function loadTexture(gl, texture, u_Sampler, image) {gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, 1) //对纹理图像镜像y轴反转gl.activeTexture(gl.TEXTURE0) //激活纹理单元0 gl.bindTexture(gl.TEXTURE_2D, texture) //绑定纹理对象gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR) //配置纹理对象参数gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image) //纹理图像分配给纹理对象gl.uniform1i(u_Sampler, 0) //纹理单元编号0传给片元着色器中uniform变量u_Sampler}init()</script>
</body></html>

WebGL入门(三十四)-三维空间中鼠标控制物体旋转,用鼠标控制立方体的旋转相关推荐

  1. WebGL入门(三十六)-HUD(平视显示器)实现

    HUD(平视显示器)实现 1. demo效果 2.实现要点 2.1 什么是HUD 2.2 HUD实现 2.2.1 准备画布 2.2.2 绘制三维图形 2.2.3 绘制HUD信息 3.demo代码 1. ...

  2. WebGL入门(三十九)-透明与不透明物体共存,绘制透明面和不透明面的立方体

    绘制透明面和不透明面的立方体 1. 绘制透明立方体 1.1 绘制透明立方体要点 1.2 绘制透明立方体demo效果 1.3 绘制透明立方体demo代码 1.4 开启隐藏面消除后 2. 透明与不透明共存 ...

  3. WebGL入门(三十五)-三维物体雾化效果,立方体雾化效果

    立方体雾化效果 1. demo效果 2.相关知识点 2.1 雾化相关概念 2.2 雾化因子计算 2.3 片元颜色计算 3.实现要点 3.1 计算顶点与视点的距离 3.2 计算雾化后片元颜色 3.3 计 ...

  4. 三十四、深入Java中的泛型(上篇)

    @Author:Runsen @Date:2019年10月22日 19:39:21 作者介绍:Runsen目前大三下学期,专业化学工程与工艺,大学沉迷日语,Python, Java和一系列数据分析软件 ...

  5. Android中SlidingDrawer介绍【安卓进化三十四】

    Android中SlidingDrawer介绍[安卓进化三十四] 安卓中1.5后加入了SlidingDrawer[隐藏式抽屉],设计原理在你的UI布局有限的情况下,放不下太多的控件的时候,可以考虑用这 ...

  6. [Python人工智能] 三十四.Bert模型 (3)keras-bert库构建Bert模型实现微博情感分析

    从本专栏开始,作者正式研究Python深度学习.神经网络及人工智能相关知识.前一篇文章开启了新的内容--Bert,首先介绍Keras-bert库安装及基础用法及文本分类工作.这篇文章将通过keras- ...

  7. Oracle入门(十四)之PL/SQL

    一.PL/SQL 基本语法 PL/SQL语言是模块式的过程化SQL,是oracle公司对SQL的扩展. (1) (2) (3) (5) (6) (7)数据类型 Number 数字型 Varchar2 ...

  8. FreeSql (三十四)CodeFirst 迁移说明

    FreeSql 支持 CodeFirst 迁移结构至数据库,这应该是(O/RM)必须标配的一个功能. 与其他(O/RM)不同FreeSql支持更多的数据库特性,而不只是支持基础的数据类型,这既是优点也 ...

  9. [转]周易入门三十五问答

    周易入门三十五问答 http://mt.sohu.com/20150714/n416782382.shtml 1.问:何谓<周易>? 答:<周易>是我国古代哲学.自然科学与社会 ...

最新文章

  1. 虚拟机类加载机制的了解
  2. 对部门的建议和期待怎么写_教学反思到底该怎么写?这些要点一个都不能少(建议收藏)...
  3. shell命令卸载mysql_centos8中的MySQL卸载和安装
  4. 1.1 Java流是什么?输入/输出流又是什么?
  5. 教你从0到1搭建秒杀系统-抢购接口隐藏与单用户限制频率
  6. Linux系统资源监控--linux命令、nmon和spotlight
  7. img should be PIL Image. Got <class ‘torch.Tensor‘>
  8. 【转载】直到活动结束时的openeim
  9. 2017.7.17数据类型
  10. 装完机,启grub+Linux,linux一路填坑...
  11. 只有得到祝福才是好婚姻
  12. Flutter:手拉手带你极速构建漂亮的跨平台(iOS/Android)移动应用 ✿ 初识
  13. 更改mysql数据库存储引擎_MySQL更改数据库表的存储引擎
  14. [it-ebooks]电子书列表v0.1.1
  15. 如何使用Arduino开发板和ADXL345加速度计跟踪方向
  16. 基于JAVA高校实习实训管理系统计算机毕业设计源码+数据库+lw文档+系统+部署
  17. 如何快速成为数据分析师
  18. 英语常见词根词缀大全(二)
  19. 微信小程序开发者工具出现Framework inner error错误
  20. C++入门第二天前向声明

热门文章

  1. Material Design学习之 Button(详细分析,传说中的水滴动画)
  2. 初级驱动调试笔记-总结
  3. 怎么彻底卸载cad2017_mac装了2017cad后卸载后,想把该软件注册表里的东西也删除,不知道如何处理?...
  4. Maven 编译遇到 Process terminated【四种情况全部解决】
  5. 如何设计财务对账系统
  6. 手机时间用长了会不会没信号服务器,手机卡能用多久?用久磨损了会影响手机信号么?...
  7. python前景怎么样-Python在中国的发展前景怎么样?有哪些就业方向?
  8. 【听】失控,互联网前瞻大畅想
  9. 3704对象关闭时_实时错误‘3704’:对象关闭时,不允许操作。
  10. cartoon drawing_‎App Store 上的“卡通绘画(Cartoon drawing)”