threejs 学习之射线的使用

主要内容: 使用 threejs 创建 20x20 的网格,鼠标移动时,方块跟随移动,点击时在网格任意位置放置方块,按 shift 时,删除当前位置方块。

流程如下:

  • 创建网格
  • 创建一个与网格同样尺寸的平面
  • 创建一个方块 mesh_1 与网格同样的尺寸
  • 一个与网格同样的方块 geometry_2 , 不加入 scene 中
  • 三个事件:
    • 鼠标移动事件,随着鼠标移动,更改 mesh_1 位置,并重新渲染
    • 鼠标点击事件,在交点位置,创建新 mesh, 若是相交对象不为 平面,则删除当前对象
    • keydown, keyup, 更改是否删除的状态

详细代码如下:


import * as THREE from './build/three.module'
import { stat } from 'fs';

var camera, scene, renderer;
var moveMesh, staticGeo,staticMat, plane;
var objects = [];
var raycaster, mouse;
var isShiftDown = false;

function init() {
    camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 10000);
    camera.position.set(500, 800, 1300);
    camera.lookAt(0, 0, 0);

    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xf0f0f0);

    // lights
    var light = new THREE.AmbientLight(0x606060);
    scene.add(light);

    // grids
    var grid = new THREE.GridHelper(1000, 20); 
    scene.add(grid);

    // plane, 辅助碰撞检测
    var planeGeo = new THREE.PlaneBufferGeometry(1000, 1000);
    var planMat = new THREE.MeshBasicMaterial({color: 0xffff00, visible : true});
    plane = new THREE.Mesh(planeGeo, planMat);
    plane.rotateX(-Math.PI /2);
    scene.add(plane);
    objects.add(plane);

    // 射线 raycaster = new THREE.Raycaster();
    mouse = new THREE.Vector2();

    // moveCube;
    var moveGeo = new THREE.BoxBufferGeometry(50, 50, 50);
    var moveMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true });
    moveMesh = new THREE.Mesh(moveGeo, moveMaterial);
    scene.add(moveMesh);

    // static cube
    staticGeo = new THREE.BoxBufferGeometry(50, 50, 50);
    staticMat = new THREE.MeshLambertMaterial({ color: 0xfeb74c, map: new THREE.TextureLoader().load('textures/square-outline-textured.png') });

    renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);

    document.body.appendChild(renderer.domElement);
    document.body.addEventListener('mousemove', onDocuementMouseMove, false);
    document.body.addEventListener('mousedown', onDocumentMouseDown, false);
    document.body.addEventListener('keydown', onDocuementKeyDown, false);
    document.body.addEventListener('keyup', onDocuementKeyUp, false);

    window.addEventListener('resize', onWindowResize, false);
}


function onDocumentMouseDown(event) {
    event.preventDefault();

    // 鼠标位置归一化
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    // 通过摄像机与鼠标更新射线
    raycaster.setFromCamera(mouse, camera);
    var intersects = raycaster.intersectObjects(objects);
    if(intersects.length > 0) {
        var intersect = intersects[0];
        if (isShiftDown) {
            if(intersect.object !== plane) {
                scene.remove(intersect.object);
                objects.splice(objects.indexOf(intersect.object), 1);
            }
        }
        else
        {
            var staticMesh = new THREE.Mesh(staticGeo, staticMat);
            staticMesh.position.copy(intersect.point).add(intersect.face.normal);
            staticMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25);
            scene.add(staticMesh);
            objects.push(staticMesh);
        }
    }
    
}

function onDocuementMouseMove(event) {
    event.preventDefault();
    // 鼠标位置归一化
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;

    // 通过摄像机与鼠标更新射线
    raycaster.setFromCamera(mouse, camera);
    var intersects = raycaster.intersectObjects(objects);

    if(intersects.length > 0) {
        intersect = intersects[0];
        // 移动位置到目标点
        moveMesh.position.copy(intersect.point).add(intersect.face.normal);
        // 计算具体方格
        moveMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25);
    }
    render();
}

function onDocuementKeyDown(event) {
    switch (event.keyCode) {
        case 16: isShiftDown = true; break;
    }
}

function onDocuementKeyUp(event) {
    switch (event.keyCode) {
        case 16: isShiftDown = false; break;
    }
}

function onWindowResize() {
    camera.aspect = window.innerWidth/ window.innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(window.innerWidth, window.innerHeight);

}

function render()
{
    renderer.render(scene, camera);
}
上一篇:1603B - Moderate Modular Mode


下一篇:1603B - Moderate Modular Mode(数论)