透视相机模拟的效果与人眼看到的景象最接近,在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 — 摄像机视锥体远端面
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);
源代码:
<!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的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
可以使用两种方法来使对象冻结或不可移动。
在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 });
有一个方面,无法与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();
};
https://github.com/mrdoob/three.js https://threejs.org/ https://github.com/chandlerprall/Physijs http://www.hewebgl.com/article/articledir/1 https://github.com/to-be-architect/learning-threejs https://threejs.org/examples/#webgl_postprocessing_nodes_pass https://www.zhihu.com/topic/20024068/hot https://www.cnblogs.com/w-wanglei/p/6790942.html
国内第一Kotlin 开发者社区公众号,主要分享、交流 Kotlin 编程语言、Spring Boot、Android、React.js/Node.js、函数式编程、编程思想等相关主题。
越是喧嚣的世界,越需要宁静的思考。