Skip to content

BatchedText: billboard material #341

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
upisfree opened this issue Nov 8, 2024 · 10 comments
Open

BatchedText: billboard material #341

upisfree opened this issue Nov 8, 2024 · 10 comments

Comments

@upisfree
Copy link

upisfree commented Nov 8, 2024

Hello again :)

I'm trying to combine BatchedText and your createBillboardMaterial, but I'm not good at shaders so I don't know why it doesn't work. Maybe I need to do some magic matrix stuff, but I can't figure it out which matrix to use :)

Could you please suggest what I'm doing wrong? Thanks again :D

createBillboardMaterial source (it works with usual Text class just fine):
#101 (comment)
https://codesandbox.io/s/createbillboardmaterial-xl6mt?file=/src/createBillboardMaterial.js:0-580

BatchedText + createBillboardMaterial — all texts are fixed in camera view

image
image

BatchedText + material without modifications — all texts are in right places, but they don't look at the camera

image

P. S. It might make sense to add the createBillboardMaterial implementation to the repository, since Text looking always at the camera is a very common request among developers?

@lojjic
Copy link
Collaborator

lojjic commented Nov 9, 2024

Hmm, that's a tricky one. I'll try to see if I can make the batched matrix transforms work with other custom shader transforms. But you may be right about this being a common option that would belong in the base shader.

@Methuselah96
Copy link

I am looking to use BatchedText with billboarding as well, text rendering is the most expensive part of our current three.js scene and would love to use BatchedText to speed things up.

@upisfree
Copy link
Author

upisfree commented Feb 10, 2025

For anyone needed this, I wrote typescript version of this shader:

import { Camera, Vector3 } from 'three'
import { BatchedText } from 'troika-three-text'

const _position = new Vector3()
const _scale = new Vector3()
const _cameraPosition = new Vector3()

// usage: add Text with `billboardBatchedText.addText(text)`,
//        and then add it to three's parent with `group.add(text)`
class BillboardBatchedText extends BatchedText {
  constructor(public scaleFactor: number = 1) {
    super()
  }

  update(camera: Camera) {
    const texts = this._members.keys()

    camera.getWorldPosition(_cameraPosition)

    texts.forEach((text) => {
      const parent = text.parent

      parent.updateMatrix()
      parent.updateWorldMatrix(false, false)
      parent.getWorldPosition(_position)

      text.position.copy(_position)

      camera.getWorldQuaternion(text.quaternion)

      // size attenuation
      const scale =
        _scale.subVectors(text.position, _cameraPosition).length() * this.scaleFactor
      text.scale.set(scale, scale, scale)
    })
  }
}

export default BillboardBatchedText

@crystalfp
Copy link

Thanks @upisfree for your code! Unfortunately in the loop on the texts inside the BatchText, the const parent = text.parent is always null so nothing happens.

Sorry for my ignorance, but I cannot decipher the entire content of the loop.

Anyway, leaving only the camera.getWorldQuaternion(text.quaternion); line inside the loop gives a perfectly acceptable billboard text.

Thanks!
mario

@upisfree
Copy link
Author

upisfree commented Apr 3, 2025

@crystalfp

the const parent = text.parent is always null so nothing happens.

I assumed that it would take one instance of BillboardBatchedText and one instance of three.js' Object3D to use for proper positioning in the world.
Here's how you can use it:

const group = new Group();
// position, scale or rotate the group somehow. it will be used to position our text as a parent
const batchedText = new BillboardBatchedText();

const text = new Text();
batchedText.addText(text);
group.add(text);

@crystalfp
Copy link

Thanks @upisfree !
I was not aware that you have to add the Text to a group (and then add the group to the scene, I suppose). What I'm doing is:

const batchedText = new BillboardBatchedText();

const text = new Text();
batchedText.addText(text);
scene.add(batchedText);

What I cannot evaluate is if this setup will speedup label rendering.

Hopefully the documentation for batchedText will be ready someday...
Thanks for clarifying!
mario

@lojjic
Copy link
Collaborator

lojjic commented Apr 9, 2025

The Text instances should not be added to the scene outside the BatchedText container -- doing so would result in them being rendered twice.

@crystalfp
Copy link

@lojjic This means my way to use BatchedText (Adding it to the scene) is the correct one. Correct?

@lojjic
Copy link
Collaborator

lojjic commented Apr 9, 2025

@crystalfp Yes, it makes sense to me. The billboarding should be achievable by just setting the Text's quaternion, it doesn't need to be added to any external group to do so.

@upisfree
Copy link
Author

upisfree commented Apr 9, 2025

The reason I did put Text into other group is because I needed BatchedText AND use a different coordinate system (Z+ or something like that). I didn‘t want to calculate positions and quaternions by myself, I wanted to change coordinate system of a group and just put children in it. I don‘t remember why I didn’t just put BatchedText into the group, I guess something didn‘t work there.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants