Three.js: 改变精灵的枢轴点

4
我创建了一个3D地图,并通过Sprites在该地图上标记点。这本身没有问题,但是Sprite标签的定位有些棘手。
因为我正在创建地图,相机可以从0度到90度倾斜,而理想情况下,标签始终保持一定距离直接在屏幕上方的物品上方。但不幸的是,由于Sprites始终围绕其原点居中,这会重叠物品,所以我必须在Y世界轴上将Sprite向上移动,这样Sprite的中心位置随着相机的倾斜而改变。如果查看的物品偏离中心,则会出现奇怪的效果,并且当相机朝向直下时效果也不太好。
没有jsfiddle方便,但我的应用程序http://leeft.eu/starcitizen/应该能很好地展示它的外观。
THREE.SpritePlugin的代码表明,我应该可以使用“matrixWorld”在渲染时将Sprite向上移动一定距离,但我无法弄清楚如何使用它,也不确定这是我需要使用的东西。

在渲染时,是否可以将精灵向上移动,或者改变它们的原点?或者还有其他方式可以实现相同的效果吗?

Three.js r.67


在three.js r.67中不支持更改精灵的原点。您可以根据相机视角移动精灵的位置。您还可以使用“OrthographicCamera”在第二遍渲染精灵,例如http://threejs.org/examples/webgl_sprites.html,但是现在您拥有的可能更好。 - WestLangley
3个回答

2
根据WestLangley的建议,我创建了一个可行的解决方案,通过根据观察角度更改精灵位置来实现,虽然花费了我几个小时才得到几行代码的数学工作。我也更新了我的应用程序,因此可以查看实时演示。
使用从OrbitControls.js中计算出的相机的倾斜角phi和方位角theta,以下代码计算出正好符合我的要求的精灵偏移量:
// Given:
// phi = tilt; 0 = top down view, 1.48 = 85 degrees (almost head on)
// theta = heading; 0 = north, < 0 looking east, > 0 looking west

// Compute an "opposite" angle; note the 'YXZ' axis order is important
var euler = new THREE.Euler( phi + Math.PI / 2, theta, 0, 'YXZ' );
// Labels are positioned 5.5 units up the Y axis relative to its parent
var spriteOffset = new THREE.Vector3( 0, -5.5, 0 );
// Rotate the offset vector to be opposite to the camera
spriteOffset.applyMatrix4( new THREE.Matrix4().makeRotationFromEuler( euler ) );

scene.traverse( function ( object ) {
   if ( ( object instanceof THREE.Sprite ) && object.userData.isLabel ) {
      object.position.copy( spriteOffset );
   }
} );

对于使用此代码的任何人,请注意:精灵标签是它们所指向的对象组的子项,这只会从父对象设置本地偏移量。


有没有三.js的增强功能可以让你的生活更轻松?顺便说一句,非常不错的演示。 - WestLangley
谢谢 :) ... 我真的想不出有什么特别的改进可以让使用three.js更容易。数学很难(至少对我来说是这样),而且组合如何调用东西使其工作有时非常困难。一些关于如何做某些事情的零碎信息可以在各个地方找到,但它们都散落在各处。 - Leeft
如果在three.js核心中可以像我回答中的代码片段那样做某些事情,那么我想对于一些人来说,拥有这样一种改变精灵原点的方法可能会很有用。 - Leeft

0
这是一个非常糟糕的方法,但如果您只以这种方式使用精灵,并且可以容忍对如何呈现精灵进行全局更改,那么您可以更改编译的three.js脚本中的以下行:
在代码中查找(ctrl + F) THREE.SpritePlugin = function ,然后您会看到:
this.init = function ( renderer ) {

    _gl = renderer.context;
    _renderer = renderer;

    vertices = new Float32Array( [
        - 0.5, - 0.5, 0, 0, 
          0.5, - 0.5, 1, 0,
          0.5,   0.5, 1, 1,
        - 0.5,   0.5, 0, 1
    ] );

我将数组的定义更改为以下内容:
    var vertices = new Float32Array( [
        - 0.5, - 0.0,  0, 0,
          0.5, - 0.0,  1, 0,
          0.5,   1.0,  1, 1,
        - 0.5,   1.0,  0, 1 
    ] );

现在我的所有精灵都以底部为旋转原点进行渲染。

如果您使用的是压缩版本,请搜索THREE.SpritePlugin=function,并将光标向右移动,直到找到定义的Float32Array,并在那里进行相同的更改。

注意:这仅会更改使用WebGL时的渲染方式。对于画布渲染器,您需要在THREE.CanvasRenderer中播放一个名为renderSprite()的函数。我猜测调整这些行就可以了:

var dist = 0.5 * Math.sqrt( scaleX * scaleX + scaleY * scaleY ); // allow for rotated sprite
_elemBox.min.set( v1.x - dist, v1.y - dist );
_elemBox.max.set( v1.x + dist, v1.y + dist );

这个函数在被压缩后,将会更难找到,因为renderSprite()不是作为一个公开的函数存在的,它很可能会被重命名为一些晦涩而小的东西。

注2:我尝试使用“polyfills”(或者说,在Three.js定义之后重新定义SpritePlugin),但由于某些原因,这导致了许多问题。同时,“polyfill”方法也存在作用域问题。

注3:我的three.js版本是r69,所以可能会有一些差别。


很遗憾,这对我也不是一个解决方案。首先,目前我通过Bower包含了三个内容,而不是自己编译。但我还使用了除了名牌之外的其他精灵。不过还是谢谢你的努力。 :) - Leeft
没问题,因为我遇到了和你一样的问题。这是我为自己解决它的方法,因为我不打算在3D世界中使用精灵以外的任何东西,而且中心枢轴点看起来很糟糕。希望在未来的Three.js版本中会有更好的解决方案。 :) - Tustin2121

0

我曾经遇到过类似的问题,但是是关于平面精灵的;我在地图上放置了树木,并希望它们能够以其底部为中心旋转,而不是以中心旋转。为了实现这一点,我只需编辑树木的图像文件,使其高度加倍,底部仅为透明:

http://imgur.com/ogFxyFw

如果您将第一张图片转换为精灵,当相机旋转时,它将围绕树的中心旋转。第二棵树将在相机旋转时围绕其底部旋转。

对于您的应用程序,如果您调整文本框的大小,使其中心与星号重合;可以通过添加几个换行符或编辑精灵的高度来实现。


我确实考虑过这个问题,但是由于我目前需要107个相当高精度的独立纹理(呈现在画布上),并且它们是即时生成的,这会带来相当大的内存开销。很多空白区域只会让情况更糟,因为纹理需要是2的幂次方大小的正方形,并且不适合使用。纹理图集效果更好,尽管在当前的three.js中还没有正确共享这些内存。 - Leeft

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接