這是threejs官方的一個例子webgl_interactive_voxelpainter.html 查看效果 查看效果javascript
紅色的正方體隨着鼠標的移動而移動,單擊時會建立另外一個正方體,按住shift單擊會刪除點擊的正方體。html
主要用到的知識點java
一、設置物體的位置爲射線與物體相交的位置,而且把物體的位置設置到網格中心且高度所有在網格的上面web
rollOverMesh.position.copy(intersect.point).add(intersect.face.normal) rollOverMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25)
完整的代碼:canvas
<script src="../dist/js/three.js"></script> <script src="../dist/js/WebGL.js"></script> <script> if (WEBGL.isWebGLAvailable() === false) { document.body.appendChild(WEBGL.getWebGLErrorMessage()); } var camera, scene, renderer; var plane, cube; var mouse, raycaster, isShiftDown = false; var rollOverMesh, rollOverMaterial; var cubeGeo, cubeMaterial; var objects = []; var width, height; init(); render(); function init() { var container = document.getElementById('canvasWrap') width = document.getElementById('canvasWrap').clientWidth; height = document.getElementById('canvasWrap').clientHeight; camera = new THREE.PerspectiveCamera(45, width / height, 1, 10000); camera.position.set(150, 350, 1000); //position(0,300,1000) 中間的線會不顯示 camera.lookAt(0, 0, 0); scene = new THREE.Scene(); scene.background = new THREE.Color(0xf0f0f0); //roll-over helpers var rollOverGeo = new THREE.BoxBufferGeometry(50, 50, 50); rollOverMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000, opacity: 0.5, transparent: true }) rollOverMesh = new THREE.Mesh(rollOverGeo, rollOverMaterial); rollOverMesh.position.set(0, 25, 0) //console.log(rollOverMesh) scene.add(rollOverMesh); //cubes cubeGeo = new THREE.BoxBufferGeometry(50, 50, 50); cubeMaterial = new THREE.MeshLambertMaterial({ color: 0xfeb74c, map: new THREE.TextureLoader().load("../dist/textures/square-outline-textured.png") }); var voxel = new THREE.Mesh(cubeGeo, cubeMaterial); voxel.position.set(500, 500, 0) console.log(voxel) //grid var gridHelper = new THREE.GridHelper(1000, 20, "red", "green"); //gridHelper.position.set(0,-50,0) //console.log(gridHelper) scene.add(gridHelper) //raycaster raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2(); var geometry = new THREE.PlaneBufferGeometry(1000, 1000) geometry.rotateX(-Math.PI / 2); plane = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ visible: true })); //plane.position.set(0,-50,0) //console.log(plane) scene.add(plane); objects.push(plane); // lights var ambientLight = new THREE.AmbientLight(0x606060); scene.add(ambientLight); var directionalLight = new THREE.DirectionalLight(0xf3821e); //向量座標都除於向量的長度 //{1,2,3},長度是√1²+2²+3²=√14 //標準化以後是 //{1/√14,2/√14,3/√14} //新向量的長度剛好爲1 //directionalLight.position.set(1,0.75,0.5); //.normalize()好像不用也沒看出啥區別 directionalLight.position.set(100, 300, 300) //.normalize(); //設置平行光方向,normalize()正則化,變爲單位向量 scene.add(directionalLight); renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(width, height); container.appendChild(renderer.domElement); document.addEventListener("mousemove", onDocumentMouseMove, false); document.addEventListener("mousedown", onDocumentMouseDown, false); document.addEventListener("keydown", onDocumentKeyDown, false); document.addEventListener("keyup", onDocumentKeyUp, false); window.addEventListener("resize", onWindowResize, false); } function onWindowResize(event) { camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize(width, height); } function onDocumentMouseMove(event) { event.preventDefault(); //轉換座標至(-1,1)範圍 mouse.set((event.layerX / width) * 2 - 1, -(event.layerY / height) * 2 + 1); raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(objects); if (intersects.length > 0) { var intersect = intersects[0]; rollOverMesh.position.copy(intersect.point).add(intersect.face.normal) rollOverMesh.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25) } render(); } function onDocumentMouseDown(event) { event.preventDefault(); mouse.set((event.layerX / width) * 2 - 1, -(event.layerY / height) * 2 + 1); raycaster.setFromCamera(mouse, camera); var intersects = raycaster.intersectObjects(objects); if (intersects.length > 0) { var intersect = intersects[0]; //delete cube if (isShiftDown) { if (intersect.object !== plane) { scene.remove(intersect.object); objects.splice(objects.indexOf(intersect.object), 1) } // create cube } else { var voxel = new THREE.Mesh(cubeGeo, cubeMaterial); voxel.position.copy(intersect.point).add(intersect.face.normal); //.divideScalar ( s : Float )將該向量除以標量S //.multiplyScalar ( s : Float )將該向量與所傳入的標量s進行相乘。 //.floor () 向量的份量向下取整爲最接近的整數值。 //addScalar(s:Float)將傳入的標量S和這個向量的X值、Y值以及Z值相加 //下面從新設置position的目的是移動的方塊的中心在網格的中心,Y軸爲25 voxel.position.divideScalar(50).floor().multiplyScalar(50).addScalar(25); scene.add(voxel); objects.push(voxel) } render() } } function onDocumentKeyDown(event) { switch (event.keyCode) { case 16: isShiftDown = true; break; } } function onDocumentKeyUp(event) { switch (event.keyCode) { case 16: isShiftDown = false; break; } } function render() { renderer.render(scene, camera); } </script>