【问题标题】:Three.js: change the pivot point of a spriteThree.js:改变精灵的轴心点
【发布时间】:2015-01-08 03:14:29
【问题描述】:

我创建了一个 3D 地图,并通过 Sprites 在该地图上标记点。除了精灵标签的定位之外,这本身就可以正常工作。

因为我正在创建一个地图,所以相机可以从 0 度倾斜到 90 度,而理想情况下,标签总是在屏幕上它正在标记的项目正上方保持一定距离。但不幸的是,由于精灵总是以它们的原点为中心并且与项目重叠,我必须在 Y 世界轴上向上移动精灵,并且随着相机的倾斜,精灵的中心位置会发生变化。如果查看的项目偏离中心,这看起来很奇怪,并且当相机直视下方时效果不佳。

没有 jsfiddle 方便,但我在 http://leeft.eu/starcitizen/ 的应用程序应该会给人一个很好的印象。

THREE.SpritePlugin 的代码向我建议应该可以使用“matrixWorld”在渲染时将精灵在屏幕的 Y 轴上向上移动一段距离,但我不知道如何使用它,我也不知道我完全确定这是我首先需要使用的。

渲染时是否可以在屏幕上向上移动精灵,或者改变它们的原点?或者有没有其他方法可以达到同样的效果?

Three.js r.67

【问题讨论】:

  • three.js r.67 不支持更改精灵的来源。您可以根据相机视角移动精灵的位置。您还可以使用OrthographicCamera 在第二遍中渲染精灵,就像在threejs.org/examples/webgl_sprites.html 中一样,但是您现在拥有的可能会更好。

标签: three.js


【解决方案1】:

正如 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 );
   }
} );

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

【讨论】:

  • three.js 的增强功能是否可以让您的生活更轻松?非常好的演示,顺便说一句。
  • 谢谢 :) ... 我想不出有什么特别的增强功能可以让three.js 的生活更轻松。数学很难(无论如何对我来说),并且将如何调用事物以使其工作有时非常困难。可以找到关于如何做一些事情的信息花絮,但它们无处不在。
  • 如果可以在three.js 核心中在我的答案中做类似sn-p 的事情,那么我想有这样一种方法来改变精灵的起源可能对某些人有用。
【解决方案2】:

我也遇到过类似的问题,但是是扁平的精灵;我把树放在地图上,并希望它们以这样一种方式旋转,即它们围绕它们的基地而不是它们的中心旋转。为此,我简单地将树木的图像文件编辑为两倍高,底部只是一个透明度:

http://imgur.com/ogFxyFw

如果你将第一张图片变成一个精灵,它会在相机旋转时围绕树的中心旋转。当相机旋转时,第二棵树将围绕它的基部旋转。

对于您的应用程序,如果您调整文本框的大小以使其中心与星星重合;也许通过添加一些换行符或编辑精灵的高度

【讨论】:

  • 我当然考虑过这一点,但由于我目前需要 107 个具有相当高细节的独特纹理(文本渲染到画布上)并且它们是动态生成的,这将增加大量内存开销。大量的空白会使情况变得更糟,因为纹理需要是一个大小为 2 次方的正方形,而且它不适合。纹理图集效果更好,尽管当前的 three.js 中尚未正确共享内存。
【解决方案3】:

这在很大程度上是一种 hack,但如果您只以这种方式使用 sprite,并且可以容忍对 sprite 的渲染方式进行全局更改,您可以在编译的 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 时,这会改变事物的呈现方式only。对于画布渲染器,您必须在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 之后重新定义 SpritePlugin),但是由于某些原因导致未正确定义的东西导致了重大问题.范围也是“polyfill”方法的一个问题。

注3:我的three.js版本是r69。所以上面可能会有差异。

【讨论】:

  • 不幸的是,这也不是我的解决方案。首先,目前我通过 Bower 包含三个,而不是自己编译。但我也在使用其他精灵,而不仅仅是铭牌。谢谢你的努力。 :)
  • 没问题,因为我遇到了你的问题,因为这也是我的问题。这就是我为我解决的方法,因为我不打算将精灵用于 3d 世界中的 2D 角色以外的任何东西,而且中心枢轴点看起来很糟糕。希望在 Three.js 的未来版本中会有更好的解决方案。 :)
猜你喜欢
  • 1970-01-01
  • 2018-02-25
  • 2016-04-19
  • 2017-06-29
  • 1970-01-01
  • 1970-01-01
  • 2013-03-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多