• 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);
    }
    
  • 相关阅读:
    Eclipse和PyDev搭建完美Python开发环境(Windows篇)
    Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition
    Java并发编程:Timer和TimerTask(转载)
    Java并发编程:并发容器之CopyOnWriteArrayList(转载)
    Java并发编程:阻塞队列
    深入理解Java的接口和抽象类
    Java并发编程:线程池的使用
    Java并发编程:同步容器
    Java并发编程:深入剖析ThreadLocal
    Java并发编程:volatile关键字解析
  • 原文地址:https://www.cnblogs.com/yaolin1228/p/11374596.html
Copyright © 2020-2023  润新知