第33节 实例-屏幕坐标转三维世界坐标

本节功能

  • 本节将二维的屏幕坐标转换成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
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片