1

I'm using the threejs editor as base for some threejs work. I'm trying to show some tooltips when the user hovers over certain elements. To achieve this I use a canvas which is rendered as texture on a sprite.

// create a canvas element
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 20px Arial";
context1.fillStyle = "rgba(0,0,0,0.95)";
context1.fillText('Hello, world!', 0, 20);

// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;

// use the texture as material
var spriteMaterial = new THREE.SpriteMaterial({
    map: texture1,
    useScreenCoordinates: false
});

// create the sprite and add it tho the scene
var sprite1 = new THREE.Sprite(spriteMaterial);
sprite1.scale.set(1000, 500, 1.0);
sprite1.position.set(50, 50, 0);
scene.add(sprite1);

var i = 0;
// change the canvas' content
setInterval(function () {
    context1.clearRect(0, 0, canvas1.width, canvas1.height);
    var message = 'test' + i++;
    context1.fillText(message, 4, 20);
    texture1.needsUpdate = true;
}, 1000)

"test0" is shown, but the sprite does not update. How to update the text on the sprite? Am I missing some cache invalidation here?

enter image description here

Akkumulator
  • 995
  • 1
  • 9
  • 26
  • 1
    I found a three.js boilerplate codepen and inserted your code and it worked like you want it to. I am not familiar with the three.js editor though. Boilerplate: [link](http://codepen.io/wpdildine/pen/OVWMOO). Working example: [link](http://codepen.io/anon/pen/NxYyqX). I inserted your code into the `addSprite()` function – micnil Jan 26 '16 at 13:54

3 Answers3

1

Why render the text box on your 3D canvas? It would be much better IMO to position your 2D html object by projecting the 3D object position in the 2D context of your page.

The advantage is that there won't be any rendering performance decrease and you can use normal CSS/HTML styling for your info box.

Check a great example on how you can do that here.

And another one belonging to this stackoverflow answer. Works as simple as this:

var proj = toScreenPosition(divObj, camera);

divElem.style.left = proj.x + 'px';
divElem.style.top = proj.y + 'px';

But if you do some googling you can easily find more examples...

Here is a fiddle to fool around with.

Community
  • 1
  • 1
Wilt
  • 41,477
  • 12
  • 152
  • 203
  • Using CSS renderer does give you some flexibility, at the expense of more expensive browser layer compositing, and losing the ability to render text hidden behind other 3d objects, (unless you do some tricks.) – manthrax Aug 25 '18 at 06:14
0

I think it is not working because you have not set the height and width of the canvas.

canvas1.width = 256;
canvas1.height = 256;

This line was also a problem: context1.fillStyle = "rgba(0.0,0,0,0.95)";
I changed it to : context1.fillStyle = '#ff0000'; for mine to work

Here is a fiddle with your code implemented, just added height and width to canvas and changed fillStyle

Wilt's answer is probably an better alternative if it fills your needs.

2pha
  • 9,798
  • 2
  • 29
  • 43
-1

EDIT: I'm a moron. This question is 2 years old.

It seems to be working for me... Are you sure you're not overlooking some errors in the console? Did you put a breakpoint in your setInterval to make sure it's getting called?

I did have to move the update to the renderloop as setInterval didn't seem to be defined for SO snippets, but check it out here:

var renderer = new THREE.WebGLRenderer();
var w = 300;
var h = 200;
renderer.setSize(w, h);
document.body.appendChild(renderer.domElement);

var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(
  45, // Field of view
  w / h, // Aspect ratio
  0.1, // Near
  10000 // Far
);
camera.position.set(15, 10, 15);
camera.lookAt(scene.position);
controls = new THREE.OrbitControls(camera, renderer.domElement);

var light = new THREE.PointLight(0xFFFF00);
light.position.set(20, 20, 20);
scene.add(light);
var light1 = new THREE.AmbientLight(0x808080);
light1.position.set(20, 20, 20);
scene.add(light1);

var sphereGeom = new THREE.SphereGeometry(5, 16, 16);
for (var i = 0; i < sphereGeom.vertices.length; i++) {
  var v = sphereGeom.vertices[i];
  if (v.y < 0) v.y = 0;
}
sphereGeom.verticesNeedUpdate = true;
sphereGeom.computeFaceNormals();
sphereGeom.computeVertexNormals();

var material = new THREE.MeshLambertMaterial({
  color: 0x808080
});
var mesh = new THREE.Mesh(sphereGeom, material);
scene.add(mesh);
renderer.setClearColor(0xdddddd, 1);

// create a canvas element
var canvas1 = document.createElement('canvas');
var context1 = canvas1.getContext('2d');
context1.font = "Bold 20px Arial";
context1.fillStyle = "rgba(0,0,0,0.95)";
context1.fillText('Hello, world!', 0, 20);

// canvas contents will be used for a texture
var texture1 = new THREE.Texture(canvas1)
texture1.needsUpdate = true;

// use the texture as material
var spriteMaterial = new THREE.SpriteMaterial({
  map: texture1,
  useScreenCoordinates: false
});

// create the sprite and add it tho the scene
var sprite1 = new THREE.Sprite(spriteMaterial);
sprite1.scale.set(40, 20, 1.0);
sprite1.position.set(-5, 0, -5);
scene.add(sprite1);

var i = 0;

(function animate() {
  requestAnimationFrame(animate);
  controls.update();
  renderer.render(scene, camera);



  context1.clearRect(0, 0, canvas1.width, canvas1.height);
  var message = 'test' + i++;
  context1.fillText(message, 4, 20);
  texture1.needsUpdate = true;

})();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://cdn.rawgit.com/mrdoob/three.js/master/examples/js/controls/OrbitControls.js"></script>
manthrax
  • 4,918
  • 1
  • 17
  • 16