ThreeJS之动画交互逻辑及特效

摘要:
为了满足工作需要,我们研究了三个js简单逻辑动画的交互方法。当然,第一步是初始化threejs的渲染场景。在nodejs的任意位置运行该示例:加载模型文件,并将文件中的相关对象添加到组:varurl='nobloor。json';varloader=newTHREE。ObjectLoader();vargeometry=newTHREE。几何();//存储物体的位置坐标并为装载机服务。荷载作为以下线的起点坐标;导入的模型几乎是这样的,以及stats和dat。将gui添加到示例程序中,以检测渲染效果并更改特殊效果参数。

 工作需要,研究了一下 threejs 简单逻辑动画交互方法。写了一个小示例,分享一下,挺丑的。

第一步

当然就是初始化 threejs 的渲染场景了。

var camera; //相机
var scene;//场景
var renderer;//webGL渲染器
var controls;//轨道控件,用于特定场景,模拟轨道中的卫星,可以用鼠标和键盘在场景中游走 
var raycaster;//THREE.Raycaster对象从屏幕上的点击位置想场景中发射一束光线,返回射线穿透物体的数组
var composer;//后期特效合成器,给场景选中物体添加发光特效

第二步

在 ThreeJs Editor 中建立简单的示例模型,“Export Scene”,导出。并导入示例程序。免去了在示例程序中自己建模的麻烦,不过因为示例程序要加载本地的json,所以可以设置一个简单的 nodejs 服务器。

在 nodejs 的 anywhere 下运行该示例:

ThreeJS之动画交互逻辑及特效第1张

加载模型文件,将文件中的相关 object 加入 group中:

          var url = 'nofloor.json';
                var loader= new THREE.ObjectLoader();
          var geometry = new THREE.Geometry();//存放objects的position坐标,为之后线条的起始点坐标服务 loader.load( url,
function ( loadedScene ) { //scene = loadedScene; var objects = loadedScene.children; for(var i=0;i<objects.length;i++){ if(objects[i].type == 'Mesh' ){ objects[i].receiveShadow = true; objects[i].castShadow = true; geometry.vertices.push(objects[i].position); group.add(objects[i]); } } } , onProgress, onError);

导入的模型差不多就是这样子(丑一点,担待),并在示例程序中加了stats 和 dat.gui 用来检测渲染效果和改变特效参数。

ThreeJS之动画交互逻辑及特效第2张

 第三步

完成的交互目标是,点击上图中某个柱体选中,出现相应的连线,并且让选中的柱体和连线发光。

现在先利用 raycaster 选中物体:

var mouse = new THREE.Vector2(); //鼠标经过或者点击的屏幕 canvas 上的位置           
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;//将 canvas 坐标系转换为 WebGL 坐标系
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );// raycaster的作用场景
var intersects = raycaster.intersectObjects( [group], true );//从鼠标 mouse 位置发射线,选中 group 组中的objects ,并返回 objects 给 intersects 

一旦 intersects  不为空,intersects[0].object 就是鼠标选中的物体,可以是上图中的正方体,也可以是上图中的地板。

接下来,皆可以根据选中物体来连线了。连线呢有两种方法。

第一种

ThreeJS之动画交互逻辑及特效第3张

代码如下:

var material = new THREE.LineBasicMaterial({
    color: 0x0000ff
});

var geometry = new THREE.Geometry();
geometry.vertices.push(
    //各个柱体的 position 坐标,也就是上面加载模型文件时候生成的 geometry.vertices
 );
// THREE.Line 会将 geometry.vertices 中所有坐标点连成一条连续线,但不是首尾相接。
//比如 geometry.vertices 中存放了 v1,v2,v3,v4四个三维坐标点,就会生成v1->v2->v3->v4 连续线,中间有三条线。
var line = new THREE.Line( geometry, material );

但是有一个缺点就是由于受限于 角度层(ANGLE layer),在Windows平台上使用 WebGL,线宽将总是为1而不管设置的值。这样一旦模型是五六米高,可是线条只有不到1cm的宽度,看起来模型就会很奇怪了。这时候就要用第二种方法。

第二种

画圆柱体,用圆柱体代替线段,但是生成圆柱体就比线段复杂多了。大家知道,threejs 只会指定一个object的position(中心),而不能指定两端的位置(我还没发现,若有错误,请指正),所以画的初始圆柱体是直立的,如下图

ThreeJS之动画交互逻辑及特效第4张

让我们要的是下图这样的圆柱体(下面统称为“柱子线”)

ThreeJS之动画交互逻辑及特效第5张

因为半径是 0.02,所以看起来像线段,这也是为什么要用圆柱体模拟线段的原因了,逼真,而且还能根据模型大小调整这个柱子线的“linewidth”。

 下面我们看看怎么怎么根据正方体个球体的坐标动态生成柱子线吧。

上面我们讲了如何选中物体,选中之后,我们把这个物体相邻的物体(建模时候,将所有物体坐标顺序放在 geometry.vertices 中,此处默认坐标相邻就是物体相邻)的 position 坐标加入 geometryChange.vertices  中

               var object = intersects[0].object;
                        geometryChange = new THREE.Geometry();
                        var position = intersects[0].object.position;//当前选中物体的坐标
                        //搜索 geometry.vertices 中的 position 重新绘制选中物体相关linet
                        var p = geometry.vertices.length;
                        for(i=0;i<p;i++){
                            if(geometry.vertices[i] == position){

                                if (i == p - 1){//将最后一个物体的前一个物体坐标加入
                                    geometryChange.vertices.push(geometry.vertices[p - 2]);
                                    geometryChange.vertices.push(position);
                                }
                                else if(i == 0){//将第一个物体的后一个坐标加入
                                    geometryChange.vertices.push(position);
                                    geometryChange.vertices.push(geometry.vertices[i+1]);
                                }

                                else{
                                    geometryChange.vertices.push(geometry.vertices[i-1]);
                                    geometryChange.vertices.push(position);//将物体前后相邻的加入
                                    geometryChange.vertices.push(geometry.vertices[i+1]);
                                  
                                }
                            }
                        }

这样我们就把选中物体相邻的物体坐标放在 geometryChange.vertices。现在我们知道柱子线的起点和终点坐标了,那柱子线怎么画,画哪里呢?

              var temp = geometryChange.vertices.length;
                        var xyz = geometryChange.vertices;
               //position(x,y,z),就是柱子线的中点位置,xw是起点和中点的 X 轴方向距离,zh是起点和中点的 Z 轴方向距离,cheight是起点和中点的空间距离
               var x,y,z,xw,zh,cheight;

先知道柱子线的position(x,y,z),xyz[i]是柱子线起点,xyz[i+1]是柱子线终点。

x= (xyz[i].x+xyz[i+1].x)/2;
y=0.1;//线我是画在地面附近的,所以y默认0.1
z=(xyz[i].z+xyz[i+1].z)/2

再来求柱子线的长度

xw=xyz[i].x-xyz[i+1].x;
zh=xyz[i].z-xyz[i+1].z;
cheight=Math.sqrt(xw*xw+zh*zh);//圆柱体长度,勾股定理

这下画柱子线

var material = new THREE.MeshPhongMaterial( {
                                        color: 0x156289,
                                        emissive: 0x00FFFF,
                                        side: THREE.DoubleSide,
                                        shading: THREE.FlatShading,
                                        vertexColors:THREE.FaceColors
                                    } );
var cylinder = new THREE.Mesh( geometryCylinderLine, material );
cylinder.position.set( x, y, z );//两实体的中点,也就是柱子线的中点,自己理解

可是发现画的柱子是竖直向上的

ThreeJS之动画交互逻辑及特效第6张

这个时候就需要改变柱子线的模型矩阵的,对它做旋转,达到我们理想的效果。

我们先分析一下怎么旋转,首先将绕 x 轴转90° ,让柱子线躺地上。

cylinder.rotation.x -= Math.PI * 0.5;

之后如下图分析所示,红线就是躺地上的柱子(自行脑补3D场景)。

ThreeJS之动画交互逻辑及特效第7张

其中红线和黑线长度相同,红线只需要旋转 θ 角度之后就可以和黑线重合,达到我们要的效果。

θ = Math.asin(xw/cheight);//弧度制

这个时候知道转多少度了,转就ok

                 //考虑到局部坐标系和全局坐标系的转换,柱体是在全局坐标系下旋转
                            if(xyz[i].x > xyz[i+1].x && xyz[i].z < xyz[i+1].z)
                                cylinder.rotation.z -= Math.asin(xw/cheight);//Math.asin(xw/cheight)为柱体要旋转的角度
                            else if(xyz[i].x > xyz[i+1].x && xyz[i].z > xyz[i+1].z)
                                cylinder.rotation.z += Math.asin(xw/cheight);
                            else if(xyz[i].x < xyz[i+1].x && xyz[i].z < xyz[i+1].z)
                                cylinder.rotation.z -= Math.asin(xw/cheight);
                            else
                                cylinder.rotation.z += Math.asin(xw/cheight);

好了,柱子线画出来了。

当然不止一条柱子线,把当前的都加入lineGroup 中

lineGroup.add( cylinder );
scene.add( lineGroup );

 下次绘制的时候只要移除当前的 lineGroup 即可。

scene.remove( lineGroup );

第四步

加发光特效

借助threejs的 outlinePass 通道

          composer = new THREE.EffectComposer( renderer );

                var renderPass = new THREE.RenderPass( scene, camera );
                composer.addPass( renderPass );

                outlinePass = new THREE.OutlinePass( new THREE.Vector2( window.innerWidth, window.innerHeight ), scene, camera );
                composer.addPass( outlinePass );

                var onLoad = function ( texture ) {

                    outlinePass.patternTexture = texture;
                    texture.wrapS = THREE.RepeatWrapping;
                    texture.wrapT = THREE.RepeatWrapping;

                };

                var loader = new THREE.TextureLoader();

                loader.load( 'tri_pattern.jpg', onLoad );

                effectFXAA = new THREE.ShaderPass( THREE.FXAAShader );
                effectFXAA.uniforms[ 'resolution' ].value.set( 1 / window.innerWidth, 1 / window.innerHeight );
                effectFXAA.renderToScreen = true;
                composer.addPass( effectFXAA );

在选中时,将物体和相应柱子线加入  outlinePass  渲染目标中即可。

               selectedObjects = [];
                        selectedObjects.push( lineGroup );//给选中的线条和物体加发光特效
                        selectedObjects.push( intersects[ 0 ].object );
                        outlinePass.selectedObjects = selectedObjects;

ok,这就实现了,点击交互的简单特效。

ThreeJS之动画交互逻辑及特效第8张

当然,这只是个示例,要把它用到复杂的3D场景中,还需要很多的事情要做,加油。

 有错误敬请指正。

免责声明:文章转载自《ThreeJS之动画交互逻辑及特效》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇DDMS工具使用(转)【高德地图API】从零开始学高德JS API(八)——地址解析与逆地址解析下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

Threejs的学习 2、点,线,面

在Threejs中定义一个点 在three.js中,点可以在右手坐标系中表示:空间几何中,点可以用一个向量来表示,在Three.js中也是用一个向量来表示的,代码如下所示: THREE.Vector3 = function ( x, y, z ) { this.x = x || 0; this.y = y || 0; this.z = z || 0;...

m3u8编码视频webgl、threejs渲染视频纹理demo

<!DOCTYPE html> <html> <head> <meta charset=utf-8 /> <title>fz-live</title> <link href="http://t.zoukankan.com/css/video.css" rel="stylesh...

WebGL的3D框架比较 ThingJS 和 Three.js

随着flash的没落,浏览器的原生能力的兴起。在3D方面WebGL不管从功能还是性能方面都在逐渐加强。2D应用变为3D应用的需求也越来越强烈。 win10的画图板支持3D图片,2d工具photoshop也开始逐步集成了3D工具。 下面就基于WebGL技术探讨一下现在的两款3D框架。Threejs(http://threejs.org/) 目前最流行的开源3...

threejs- z-fighting 问题(模型的重叠部位便不停的闪烁起来。这便是Z-Fighting问题)

Z-Buffer 在threejs中,使用深度缓冲(Z-Buffer)来完成场景可见性计算,即确定场景哪部分可见,哪部分不可见。深度缓冲(Z-Buffer)是一个二维数组,其中的每一个元素对应屏幕上的一个像素,如果场景中的两个模型在同一个像素生成渲染结果,那么图形处理卡就会比较二者的深度,并且保留距离观察者较近的物体在该像素点的渲染结果,这样就形成了近的模...

ThreeJs 基础入门

本文来自网易云社区 作者:唐钊 Three.js 是一款运行在浏览器中的 3D 引擎,你可以用它在 web 中创建各种三维场景,包括了摄影机、光影、材质等各种对象。使用它可以让我们更加直观的了解 webgl 的世界。 3D 场景前置知识 1.场景(Scene):是物体、光源等元素的容器,可以配合 chrome 插件使用,抛出 window.scene即可...

【BIM】BIMFACE中创建矢量文本[上篇]

背景 在三维模型产品的设计中,针对空间的管理存在这样一个普遍的需求,那就是在三维模型中,将模型所代表的空间通过附加文本的方式来展示其所代表的实际位置或功能,之前尝试过若干方式,比如直接在建模的时候,将文本以构件的方式建在模型里,其优点是展示效果好、位置可控、放大后不失真,但是缺少灵活性,一旦加入到模型中,相当于焊死在上面;另一种方式则是通过Canvas绘制...