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. 使用 P6Spy 来格式化 SQL 语句,支持 Hibernate 和 iBATIS

    事情起因 在处理一个查询小功能的时候,自认为 SQL 语句和传参均正确,然而查询结果无匹配数据,在查看 Hibernate 自带 SQL 语句输出的时候带着问好感觉有点不爽,特别是想复制 SQL 语句 ...

  2. android studio 中去除应用标题栏

    android studio 中去除应用标题栏 ㈠ ⒈ mainifests中设置: android:theme="@style/AppTheme"(即默认设置). ⒉ value ...

  3. hadoop三种运行模式

    1.单机模式:安装简单,几乎不用做任何配置,但仅限于调试用途 2.伪分布模式:在单节点上同时启动namenode.datanode.jobtracker.tasktracker.secondaryna ...

  4. Qt 4.8.2.+VS2008静态编译

    一.下载Qt 4.8.2-opensource. 二.解压到C:\Qt\4.8.2_static 修改C:\Qt\4.8.2_static\projects.pro文件,删除demos,doc,exa ...

  5. c# pictureBox1.Image的获得图片路径的三种方法 winform

    代码如下:c# pictureBox1.Image的获得图片路径的三种方法 winform 1.绝对路径:this.pictureBox2.Image=Image.FromFile("D:\ ...

  6. 关于在win8系统下用VMware 9.0装系统导致物理机不断重启的解决办法

    一.问题描述 前段时间将操作系统换成了Win8,安装上VMware 9.0英文版.然后在VMware中安装centos系统,结果每次到安装系统的时候,物理机系统就会莫名其妙地自动重启,毫无征兆地出现, ...

  7. 在 Python 中使用 in_memory 工作空间

    在 Python 中使用 in_memory 工作空间 在 Python 脚本中,in_memory 工作空间仅对地理处理工具有效:它不是可以写入任何数据的通用虚拟目录. 您可以按以下代码示例所示使用 ...

  8. escape()、encodeURI()、encodeURIComponent()区别详解 (转)

    JavaScript中有三个可以对字符串编码的函数,分别是: escape,encodeURI,encodeURIComponent,相应3个解码函数:,decodeURI,decodeURIComp ...

  9. oracle-用户和表空间创建

    windows下 创建临时表空间 create temporary tablespace user_temp tempfile 'D:\oracle\oradata\Oracle11i\user_te ...

  10. MySQL查看数据库表容量大小

    本文介绍MySQL查看数据库表容量大小的命令语句,提供完整查询语句及实例,方便大家学习使用. 1.查看所有数据库容量大小 select table_schema as '数据库', sum(table ...