I've looked at a lot of examples -- and borrowed from some -- and can't seem to get this to work right. What I want is for the raycaster in onDocumentMouseDown to pick up sprites when the user clicks anywhere on the visible surface of a sprite. What I'm getting is a misaligned result, in that a sprite may be picked up if the user clicks somewhat to the right, above, or below the sprite, and will not pick it up at all if the user clicks on the left edge of the sprite. So basically something is misaligned, and I am at a loss for figuring out what I am doing wrong. Any guidance would be appreciated.
<script src="/common/three.js"></script>
<script src="/common/Detector.js"></script>
<script src="/common/CanvasRenderer.js"></script>
<script src="/common/GeometryUtils.js"></script>
<script src="/common/OrbitControls.js"></script>
<div id="WebGLCanvas"></div>
<script>
var container, scene, camera, renderer, controls;
var keyboard;
</script>
<script>
// custom global variables
var mouse = { x: 0, y: 0 };
var raycaster;
var sprites = new Array();
init();
try {
for (i = 0; i < 10; i++) {
var text = "Text " + i;
var x = Math.random() * 100;
var y = Math.random() * 100;
var z = Math.random() * 100;
var spritey = addOrUPdateSprite(text, i, x, y, z);
}
}
catch (ex) {
alert("error when creating sprite: " + ex.message);
}
animate();
function init() {
try {
scene = new THREE.Scene();
// CAMERA
var cont = document.getElementById("WebGLCanvas");
var SCREEN_WIDTH = window.innerWidth;
OFFSET_TOP = document.getElementById("WebGLCanvas").getBoundingClientRect().top;
var SCREEN_HEIGHT = window.innerHeight - OFFSET_TOP; //; //-document.getElementById("upper").clientHeight;
var VIEW_ANGLE = 60;
var ASPECT = SCREEN_WIDTH / SCREEN_HEIGHT;
var NEAR = 0.1;
var FAR = 1000;
camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
scene.add(camera);
camera.position.set(0, 100, 200);
camera.lookAt(new THREE.Vector3());
renderer = new THREE.WebGLRenderer({ antialias: true });
container = document.getElementById('WebGLCanvas');
container.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, SCREEN_HEIGHT);
controls = new THREE.OrbitControls(camera, renderer.domElement);
// spritey.position.normalize();
raycaster = new THREE.Raycaster();
document.addEventListener('mousedown', onDocumentMouseDown, false);
document.addEventListener('touchstart', onDocumentTouchStart, false);
}
catch (ex) {
alert("error " + ex.message);
}
}
function animate() {
requestAnimationFrame(animate);
render();
update();
}
function update() {
controls.update();
}
function render() {
renderer.render(scene, camera);
}
function addOrUPdateSprite(text, name, x, y, z) {
var sprite = scene.getObjectByName(name);
if (sprite == null) {
sprite = makeTextSprite(text, { fontsize: 36, borderColor: { r: 255, g: 0, b: 0, a: 1.0 }, backgroundColor: { r: 255, g: 100, b: 100, a: 0.8 } });
sprite.name = name;
sprites.push(sprite);
scene.add(sprite);
}
sprite.position.set(x, y, z);
}
function makeTextSprite(message, parameters) {
if (parameters === undefined) parameters = {};
var fontface = parameters.hasOwnProperty("fontface") ? parameters["fontface"] : "sans-serif";
var fontsize = parameters.hasOwnProperty("fontsize") ? parameters["fontsize"] : 36;
var borderThickness = parameters.hasOwnProperty("borderThickness") ? parameters["borderThickness"] : 1;
var borderColor = parameters.hasOwnProperty("borderColor") ? parameters["borderColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
var backgroundColor = parameters.hasOwnProperty("backgroundColor") ? parameters["backgroundColor"] : { r: 255, g: 255, b: 255, a: 1.0 };
var textColor = parameters.hasOwnProperty("textColor") ? parameters["textColor"] : { r: 0, g: 0, b: 0, a: 1.0 };
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = fontsize + "px " + fontface;
var metrics = context.measureText(message);
var textWidth = metrics.width;
context.fillStyle = "rgba(" + backgroundColor.r + "," + backgroundColor.g + "," + backgroundColor.b + "," + backgroundColor.a + ")";
context.strokeStyle = "rgba(" + borderColor.r + "," + borderColor.g + "," + borderColor.b + "," + borderColor.a + ")";
context.lineWidth = borderThickness;
roundRect(context, borderThickness / 2, borderThickness / 2, (textWidth + borderThickness) * 1.1, fontsize * 1.4 + borderThickness, 8);
context.fillStyle = "rgba(" + textColor.r + ", " + textColor.g + ", " + textColor.b + ", 1.0)";
context.fillText(message, borderThickness, fontsize + borderThickness);
var texture = new THREE.Texture(canvas)
texture.needsUpdate = true;
var spriteMaterial = new THREE.SpriteMaterial({ map: texture, useScreenCoordinates: false });
var sprite = new THREE.Sprite(spriteMaterial);
sprite.scale.set(1.0 * fontsize, 0.5 * fontsize, 1.5 * fontsize);
return sprite;
}
// function for drawing rounded rectangles
function roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
ctx.fill();
ctx.stroke();
}
function onDocumentTouchStart(event) {
event.preventDefault();
event.clientX = event.touches[0].clientX;
event.clientY = event.touches[0].clientY;
onDocumentMouseDown(event);
}
function onDocumentMouseDown(event) {
mouse.x = (event.clientX / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -((event.clientY) / renderer.domElement.clientHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
var intersects = raycaster.intersectObjects(sprites, true);
if (intersects.length > 0) {
var obj = intersects[0].object;
alert(obj.name);
event.preventDefault();
}
}
</script>
In your makeTextSprite()
function, after
var textWidth = metrics.width;
add this
context.strokeStyle = "white";
context.lineWidth = 5;
context.strokeRect(0,0,canvas.width, canvas.height);
and you will see, that your sprites have not the size you think of.
UPD. You can set the size of a canvas like this
var ctxFont = "bold " + fontsize + "px " + fontface;
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
context.font = ctxFont;
var metrics = context.measureText(message);
var textWidth = metrics.width;
canvas.width = textWidth + borderThickness * 2;
canvas.height = fontsize * 1.2 + (borderThickness * 2);
context = canvas.getContext('2d');
context.font = ctxFont;
and then set the scale of a sprite
sprite.scale.set(canvas.width / 10, canvas.height / 10, 1);
jsfiddle example
Your Three.js canvas probably isn't at the top left of the screen, and you're not taking into account the offset from 0,0 on the page. To fix it, adjust the mouse position to subtract the offset.
var rect = container.getBoundingClientRect();
mouse.x = ((event.clientX - rect.left) / renderer.domElement.clientWidth) * 2 - 1;
mouse.y = -((event.clientY - rect.top) / renderer.domElement.clientHeight) * 2 + 1;
来源:https://stackoverflow.com/questions/40541390/cant-get-three-js-ondocumentmousedown-to-work-correctly