本节功能
- 本节将二维的屏幕坐标转换成3维的世界坐标,并间隔绘制小球。也即在视平面的近面上间隔绘制小球。
- 同时将在视锥体内,间隔绘球,铺满整个视锥体的近面与远面之间
本节的内容在网盘中:
请使用浏览器打开,平时遇到问题或加群也可以加我微信:13324598743:
【击此打开网盘资源链接】
理论知识
- 窗口坐标转三维坐标其实是三维坐标转窗口坐标的逆过程
- 三维坐标p,转窗口坐标,其过程是这样的,先乘观察矩阵viewMatrix=V,转换为以相机为局部坐标系下的坐标,然后再乘投影矩阵ProjectionMatrix=P转换为视锥体内的规范化坐标,将所有视锥体内的可视的内容压缩到[0, 1]的方块内,最后再乘WindowsMatrix=W,就是转变为窗口坐标显示在窗口上了。窗口坐标是一个三维的, x, y, z, 其中xy好理解,就是800×600里的长宽,z的范围是[0, 1],代表由近面到远面的整个视锥体
实操
- 场景加牛,写一个事件,当点击p的时候,我们获取当前视口的长宽,每隔100距离画一个半径为0.1的小球
osg::Viewport* vp = viewer->getCamera()->getViewport();
int width = vp->width();
int height = vp->height();
//每隔20画个球
for (int i = 0; i < width; i += 100)
{
for (int j = 0; j < height; j += 100)
{
for (float k = 0; k <1; k+=0.1)
{
上面的代码可以看到,i, j都好理解,k就是深度,从0近面开始,到1远面止,每加0.1代表间隔,就实现了球铺满了整个视锥体。
- 计算VPW的逆矩阵,然后用ijk来与之乘得到球心,这个很简单,我们有camera就什么都有了,比如你想由屏幕中心点出发一条线,则ij好算,k取0,再取一个0~1范围内的值,得到另一点,就得到了一个射线:
osg::Matrix inverseVPW = osg::Matrix::inverse(viewer->getCamera()->getViewMatrix() *
viewer->getCamera()->getProjectionMatrix()*
viewer->getCamera()->getViewport()->computeWindowMatrix());
osg::Vec3 center = osg::Vec3(i, j, k) * inverseVPW;
_root->addChild(createSphere(center, 0.1));
实际效果
正面:
侧面可以看到,铺满了整个近面与远面:
全部代码
#include <osgViewer/Viewer>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/PrimitiveSet>
#include <osgGA/GUIEventHandler>
#include <osgGA/CameraManipulator>
#include <osgDB/ReadFile>
#include <osg/Geode>
#include <osg/ShapeDrawable>
osg::Group* _root = new osg::Group;
osg::Node* createSphere(osg::Vec3 center, float r)
{
osg::Geode* gnode = new osg::Geode;
gnode->addDrawable(new osg::ShapeDrawable(new osg::Sphere(center, r)));
return gnode;
}
class MyEventHandler :public osgGA::GUIEventHandler
{
public:
MyEventHandler() {};
virtual bool handle(const osgGA::GUIEventAdapter& ea, osgGA::GUIActionAdapter& aa)
{
if (ea.getEventType() == ea.KEYDOWN)
{
if (ea.getKey() == 'p')
{
//获取当前视口的分辨率
osgViewer::Viewer *viewer = dynamic_cast<osgViewer::Viewer*>(&aa);
if (viewer)
{
osg::Viewport* vp = viewer->getCamera()->getViewport();
int width = vp->width();
int height = vp->height();
//每隔20画个球
for (int i = 0; i < width; i += 100)
{
for (int j = 0; j < height; j += 100)
{
for (float k = 0; k <1; k+=0.1)
{
osg::Matrix inverseVPW = osg::Matrix::inverse(viewer->getCamera()->getViewMatrix() *
viewer->getCamera()->getProjectionMatrix()*
viewer->getCamera()->getViewport()->computeWindowMatrix());
osg::Vec3 center = osg::Vec3(i, j, k) * inverseVPW;
_root->addChild(createSphere(center, 0.1));
}
}
}
}
}
}
return false;
}
};
int main()
{
osgViewer::Viewer viewer;
_root->addChild(osgDB::readNodeFile("cow.osg"));
viewer.setSceneData(_root);
viewer.addEventHandler(new MyEventHandler);
return viewer.run();
}
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END
暂无评论内容