# Issue with my billboarding / LookAt

**URL:** <https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825>\
**Category:** Questions\
**Created:** [December 19, 2021, 10:16pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825 "2021-12-19T22:16:46Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![sporkmilk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/sporkmilk/32/14377_2.png) [@sporkmilk](https://discourse.threejs.org/u/sporkmilk)\
**Post date:** [December 19, 2021, 10:16pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/1 "2021-12-19T22:16:46Z")

</div>

Hey everyone! I am refining my projects billboarding. Basically each of these labels currently rotate on their X and Z based on where the camera is - but now I am trying to make them lay flat basically if I look directly down on them. I believe I need to do something with the Y value but unsure if I am missing some kind of trig math, as setting it directly to the Y makes the labels vanish. I am working with 8thwall/vue/aframe/threejs Here is how I am currently handling it from the tick function:

```javascript
for (var i = 0; i < this.objs.length; i++) {
var pos = document.getElementById('camera').getAttribute('position');
this.objs[i].lookAt(new THREE.Vector3(pos.x,0,pos.z));
}

```

Here is a video of the current behavior: [Screen\_Recording\_20211217-172131\_Chrome.mp4 - Google Drive](https://drive.google.com/file/d/1M2nWUcLbpQP-JOkq2PPL8VrDsSdwRyCb/view?usp=sharing)

Thanks guys!

---

<div class="post-metadata">

**Author:** ![drcmda](https://avatars.discourse-cdn.com/v4/letter/d/91b2a8/32.png) [@drcmda](https://discourse.threejs.org/u/drcmda)\
**Post date:** [December 20, 2021, 11:33am UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/2 "2021-12-20T11:33:58Z")

</div>

not sure if it helps but ive had better luck with quaternions: [Spherical word-cloud - CodeSandbox](https://codesandbox.io/s/wordcloud-yup2o?file=/src/App.js:910-956)

```jsx
this.objs[i].quaternion.copy(camera.quaternion)

```

ps

> Here is how I am currently handling it from the tick function:

better you don’t create classes inside the render loop (`new THREE.Vector3(...)`), that will eventually trip the garbage collector.

---

<div class="post-metadata">

**Author:** ![sporkmilk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/sporkmilk/32/14377_2.png) [@sporkmilk](https://discourse.threejs.org/u/sporkmilk)\
**Post date:** [December 20, 2021, 3:56pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/3 "2021-12-20T15:56:15Z")

</div>

Thanks for this! I had tried this previously and it doesn’t give me the desired behavior. I think it is related to polling my a-frame camera in this AR app.

---

<div class="post-metadata">

**Author:** ![vielzutun.ch](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/vielzutun.ch/32/21366_2.png) [@vielzutun.ch](https://discourse.threejs.org/u/vielzutun.ch)\
**Post date:** [December 20, 2021, 5:25pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/4 "2021-12-20T17:25:34Z")

</div>

> [@sporkmilk](#):
>
> but unsure if I am missing some kind of trig math

If you don’t understand what the function does or requires, it sometimes helps to step back and think about whether you really need that function or might as well build something yourself which you do understand. BTW: I looked at the source code of Object lookat() and frankly: didn’t immediately understand it either.

On the other hand, the math behind orienting a billboard towards the camera isn’t rocket science. My proposed way of doing it “by hand” goes like this:

**B** illboard position: bx, by, bz  
**C** amera position: cx, cy, cz

**D** irection from camera to billboard:  
dx = bx - cx,  
dy = by - cy,  
dz = bz - cz

Now you need a sketch like this (source: [Wikipedia](https://de.wikipedia.org/wiki/Azimut#/media/Datei:Azimuth-Altitude_schematic.svg)):

[![](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Azimuth-Altitude_schematic.svg/300px-Azimuth-Altitude_schematic.svg.png) ](https://upload.wikimedia.org/wikipedia/commons/thumb/f/f7/Azimuth-Altitude_schematic.svg/300px-Azimuth-Altitude_schematic.svg.png)

If you take “ **N** orth” as the positive X, “Zenith” as positive “Y”, “Observer” as your camera and “Star” as your billboard, you can compute the `azimuth` angle as `Math.atan2( dz, dx );`

Rotate your billboard around its local y-axis by `azimuth`.

The `altitude` angle, sometimes also referred to as “elevation” can be computed as

`Math.atan2( dy, Math.sqrt( dx * dx + dz * dz) );`

Tilt your billboard around its local x.axis by `altitude`.

You may have to fiddle a bit until you get the signs of the angles right.

P.S.:  
**assumption** : for altitude = 0 and azimuth = 0, the billboard faces the positive z-axis

---

<div class="post-metadata">

**Author:** ![sporkmilk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/sporkmilk/32/14377_2.png) [@sporkmilk](https://discourse.threejs.org/u/sporkmilk)\
**Post date:** [December 20, 2021, 5:52pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/5 "2021-12-20T17:52:56Z")

</div>

Thank you so much for this breakdown.

This is what I got so far based on your reply, I’m still not getting the desired behavior but I may be overlooking something simple.

```javascript
  for (var i = 0; i < this.objs.length; i++) {
  var pos = document.getElementById('camera').getAttribute('position');
  var dir = new THREE.Vector3(this.objs[i].position.x - pos.x, this.objs[i].position.y - pos.y, this.objs[i].position.z - pos.z);
  this.objs[i].rotation.y = Math.atan2(dir.z, dir.x);
  this.objs[i].rotation.x = Math.atan2(dir.y, Math.sqrt( dir.x * dir.x + dir.z * dir.z));
  }

```

---

<div class="post-metadata">

**Author:** ![Arthur](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/arthur/32/22954_2.png) [@Arthur](https://discourse.threejs.org/u/Arthur)\
**Post date:** [December 20, 2021, 6:13pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/6 "2021-12-20T18:13:24Z")

</div>

Not sure I understand the behaviour you’re trying to achieve, but have you looked at using / modifying a Sprite?  
[https://threejs.org/examples/?q=sprite#webgl\_sprites](https://threejs.org/examples/?q=sprite#webgl_sprites)

---

<div class="post-metadata">

**Author:** ![sporkmilk](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/sporkmilk/32/14377_2.png) [@sporkmilk](https://discourse.threejs.org/u/sporkmilk)\
**Post date:** [December 20, 2021, 7:54pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/7 "2021-12-20T19:54:38Z")

</div>

The labels are object3D’s and children of my gltf. I am trying to have it so no matter how I view them I can read them. Currently they will rotate with me based on where I am standing (in AR) but if I look directly down at them, at the floor basically - all I see is the top of them. So I need that axis to basically face the camera.

---

<div class="post-metadata">

**Author:** ![Arthur](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/arthur/32/22954_2.png) [@Arthur](https://discourse.threejs.org/u/Arthur)\
**Post date:** [December 20, 2021, 11:35pm UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/8 "2021-12-20T23:35:58Z")

</div>

If you want to be able to read them no matter where the camera is, then using a THREE.Sprite is the easiest way to do so.  
Try replacing the labels with Sprites with something like this:

```javascript
for ( let label of labels ){
  // get the label's texture
  const { map } = label.material
  // create a new sprite using the texture
  const material = new THREE.SpriteMaterial({ map })
  const sprite = new THREE.Sprite( material )
  // position the sprite where the label is
  sprite.position.copy( label.position )
  label.parent.add( sprite )
  // remove the original object
  label.parent.remove( label )
  label.geometry.dispose()
  label.material.dispose()
}

```

You might need to adjust the sprites’ scales to match the labels’ scales.

---

<div class="post-metadata">

**Author:** ![vielzutun.ch](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.threejs.org/vielzutun.ch/32/21366_2.png) [@vielzutun.ch](https://discourse.threejs.org/u/vielzutun.ch)\
**Post date:** [December 21, 2021, 7:52am UTC](https://discourse.threejs.org/t/issue-with-my-billboarding-lookat/32825/9 "2021-12-21T07:52:38Z")

</div>

> [@sporkmilk](#):
>
> I’m still not getting the desired behavior but I may be overlooking something simple.

You provided a verbatim implementation of my rough suggestion, so that’s my fault.

One simple thing, which occurred to me only on 2nd thought is, that the formula for the azimuth angle is in fact only the angle of the `direction` vector. However, the billboard’s orientation is **at right angles** to the `direction` vector, so you’ll have to subtract `PI/2` (90°) from the computed azimuth angle to account for that:

```javascript
this.objs[i].rotation.y = Math.atan2(dir.z, dir.x) - Math.PI / 2;

```

Please report back if this fixes the azimuth rotation of the billboard.
