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);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM