Подтвердить что ты не робот

Динамически создавать 2D-текст в файле three.js

У меня есть 3D-модель, которую я создал в three.js. Основываясь на некоторых данных, я хочу создать набор стрелок, которые украшены небольшой текстовой меткой. Эти метки должны быть в 2D.

Похоже, у меня есть две альтернативы: либо использовать отдельный элемент canvas для создания текстуры, которая в свою очередь используется в 3D-модели, либо использовать HTML поверх элемента холста 3D-модели.

Мне интересно, как это сделать. Каков "правильный" способ сделать это? Любые предложения и примерный код очень приветствуются!

4b9b3361

Ответ 1

Если вы не возражаете, что текст всегда будет сверху (например, если объект заблокирован чем-то другим, его текстовая метка все еще будет видна и поверх всего остального) и что текст не будет под воздействием какого-либо рендеринга, такого как lights/shadow и т.д., тогда HTML - это самый простой способ пойти imho.

Вот пример кода:

var text2 = document.createElement('div');
text2.style.position = 'absolute';
//text2.style.zIndex = 1;    // if you still don't see the label, try uncommenting this
text2.style.width = 100;
text2.style.height = 100;
text2.style.backgroundColor = "blue";
text2.innerHTML = "hi there!";
text2.style.top = 200 + 'px';
text2.style.left = 200 + 'px';
document.body.appendChild(text2);

Замените 200 в переменных style.top и style.left для координат y и x (соответственно), которые вы хотите поместить в текст. Они будут положительными, где (0,0) - верхний левый холст. Для некоторых рекомендаций о том, как проектировать из трехмерной точки вашего холста в 2D-точку в пикселях в окне браузера, используйте фрагмент кода, например:

function toXYCoords (pos) {
        var vector = projector.projectVector(pos.clone(), camera);
        vector.x = (vector.x + 1)/2 * window.innerWidth;
        vector.y = -(vector.y - 1)/2 * window.innerHeight;
        return vector;
}

Убедитесь, что вы заранее вызвали camera.updateMatrixWorld().

Ответ 2

Если вы действительно хотите включить текст (или любое изображение) из объекта canvas как часть вашей 3D-сцены, проверьте код образца по адресу: http://stemkoski.github.com/Three.js/Texture-From-Canvas.html

Ответ 3

Ознакомьтесь с демоверсией.

TextGeometry занимает много памяти, и вам нужно загрузить шрифт, который содержит геометрию для каждой буквы, которую вы будете использовать. Если у меня в сцене более 100 текстовых сеток, мой браузер падает.

Вы можете нарисовать текст на холсте и включить его в свою сцену с помощью Sprite. Проблема в том, что вам нужно рассчитать размер шрифта. Если у вас есть движущаяся камера, вам необходимо периодически рассчитывать размер шрифта. В противном случае текст станет размытым, если камера окажется слишком близко к тексту.

Я написал класс TextSprite, который автоматически вычисляет наилучший возможный размер шрифта в зависимости от расстояния до камеры и размера элемента рендерера. Хитрость заключается в использовании обратного вызова .onBeforeRender класса Object3D, который получает камеру и средство визуализации.

let sprite = new THREE.TextSprite({
  text: 'Hello World!',
  fontFamily: 'Arial, Helvetica, sans-serif',
  fontSize: 12,
  fillColor: '#ffbbff',
});
scene.add(sprite);

Вы также можете изменить текст и шрифт на лету.

sprite.text = 'Hello Qaru!';
sprite.fontFamily = 'Tahoma, Geneva, sans-serif';
sprite.fontSize = 24;

Ответ 4

Обновить. Этот метод устарел и сильно загружен процессором. Не используйте его.

Я узнал еще одно решение на основе трёх .js,

var textShapes = THREE.FontUtils.generateShapes( text, options );
var text = new THREE.ShapeGeometry( textShapes );
var textMesh = new THREE.Mesh( text, new THREE.MeshBasicMaterial( { color: 0xff0000 } ) ) ;
scene.add(textMesh);
// Example text options : {'font' : 'helvetiker','weight' : 'normal', 'style' : 'normal','size' : 100,'curveSegments' : 300};

Теперь, чтобы динамически редактировать этот текст,

var textShapes = THREE.FontUtils.generateShapes( text, options );
var text = new THREE.ShapeGeometry( textShapes );
textMesh.geometry = text;
textMesh.geometry.needsUpdate = true;

Ответ 5

Я опубликовал модуль NPM, который делает это для вас: https://github.com/gamestdio/three-text2d

Это позволяет вам иметь Sprite или Mesh с визуализированным текстом, не имея дело с холстом вручную.

Пример:

var Text2D = require('three-text2d').Text2D

var text = new Text2D("Hello world!", { font: '30px Arial', fillStyle: '#000000', antialias: true })
scene.add(text) 

Ответ 6

Вы можете создать холст 2d и использовать css, чтобы поместить его в верхнюю часть холста webgl. Затем вы можете нарисовать текст с помощью методов ctx.fillText(text,x,y) или ctx.strokeText(text,x,y), предоставленных контекстом canvas 2d. Используя этот метод, вы можете рисовать другие вещи помимо текста, например стрелки и метры.

Вы можете использовать метод, предложенный @kronuus для преобразования точки из 3d в 2d.

Ответ 7

Ответ @LeeStemkoski был весьма полезен. Тем не менее есть небольшое изменение, которое необходимо внести в код , чтобы текст соответствовал вашим стрелкам вокруг, то есть в случае, когда стрелка перемещается, поворачивается и т.д.

Смотрите следующий код

                        var canvas1 = document.createElement('canvas');
                        var context1 = canvas1.getContext('2d');
                        context1.font = "Bold 10px Arial";
                        context1.fillStyle = "rgba(255,0,0,1)";
                        context1.fillText('Hello, world!', 0, 60);

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

                        var material1 = new THREE.MeshBasicMaterial({ map: texture1, side: THREE.DoubleSide });
                        material1.transparent = true;

                        var mesh1 = new THREE.Mesh(
                            new THREE.PlaneGeometry(50, 10),
                            material1
                          );
                        mesh1.position.set(25, 5, -5);
                        mesh1.rotation.x = -0.9;
                        shape.add(mesh1);
                        // Note that mesh1 gets added to the shape and not to the scene

                       scene.add(shape)

Как вы можете видеть, трюк заключается в том, чтобы добавить текст (mesh1) в shape (ваша "стрелка" ) вместо добавления его в сцену.

Надеюсь, это поможет.