首页 > 其他分享 >【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果

【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果

时间:2022-12-25 19:32:24浏览次数:63  
标签:Physijs constraint scene js Three var new THREE





【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果_javascript


Three.js 极简教程

简介

Three.JS 是什么

  • Three.JS是基于WebGL的Javascript开源框架,简言之,就是能够实现3D效果的JS库。

Three.JS 能做什么

  • 利用Three.JS可以制作出很多酷炫的3D动画,并且Three.js还可以通过鼠标、键盘、拖拽等事件形成交互,在页面上增加一些3D动画和3D交互可以产生更好的用户体验。通过Three.JS可以实现全景视图,这些全景视图应用在房产、家装行业能够带来更直观的视觉体验。在电商行业利用Three.JS可以实现产品的3D效果,这样用户就可以360度全方位地观察商品了,给用户带来更好的购物体验。另外,使用Three.JS还可以制作类似微信跳一跳那样的小游戏。随着技术的发展、基础网络的建设,web3D技术还能得到更广泛的应用。

WebGL

  • WebGL是一种Javascript的3D图形接口,把JavaScript和OpenGL ES 2.0(OpenGL for Embedded Systems)结合在一起。

OpenGL

  • OpenGL是开放式图形标准,跨编程语言、跨平台,Javascript、Java 、C、C++ 、 python 等都能支持OpenG ,OpenGL的Javascript实现就是WebGL,另外很多CAD制图软件都采用这种标准。OpenGL ES 2.0是OpenGL的子集,针对手机、游戏主机等嵌入式设备而设计。

Canvas

  • Canvas是HTML5的画布元素,在使用Canvas时,需要用到Canvas的上下文,可以用2D上下文绘制二维的图像,也可以使用3D上下文绘制三维的图像,其中3D上下文就是指WebGL。

基础知识

主要组件(现实世界的抽象3D模型)

  • 场景(scene)
  • 场景是一个容器,可以看做摄影的房间,在房间中可以布置背景、摆放拍摄的物品、添加灯光设备等。
  • 相机(camera)
  • 相机是用来拍摄的工具,通过控制相机的位置和方向可以获取不同角度的图像。
  • 常用相机
  • 透视相机

透视相机模拟的效果与人眼看到的景象最接近,在3D场景中也使用得最普遍,这种相机最大的特点就是近大远小,同样大小的物体离相机近的在画面上显得大,离相机远的物体在画面上显得小。
​​​PerspectiveCamera( fov : Number, aspect : Number, near : Number, far : Number )​

fov — 摄像机视锥体垂直视野角度
aspect — 摄像机视锥体长宽比
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面

  • 正交相机
    使用正交相机时无论物体距离相机远或者近,在最终渲染的图片中物体的大小都保持不变。
    ​​​OrthographicCamera( left : Number, right : Number, top : Number, bottom : Number, near : Number, far : Number )​

left — 摄像机视锥体左侧面
right — 摄像机视锥体右侧面
top — 摄像机视锥体上侧面
bottom — 摄像机视锥体下侧面
near — 摄像机视锥体近端面
far — 摄像机视锥体远端面

  • 渲染器(renderer)
  • 渲染器利用场景和相机进行渲染,渲染过程好比摄影师拍摄图像,如果只渲染一次就是静态的图像,如果连续渲染就能得到动态的画面。在JS中可以使用requestAnimationFrame实现高效的连续渲染。

代码实例

  • 在Three.js中,要渲染物体到网页中,我们需要3个组建:场景(scene)、相机(camera)和渲染器(renderer)。有了这三样东西,才能将物体渲染到网页中去。
var scene = new THREE.Scene();  // 场景
var camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);// 透视相机
var renderer = new THREE.WebGLRenderer(); // 渲染器
renderer.setSize(window.innerWidth, window.innerHeight); // 设置渲染器的大小为窗口的内宽度,也就是内容区的宽度
document.body.appendChild(renderer.domElement);

进阶实战

组织网络协同图谱




【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果_3D_02


源代码:

<!DOCTYPE html>
<html>
<head>
<style>
body {
/* set margin to 0 and overflow to hidden, to go fullscreen */
margin: 0;
overflow: hidden;
background-color: #000000;
}
</style>

<title>ANT</title>

<script src="/libs/three.js" type="text/javascript"></script>
<script src="/libs/stats.js" type="text/javascript"></script>
<script src="/libs/physi.js" type="text/javascript"></script>
<script src="/libs/dat.gui.js" type="text/javascript"></script>
<script src="/libs/chroma.js" type="text/javascript"></script>
<script src="/libs/perlin.js" type="text/javascript"></script>

<script type="text/javascript">

'use strict';

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

var scale = chroma.scale(['green', 'red']);

var initScene, render, meshes = [], lines = [],
renderer, scene, light, camera;

initScene = function () {
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setSize(window.innerWidth, window.innerHeight);

renderer.setClearColor(new THREE.Color(0x000000));
renderer.shadowMapEnabled = true;

document.getElementById('viewport').appendChild(renderer.domElement);

scene = new Physijs.Scene({reportSize: 20, fixedTimeStep: 1 / 60});

scene.setGravity(new THREE.Vector3(0, 0, 0));

camera = new THREE.PerspectiveCamera(
35,
window.innerWidth / window.innerHeight,
1,
1000
);
camera.position.set(105, 85, 85);
camera.lookAt(new THREE.Vector3(0, 0, 0));
scene.add(camera);

// ambi
var ambi = new THREE.AmbientLight(0x222222);
scene.add(ambi);

// Light
light = new THREE.SpotLight(0xFFFFFF);
light.position.set(40, 50, 100);
light.castShadow = true;
light.shadowMapDebug = true;
light.shadowCameraNear = 10;
light.shadowCameraFar = 200;
light.intensity = 1.5;
scene.add(light);

var controls = new function () {
this.addSphereMesh = function (radius, widthSegments, heightSegments) {
var sphere = new Physijs.SphereMesh(
// function ( radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength )
new THREE.SphereGeometry(radius, widthSegments, heightSegments),
getMaterial()
);

setPosAndShade(sphere);

meshes.push(sphere);
scene.add(sphere);
};

this.clearMeshes = function () {
meshes.forEach(function (e) {
scene.remove(e);
});
meshes = [];
}

};

// add sphere nodes
for (let i = 0; i < 7; i++) {
controls.addSphereMesh(3, 20, 20);
}
// link sphere nodes
drawLinks();
addConstraints();

requestAnimationFrame(render);
scene.simulate();
};

function addConstraints() {
for (let i = 0; i < meshes.length - 1; i++) {
let node1 = meshes[i];
let node2 = meshes[i + 1];
// 铰链约束 HingeConstraint
var constraint = new Physijs.HingeConstraint(node1, node2, node2.position, new THREE.Vector3(0, 2, 0));
scene.addConstraint(constraint);
constraint.setLimits(
-3.14, // minimum angle of motion, in radians, from the point object 1 starts (going back)
3.14, // 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.2 // controls bounce at limit (0.0 == no bounce)
);
}
}

function drawLinks() {
lines.forEach(function (e) {
scene.remove(e);
});
lines = [];

// 中心节点跟所有节点相连
for (let i = 1; i < meshes.length; i++) {
let node1 = meshes[0];
let node2 = meshes[i];
// add a line to link cube and sphere
// 声明一个几何体geometry
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial({
vertexColors: true
});
// 定义两种颜色,分别表示线条两个端点的颜色
var color1 = new THREE.Color(0x444444), color2 = new THREE.Color(0x00FF00);
// 线的材质可以由2点的颜色决定, 定义2个顶点的位置,并放到geometry中
var p1 = new THREE.Vector3(node1.position.x, node1.position.y, node1.position.z);
var p2 = new THREE.Vector3(node2.position.x, node2.position.y, node2.position.z);
// 几何体里面有一个vertices变量,用来存放点。
geometry.vertices.push(p1);
geometry.vertices.push(p2);
geometry.colors.push(color1, color2);
// 定义线条,使用THREE.Line类
var line = new THREE.Line(geometry, material, THREE.LineSegments);
lines.push(line);
// 将这条线加入到场景中
scene.add(line);
}

// 首尾相连
for (let i = 0; i < meshes.length - 1; i++) {
let node1 = meshes[i];
let node2 = meshes[i + 1];
// add a line to link cube and sphere
// 声明一个几何体geometry
var geometry = new THREE.Geometry();
var material = new THREE.LineBasicMaterial({
vertexColors: true
});
// 定义两种颜色,分别表示线条两个端点的颜色
var color1 = new THREE.Color(0x444444), color2 = new THREE.Color(0x00FF00);
// 线的材质可以由2点的颜色决定, 定义2个顶点的位置,并放到geometry中
var p1 = new THREE.Vector3(node1.position.x, node1.position.y, node1.position.z);
var p2 = new THREE.Vector3(node2.position.x, node2.position.y, node2.position.z);
// 几何体里面有一个vertices变量,用来存放点。
geometry.vertices.push(p1);
geometry.vertices.push(p2);
geometry.colors.push(color1, color2);
// 定义线条,使用THREE.Line类
var line = new THREE.Line(geometry, material, THREE.LineSegments);
lines.push(line);
// 将这条线加入到场景中
scene.add(line);
}
}

function setPosAndShade(obj) {
let X = Math.random() * 20;
let Y = 0;
let Z = Math.random() * 20;
obj.position.set(X, Y, Z);

obj.rotation.set(Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI, Math.random() * 2 * Math.PI);
obj.castShadow = true;
}

function getMaterial() {
var material = Physijs.createMaterial(new THREE.MeshLambertMaterial({
color: scale(Math.random()).hex(),
// opacity: 0.8,
// transparent: true
}), 0.5, 0.7);
return material;
}

render = function () {
requestAnimationFrame(render);
renderer.render(scene, camera);
drawLinks();
addConstraints();
scene.simulate(undefined, 2);
};
window.onload = initScene;

</script>
</head>

<body>
<div id="viewport"></div>
</body>

</html>

用Physijs在场景中添加物理效果

创建一个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);
}

约束

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对象创建出一个高度场。

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

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

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

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

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

DOFConstraint/通过自由度约束,你可以限制对象在任意轴上的活动,你可以设置对象活动的额最小、最大角度。这是最灵活的约束方式
点对点
var constraint = new Physijs.PointConstraint(
physijs_mesh_a, // First object to be constrained
physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
new THREE.Vector3( 0, 10, 0 ) // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
铰链约束
var constraint = new Physijs.HingeConstraint(
physijs_mesh_a, // First object to be constrained
physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
new THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis
);
scene.addConstraint( constraint );
constraint.setLimits(
low, // minimum angle of motion, in radians
high, // maximum angle of motion, in radians
bias_factor, // applied as a factor to constraint error
relaxation_factor, // controls bounce at limit (0.0 == no bounce)
);
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableMotor();
滑块约束
var constraint = new Physijs.SliderConstraint(
physijs_mesh_a, // First object to be constrained
physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
new THREE.Vector3( 1, 0, 0 ) // Axis along which the hinge lies - in this case it is the X axis
);
scene.addConstraint( constraint );
constraint.setLimits(
linear_lower, // lower limit of linear movement, expressed in world units
linear_upper, // upper limit of linear movement, expressed in world units
angular_lower, // lower limit of angular movement, expressed in radians
angular_upper // upper limit of angular movement, expressed in radians
);
constraint.setRestitution(
linear, // amount of restitution when reaching the linear limits
angular // amount of restitution when reaching the angular limits
);
constraint.enableLinearMotor( target_velocity, acceration_force );
constraint.disableLinearMotor();
constraint.enableAngularMotor( target_velocity, acceration_force );
constraint.disableAngularMotor();
锥形约束
var constraint = new Physijs.ConeTwistConstraint(
physijs_mesh_a, // First object to be constrained
physijs_mesh_b, // Second object to be constrained
new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLimit( x, y, z ); // rotational limit, in radians, for each axis
constraint.setMotorMaxImpulse( max_impulse ); // float value of the maximum impulse the motor can apply toward its target
constraint.setMotorTarget( target ); // target is the desired rotation for the constraint and can be expressed by a THREE.Vector3, THREE.Matrix4, or THREE.Quaternion
constraint.enableMotor();
constraint.disableMotor();
自由度约束
var constraint = new Physijs.DOFConstraint(
physijs_mesh_a, // First object to be constrained
physijs_mesh_b, // OPTIONAL second object - if omitted then physijs_mesh_1 will be constrained to the scene
new THREE.Vector3( 0, 10, 0 ), // point in the scene to apply the constraint
);
scene.addConstraint( constraint );
constraint.setLinearLowerLimit( new THREE.Vector3( -10, -5, 0 ) ); // sets the lower end of the linear movement along the x, y, and z axes.
constraint.setLinearUpperLimit( new THREE.Vector3( 10, 5, 0 ) ); // sets the upper end of the linear movement along the x, y, and z axes.
constraint.setAngularLowerLimit( new THREE.Vector3( 0, -Math.PI, 0 ) ); // sets the lower end of the angular movement, in radians, along the x, y, and z axes.
constraint.setAngularUpperLimit( new THREE.Vector3( 0, Math.PI, 0 ) ); // sets the upper end of the angular movement, in radians, along the x, y, and z axes.
constraint.configureAngularMotor(
which, // which angular motor to configure - 0,1,2 match x,y,z
low_limit, // lower limit of the motor
high_limit, // upper limit of the motor
velocity, // target velocity
max_force // maximum force the motor can apply
);
constraint.enableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z
constraint.disableAngularMotor( which ); // which angular motor to configure - 0,1,2 match x,y,z

冻结一个对象

可以使用两种方法来使对象冻结或不可移动。

  1. 如果对象始终是静态的,例如地面,则可以0使用第三个参数创建网格时将其设置为质量:new Physijs.BoxMesh( geometry, material, 0)。任何具有质量的对象0将永远是静态的。
  2. 用于对象在某些时候是静态的,并且在其他方​​面是动态的。
    The second method can be used for objects when they will be static at some times and dynamic at others, like in the ​​​jenga example​​​. If you call object.setAngularFactor
    and object.setLinearFactor
    with a THREE.Vector3( 0, 0, 0 )
    then no energy will be applied to the object. You can use object.setAngularVelocity
    and object.setLinearVelocity
    in the same way to clear any velocities the object may already have. At a later point you can reset the object's linear and angular factors to ( 1, 1, 1 )
    , again as it's done in the ​​​jenga example​​.

材质Materials

在THREE材质基础上增加了摩擦度和恢复度

var friction = 0.8; // 摩擦度
var restitution = 0.3; // 恢复度
var material = Physijs.createMaterial(
new THREE.MeshBasicMaterial({ color: 0x888888 }),
friction,
restitution
);
var mesh = new Physijs.BoxMesh(
new THREE.CubeGeometry( 5, 5, 5 ),
material
);

暂停/恢复模拟

var render = function() {
if (!isPaused) {
scene.simulate();
}
renderer.render();
};
var unpauseSimulation = function() {
isPaused = false;
scene.onSimulationResume();
};

恢复模拟需要调用场景的onSimulationResume方法.

场景配置

var scene = new Physijs.Scene({ reportsize: 50, fixedTimeStep: 1 / 60 });
  1. fixedTimeStep default=1/60 此数字确定模拟步骤的模拟时间。数字越小,模拟越准确
  2. broadphase 指定将使用哪个宽带,选择是dynamic和sweepprune。
  3. reportsize default 50 作为优化,包含对象位置的世界报告基于此数字预先初始化。最好将其设置为您的场景将具有的对象数量。
  4. setGravity方法 default ( 0, -10, 0 ) 设定重力的数量和方向
  5. setFixedTimeStep 在构造函数中default 1 / 60 重置fixedTimeStep给定的值

更新对象的位置和旋转

有一个方面,无法与three.js进行无缝集成:更改对象的位置和/或旋转。如果这样做,您必须将该对象__dirtyPosition或__dirtyRotation标志设置为true,否则将从模拟中的最后一个已知值覆盖。

var mesh = new Physijs.BoxMesh( geometry, material );
scene.add( mesh );

var render = function() {
// Change the object's position
mesh.position.set( 0, 0, 0 );
mesh.__dirtyPosition = true;

// Change the object's rotation
mesh.rotation.set(0, 90, 180);
mesh.__dirtyRotation = true;

// You may also want to cancel the object's velocity
mesh.setLinearVelocity(new THREE.Vector3(0, 0, 0));
mesh.setAngularVelocity(new THREE.Vector3(0, 0, 0));

scene.simulate();
renderer.render();
};


标签:Physijs,constraint,scene,js,Three,var,new,THREE
From: https://blog.51cto.com/u_15236724/5968281

相关文章