【发布时间】:2020-02-04 12:30:12
【问题描述】:
我试图根据 three.js 文档中的布料示例构建一个标志,但我在定义标志的约束时遇到了麻烦。
示例链接:
https://threejs.org/examples/#webgl_animation_cloth
示例源码:https://github.com/mrdoob/three.js/blob/master/examples/webgl_animation_cloth.html
由于代码大约有 300 行,我已经尽力注释每个代码块。
在我的解释中, function SatisfyConstraints在两点之间进行减法以获得微分向量。然后它计算长度并从中得出修正。
代码末尾的“模拟”功能使用 SatisfyConstraints来调节旗帜应该如何飘扬。
var container, renderer, scene, camera, controls;
var near, far;
var params = {
enableWind: true,
showBall: false
}
var DAMPING = 0.03;
var MASS = 0.15;
var DRAG = 1 - DAMPING;
var DistanceUntilRest = 25;
var GRAVITY = 981 * 1.4;
var gravityVector = new THREE.Vector3( 0, - GRAVITY, 0 ).multiplyScalar( MASS ); // F = mg
var ForceVector = new THREE.Vector3();
var windForce = new THREE.Vector3( 0, 0, 0 );
var TIMESTEP = 18 / 1000;
var TIMESTEP_SQ = TIMESTEP * TIMESTEP;
var xSegs = 10, ySegs = 10;
var clothFunction = planeEquation( DistanceUntilRest * xSegs, DistanceUntilRest * ySegs );
var cloth = new Cloth(xSegs, ySegs);
function planeEquation (width, height) {
return function (a, b, target) {
var x = (a - 0.5) * width;
var y = (b + 0.5) * height;
var z = 0; // idk why z has to be 0
target.set( x, y, z);
}
}
function Particles (x, y, z, mass) {
this.position = new THREE.Vector3();
this.previous = new THREE.Vector3();
this.original = new THREE.Vector3();
this.acceleration = new THREE.Vector3( 0 , 0 , 0);
this.mass = mass;
this.invMass = 1 / mass;
this.FVector = new THREE.Vector3();
this.tmp = new THREE.Vector3();
// initializing clothFunction so planeEquation can set the target
clothFunction(x, y, this.position);
clothFunction(x, y, this.previous);
clothFunction(x, y, this.original);
}
// converting force into acceleration
Particles.prototype.addForce = function (force) {
this.acceleration.add( this.FVector.add(force).multiplyScalar(this.invMass) )
};
// after each time sequence the new position adds itself by the amount of drag and acceleration
Particles.prototype.integrate = function (timeSequence) {
var newPos = this.tmp.subVectors(this.position, this.previous);
newPos.multiplyScalar(DRAG).add(this.position);
newPos.add( this.acceleration.multiplyScalar( timeSequence ) );
// passes tmp down to this.previous then assumes this.position as the new position
this.tmp = this.previous;
this.previous = this.position;
this.position = newPos;
// resetting acceleration
this.acceleration.set(0, 0, 0);
};
var diff = new THREE.Vector3();
function SatisfyConstraints (point, next_point, distance) {
diff.subVectors(next_point.position, point.position);
var currentDist = diff.length();
if (currentDist === 0) return;
var correction = diff.multiplyScalar(1 - distance / currentDist);
var correctionHalf = correction.multiplyScalar(0.5);
point.position.add( correctionHalf );
next_point.position.sub( correctionHalf );
}
function Cloth(width, height) {
this.width = width || 10;
this.height = height || 10;
var particles = [], constraints = [];
var i, j, u, v;
for (i = 0; i <= height; i++ ) {
for (j = 0; j <= width; j++) {
particles.push( new Particles( i / width, j / height, 0, MASS) )
}
}
// Providing structure for cloth - similar to drawing
for (i = 0; i <= height; i++ ) {
for (j = 0; j <= width; j++) {
constraints.push([
particles[index(i, j)],
particles[index(i, j + 1)],
DistanceUntilRest
]);
constraints.push([
particles[index(i, j)],
particles[index(i + 1, j)],
DistanceUntilRest
])
}
}
for ( u = width, v = 0; v < height; v ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u, v + 1 ) ],
DistanceUntilRest
] );
}
for ( v = height, u = 0; u < width ; u ++ ) {
constraints.push( [
particles[ index( u, v ) ],
particles[ index( u + 1, v ) ],
DistanceUntilRest
] );
}
this.particles = particles;
this.constraints = constraints;
function index( u, v ) {
return u + v * ( width + 1 );
}
this.index = index;
}
function simulate( time ) {
if ( ! lastTime ) {
lastTime = time;
return;
}
var i, j, il, particles, particle, constraints, constraint;
// Aerodynamics forces
if ( params.enableWind ) {
var indx;
var normal = new THREE.Vector3();
var indices = clothGeometry.index;
var normals = clothGeometry.attributes.normal;
particles = cloth.particles;
for ( i = 0, il = indices.count; i < il; i += 3 ) {
for ( j = 0; j < 3; j ++ ) {
indx = indices.getX( i + j );
normal.fromBufferAttribute( normals, indx );
tmpForce.copy( normal ).normalize().multiplyScalar( normal.dot( windForce ) );
particles[ indx ].addForce( tmpForce );
}
}
}
for ( particles = cloth.particles, i = 0, il = particles.length; i < il; i ++ ) {
particle = particles[ i ];
particle.addForce( gravityVector );
particle.integrate( TIMESTEP_SQ );
}
// Start Constraints
constraints = cloth.constraints;
il = constraints.length;
for ( i = 0; i < il; i ++ ) {
constraint = constraints[ i ];
SatisfyConstraints( constraint[ 0 ], constraint[ 1 ], constraint[ 2 ] );
}
var pos;
// Ball Constraints
ballPosition.z = - Math.sin( Date.now() / 600 ) * 90; //+ 40;
ballPosition.x = Math.cos( Date.now() / 400 ) * 70;
if ( params.showBall ) {
sphere.visible = true;
for ( particles = cloth.particles, i = 0, il = particles.length; i < il; i ++ ) {
particle = particles[ i ];
pos = particle.position;
diff.subVectors( pos, ballPosition );
if ( diff.length() < ballSize ) {
// collided
diff.normalize().multiplyScalar( ballSize );
pos.copy( ballPosition ).add( diff );
}
}
} else {
sphere.visible = false;
}
// Floor Constraints
for ( particles = cloth.particles, i = 0, il = particles.length; i < il; i ++ ) {
particle = particles[ i ];
pos = particle.position;
if ( pos.y < - 250 ) {
pos.y = - 250;
}
}
}
我还尝试在 SatisfyConstraints 中使用 position.copy(point), position.copy(next_point) 来复制坐标并将传递的参数粘贴到新变量中,但它不起作用。
function SatisfyConstraints (point, next_point, distance) {
var pointPos, next_pointPos;
pointPos.position.copy(point);
next_pointPos.position.copy(next_point);
diff.subVectors(pointPos, next_pointPos);
...
}
我原以为 SatisfyConstraints 会像往常一样工作,但我收到了如下错误消息:
未捕获的类型错误:无法读取未定义的属性“位置”
在 SatisfyConstraints (simulation3D.html:102)
在模拟 (simulation3D.html:212)
在动画 (simulation3D.html:419)
【问题讨论】:
标签: javascript three.js