WebGL模型拾取——射线法二
这篇文章是对射线法raycaster的补充,上一篇文章主要讲的是raycaster射线法拾取模型的原理,而这篇文章着重讲使用射线法要注意的地方。首先我们来看下图。
我来解释一下上图中的originTriangle,这就是Triangle2三角形第一次绘制在空间中的位置,而Triangle2当前的位置是经过一系列空间变换而来的(这些位置姿态变换大多是由用户鼠标交互产生),变换矩阵就是transformMatrix。这下就引出了本文第一个重点,那就是做raycaster的时候要保证线段碰撞模型的时候一定是模型当前所处的空间位置,即已经做过transformMatrix空间(姿态,位置)变换,否则线段如果和模型之前的初始化位置求交显然没有交点,就拾取失败了。这就是做raycaster要注意的第一个重点,即射线一定要和空间变换后的模型求交。
接下来我们再看一张图,请看下图。
我们看到,射线和模型有2个交点,P2_0和P2_1,分别交四面体的前面于P2_0和交四面体的后面于P2_1。这就是我们要着重关注的本文第二个重点,即raycaster射线拾取模型过程中射线与单个模型有多个交点的问题。处理这个问题其实有很多办法,这里我们采用最简单的方式,就是距离相机(人眼)位置近者胜出的策略。计算交点的算法上一篇文章已经提到,这里不再赘述,但要说明的是,我们计算的每一个面在数据结构中都有自身模型父节点geometry,如果像上图一个四面体的geometry和射线产生了多个面相交,那我们就认为鼠标选中的是该模型geometry离相机(camera)(人眼)最近的交面上的交点。
对于上面2点的叙述,配合部分代码展示,是geometry空间变换的,代码如下。
Object.assign(CubeSection.prototype, {
//重载,每一帧同步数据
sync: function () {
if(this._mode === "face") {//根据剖切模式管理鼠标拖拽逻辑
if (this._selectFace) {
let camera = this._viewer.getMainCamera();
let last = this._mousePoints.getLast();
let lastX = camera.getNormalizedX(last[]);
let lastY = camera.getNormalizedY(last[]);
let current = this._mousePoints.getCurrent();
let currentX = camera.getNormalizedX(current[]);
let currentY = camera.getNormalizedY(current[]); this._mousePoints.sync();
let deltaX = currentX - lastX;
let deltaY = currentY - lastY;
if (Math.abs(deltaX) < Algorithm.EPSILON && Math.abs(deltaY) < Algorithm.EPSILON)
return; //如果面被选中,并且有移动量,需要进行剖切面移动处理
let start = Vec3.MemoryPool.alloc();
let end = Vec3.MemoryPool.alloc();
camera.computeScreenToWorldNearFar(lastX, lastY, start, end, true);
//获取起点与平面的交点
let plane = this._cubeClip.getClipPlane(this._selectFace.getName());
let planePt1 = Vec3.MemoryPool.alloc();
if (Plane.intersectLine(planePt1, start, end, plane)) {
//将模型交点再转换到屏幕坐标上,主要为了获取z值给终点
let temppt = Vec3.MemoryPool.alloc();
camera.computeWorldToScreen(planePt1, temppt); Vec3.set(temppt, currentX, currentY, temppt[]); let planePt2 = Vec3.MemoryPool.alloc();
camera.computeScreenToWorld(temppt, planePt2);
Vec3.sub(temppt, planePt2, planePt1);
let dist = Vec3.dot(plane, temppt);
this.move(dist);
Vec3.MemoryPool.free(planePt2);
Vec3.MemoryPool.free(temppt);
}
Vec3.MemoryPool.free(planePt1);
Vec3.MemoryPool.free(start);
Vec3.MemoryPool.free(end);
}
} else if(this._mode === "translate"){
if(this._selectAxis) {
let camera = this._viewer.getMainCamera();
let last = this._mousePoints.getLast();//前一帧鼠标的XY坐标
let lastX = camera.getNormalizedX(last[]);
let lastY = camera.getNormalizedY(last[]);
let current = this._mousePoints.getCurrent();//目前帧鼠标的XY坐标
let currentX = camera.getNormalizedX(current[]);
let currentY = camera.getNormalizedY(current[]); this._mousePoints.sync();//继续下一帧同步鼠标XY坐标
let deltaX = currentX - lastX;//X偏移量
let deltaY = currentY - lastY;//Y偏移量
if (Math.abs(deltaX) < Algorithm.EPSILON && Math.abs(deltaY) < Algorithm.EPSILON) {
//如果XY偏移量都为零,就直接返回,什么操作都不做
return;
} //坐标系轴被选中,并且有偏移量,就要移动整个包围盒子
let start = Vec3.MemoryPool.alloc();
let end = Vec3.MemoryPool.alloc();
//把屏幕上的XY坐标换算到视棱台near,far截面上的XY坐标
camera.computeScreenToWorldNearFar(lastX, lastY, start, end, true);
//当前pick的坐标轴
let axis = this._selectAxis;
//near-far线段截axis坐标轴的交点
let intersectPoint1 = Vec3.MemoryPool.alloc();
//射线碰撞
let intersections = this._drawActor.linesegmentIntersect(start, end);//对场景中的所有物体进行线段碰撞检测
//遍历intersections列表,按照离相机从远到近排列
for (let i = ; i < intersections.length; i++) {
let geometry = intersections[i].getDrawable().getGeometry();
if (geometry && new String(geometry._name).substring(, ) === "axis") {
intersectPoint1 = intersections[i]._point;//获取到near-far线段和坐标轴的交点
break;
}
}
//将near-far和坐标轴的交点再转换到屏幕坐标上,主要为了获取z值给终点
let screenPoint = Vec3.MemoryPool.alloc();
camera.computeWorldToScreen(intersectPoint1, screenPoint);
//screePoint(currentX, currentY, screenPoint.z)
Vec3.set(screenPoint, currentX, currentY, screenPoint[]);
//鼠标移动的第二个场景坐标系里的点坐标
let intersectPoint2 = Vec3.MemoryPool.alloc();
//把屏幕归一化坐标转化为场景世界坐标
camera.computeScreenToWorld(screenPoint, intersectPoint2);
Vec3.sub(screenPoint, intersectPoint2, intersectPoint1);
let dist = ;
if(this._selectAxis._name === "axisX"){
dist = screenPoint[];
}else if(this._selectAxis._name === "axisY"){
dist = screenPoint[];
}else if(this._selectAxis._name === "axisZ"){
dist = screenPoint[];
}
this.move(dist);
//析构向量
Vec3.MemoryPool.free(intersectPoint1);
Vec3.MemoryPool.free(intersectPoint2);
Vec3.MemoryPool.free(screenPoint);
Vec3.MemoryPool.free(start);
Vec3.MemoryPool.free(end);
}
} else if(this._mode === "rotate"){ } else if(this._mode === "scale"){ }
},
updateTransform: function () {
let mat = this._cubeRoot.getMatrix(); //重新计算坐标系模型的_matrix
this._coordinateSection.update(this._clipBox, this._scale, this._translate, this._rotate, this._scaleMatrix, this._translateMatrix, this._rotateMatrix, mat); Mat4.fromScaling(this._scaleMatrix, this._scale);
Mat4.fromTranslation(this._translateMatrix, this._translate);
Mat4.fromQuat(this._rotateMatrix, this._rotate); Mat4.mul(mat, this._translateMatrix, this._rotateMatrix);
Mat4.mul(mat, mat, this._scaleMatrix); //剖切面数据的变换
this._cubeClip.resetClipPlane();
this._cubeClip.transformClipPlane(mat); //包围盒子更新
this._clipBox.setMaxValue(0.5, 0.5, 0.5);
this._clipBox.setMinValue(-0.5, -0.5, -0.5);
this._clipBox.transformMat4(mat);
},
接下来是选取离相机近的交点,代码如下
//拾取物体,根据当前剖切模式选择intersections列表中的碰撞对象
pick: function (x, y) {
let camera = this._viewer.getMainCamera();
let start = Vec3.MemoryPool.alloc();
let end = Vec3.MemoryPool.alloc();
camera.computeScreenToWorldNearFar(x, y, start, end);
let intersections = this._drawActor.linesegmentIntersect(start, end);
let l = intersections.length;
if (l !== ) {
switch(this._mode){
case "face" : {//面剖切
let intersection = intersections[];//LineSegmentIntersection
let geometry = intersection.getDrawable().getGeometry();
if (geometry) {
this._selectFace = geometry;
this._selectFace.setStateSet(this._selectState);
return true;
}
}
case "translate" : {//平移剖切
//遍历intersections列表,按照离相机从远到近排列
for(var i=; i<l; i++){
let geometry = intersections[i].getDrawable().getGeometry();
if(geometry && new String(geometry._name).substring(, ) === "axis"){
this._selectAxis = geometry;
this._selectAxis.setStateSet(this._selectStateAxis);
break;
}
}
return true;
}
case "rotate" : {//旋转剖切
//遍历intersections列表,按照离相机从远到近排列
for(var i=; i<l; i++){
let geometry = intersections[i].getDrawable().getGeometry();
if(geometry && new String(geometry._name).substring(, ) === "face"){
this._selectAxisFace = geometry;
this._selectAxisFace.setStateSet(this._selectStateAxisFace);
break;
}
}
return true;
}
case "scale" : {//缩放剖切
//遍历intersections列表,按照离相机从远到近排列
for(var i=; i<l; i++){
let geometry = intersections[i].getDrawable().getGeometry();
if(geometry && new String(geometry._name).substring(, ) === "axis"){
this._selectAxis = geometry;
this._selectAxis.setStateSet(this._selectStateAxis);
break;
}
}
return true;
}
}
}
return false;
},
其中intersections[]交点列表是按照离相机由远到近距离排序的,intersection[i]交点离相机距离比intersection[i+1]交点离相机距离要近。这就是我们采取的离相机近交点胜出原则。
好了,以上就是raycaster射线拾取模型要注意的地方,如有错误,希望读者斧正,欢迎诸位同学留言。如需转载本文,请注明出处:https://www.cnblogs.com/ccentry/p/9977490.html
WebGL模型拾取——射线法二的更多相关文章
- WebGL模型拾取——射线法
今天要把WebGL中一个非常重要的算法记录下来——raycaster射线法拾取模型.首先我们来了解一下为什么要做模型拾取,我们在做webgl场景交互的时候经常要选中场景中的某个模型,比如鼠标拖拽旋转, ...
- Away3D引擎学习笔记(三)模型拾取(翻译)
原文详见http://away3d.com/tutorials/Introduction_to_Mouse_Picking.本文若有翻译不对的地方,敬请指出. 本教程详细介绍了Away3D 4.x中鼠 ...
- 射线法(1190 - Sleepwalking )
题目:http://lightoj.com/volume_showproblem.php?problem=1190 参考链接:https://blog.csdn.net/gkingzheng/arti ...
- matlab练习程序(射线法判断点与多边形关系)
依然是计算几何. 射线法判断点与多边形关系原理如下: 从待判断点引出一条射线,射线与多边形相交,如果交点为偶数,则点不在多边形内,如果交点为奇数,则点在多边形内. 原理虽是这样,有些细节还是要注意一下 ...
- LightOj1190 - Sleepwalking(判断点与多边形的位置关系--射线法模板)
题目链接:http://lightoj.com/volume_showproblem.php?problem=1190 题意:给你一个多边形含有n个点:然后又m个查询,每次判断点(x, y)是否在多边 ...
- Codeforces 375C Circling Round Treasures - 最短路 - 射线法 - 位运算
You have a map as a rectangle table. Each cell of the table is either an obstacle, or a treasure wit ...
- Revit API射线法读取空间中相交的元素
Revit API提供根据射线来寻找经过的元素.方法是固定模式,没什么好说.关键代码:doc.FindReferencesWithContextByDirection(ptStart, (ptEnd ...
- 【BZOJ1294】[SCOI2009]围豆豆Bean 射线法+状压DP+SPFA
[BZOJ1294][SCOI2009]围豆豆Bean Description Input 第一行两个整数N和M,为矩阵的边长. 第二行一个整数D,为豆子的总个数. 第三行包含D个整数V1到VD,分别 ...
- SGU 124. Broken line 射线法 eps的精准运用,计算几何 难度:3
124. Broken line time limit per test: 0.25 sec. memory limit per test: 4096 KB There is a closed bro ...
随机推荐
- ASA 用TFTP 备份配置方法
一种方法是用ASDM,在菜单样哪项里有一个backup,保存为一个压缩文件rar,恢复也是用ASDM.另一种是用TFTP SERVER 来做,电脑用网线接上ASA,还要把cisco TFTP SERV ...
- MySQL基础之 逻辑运算符
mysql的逻辑运算符有四个:与.或.非.异或 我们平时在谈论的时候不考虑带有NULL的情况,今天我们就来考虑一下带有NULL值,他们的结果是怎么样的 AND(&&)运算符 mysql ...
- 4星|《门口的野蛮人2》:美国宝万之争专业户KKR公司的疯狂借债收购史
门口的野蛮人2:KKR与资本暴利的崛起(珍藏版) 英文版是1992年出的.主要内容是1977-1998年之间KKR在美国的杠杆收购简史.从KKR创立开始,讲到1990年KKR差点倒闭.国内A股市场上前 ...
- Q矩阵输出
程序启动时: 1.Q矩阵在InitQX中对角阵赋初值为0.25,GPS卫星数6 2.Q矩阵初值在初始化时由GetBL获得,改变Q对角阵 Q初值第0个卫星 10000000000.000 X初值第0个卫 ...
- 投稿核心期刊、中文重要期刊、SCI二区及以上期刊目录
大家在研究生期间想必均经历过投稿核心期刊的烦恼,不知道哪些是核心期刊,那些是普通期刊,万一选的不对岂不是浪费了时间,因此小顾在网络上收集了了2018北大核心期刊目录及全国中文重要期刊目录和SCI二区及 ...
- (排序算法整理)NEFU 30/32
版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/caihongshijie6/article/details/26165093 事实上, ...
- 一个web项目web.xml的配置中<context-param>配置作用
<context-param>的作用: web.xml的配置中<context-param>配置作用 1. 启动一个WEB项目的时候,容器(如:Tomcat)会去读它的配置文件 ...
- docker安装jenkins及其相关问题解决
1.拉取镜像并启动容器 docker run -d -p 8080:8080 -p 50000:50000 -v $(pwd)/data:/var/jenkins_home --name jenkin ...
- Node.js实战(六)之Npm
NPM是随同NodeJS一起安装的包管理工具,能解决NodeJS代码部署上的很多问题,常见的使用场景有以下几种: 允许用户从NPM服务器下载别人编写的第三方包到本地使用. 允许用户从NPM服务器下载并 ...
- C#控件中的KeyDown、KeyPress 与 KeyUp事件浅谈
研究了一下KeyDown,KeyPress 和 KeyUp 的学问.让我们带着如下问题来说明: 1.这三个事件的顺序是怎么样的? 2.KeyDown 触发后,KeyUp是不是一定触发? 3.三个事件的 ...