[WebGL入门]十七,递归处理和移动・旋转・缩放

翻译 2014年08月13日 00:16:37

注:文章译自http://wgld.org/,原作者杉本雅広(doxas),文章中如果有我的额外说明,我会加上[lufy:],另外,鄙人webgl研究还不够深入,一些专业词语,如果翻译有误,欢迎大家指正。



本次的demo的运行结果


模型坐标变换矩阵的好处

上次,通过操作模型坐标变换矩阵,绘制了多个模型。这次,继续在此基础上,给多个模型再添加上旋转和放大缩小等处理。
看过上一篇文章的人应该知道,在3D渲染的世界里,利用VBO和一部分坐标变换矩阵,只需要少量修改,就可以绘制出大量的模型。当然,计算量也会变少,效率也自然得到了提升。
换句话说,能够熟练使用模型坐标变换矩阵的话,就可以实现高效的渲染,这就是模型坐标变换的好处。
这次,试着绘制三个多边形,一个多边形沿着一个圆进行移动,一个多边形沿Y轴进行旋转,第三个多边形进行放大缩小。


添加一个持续循环的处理

上次的demo,只是在canvas上更新了一次,这次的demo会让模型持续变化,做成一个简单的动画效果。
为了进行动画处理,需要添加一个持续的循环,在javascript中持续循环有很多种方法,本网站基本上就是使用setTimeout函数。
这里说的setTimeout函数是javascript中window对象内置的函数,在指定的时间过后,可以进行指定的处理。
经过的时间的单位是毫秒,1秒是1000毫秒,所以需要指定描述的1千倍。


使用setTimeout实现递归处理

按照刚才说的,使用setTimeout函数可以实现反复的循环处理,那么具体的做法是怎样的呢?
setTimeout函数的第一个参数是调用的函数,第二个参数是需要经过多长时间(毫秒)后调用这个函数。如果第一个参数指定为当前所运行的函数的话,那么就可以实现持续循环了。
・函数A被调用
・在函数A中,使用setTimeout,并传入函数A作为参数
・经过指定的时间后,函数A被调用
按照上面的步骤,把WebGL中绘图部分写成递归函数,就可以持续循环了。


arguments和callee属性

函数的内部调用函数本身的话,可以直接写函数的名字来实现,但是如果是匿名函数的话,这样的做法就行不通了。
解决的办法是有的,使用arguments和callee属性的话就可以调用函数本身了。arguments对象是函数被调用的时候自动生成的,而callee属性就是这个函数本身的引用,使用这种方法的话,即使是匿名函数也可以实现递归。
这次的递归处理,就是使用这里所说的setTimeout+arguments.callee组合来实现。


递归函数中的处理

向递归函数中,添加最少限度的处理。因为在反复循环的处理中,如果添加了多余的没用的处理的话,会导致程序效率降低。
比如,WebGL的context的获取,VBO和着色器的生成等,是不用每次都调用的。一旦对象被生成的话,就可以反复利用了,同样,视图坐标变换矩阵和投影坐标变换矩阵的生成也没有必要放在递归函数中,因为每次都是使用相同的东西。
这样的话,递归函数中所包含处理就基本上限定了,具体的就是下面列出的处理。
・清空画面
・模型坐标变换矩阵的生成
・向uniform中传入坐标变换矩阵
・绘图命令
・画面刷新
・setTimeout+arguments.callee
那么,看一下递归函数部分的代码吧。
>递归函数的代码

// minMatrix.js中的矩阵相关的处理
// matIV对象的生成
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());

// 视图x投影坐标变换矩阵
m.lookAt([0.0, 0.0, 5.0], [0, 0, 0], [0, 1, 0], vMatrix);
m.perspective(45, c.width / c.height, 0.1, 100, pMatrix);
m.multiply(pMatrix, vMatrix, tmpMatrix);

// 声明计数器
var count = 0;

// 持续循环
(function(){
    // canvasを初期化
    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++;
    
    // 使用计数器算出角度
    var rad = (count % 360) * Math.PI / 180;
    
    // 模型1按照一个圆形轨道进行旋转
    var x = Math.cos(rad);
    var y = Math.sin(rad);
    m.identity(mMatrix);
    m.translate(mMatrix, [x, y + 1.0, 0.0], mMatrix);
    
    // 完成模型1的坐标变换矩阵,并进行绘图
    m.multiply(tmpMatrix, mMatrix, mvpMatrix);
    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    
    // 模型2沿Y轴进行旋转
    m.identity(mMatrix);
    m.translate(mMatrix, [1.0, -1.0, 0.0], mMatrix);
    m.rotate(mMatrix, rad, [0, 1, 0], mMatrix);
    
    // 完成模型2的坐标变换矩阵,并进行绘图
    m.multiply(tmpMatrix, mMatrix, mvpMatrix);
    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    
    // 模型3进行放大缩小
    var s = Math.sin(rad) + 1.0;
    m.identity(mMatrix);
    m.translate(mMatrix, [-1.0, -1.0, 0.0], mMatrix);
    m.scale(mMatrix, [s, s, 0.0], mMatrix)
    
    // 完成模型3的坐标变换矩阵,并进行绘图
    m.multiply(tmpMatrix, mMatrix, mvpMatrix);
    gl.uniformMatrix4fv(uniLocation, false, mvpMatrix);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    
    // context刷新
    gl.flush();
    
    // 为了循环,进行递归处理
    setTimeout(arguments.callee, 1000 / 30);
})();

文章的最后,会给出demo的链接,大家可以直接查看完整的代码,HTML代码依然没有做任何修改。

这次处理的重点是在匿名函数外面声明了count,匿名函数中让这个变量每次递增,然后使用这个变量的值来进行模型坐标变换。
变量count对360取余数,然后用这个余数来计算弧度。
>获取弧度的代码

var rad = (count % 360) * Math.PI / 180;
无论变量count有多大,余数始终是在0 ~ 359这个范围内,这样就能得到正确的角度了,从而得到弧度,然后使用这个弧度作为其中一个参数,来进行模型坐标变换。

第一个模型,使用弧度来计算sin和cos,得到X和Y就是移动的量。
第二个模型,为了沿着Y轴旋转,也使用了这个弧度作为旋转量。
第三个模型,使用弧度的sin值来计算了放大缩小的量。
注意这里频繁出现的multiply和rotate等函数,都是为了实现各种坐标变换而封装在minMatrix.js中函数。参数的指定可以看之前的文章(minMatrix.js和坐标变换矩阵),里面已经做了详细的解说。
另外,需要注意模型坐标变换的顺序,移动,旋转和放大缩小等各种变换,执行的顺序如果不对的话,结果就有变化了。这里之前的文章也都说过了,不清楚的人可以复习一下前面的内容。


总结

这次利用递归处理实现了持续循环,并介绍了模型坐标变换矩阵的处理。以后,动态的demo会越来越多,这次介绍的持续循环也会被更多的用到。
匿名函数或者普通的函数的递归可能是个稍微难理解的概念,也要着急,仔细考虑的话会理解的。如果处理比较多的话,这一部分处理可能应该单独分离出来。


下次,介绍一个缓存索引。


下面的链接是这次的demo,可以直接运行,所以一定参考一下。
模型的移动,旋转和放大缩小的动态demo

http://wgld.org/s/sample_005/


转载请注明:转自lufy_legend的博客http://blog.csdn.net/lufy_legend

【WebGL初学系列之五】旋转,平移,缩放

nbcoder.com地址:http://nbcoders.com/webgl-chu-xue-xi-lie-zhi-wu-ai.html     最近把WebGL做的相关Demo已经放在 http...
  • ixshells
  • ixshells
  • 2014年12月06日 16:18
  • 2532

我的threejs学习笔记(三)——相机旋转

写在前面虽然看起来好像是物体自身在旋转,但确实是相机在围绕场景旋转。 另外,用简单的三个变量实现了重力模拟。相机旋转方法 var theta=0; var render=functi...
  • chenqiong1991
  • chenqiong1991
  • 2017年03月21日 21:18
  • 1756

webGL第七课 —— 平移、旋转和缩放

在图形显示中,图形变换只有平移、旋转和缩放三种情况。他们都是通过矩阵运算获得的。...
  • lzwdlut
  • lzwdlut
  • 2016年08月26日 13:57
  • 829

[WebGL入门]二十八,纹理参数

为了进一步提升纹理的渲染质量,来介绍一下纹理参数。通过控制纹理参数,可以很好的提升模型的渲染质量,虽然会有些比较难理解的地方,这些可以以后再考虑。 那么,WebGL中的纹理参数到底是什么呢?现在就从这...
  • lufy_Legend
  • lufy_Legend
  • 2017年08月16日 21:34
  • 698

Three.js自定义相机旋转动画:沿圆弧旋转

/* *camera:相机 *angle:旋转角度 *segs:分段,即圆弧对应的路径分为几段 *during:动画执行的时间 ...
  • birdflyto206
  • birdflyto206
  • 2016年08月17日 09:41
  • 5741

14 WebGL 使用矩阵实现图形 旋转+平移并解释一下缩放

由于时间比较晚,明天还需要上班,今天先上传代码,明天再更新思路。 Document...
  • qq_30100043
  • qq_30100043
  • 2017年05月11日 00:04
  • 808

three.js中3D视野的缩放实现

首先,不再废话了,什么是three.js,是干什么的,知道的就是知道,不知道的就百度吧。昨儿发现three.js中的3D视野的缩小和放大效果可以用照相机的远近焦来实现。 缩小后: 这里采用的是...
  • U_9_5
  • U_9_5
  • 2016年01月19日 15:32
  • 5315

webgl学习笔记三-平移旋转缩放

写在前面 建议先阅读下前面我的两篇文章。webgl学习笔记一-绘图单点webgl学习笔记二-绘图多点 平移 1、关键点说明 顶点着色器需要加上 uniform vec4 u_Translation...
  • csdn_xpw
  • csdn_xpw
  • 2017年09月05日 00:22
  • 124

WebGL之旋转三角形

02.WebGL之旋转三角形 attribute vec3 v3Position; attribute vec3 av3Color; varying vec3 vv3Color; attr...
  • shaoxiaoning
  • shaoxiaoning
  • 2014年10月17日 14:55
  • 1266

OpenGL编程逐步深入(七)旋转变换

这一节我们来看一下旋转变换。旋转变换指的是给我们一个指点的点和角度,我们需要绕着过该点的轴线將对象旋转对应的角度。这里我们只改变X/Y/Z中的两个分量,第三个分量保持不变。这意味着我们的图形只在三个平...
  • Rongbo_J
  • Rongbo_J
  • 2015年04月23日 20:27
  • 1394
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[WebGL入门]十七,递归处理和移动・旋转・缩放
举报原因:
原因补充:

(最多只允许输入30个字)