Three.js: change the pivot point of a sprite

后端 未结 3 1143
南旧
南旧 2021-01-06 07:25

I\'ve created a 3D map and I\'m labelling points on this map through Sprites. This in itself works fine, except for the positioning of the sprite labels.

Because I\'

相关标签:
3条回答
  • 2021-01-06 07:45

    I had a similar problem, but with flat sprites; I put trees on a map and wanted them to rotate in such a way that they'd rotate around their base, rather than their center. To do that, i simply edited the image files of the trees to be twice as tall, with the bottom as just a transparency:

    http://imgur.com/ogFxyFw

    if you turn the first image into a sprite, it'll rotate around the tree's center when the camera rotates. The second tree will rotate around it's base when the camera rotates.

    For your application, if you resize the textbox in such a way that the center of it would be coincide with the star; perhaps by adding a few newlines or editing the height of the sprite

    0 讨论(0)
  • 2021-01-06 07:49

    As suggested by WestLangley, I've created a workable solution by changing the sprite position based on the viewing angle though it took me hours to work out the few lines of code needed to get the math working. I've updated my application too, so see that for a live demo.

    With the tilt angle phi and the heading angle theta as computed from the camera in OrbitControls.js the following code computes a sprite offset that does exactly what I want it to:

    // 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 );
       }
    } );
    

    Note for anyone using this code: that the sprite labels are children of the object group they're referring to, and this only sets a local offset from that parent object.

    0 讨论(0)
  • 2021-01-06 07:56

    This is very much a hack, but if you will only use sprites in this way, and could tolerate a global change to how sprites were rendered, you could change the following line in the compiled three.js script:

    Find (ctrl+F) THREE.SpritePlugin = function, and you'll see:

    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
        ] );
    

    I changed the definition of the array to the following:

        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 
        ] );
    

    And now all my sprites render with the rotation origin at the bottom.

    If you use the minified version, search for THREE.SpritePlugin=function and move the cursor right until you find the Float32Array defined, and make the same changes there.

    Note: this changes how things render only when using WebGL. For the canvas renderer you'll have to play a function called renderSprite() in the THREE.CanvasRenderer. I suspect playing with these lines will do it:

    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 );
    

    This function will also be a lot more difficult to find in the minified version, since renderSprite() is not an outward facing function, it'll likely be renamed to something obscure and small.

    Note 2: I did try making these modifications with "polyfills" (or rather, redefining the SpritePlugin after Three is defined), but it caused major problems with things not being properly defined for some reason. Scoping is also an issue with the "polyfill" method.

    Note 3: My version of three.js is r69. So there may be differences above.

    0 讨论(0)
提交回复
热议问题