首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >具有THREE.js ()能力的lookAt厚箭

具有THREE.js ()能力的lookAt厚箭
EN

Stack Overflow用户
提问于 2020-12-03 12:09:06
回答 2查看 1.5K关注 0票数 2

我想做一个“厚箭”网格,也就是像标准箭手这样的箭头,但用的是cylinder而不是line制成的轴。

tldr;不复制Arrow设计;请参阅问题末尾的Epilogue部分。

因此,我根据自己的需要复制和修改了代码(不用构造函数和方法)并进行了更改,现在它可以工作了:-

代码语言:javascript
运行
复制
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =  
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

//... START of ARROWMAKER SET of FUNCTIONS

// adapted from https://github.com/mrdoob/three.js/blob/master/src/helpers/ArrowHelper.js
        
    //====================================
    function F_Arrow_Fat_noDoesLookAt_Make ( dir, origin, length,  shaftBaseWidth, shaftTopWidth, color, headLength, headBaseWidth, headTopWidth ) 
    {

        //... dir is assumed to be normalized
        
        var thisArrow = new THREE.Object3D();////SW

        if ( dir            === undefined ) dir             = new THREE.Vector3( 0, 0, 1 );
        if ( origin         === undefined ) origin          = new THREE.Vector3( 0, 0, 0 );
        if ( length         === undefined ) length          = 1;
        if ( shaftBaseWidth     === undefined ) shaftBaseWidth  = 0.02 * length;
        if ( shaftTopWidth  === undefined ) shaftTopWidth   = 0.02 * length;
        if ( color          === undefined ) color           = 0xffff00;
        if ( headLength     === undefined ) headLength      = 0.2 * length;
        if ( headBaseWidth  === undefined ) headBaseWidth   = 0.4 * headLength;
        if ( headTopWidth   === undefined ) headTopWidth    = 0.2 * headLength;//... 0.0 for a point.


        
        /* CylinderBufferGeometry parameters from:-
        // https://threejs.org/docs/index.html#api/en/geometries/CylinderBufferGeometry
            * radiusTop — Radius of the cylinder at the top. Default is 1.
            * radiusBottom — Radius of the cylinder at the bottom. Default is 1.
            * height — Height of the cylinder. Default is 1.
            * radialSegments — Number of segmented faces around the circumference of the cylinder. Default is 8
            * heightSegments — Number of rows of faces along the height of the cylinder. Default is 1.
            * openEnded — A Boolean indicating whether the ends of the cylinder are open or capped. Default is false, meaning capped.
            * thetaStart — Start angle for first segment, default = 0 (three o'clock position).
            * thetaLength — The central angle, often called theta, of the circular sector. The default is 2*Pi, which makes for a complete cylinder.        
        */
        //var shaftGeometry  = new THREE.CylinderBufferGeometry( 0.0, 0.5,    1, 8, 1 );//for strongly tapering, pointed shaft
          var shaftGeometry  = new THREE.CylinderBufferGeometry( 0.1, 0.1,    1, 8, 1 );//shaft is cylindrical
        //shaftGeometry.translate( 0, - 0.5, 0 );
        shaftGeometry.translate( 0, + 0.5, 0 );
    
    //... for partial doesLookAt capability
    //shaftGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
    
        var headGeometry = new THREE.CylinderBufferGeometry( 0, 0.5, 1, 5, 1 ); //for strongly tapering, pointed head
        headGeometry.translate( 0, - 0.5, 0 );
    
    //... for partial doesLookAt capability
    //headGeometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );
            
        thisArrow.position.copy( origin );

        /*thisArrow.line = new Line( _lineGeometry, new LineBasicMaterial( { color: color, toneMapped: false } ) );
        thisArrow.line.matrixAutoUpdate = false;
        thisArrow.add( thisArrow.line ); */

        thisArrow.shaft = new THREE.Mesh( shaftGeometry, new THREE.MeshLambertMaterial( { color: color  } ) );
        thisArrow.shaft.matrixAutoUpdate = false;
        thisArrow.add( thisArrow.shaft );
        
        thisArrow.head = new THREE.Mesh( headGeometry, new THREE.MeshLambertMaterial( { color: color } ) );
        thisArrow.head.matrixAutoUpdate = false;
        thisArrow.add( thisArrow.head );

        //thisArrow.setDirection( dir );
        //thisArrow.setLength( length, headLength, headTopWidth );
        
        var arkle = new THREE.AxesHelper (2 * length);
        thisArrow.add (arkle);
                
        F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir                               ) ;////SW
        F_Arrow_Fat_noDoesLookAt_setLength   ( thisArrow, length, headLength, headBaseWidth ) ;////SW
        F_Arrow_Fat_noDoesLookAt_setColor    ( thisArrow, color                             ) ;////SW
                
        scene.add ( thisArrow );
        
        //... this screws up for the F_Arrow_Fat_noDoesLookAt  kind of Arrow
        //thisArrow.lookAt(0,0,0);//...makes the arrow's blue Z axis lookAt Point(x,y,z).
    }
    //... EOFn F_Arrow_Fat_noDoesLookAt_Make().
    

    //=============================================
    function F_Arrow_Fat_noDoesLookAt_setDirection( thisArrow, dir ) 
    {
        // dir is assumed to be normalized
        if ( dir.y > 0.99999 ) 
        {
            thisArrow.quaternion.set( 0, 0, 0, 1 );

        } else if ( dir.y < - 0.99999 ) 
        {
            thisArrow.quaternion.set( 1, 0, 0, 0 );

        } else 
        {
            const _axis = /*@__PURE__*/ new THREE.Vector3();
            
            _axis.set( dir.z, 0, - dir.x ).normalize();

            const radians = Math.acos( dir.y );

            thisArrow.quaternion.setFromAxisAngle( _axis, radians );
        }
    }
    //... EOFn F_Arrow_Fat_noDoesLookAt_setDirection().


    //========================================= 
    function F_Arrow_Fat_noDoesLookAt_setLength( thisArrow, length, headLength, headBaseWidth ) 
    {
        if ( headLength     === undefined ) headLength      = 0.2 * length;
        if ( headBaseWidth  === undefined ) headBaseWidth   = 0.2 * headLength;

        thisArrow.shaft.scale.set( 1, Math.max( 0.0001, length - headLength ), 1 ); // see #17458
                                                                                  //x&z the same, y as per length-headLength
    //thisArrow.shaft.position.y = length;//SW ???????
        thisArrow.shaft.updateMatrix();

        thisArrow.head.scale.set( headBaseWidth, headLength, headBaseWidth ); //x&z the same, y as per length
        
        thisArrow.head.position.y = length;
        thisArrow.head.updateMatrix();
    }
    //...EOFn  F_Arrow_Fat_noDoesLookAt_setLength().

    //======================================== 
    function F_Arrow_Fat_noDoesLookAt_setColor( thisArrow, color ) 
    {
        thisArrow.shaft.material.color.set( color );
        thisArrow.head.material.color.set( color );
    }
    //...EOFn  F_Arrow_Fat_noDoesLookAt_setColor().
        
//... END of ARROWMAKER SET of FUNCTIONS
// = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =  
//= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = 

这可以用于固定方向箭头,其中箭头方向可以在施工时提供。

但是现在我需要随着时间的推移改变箭头的方向(用于跟踪移动的目标)。目前,Object3D.lookAt()函数是不够的,因为箭头沿其Object3D y轴指向,而lookAt()则将Object3D z轴定向到给定的目标位置。

通过实验,我在那里得到了一些帮助:-

代码语言:javascript
运行
复制
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( Math.PI / 2 ) );

在轴和头部几何图形上(在上面的代码摘录中注释掉了这两行)。这似乎使圆柱网格指向正确的方向。但问题是,网格是错误的形状和头部网格被移动远离轴网格。

通过反复尝试,我可能能够调整代码,使箭头适用于我现在的示例。但是(鉴于我对四元数理解不足),我不相信它将(a)足够普遍地适用于所有情况,或(b)是充分未来的证据,以防止THREE.js的演变。

因此,对于如何实现“厚箭”的lookAt()功能,我将不胜感激。

结束语

我的主要目的是不遵循“帮助箭”的设计。

正如s 01和其他一些问题的答案所表明的那样,使用Object3D.add()“嵌套”函数有一种更简单的方法。

例如:-

(1)创建两个圆柱网格(用于箭头和箭头),这些网格在默认情况下将指向Y方向;使几何长度=1.0以协助将来的重新缩放。

(2)将网格添加到父Object3D对象中。

(3)用parent.rotateX(Math.PI/2)将母轴+90度绕X轴旋转.

(4)将父对象添加到祖父母对象中。

(5)随后使用grandparent.lookAt(target_point_as_World_position_Vec3_or_x_y_z)

注:如果父母或祖父母的比例不是lookAt(),则(n,n,n) ()将不能正常工作。

父对象和祖父母对象类型可以是普通的THREE.Object3D,或者THREE.Group,或者THREE.Mesh (如果需要的话,可以是不可见的,例如通过设置小维度或.visibility=false)。

箭头助手可以动态使用,但前提是内部方向在使用lookAt()之前被设置为(0,0,1)。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2020-12-03 15:07:54

您可以将lookAt应用于任何Object3DObject3D.lookAt( ... )

您已经发现lookAt会导致形状指向+Z方向,并且正在对此进行补偿。但是,随着Group的引入,它可以更进一步。Group也是从Object3D派生的,所以它们也支持lookAt方法。

代码语言:javascript
运行
复制
let W = window.innerWidth;
let H = window.innerHeight;

const renderer = new THREE.WebGLRenderer({
  antialias: true,
  alpha: true
});
document.body.appendChild(renderer.domElement);

const scene = new THREE.Scene();

const camera = new THREE.PerspectiveCamera(28, 1, 1, 1000);
camera.position.set(10, 10, 50);
camera.lookAt(scene.position);
scene.add(camera);

const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(0, 0, -1);
camera.add(light);

const group = new THREE.Group();
scene.add(group);

const arrowMat = new THREE.MeshLambertMaterial({color:"green"});

const arrowGeo = new THREE.ConeBufferGeometry(2, 5, 32);
const arrowMesh = new THREE.Mesh(arrowGeo, arrowMat);
arrowMesh.rotation.x = Math.PI / 2;
arrowMesh.position.z = 2.5;
group.add(arrowMesh);

const cylinderGeo = new THREE.CylinderBufferGeometry(1, 1, 5, 32);
const cylinderMesh = new THREE.Mesh(cylinderGeo, arrowMat);
cylinderMesh.rotation.x = Math.PI / 2;
cylinderMesh.position.z = -2.5;
group.add(cylinderMesh);

function render() {
  renderer.render(scene, camera);
}

function resize() {
  W = window.innerWidth;
  H = window.innerHeight;
  renderer.setSize(W, H);
  camera.aspect = W / H;
  camera.updateProjectionMatrix();
  render();
}

window.addEventListener("resize", resize);

resize();

let rad = 0;

function animate() {
  rad += 0.05;
  group.lookAt(Math.sin(rad) * 100, Math.cos(rad) * 100, 100);
  renderer.render(scene, camera);
  requestAnimationFrame(animate);
}
requestAnimationFrame(animate);
代码语言:javascript
运行
复制
html,
body {
  width: 100%;
  height: 100%;
  padding: 0;
  margin: 0;
  overflow: hidden;
  background: skyblue;
}
代码语言:javascript
运行
复制
<script src="https://threejs.org/build/three.min.js"></script>

这里的关键是使锥/轴指向+Z方向,然后添加到Group中。这意味着他们的方向现在是本地的团体。当组的lookAt发生变化时,形状也随之变化。因为“箭头”形状指向小组的局部+Z方向,这意味着它们也指向给group.lookAt(...);的任何位置。

进一步工作

这只是一个起点。您将需要根据您希望它在正确位置上构造箭头、具有正确长度等方面进行调整。尽管如此,分组模式应该使lookAt更容易使用。

票数 2
EN

Stack Overflow用户

发布于 2020-12-03 17:37:56

你所需要的只是对嵌套有更多的了解,这样你就可以相对于他们的父母放置物体了。正如上面的答案所提到的,您可以使用GroupObject3D,但不必使用。你可以在你的圆柱体上嵌套你的箭头,把你的圆柱体指向z方向,然后使用内置的,不太复杂的方法lookAt

不要在这样简单的事情上使用矩阵或四元数,因为这会使我们更难搞清楚事情。既然THREE.js允许嵌套帧,那么就利用它吧!

代码语言:javascript
运行
复制
const renderer = new THREE.WebGLRenderer;
const camera = new THREE.PerspectiveCamera;
const scene = new THREE.Scene;
const mouse = new THREE.Vector2;
const raycaster = new THREE.Raycaster;
const quaternion = new THREE.Quaternion;
const sphere = new THREE.Mesh(
    new THREE.SphereGeometry( 10, 10, 10 ),
    new THREE.MeshBasicMaterial({ transparent: true, opacity: .1 })
);
const arrow = new THREE.Group;
const arrowShaft = new THREE.Mesh(
    // We want to ensure our arrow is completely offset into one direction
    // So the translation ensure every bit of it is in Y+
    new THREE.CylinderGeometry( .1, .3, 3 ).translate( 0, 1.5, 0 ),
    new THREE.MeshBasicMaterial({ color: 'blue' })
);
const arrowPoint = new THREE.Mesh(
    // Same thing, translate to all vertices or +Y
    new THREE.ConeGeometry( 1, 2, 10 ).translate( 0, 1, 0 ),
    new THREE.MeshBasicMaterial({ color: 'red' })
);
const trackerPoint = new THREE.Mesh(
  new THREE.SphereGeometry( .2 ),
  new THREE.MeshBasicMaterial({ color: 'green' })
);
const clickerPoint = new THREE.Mesh(
  trackerPoint.geometry,
  new THREE.MeshBasicMaterial({ color: 'yellow' })
);

camera.position.set( 10, 10, 10 );
camera.lookAt( scene.position );

// Place the point at the top of the shaft
arrowPoint.position.y = 3;
// Point the shaft into the z-direction
arrowShaft.rotation.x = Math.PI / 2;

// Attach the point to the shaft
arrowShaft.add( arrowPoint );
// Add the shaft to the global arrow group
arrow.add( arrowShaft );
// Add the arrow to the scene
scene.add( arrow );
scene.add( sphere );
scene.add( trackerPoint );
scene.add( clickerPoint );

renderer.domElement.addEventListener( 'mousemove', mouseMove );
renderer.domElement.addEventListener( 'click', mouseClick );
renderer.domElement.addEventListener( 'wheel', mouseWheel );

render();

document.body.appendChild( renderer.domElement );

function render(){

    renderer.setSize( innerWidth, innerHeight );
    camera.aspect = innerWidth / innerHeight;
    camera.updateProjectionMatrix();
    renderer.render( scene, camera );
    
}
function mouseMove( event ){

    mouse.set(
        event.clientX / event.target.clientWidth * 2 - 1,
        -event.clientY / event.target.clientHeight * 2 + 1
    );

    raycaster.setFromCamera( mouse, camera );
    
    const hit = raycaster.intersectObject( sphere ).shift();

    if( hit ){

      trackerPoint.position.copy( hit.point );
      render();
       
    }
    
    document.body.classList.toggle( 'tracking', !!hit );

}
function mouseClick( event ){
  
    clickerPoint.position.copy( trackerPoint.position );
    arrow.lookAt( trackerPoint.position );
    render();
    
}
function mouseWheel( event ){

    const angle = Math.PI * event.wheelDeltaX / innerWidth;
    
    camera.position.applyQuaternion(
      quaternion.setFromAxisAngle( scene.up, angle )
    );
    camera.lookAt( scene.position );
    render();
    
}
代码语言:javascript
运行
复制
body { padding: 0; margin: 0; }
body.tracking { cursor: none; }
代码语言:javascript
运行
复制
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r123/three.min.js"></script>

你可以使用你的鼠标(如果它有水平滚动,应该在履带上)并点击指向箭头。我还添加了一些跟踪点,这样您就可以看到‘`lookAt’可以工作,而不会使其过于复杂,这就是指向您单击的包装球的点。

有了这个,我肯定把shaft这个词打得太频繁了。听起来有点奇怪。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/65125697

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档