无法使 Three.js onDocumentMouseDown 正常工作

Can't get Three.js onDocumentMouseDown to work correctly

我看过很多例子——并借鉴了一些例子——但似乎无法让它正常工作。我想要的是让 onDocumentMouseDown 中的光线投射器在用户单击精灵可见表面上的任意位置时拾取精灵。我得到的是一个未对齐的结果,如果用户点击精灵的右侧、上方或下方,精灵可能会被拾取,而如果用户点击左边缘则根本不会拾取它的精灵。所以基本上有些地方错位了,我不知道自己做错了什么。任何指导将不胜感激。

<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>

您的 Three.js canvas 可能不在屏幕的左上角,并且您没有考虑页面上 0,0 的偏移量。要修复它,请调整鼠标位置以减去偏移量。

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;

在您的 makeTextSprite() 函数中,在

之后
var textWidth = metrics.width;

添加这个

context.strokeStyle = "white";
context.lineWidth = 5;
context.strokeRect(0,0,canvas.width, canvas.height);

你会发现,你的精灵没有你想象的那么大。

更新。您可以像这样设置 canvas 的大小

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;

然后设置精灵的比例

sprite.scale.set(canvas.width / 10, canvas.height /  10, 1);

jsfiddle 示例