Three.js:如何在three.js(r.64 +)中定位文本精灵?

时间:2018-04-23 13:02:57

标签: three.js

问题

我在场景中有一个BoxGeometry,我想在边缘添加边缘的大小,如下所示:

enter image description here

我找到this article,使用旧版本的three.js,其中包含以下代码段:

function makeTextSprite(message, opts) {
  var parameters = opts || {};
  var fontface = parameters.fontface || 'Helvetica';
  var fontsize = parameters.fontsize || 70;
  var canvas = document.createElement('canvas');
  var context = canvas.getContext('2d');
  context.font = fontsize + "px " + fontface;

  // get size data (height depends only on font size)
  var metrics = context.measureText(message);
  var textWidth = metrics.width;

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

  var spriteMaterial = new THREE.SpriteMaterial({
      map: texture,
      useScreenCoordinates: false
  });
  var sprite = new THREE.Sprite(spriteMaterial);
  sprite.scale.set(100, 50, 1.0);
  return sprite;
}

(好像在this answer中使用了相同的代码段)

但是,如果我正确地阅读了历史记录,那么根据this answer r.64 0,0,0,这已经有效了一段时间。当我尝试设置精灵的位置时,位置似乎是在2d而不是3d中设置 - 当我旋转相机时,文本不会在画布上移动(当我将位置设置为r.91时)。

stackoverflow回答链接到this demo,但我不确定如何调整他们的代码,因为他们加载图像纹理,而不是文本,似乎我的问题可能是创建一个合适的纹理,可以是分配给精灵并在3D空间中正确移动。

TL; DR

我很想看到更新版本的three.js上面的代码段的更新版本(我正在使用 sprite = makeTextSprite("Hello World", {}); sprite.position.set( 0,0,0 ) sprites.add( sprite ); )。我认为与上述代码片段相同的呼叫签名会很好,即

 library(RcppRoll)     
 windows <- c(45,60)
 multi_avg <- function(df, windows ) {
 for(i in seq_along(windows)){
    varname <- paste("avg", i , sep="_")
   df[[varname]] <- with(df, roll_meanr(x, n = windows[i], fill = NA))
 }
 df
}

会在场景中创建一个以0,0,0为中心的精简版“Hello World”。

更新

2 个答案:

答案 0 :(得分:1)

我建议使用THREE.CSS2DObject。你可以设置它的3d位置并使用HTML控制它的内容(包括css,你可以用像素而不是世界单位来调整大小)。内容将始终面向相机(面向)。

let el = document.createElement("div");
el.className = cssClassName;
el.textContent = "blabla";
el.style.visibility = "visible";
let obj = new THREE.CSS2DObject(el);
obj.position.copy(...)

但是这种方法很慢(适用于&lt; 50个物体)。如果你需要很多这样的对象 - 你需要使用单独的渲染过程:使用相同的WebGL渲染器和特殊的HUD OrthographicCamera渲染额外的HUD场景,使用画布3d上下文程序纹理绘制:

...设置:

let self = this;
self.renderers.WebGL.autoClear = false;
let w = self.outputContainer.offsetWidth || self.parentElem.get(0).offsetWidth;
let h = self.outputContainer.offsetHeight || self.parentElem.get(0).offsetHeight;
let fov = 75;
let aspectRatio = w / h;
let near = 1;
let far = 20000000;
self.camera = new THREE.PerspectiveCamera(fov, aspectRatio, near, far);

let hw = w / 2;
let hh = h / 2;
self.cameraHUD = new THREE.OrthographicCamera(-hw, hw, hh, -hh, 0, w); 

...绘图:

    function updateHUD(callback: (context: CanvasRenderingContext2D) => void) {
        let self = this;

        self.sceneHUD.children.slice(0).forEach(obj => {
            if (!(obj instanceof THREE.OrthographicCamera)) {
                self.sceneHUD.remove(obj);
            }
        });

        let canvasEl = self.renderers.WebGL.domElement;

        let bounds = canvasEl.getBoundingClientRect(); //canvasEl.offsetLeft, canvasEl.offsetTop

        let canvasElHUD = self.canvasElHUD;
        if (!canvasElHUD) {
            canvasElHUD = document.createElement("canvas");
            self.canvasElHUD = canvasElHUD;
            // Get 2D context and draw.
            self.bitmapHUD = canvasElHUD.getContext("2d");
            // Create texture from rendered graphics.
            self.textureHUD = new THREE.Texture(canvasElHUD);
            self.textureHUD.minFilter = THREE.NearestFilter;
            // Create HUD material.
            self.materialHUD = new THREE.MeshBasicMaterial({ map: self.textureHUD });
            self.materialHUD.transparent = true;
        }

        //set dimensions to fit the screen.
        if (canvasElHUD.width != bounds.width) {
            canvasElHUD.width = bounds.width;
        }
        if (canvasElHUD.height != bounds.height) {
            canvasElHUD.height = bounds.height;
        }

        if (callback) {
            callback(self.bitmapHUD); //custom draw
        }

        self.textureHUD.needsUpdate = true;

        // Create plane to render the HUD. This plane fill the whole screen.
        let planeGeometry = new THREE.PlaneGeometry(bounds.width, bounds.height);
        let planeMesh = new THREE.Mesh(planeGeometry, self.materialHUD);
        self.sceneHUD.add(planeMesh);
        return planeMesh;
    }
...
self.updateHUD(bitmap => {
    bitmap.clearRect(0, 0, bounds.width, bounds.height); //clear 2D canvas
    bitmap.strokeStyle = "green";
    bitmap.lineWidth = 1;
    bitmap.beginPath();
    let width = p2.x - p1.x;
    let height = p2.y - p1.y;
    bitmap.rect(p1.x, p1.y, width, height);
    bitmap.stroke();
});

...和渲染:

self.renderers.WebGL.clear();
self.renderers.WebGL.render(self.scene, camera);

self.renderers.WebGL.clearDepth();
self.renderers.WebGL.render(self.sceneOverlay, camera);
self.renderers.WebGL.clearDepth();
self.renderers.WebGL.render(self.sceneHUD, self.cameraHUD);

self.renderers.CSS2D.render(self.scene, self.camera);
self.renderers.CSS2D.render(self.sceneOverlay, self.camera);

答案 1 :(得分:0)

我发现上面使用sprite的代码片段。

function makeTextSprite(message, opts) {
    var parameters = opts || {};
    var fontface = parameters.fontface || 'Helvetica';
    var fontsize = parameters.fontsize || 120;
    var canvas = document.createElement('canvas');
    var context = canvas.getContext('2d');
    context.font = fontsize + "px " + fontface;

    // get size data (height depends only on font size)
    var metrics = context.measureText(message);
    var textWidth = metrics.width;

    // text color
    context.fillStyle = 'rgba(0, 0, 0, 1.0)';
    context.fillText(message, 0, fontsize);

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

    var spriteMaterial = new THREE.SpriteMaterial({ map: texture });
    var sprite = new THREE.Sprite( spriteMaterial );
    sprite.scale.set( 10, 5, 1.0 );
    sprite.center.set( 0,1 );
    return sprite;
}

关键线最终为sprite.center.set( 0,1 );

像这样使用:

        sprite = makeTextSprite("bla");
        sprite.position.copy( ... );
        scene.add( sprite );

但是,此解决方案对SVGRenderer不起作用,因为该渲染器不支持纹理,所以我现在正在寻找原始问题的THREE.js SVG解决方案