У меня есть 3D-модель, которую я создал в three.js. Основываясь на некоторых данных, я хочу создать набор стрелок, которые украшены небольшой текстовой меткой. Эти метки должны быть в 2D.
Похоже, у меня есть две альтернативы: либо использовать отдельный элемент canvas для создания текстуры, которая в свою очередь используется в 3D-модели, либо использовать HTML поверх элемента холста 3D-модели.
Мне интересно, как это сделать. Каков "правильный" способ сделать это? Любые предложения и примерный код очень приветствуются!
Если вы не возражаете, что текст всегда будет сверху (например, если объект заблокирован чем-то другим, его текстовая метка все еще будет видна и поверх всего остального) и что текст не будет под воздействием какого-либо рендеринга, такого как 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().
Если вы действительно хотите включить текст (или любое изображение) из объекта canvas как часть вашей 3D-сцены, проверьте код образца по адресу: http://stemkoski.github.com/Three.js/Texture-From-Canvas.html
Обновить. Этот метод устарел и сильно загружен процессором. Не используйте его.
Я узнал еще одно решение на основе трёх .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;
Я опубликовал модуль 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)
Вы можете создать холст 2d и использовать css, чтобы поместить его в верхнюю часть холста webgl. Затем вы можете нарисовать текст с помощью методов ctx.fillText(text,x,y)
или ctx.strokeText(text,x,y)
, предоставленных контекстом canvas 2d. Используя этот метод, вы можете рисовать другие вещи помимо текста, например стрелки и метры.
Вы можете использовать метод, предложенный @kronuus для преобразования точки из 3d в 2d.
Просмотрите демо.
TextGeometry
потребляет много памяти, и вам нужно загрузить шрифт, который содержит геометрию для каждой буквы, которую вы будете использовать. Если у меня более 100 текстовых сеток в сцене, мой браузер отключается.
Вы можете нарисовать текст на холсте и включить его в свою сцену с помощью Sprite
. Проблема заключается в том, что вам нужно рассчитать размер шрифта. Если у вас есть движущаяся камера, вам нужно периодически рассчитать размер шрифта. В противном случае текст будет размытым, если вы приблизитесь к тексту с камерой.
Я написал класс TextSprite, который автоматически вычисляет наилучший размер шрифта в зависимости от расстояния до камеры и размера элемент рендеринга. Хитрость заключается в использовании обратного вызова .onBeforeRender
класса Object3D, который принимает камеру и средство визуализации.
let sprite = new THREE.TextSprite({
textSize: 10,
texture: {
text: 'Hello World!',
fontFamily: 'Arial, Helvetica, sans-serif',
},
material: {color: 0xffbbff},
});
scene.add(sprite);
Вы также можете изменить текст, размер текста и шрифт на лету.
sprite.textSize = 25;
sprite.material.map.text = 'Hello Stack Overflow!';
sprite.material.map.fontFamily = 'Tahoma, Geneva, sans-serif';
Ответ @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
(ваша "стрелка" ) вместо добавления его в сцену.
Надеюсь, это поможет.
projector is not defined
, есть ли обходной путь это? Спасибо