4-osg 拓展线段求交器(拾取:点,线,面)
实现功能
- 拓展:LineSegmentIntersector, 实现点,线,面的求交
- ray: 根据near and far plane 求出frustum所截取的线段;
- kdTree, octree(oct tree 在LOD时再补): 加速搜索
- intersection: select point, line, face and object
- hight light the selection object
实现思路
ray
ray: 单击windows Screen 上的点,映射到实际的三维场景中;
变换过程:
P
s
c
r
e
e
n
(
x
,
y
)
→
P
v
i
e
w
p
o
r
t
(
x
,
y
)
→
P
n
o
r
m
a
l
i
z
e
d
(
x
,
y
,
z
,
1
)
z
∈
[
−
1
,
1
]
→
P
c
a
m
e
r
a
(
x
,
y
,
z
,
w
)
→
P
w
o
r
l
d
(
x
,
y
,
z
,
w
)
P_{screen}(x, y) \rightarrow P_{viewport}(x, y) \rightarrow P_{normalized}(x, y, z, 1) z \in[-1, 1] \rightarrow P_{camera}(x, y, z, w) \rightarrow P_{world}(x, y, z, w)
Pscreen(x,y)→Pviewport(x,y)→Pnormalized(x,y,z,1)z∈[−1,1]→Pcamera(x,y,z,w)→Pworld(x,y,z,w)
实际变换过程如上,涉及到的变换矩阵也比较简单;前面两个是平面二维变换矩阵,在屏幕上找相应的两个点,即可以解除相应矩阵。 后面三个矩阵为MVP的逆矩阵;
P
n
o
r
m
a
l
i
z
e
d
(
x
,
y
)
P_{normalized}(x, y)
Pnormalized(x,y) =
n
o
r
m
a
l
i
z
e
d
T
v
i
e
w
p
o
r
^{normalized}T_{viewpor}
normalizedTviewpor
v
i
e
w
p
o
r
t
T
s
c
r
e
e
n
^{viewport}T_{screen}
viewportTscreen
P
s
c
r
e
e
n
(
x
,
y
)
P_{screen}(x, y)
Pscreen(x,y)
P
w
o
r
l
d
(
x
,
y
,
z
,
w
)
P_{world}(x, y, z, w)
Pworld(x,y,z,w) =
T
v
i
e
w
−
1
T^{-1}_{view}
Tview−1
T
p
r
o
j
e
c
t
i
o
n
−
1
T^{-1}_{projection}
Tprojection−1
P
n
o
r
m
a
l
i
z
e
d
(
x
,
y
,
z
,
1
)
z
∈
[
−
1
,
1
]
P_{normalized}(x, y, z, 1) z \in[-1, 1]
Pnormalized(x,y,z,1)z∈[−1,1]
P
w
o
r
l
d
(
x
,
y
,
z
)
P_{world}(x, y, z)
Pworld(x,y,z) =
P
w
o
r
l
d
(
x
,
y
,
z
)
/
w
P_{world}(x, y, z)/w
Pworld(x,y,z)/w
void createRay(int screenX, int screenY, int width, int height)
{
int viewPortX = screenX;
int viewPortY = height - screenY;
osgGA::PointerData pd;
pd.x = viewPortX;
pd.y = viewPortY;
pd.xMin = 0;
pd.yMin = 0;
pd.xMax = width;
pd.yMax = height;
osg::Vec4f startNormalized(pd.getXnormalized(), pd.getYnormalized(), -1, 1);
osg::Vec4f endNormalized(pd.getXnormalized(), pd.getYnormalized(), 1, 1);
osg::Camera* camera = _viewer->getCamera();
osg::Matrixd projectMatrix = camera->getProjectionMatrix();
osg::Matrixd viewMatrix = camera->getViewMatrix();
osg::Matrixd viewMatrixInverse = osg::Matrixd::inverse(viewMatrix);
osg::Matrixd projectMatrixInverse = osg::Matrixd::inverse(projectMatrix);
osg::Matrix matrix;
matrix.preMult(viewMatrixInverse);
matrix.preMult(projectMatrixInverse);
osg::Vec4f startPoint = matrix.preMult(startNormalized);
osg::Vec4f endPoint = matrix.preMult(endNormalized);
startPoint.x() /= startPoint.w();
startPoint.y() /= startPoint.w();
startPoint.z() /= startPoint.w();
endPoint.x() /= endPoint.w();
endPoint.y() /= endPoint.w();
endPoint.z() /= endPoint.w();
osg::Vec3f point1(startPoint.x(), startPoint.y(), startPoint.z());
osg::Vec3f point2(endPoint.x(), endPoint.y(), endPoint.z());
osg::Vec3d cameraPosition = viewMatrixInverse.getTrans();
point1 = osg::Vec3f(cameraPosition.x(), cameraPosition.y(), cameraPosition.z());
osg::Group *line = createLine(point1, point2);
_root->addChild(line);
}
kdTree
kdTree的资料网上非常多,这里大概讲一下思路;这里的kdtree作用是用于加速求交,因此primitive包含了(point, line, trangle and quad); 因此与用于point cloudy的二叉树构建是有区别的;
kdTree是一种空间二分的方法,也就是一颗二叉树
数据准备阶段:
- 确定需要构建树的深度Level 以及 每个叶子节点所容纳number of primitive
- 构建一个包含所有Primitives 的bbox, 根据Level 按照 X, Y,Z 三个轴方向进行划分,总是找最长的轴向进行二分;这样得出每一层的划分轴;
- 计算每个primitive的 bbox中心,并且存储;
划分阶段:(递归操作)
- 递归结束判断: 达到Level深度,或者primitive < 设定好的的数目;
- 在划分时,如果发现左右空间划分,有一个空间中不包含任何primitive(这里用bbox的中心替代)为空时,保持当前父节点不变,再次对不为空的空间进行划分,也就是二次划分,直到左右空间中primitive都不为空时; 才将这两个空间作为两个子节点,添加到父节点中;
- 叶子节点根据primitive, 重新构建bbox,并且传递给父节点,父节点根据子节点的bbox进行本节点bbox的构建,以此回溯到根节点;
intersection:
osg提供的LineSegmentIntersector
只实现了面的求交,继承线段求交器进行实现即可
ExpandLineSegementPickHandler : public LineSegmentIntersector
,
求交比较简单,就设计到一些简单的几何计算;包括:
直线1. 求交:
-
线段和BBox
-
线段和Bounding Sphere
-
线段和点
-
线段和线段
-
线段和三角面
可以看看osg源码,都是些简单几何运算;虽然简单,当然里面有很多编程技巧是值得我们学习的;
其中线段和点以及线段和线段的求交需要自己实现;
hightlight:
这里直接使用了OSG提供的osgFX的特效类,进行实现;
后面再根据自己需求实现自己的高亮特效
osg::ref_ptr<osgFX::Outline> outline = new osgFX::Outline();
outline->setColor(osg::Vec4(1, 1, 0, 1));
outline->setWidth(15);
outline->addChild(node);
parent->replaceChild(node, outline);
实现效果
- Ray(Line):
- Ray(Polytope)
- kdTree
- 拾取 点、 线,面,以及高亮