1.创建可用Physijs的基本Three.js场景

创建一个可用Physijs的Three.js场景非常简单,只要几个步骤即可。首先我们要包含正确的文件, 需要引入physi.js文件。实际模拟物理场景时非常耗费CPU的,如果我么能在render线程中做的话,场景的帧频会受到严重的影响。为了弥补这一点,Physijs选择在后台线程中执行计算。这里的后台是有Web workers(网页线程)规范定义的额,现在大多数浏览器都实现了该功能。

对Physijs来说也就意味着我们需要配置一个带有执行任务的JavaScipt文件,并告诉Physijs在哪里可以找到用来模拟场景的ammo.js文件。所以需要添加以下代码:

Physijs.scripts.worker = "../libs/physijs_worker.js";
Physijs.scripts.ammo = "../libs/ammo.js";

Physijs在Three.js的普通场景外又提供了一个包装器,所以我们代码可以想这样创建场景:

scene = new Physijs.Scene();
scene.setGravity(new THREE.Vector3(0, -50, 0));

在模拟物理效果之前,我们需要在场景中添加一些对象。为此,我们可以使用Three.js的普通方法来定义对象,但必须用一个特定的Physijs对象将这些对象包裹起来:

var stoneGeom = new THREE.BoxGeometry(0.6, 6, 2);
var stone = new Physijs.BoxMesh(stoneGeom, Physijs.createMaterial(new THREE.MeshPhongMaterial({
color: scale(Math.random()).hex(),
transparent: true,
opacity: 0.8
})));
...
scene.add(stone);

我们第一个Physijs场景中的各个部分都有了。剩下要做的就是告诉Physijs模拟物理效果,并更新场景中各对象的位置和角色。为此,我们可以调用创建的场景的simulate方法。修改基础render循环代码:

render = function(){
requestAnimationFrame(render);
renderer.render(scene, camera);
render_stats.update(); scene.simulate(undefined, 1);
}

假设我们要实现下面图片中放倒多米若骨牌的效果。

下面是实现功能的一段核心代码,points是所有多米诺骨牌的点集合。遍历每个骨牌的顶点,创建一个类型为BoxMesh对象(多米诺骨牌)。这里需要注意的是通过stone.lookAt()函数设置了对象的旋转角度,在手动更新了Physijs包装的对象的角度(或位置)之后,我们必须告诉Physijs有什么东西改变了。对于角度,我么可以将__dirtRotation设置为true;对于位置,我们可以将__dirtyPosition设置为true。

this.resetScene = function(){
scene.setGravity(new THREE.Vector3(controls.gravityX, controls.gravityY, controls.gravityZ));
stones.forEach(function(st){
scene.remove(st);
});
stones = []; points.forEach(function(point){
var stoneGeom = new THREE.BoxGeometry(0.6, 6, 2);
var stone = new Physijs.BoxMesh(stoneGeom, Physijs.createMaterial(new THREE.MeshPhongMaterial({
color: scale(Math.random()).hex(),
transparent: true,
opacity: 0.8
})))
//console.log(stone.position);
stone.position.copy(point);
stone.lookAt(scene.position);
stone.__dirtyRotation = true;
stone.position.y = 3.5; scene.add(stone);
stones.push(stone);
}); stones[0].rotation.x = 0.2;
stones[0].__dirtyRotation = true;
}

2.材质属性

Physijs中材质对象最重要的两个属性分别是restitution和firction。restitution设置材质弹性,值越大,弹性越强;值越小弹性越弱。而restitution设置摩擦系数,值越小,摩擦就越小,物体越容易移动;值越大,摩擦越大,物体越难移动。

假如我们要实现下图的效果。地板一直都在左右旋转,球体也会跟着地板一起移动。这里我们主要看下球体的实现代码如何。

下面的代码是圆球的实现代码。首先生成了一个随机颜色colorSphere,每次我们批量创建五个球体。创建球体对象使用Physijs.SphereMesh类创建。这里主要看下如何创建材质。创建材质和我们普通的方法不同,必须使用Physijs.createMaterial函数创建。第三个参数friction用来设置摩擦系数,范围0到1。第四个参数restitution设置弹性,范围0到1。只要我们修改这两个参数,我们就能看到球体落到地板时以及移动时的效果区别。

this.addSpheres = function () {
var colorSphere = scale(Math.random()).hex();
for(var i = 0; i < 5; i++){
box = new Physijs.SphereMesh(
new THREE.SphereGeometry(2, 20),
Physijs.createMaterial(
new THREE.MeshPhongMaterial({
color: colorSphere,
opacity: 0.8,
transparent: true
}),
controls.sphereFriction,
controls.sphereRestitution
)
);
box.position.set(
Math.random() * 50 - 25,
20 + Math.random() * 5,
Math.random() * 50 - 25
);
meshes.push(box);
scene.add(box);
}
};

3.基础图形

Physijs提供了一些可以用来包装几何体的图形类。使用这些几何体唯一要做的就是讲THREE.Mesh的构造函数替换成这些网格对象的构造函数。下表是Physijs中所有网格对象的概览:

Physijs.PlaneMesh/这个网格可以用来创建一个厚度为0的平面。这样的平面也可以用BoxMesh对象包装一个高度很低的THREE.CubeGeometry来表示

Physijs.BoxMesh/如果是类似方块的几何体,你可以使用这个网格。例如,它的属性跟THREE.CubeGeometry的属性很相配

Physijs.SphereMesh/对于球形可以使用这个网格。它跟THREE.SphereGeometry的属性很相配

Physijs.CylinderMesh/通过设置THREE.Cylinder的属性你可以创建出各种柱状图形。Physijs为各种柱性提供了不同网格。Physijs.CylinderMesh可以用于一般的、上下一致的圆柱形

Physijs.ConeMesh/如果顶部的半径为0,底部的半径值大于0,那么你可以用THREE.Cylinder创建一个圆锥体。如果你想在这样一个对象上应用物理效果,那么可以使用的、最相匹配的网格类就是ConeMesh

Physijs.CapsuleMesh(胶囊网格)/跟THREE.Cylinder属性很相似,但其底部和底部是圆的

Physijs.ConvexMesh(凸包网格)/Physijs.ConvexMesh是一种比较粗略的图形,可用于多数复杂退行。它可以创建一个模拟复杂图形的凸包

Physijs.ConcaveMesh/ConvexMesh是一个比较粗略的图形,而ConcaveMesh则可以对负责图形进行比较细致的表现。需要注意的是使用ConcaveMesh对效率的影响比较大

Physijs.HeightfieldMesh(高度场网格)/这是一种非常特别的网格。通过该网格你可以从一个THREE.PlaneGeometry对象创建出一个高度场。

4.使用约束限制对象移动

我们已经了解到各种图形如何对重力、摩擦和弹性做出反应。并影响碰撞。Physijs还提供了一些高级对象,让i可以限制对象的移动。在Physijs里,这些对象呗称作约束。下表是Physijs中可用约束概览:

PointConstraint/通过这个约束,你可以将一个对象与另一个对象之间的位置固定下来。例如一个对象动了,另一个对象也会随着移动,它们之间的距离和方向保持不变

HingeConstraint/通过活页约束,你可以限制一个对象只能像活页一样移动,例如门

SliderConstraint/将对象的移动限制在一个轴上。例如移门

ConeTwistConstraint/通过这个约束,你可以用一个对象限制另一个对象的旋转和移动。这个约束的功能类似于一个球削式关节。例如,胳膊在肩关节中的活动

DOFConstraint/通过自由度约束,你可以限制对象在任意轴上的活动,你可以设置对象活动的额最小、最大角度。这是最灵活的约束方式

5.用PointConstraint限制亮点间的移动

实现代码如下,我们在这段代码里可以看到,我们使用特定的Physijs网格创建对象,然后将它们添加到场景中。我们使用Physijs.PointConstraint构造函数创建约束。

function createPointToPoint() {
var obj1 = new THREE.SphereGeometry(2);
var obj2 = new THREE.SphereGeometry(2); var objectOne = new Physijs.SphereMesh(obj1, Physijs.createMaterial(
new THREE.MeshPhongMaterial({color: 0xff4444, transparent: true, opacity: 0.7}), 0, 0));
objectOne.position.z = -18;
objectOne.position.x = -10;
objectOne.position.y = 2;
objectOne.castShadow = true;
scene.add(objectOne); var objectTwo = new Physijs.SphereMesh(obj2, Physijs.createMaterial(
new THREE.MeshPhongMaterial({color: 0xff4444, transparent: true, opacity: 0.7}), 0, 0));
objectTwo.position.z = -5;
objectTwo.position.x = -20;
objectTwo.position.y = 2;
objectTwo.castShadow = true;
scene.add(objectTwo); // if no position two, its fixed to a position. Else fixed to objectTwo and both will move
var constraint = new Physijs.PointConstraint(objectOne, objectTwo, objectTwo.position);
scene.addConstraint(constraint);
}

构造函数有三个参数。前两个参数指定要连接的两个对象。第三个参数指定约束绑定的位置。一般来说,如果你指向将两个对象连在一起,那么你最好将这个位置设置在第二个对象的位置上。如果你不想将一个对象绑定到另一个对象,而绑定到场景中某个固定的点,那么你可以忽略第二个参数。这样第一个对象就会跟着你指定的位置保持固定距离。

6.用HingeConstraint创建类似们的约束

顾名思义,通过HingeConstraint你可以创建一个行为类似活页的对象。它可以绕固定的轴旋转,并可限制在一定角度内。假如我们现在要实现下图中框选部分的活页门效果,白色长条随着右边的小方块旋转。

实现代码如下,HingeConstraint构造函数包含四个参数,定义为new Physijs.HingeConstraint(mesh_a, mesh_b, position, axis)。mesh_a第一个对象是将要被约束的对象;mesh_b指定mesh_a受哪个对象约束。这里flipperLeft受flipperLetPivot小方块影响;position约束应用的点。在本例中这个点就是Mesh_a绕着旋转的点;axis活页绕着旋转的轴。在本例中我们将活页设置在水平方向(0, 1, 0)。最后我们还需要设置约束对象的属性,为此我们调用setLimits函数。该函数包含四个参数,分别是low(指定旋转的最下弧度)、high(指定旋转的最大弧度)、bias_factor(该属性指定处于错误位置时,约束进行纠正的速度)、relaxation_factor(改属性指定约束以什么样的比例改变速度)。如果该属性的值越高,哪儿对象在达到最小或最大角度时会被弹回来。

function createLeftFlipper() {
var flipperLeft = new Physijs.BoxMesh(
new THREE.BoxGeometry(12, 2, 2), Physijs.createMaterial(new THREE.MeshPhongMaterial(
{opacity: 0.6, transparent: true}
)), 0.3
);
flipperLeft.position.x = -6;
flipperLeft.position.y = 2;
flipperLeft.position.z = 0;
flipperLeft.castShadow = true;
scene.add(flipperLeft);
var flipperLeftPivot = new Physijs.SphereMesh(
new THREE.BoxGeometry(1, 1, 1), ground_material, 0); flipperLeftPivot.position.y = 1;
flipperLeftPivot.position.x = -15;
flipperLeftPivot.position.z = 0;
flipperLeftPivot.rotation.y = 1.4;
flipperLeftPivot.castShadow = true; scene.add(flipperLeftPivot); // when looking at the axis, the axis of object two are used.
// so as long as that one is the same as the scene, no problems
// rotation and axis are relative to object2. If position == cube2.position it works as expected
var constraint = new Physijs.HingeConstraint(flipperLeft, flipperLeftPivot, flipperLeftPivot.position, new THREE.Vector3(0, 1, 0));
scene.addConstraint(constraint); constraint.setLimits(
-2.2, // minimum angle of motion, in radians, from the point object 1 starts (going back)
-0.6, // maximum angle of motion, in radians, from the point object 1 starts (going forward)
0.1, // applied as a factor to constraint error, how big the kantelpunt is moved when a constraint is hit
0 // controls bounce at limit (0.0 == no bounce)
); return constraint;
}

7.用SliderConstraint将移动限制到一个轴

通过SliderConstraint约束,你可以将某个对象的移动限制到某个轴上。用代码 创建这些约束非常简单:

var constraint = new Physijs.SliderConstraint(sliderMesh, new THREE.Vector3(0, 0, 0), new THREE.Vector3(0, 1, 0));

            scene.addConstraint(constraint);
constraint.setLimits(-10, 10, 0, 0);
constraint.setRestitution(0.1, 0.1);

该约束对象接收三个参数(或者四个,如果想将一个对象约束到另外一个对象)。构造函数定义为new Physijs.SliderConstraint(mesh_a, mesh_b, position, axis)。这些参数和HingeConstraint的参数相似。我么还需要通过constraint.setLimits函数限定滑块能滑多远:constriant.setLimits(-10, 10, 0, 0)。参数依次为linear_lower指定对象的线性下限;linear_upper该属性指定对象的线性上限;anguar_lower该属性指定对象的角度下限;angular_higher该属性指定对象的角度上限。

8.用ConeTwistConstraint创建类似球削的约束

通过ConeTwistConstraint可以创建出一个移动受一系列角度限制的约束。我们可以指定一个对象绕着另一个对象转动时在x、y、z轴上的最小角度和最大角度。理解ConeTwistConstraint最好的方法就是看看创建约束的代码:

function createConeTwist() {
var baseMesh = new THREE.SphereGeometry(1);
var armMesh = new THREE.BoxGeometry(2, 12, 3); var objectOne = new Physijs.BoxMesh(baseMesh, Physijs.createMaterial(
new THREE.MeshPhongMaterial({color: 0x4444ff, transparent: true, opacity: 0.7}), 0, 0), 0);
objectOne.position.z = 0;
objectOne.position.x = 20;
objectOne.position.y = 15.5;
objectOne.castShadow = true;
scene.add(objectOne); var objectTwo = new Physijs.SphereMesh(armMesh, Physijs.createMaterial(
new THREE.MeshPhongMaterial({color: 0x4444ff, transparent: true, opacity: 0.7}), 0, 0), 10);
objectTwo.position.z = 0;
objectTwo.position.x = 20;
objectTwo.position.y = 7.5;
scene.add(objectTwo); objectTwo.castShadow = true; //position is the position of the axis, relative to the ref, based on the current position
var constraint = new Physijs.ConeTwistConstraint(objectOne, objectTwo, objectOne.position); scene.addConstraint(constraint);
// set limit to quarter circle for each axis
constraint.setLimit(0.5 * Math.PI, 0.5 * Math.PI, 0.5 * Math.PI);
constraint.setMaxMotorImpulse(1);
constraint.setMotorTarget(new THREE.Vector3(0, 0, 0)); // desired rotation return constraint;
}

我们先是创建出几个用约束连接起来的对象:ojectOne(球)和objectTwo(盒子)。ConeTwistConstraint的第一个参数是要约束的对象,第二个参数是第一个参数要约束到的对象,最后一个参数是约束应用的位置(在本例中,这个位置就是objectOne绕着旋转的位置)。将约束添加到场景中之后,我们就可以通过setLimts函数设置它的限制。setLimit函数接收三个弧度值,表示对象绕每个轴旋转的最大角度。

用Physijs在场景中添加物理效果的更多相关文章

  1. LoadRunner测试场景中添加负载生成器

    如何在LoadRunner测试场景中添加负载生成器 本文对如何在LoadRunner的测试场景中添加负载生成器,如何使用负载生成器的方法,总结形成操作指导手册,以指导测试人员指导开展相关工作. 1.什 ...

  2. Loadrunner:场景中添加负载生成器

    场景中添加负载生成器: (1)远程机子(假设ip为192.168.134.23)开启负载生成器 开始菜单找到:LoadRunner Agent Process 开启后任务栏会显示如下图: (2)场景中 ...

  3. THREE.JS(如何想场景中添加物体对象)

    这篇主要实现向模型对象中添加头像,并组成一个矩形 一.three.js是什么? 上篇说了点TWEEN这篇又来一根THREE是不是两兄弟啊?还真有点像,当想要做3D动画的时候,可能会考虑用TWEEN的动 ...

  4. OSG项目经验2<在场景中添加文字面版>

    添加文字版需要用到osg的三个名字空间:                         osgText::Text,这个类用来添加文字和设置文字的一些属性:                     ...

  5. Loadrunner在场景中添加多个负载机报错:Action.c(38): Error -26488: Could not obtain information about submitted解决方法

    Error -26488: Could not obtain information about submitted file "E:\.jpg": _stat32 rc=-1, ...

  6. Unity 去除场景中的雾效果

    Windows——Lighting——Setting,然后出现下面窗口,把Other Setting下,Fog的对勾去掉就可以了.

  7. 在unity中用鼠标实现在场景中拖动物体,用鼠标滚轮实现缩放

    在场景中添加一个Plan,Camera,Directional Light,Cube.添加两个脚本scrollerScirpt(挂在Camera),CubeDragScript(挂在Cube上). 1 ...

  8. 在Unity场景中控制日夜的轮转

    一.介绍 目的:通过在Unity场景中添加C#脚本完成日夜轮转的效果. 软件环境:Unity 2017.3.0f3,VS2013 二.操作过程 通过拖拽场景中的Directional Light我们知 ...

  9. 三维场景中使用BillBoard技术

    三维场景中对于渲染效果不是很精致的物体可以使用BillBoard技术实现,使用该技术需要将物体实时朝向摄像机,即计算billboard的旋转矩阵M. 首先根据摄像机位置cameraPos和billBo ...

随机推荐

  1. Filebeat 快速开始

    Filebeat可以做什么 条目 filebeat 编写语言 GO 是否支持多输出 支持 是否支持多输入 支持 是否支持修改日志内容 支持 是否会丢数据 不会 对多行文件的合并 支持 对多层目录的模糊 ...

  2. .net framework 4.6 asp.net mvc 使用NLog

    NUGET添加NLog和NLog.Config的引用 配置NLog.config <?xml version="1.0" encoding="utf-8" ...

  3. 回顾MySQL中的事务特征

    一.事务定义Transaction事务:一个最小的不可再分的工作单元:通常一个事务对应一个完整的业务(例如银行账户转账业务,该业务就是一个最小的工作单元)一个完整的业务需要批量的DML(insert. ...

  4. C#编程(七十六)----------使用指针实现基于栈的高性能数组

    使用指针实现基于栈的高性能数组 以一个案例为主来分析实现方法: using System; using System.Collections.Generic; using System.Linq; u ...

  5. 解决Cordova开发的iOS的app界面被状态栏覆盖

    解决方法如下: 一:在MainViewController.m中添加如下代码: -(void)viewWillDisappear:(BOOL)animated { if([[[UIDevice cur ...

  6. Java.lang.Character类

    Character将一个char基本数据类型封装在类中.这个类中只有一char类型的变量.Character是基于unicode码进行的Character所有的方法,都是围绕着这个char基本数据类型 ...

  7. iframe实现Ajax文件上传效果示例

    <!doctype html> <html> <head> <meta charset=utf-8> <head> <title> ...

  8. Postgres空间地理类型POINT POLYGON实现附近的定位和电子围栏功能

    目录 需求和背景 安装插件postgis 点POINT类型和距离 表添加POINT类型 添加空间索引 插入点 两个点之间的距离 附近5公里内的点 最近的10个点 面多边形'POLYGON' 添加字段类 ...

  9. Oracle NID工具修改数据库DBID、数据库名称、数据库实例名

    DBID是数据库的唯一标识符,在一些特殊场合会涉及到DBID的相关内容,本篇文章的目的是演示将DB_NAME的值从ORCL修改为ORCL1. 一.查看当前的环境 [oracle@oracledb ad ...

  10. 基于Centos搭建 Discuz 论坛

    系统要求:CentOS 6.8 64 位操作系统 搭建 Discuz 论坛 准备 LAMP 环境 LAMP 是 Linux.Apache.MySQL 和 PHP 的缩写,是 Discuz 论坛系统依赖 ...