CSS3DRenderer disable distance scaling of HTML elements

I asked the following question on Stack Overflow but got no reply and it is quite urgent that I get this fixed!

This is the link to the stack overflow post: https://stackoverflow.com/questions/53615865/css3drenderer-disable-scaling

But I’ll also copy and paste the question here:

See my previous question here for reference to what I am trying to achieve

TL;DR: I am trying to get HTML elements to rotate in conjunction with OrbitControls to make it seem as if these HTML elements are stuck to the globe and move with it. (think map markers on a 3D earth above certain countries)

I achieved this almost successfully using the THREE.js CSS3DRenderer , and was able to get the HTML elements to stick to a location on my 3D globe with a lat/long calculation and rotate with the globe when OrbitControls are used.

The problem

The only issue I am having is that the HTML elements are scaling proportionate to how close/far they are from the camera. Usually I assume this would be the desired effect to help the sense of getting closer/further, but the scaling is causing me not to be able to size my HTML elements correctly and consistently, and also causing text and SVGs inside the elements to blur/become pixelated

What I want

I am looking for a way to turn off this scaling so that the HTML elements still transform in every other way, but stay the same size (i.e. scale(1, 1, 1) or their original scale) no matter where they are in the 3D renderer created by CSS3DRenderer .

I assume I will have to edit the CSS3DRenderer.js code for this, but I have absolutely no idea where to start and I cannot find any information anywhere else.

Thanks.

Some of my code:

Creating the CSS3DRenderer

//CSS3D Renderer
rendererHTML = new THREE.CSS3DRenderer();
rendererHTML.setSize(WIDTH, HEIGHT);
rendererHTML.domElement.classList.add('CSS3D-container');

containerHTML = document.querySelector('.globe__container');
containerHTML.appendChild(rendererHTML.domElement);

Resizing function (called on window resize event)

HEIGHT = sizeControlElem.getBoundingClientRect().width;
WIDTH = sizeControlElem.getBoundingClientRect().width;

renderer.setSize(WIDTH, HEIGHT);
rendererHTML.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();

Creating the CSS3DSprite objects from <li> elements in the HTML and setting their initial positions on the globe

for (let key in this.locationsObject) {

    _this.locationsObject[key].coordinates = calcPosFromLatLonRad(this.locationsObject[key].lat, this.locationsObject[key].long, 300);

    let CSS3D_Object = new THREE.CSS3DSprite(_this.locationsObject[key].element);
    CSS3D_Object.position.set(_this.locationsObject[key].coordinates[0], _this.locationsObject[key].coordinates[1], _this.locationsObject[key].coordinates[2]);
    CSS3D_Object.receiveShadow = false;
    CSS3D_Object.castShadow = false;
    sceneHTML.add(CSS3D_Object);

    _this.locationsObject[key].CSS_Object = CSS3D_Object;

    console.info(CSS3D_Object);
}

You can see some more of my code in the question here

Instead of using HTML elements why not implementing your labels as THREE.Sprites? This would provide you more flexibility in context of rendering and transformation. Is there a special reason for using CSS3DRenderer?

I need them to be HTML elements because I need to have text as part of the markers and that text needs to be content managed i.e. it can dynamically change (with Wordpress).

I’ve attached an image of what my globe currently looks like so you can get a better idea. As you can see the faded markers “behind” the globe are scaled down, the “active” markers are scaled up and so overlap eachother (I can’t link to the site unfortunately) but I want them to all be the same size and not scale at all so that I can control the size with CSS and classes.

57

So in other words, you want no size attenuation for specific THREE.CSS3DSprite elements, correct?

If you are not familiar with this term, check the following example to see the effect of setting sizeAttenuation to true or false (default). It uses THREE.Points but it would also work with THREE.Sprite.

https://threejs.org/examples/webgl_points_billboards.html

This seems like it may be what I’m looking for. I see in the demo that the sizeAttenuation is set on the point’s material, but my CSS3DSprite doesn’t have a material so how do I achieve the same effect with a CSS3DSprite?

I’m not super familiar with CSS3DRenderer but maybe there is a way to introduce a new property CSS3DSprite.sizeAttenuation that controls the effect. Could be worth to file a feature request at github…

In the docs it says it is “not possible to use the material system of three.js” with CSS3DRenderer so I’m certain it’s not going to work atm, but yeah I will probably do a feature request if there is no way to do it… do you have any other ideas on how I could stop the scaling in the mean time? Thanks for your help

@JJGerrish I just answered your question on StackOverflow with an alternative 3d-to-2D projection. As we discussed, scaling is now gone, since it no longer uses CSS3DRenderer, which means you lose the rotations. However, you can still use the .z attribute of the 2D vector to track whether it’s inside/outside the camera’s frustum.

Thanks for that, I’m going to try and implement your code and I’ll let you know on this thread how it goes!

BTW: There is also CSS2DRenderer that performs only translation which is sometimes very useful for rendering labels:

https://threejs.org/examples/css2d_label.html

Maybe you can use this renderer instead of CSS3DRenderer. In this way, you don’t have to perform a custom modification.

1 Like

Wow so all along this is what I was looking for hahaha, thanks for pointing me to this, I don’t know how I missed that there was a 2D renderer as well as a 3D one, thank you so much!

I’ve totally forgot about CSS2DRenderer as well :sweat_smile:

2 Likes

Oh well at least we got there eventually! :joy:

1 Like