赞
踩
参考:光线投射器(Raycaster)、ThreeJS中的点击与交互——Raycaster的用法
该类用来处理光线投射。光线投射主要用于物体选择、碰撞检测以及图像成像等方面。
我们的手机屏幕是二维的,但是我们展示物体的世界是三维的,当我们在构建一个物体的时候我们是以一个三维世界既是世界坐标来构建,而转化为屏幕坐标展示在我们眼前,则需要经历多道矩阵变化,中间webGL替我们操作了许多事情。
世界坐标系:在webGL中,世界坐标系是以屏幕中心为原点(0, 0, 0),且是始终不变的。你面对屏幕,你的右边是x正轴,上面是y正轴,屏幕指向你的为z正轴。长度单位这样来定:窗口范围按此单位恰好是(-1,-1)到(1,1),即屏幕左下角坐标为(-1,-1),右上角坐标为(1,1)。
屏幕坐标系:
webGL的重要功能之一就是将三维的世界坐标经过变换、投影等计算,最终算出它在显示设备上对应的位置,这个位置就称为设备坐标。在屏幕、打印机等设备上的坐标是二维坐标。
视点坐标系:
是以视点(照相机)为原点,以视线的方向为Z+轴正方向的坐标系中的方向。webGL会将世界坐标先变换到视点坐标,然后进行裁剪,只有在视线范围(视见体)之内的场景才会进入下一阶段的计算。
鼠标在屏幕上点击的时候,得到二维坐标p(x, y),再加上深度坐标的范围(0, 1), 就可以形成两个三位坐标A(x1, y1, 0), B(x2, y, 1), 由于它们的Z轴坐标是0和1,则转变到投影坐标系的话,一定分别是前剪切平面上的点和后剪切平面上的点,也就是说,在投影坐标系中,A点一定在能看见的所有模型的最前面,B点一定在能看见的所有的模型的最后边,将AB点连成线,AB线穿过的物体就是被点击的物体。而 Three.js提供一个射线类Raycasting来拾取场景里面的物体。更方便的使用鼠标来操作3D场景。(不过在实际代码中我们组成射线的两个点是摄像机所在视点与屏幕上点击的点连接而成的射线)
完整代码:
- <!DOCTYPE html>
- <html lang="en">
-
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Raycaster射线投射器</title>
- <style>
- .body {
- margin: 0;
- overflow: hidden;
- }
- </style>
- <script src="js/three.js"></script>
- <!-- orbitcontrol.js用于鼠标自由变化 -->
- <script src="js/OrbitControls.js"></script>
- </head>
-
- <body>
-
- </body>
- <script>
- /*场景*/
- var scene = new THREE.Scene();
-
- /*相机*/
- var width = window.innerWidth;
- var height = window.innerHeight;
- var k = width / height;
- var camera = new THREE.PerspectiveCamera(45, k, 1, 1000);
- camera.position.set(300, 400, 300);
- camera.lookAt(scene.position);
-
- /*添加光源*/
- var pointlight = new THREE.PointLight(0xFFFFE0);
- pointlight.position.set(100, 200, 100);
- scene.add(pointlight);
- /*添加geometry(几何体)*/
- var boxgeomrtry = new THREE.BoxGeometry(100, 100, 100);
- var material = new THREE.MeshBasicMaterial({
- color: 0xff0000
- });
- var mesh = new THREE.Mesh(boxgeomrtry, material);
- scene.add(mesh);
-
-
- /*渲染*/
- var renderer = new THREE.WebGLRenderer();
- renderer.setSize(width, height);
- renderer.setClearColor(0xC0C0C0, 1);
- //下面一句话很重要不要忘掉
- document.body.appendChild(renderer.domElement);
- render();
-
- /*射线投射器*/
- var raycaster = new THREE.Raycaster();
- var mouse = new THREE.Vector2();
-
-
-
- function render() {
- renderer.render(scene, camera);
- }
-
- function onMouseDown(event) {
- mouse.x = (event.clientX / width) * 2 - 1;
- mouse.y = -(event.clientY / height) * 2 + 1;
-
- //将平面坐标系转为世界坐标系
- raycaster.setFromCamera(mouse, camera);
- //得到点击的几何体
- var raycasters = raycaster.intersectObjects(scene.children);
- if (raycasters.length > 0) {
- raycasters[0].object.material.color.set(0x00ff00);
- }
- render()
- }
-
-
- //监视鼠标左键按下事件
- window.addEventListener("mousedown", onMouseDown, false);
-
- //用于鼠标自由变化
- var control = new THREE.OrbitControls(camera);
- control.addEventListener("change", render);
- </script>
-
- </html>
效果:
点击几何体前
点击后
由鼠标点击转为世界坐标的过程
//得到
mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
推导过程:
设A点为点击点(x1,y1),x1=e.clintX, y1=e.clientY
设A点在世界坐标中的坐标值为B(x2,y2);
由于A点的坐标值的原点是以屏幕左上角为(0,0);
我们可以计算可得以屏幕中心为原点的B'值
x2' = x1 - innerWidth/2
y2' = innerHeight/2 - y1
又由于在世界坐标的范围是[-1,1],要得到正确的B值我们必须要将坐标标准化
x2 = (x1 -innerWidth/2)/(innerwidth/2) = (x1/innerWidth)*2-1
同理得 y2 = -(y1/innerHeight)*2 +1
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。