У меня есть вращающийся Object3D с прикрепленными к нему метками Sprite. Спрайты расположены правильно и вращаются вокруг Object3D, всегда обращенного к камере.
Мне нужно нарисовать хвосты стиля речевого пузыря на метках, чтобы они указывали назад на местоположение на Object3D. Это довольно просто с линией, но становится более сложным с формой.
- Зеленый - это куб Object3D
- Синий - это форма, которая нуждается в ее вращении. Y исправление
- Белый - это метка спрайта
До сих пор у меня была синяя форма, размер и правильное расположение. Однако ось вращения y должна динамически обновляться относительно вращения Object3D и положения камеры. Я пробовал .lookAt (), но он не работает, поскольку он вращает все оси. Мне нужно повлиять только на вращение y.
Также вопрос, является ли это лучшим подходом в целом?
Спасибо!
// Scene setup
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.z = 5;
var renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
var ambientLight = new THREE.AmbientLight(0xffffff, .5);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
var group = new THREE.Group();
scene.add(group);
// Cube
var cubeSize = 1;
var geometry = new THREE.BoxGeometry(cubeSize, cubeSize, cubeSize);
var material = new THREE.MeshLambertMaterial({
color: 0x00ff00
});
var cube = new THREE.Mesh(geometry, material);
group.add(cube);
// Text label
var config = {
fontface: 'Arial',
fontsize: 64,
fontweight: 500,
lineheight: 1,
padding: 20
};
var text = 'Hello world!';
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
const textMetrics = ctx.measureText(text);
var textWidth = textMetrics.width;
var textHeight = config.fontsize * config.lineheight;
canvas.width = textWidth + config.padding * 2;
canvas.height = textHeight + config.padding * 2;
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, textWidth + config.padding * 2, textHeight + config.padding * 2);
ctx.fillStyle = 'black';
ctx.textAlign = 'left';
ctx.textBaseline = 'top';
ctx.font = `${config.fontweight} ${config.fontsize}px/${config.lineheight} ${config.fontface}`;
ctx.fillText(text, config.padding, config.padding);
var texture = new THREE.Texture(canvas);
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
var aspectRatio = spriteMaterial.map.image.height / spriteMaterial.map.image.width;
var sprite = new THREE.Sprite(spriteMaterial);
sprite.position.set(0, cubeSize * 2, 0);
sprite.scale.set(1, aspectRatio, 1);
group.add(sprite);
// ShapeGeometry Mesh
var arrowWidth = textHeight / 220;
var arrowHeight = sprite.position.y / 1.41;
var arrowShape = new THREE.Shape();
arrowShape.moveTo(0, 0);
arrowShape.lineTo(-arrowWidth / 2, arrowHeight);
arrowShape.lineTo(arrowWidth / 2, arrowHeight);
arrowShape.lineTo(0, 0);
var arrowGeometry = new THREE.ShapeGeometry(arrowShape);
var arrowMaterial = new THREE.MeshBasicMaterial({
color: 0x0000ff,
// depthWrite: false,
side: THREE.DoubleSide
});
var arrow = new THREE.Mesh(arrowGeometry, arrowMaterial);
arrow.position.set(0, cubeSize / 2, 0);
/* arrow.up.copy(new THREE.Vector3(0, 1, 0)); */
group.add(arrow);
window.group = group;
var animate = function() {
requestAnimationFrame(animate);
group.rotation.x += 0.005;
group.rotation.y += 0.005;
group.rotation.z += 0.005;
// Using lookAt with depthWrite:false almost works
// arrow.lookAt(camera.position);
// arrow.rotation.x = 0;
// arrow.rotation.z = 0;
renderer.render(scene, camera);
};
animate();
body {
margin: 0;
}
canvas {
width: 100%;
height: 100%;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/110/three.min.js"></script>