视图与相机
三维视景体
视景体是指成像景物所在空间的集合。它是一个空间集合体。
注意视景体指的是一个空间集合。一般来说,集合中的每个空间都是个立方体。然后所有的这些空间堆在一起,在外侧取一个最大的外接立方体,就是可显示的区域,该可显示区域也是一个视景体。在这个可显示区域内,放置着可以显示的物体。注意,可以显示的物体仅仅是拥有显示权限,但是不一定显示,这取决于相机等其他因素。
单个的视景体,比如一个球体,若要完全显示,其视景体应该是该球体的最小外接立方体;若要只显示上半部分,则取上半球,其视景体是上半球的最小外接立方体。对于半球而言,上半球是视景体,那么其只有上半球有显示权限,下半球没有,所以就算是将该球体位置拉远,也只能看到上半球。
比如一个球体,视景体为该球体的最小外接立方体。可以这样理解:该视景体的内容完完全全绘制在一张大小正好的画布(投影)上,画布(投影)以外都是背景色,取黑色。然后摄像机刚好看着这张画布(投影),画布(投影)上的内容完全显示在摄像机屏幕上。接着,对摄像机进行了右移,在摄像机的画面上,左侧是画布上的部分内容,右侧是黑色背景色。也就是说此时立方体虽然有显示权限,但是却并没有完全显示。但未显示的部分依然是有显示的可能的。
若视景体仅仅为上半球,那么默认情况下,上半球所映射的画布刚好充满摄像机。将摄像机下移,则摄像机画面中显示的是上半球切面的水平线,水平线以下是黑色背景色。即使此时从理论上来说摄像机对着下半球,但由于视景体仅为上半球,所以下半球是没有显示权限的,是不会被显示的。
视景体就意味着可显示空间,在该空间内的一切物体都可以被显示,都可以被看到,该空间外的一切物体都不能被看到。相机若要看到该空间中的物体,则相机本身就必须处于该视景体空间中。若相机在视景体空间外,哪怕该视景体就在相机正对的面前,相机依然无法看到。
视点变换
视点变换就是设置视点的方向和位置。默认情况下,视点定为坐标原点,指向Y正方向。
投影变换
投影的作用:将要显示的三维视景体按照指定角度和方向投影到二维平面上。
投影分为透视投影和正视投影。根据这两种投影方式的不同,映射出的二维图像也不同:透视投影会出现近大远小的透视效果;正视投影不会影响物体的相对大小,即同样长的边,在远处与在近处看到的长度是相同的。
透视投影分为透视视景体和对称透视视景体:
- 透视视景体
这个函数的前4个参数定义了近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near)。如下图(近大远小):
- 对称透视视景体
该函数的参数fovy定义视野的角度,以z轴为中心向两侧平分,范围是[0.0, 180.0],也就是说最大为上下各90°;参数aspect是投影平面宽度与高度的比,或者说是视景体的宽高比;参数zNear和Far分别是远近裁剪面沿Z负轴到视点的距离,它们总为正值。
以上两个函数缺省时,视点都在原点,视线沿Z轴指向负方向。
正视投影分为正射投影和特殊正射投影:
- 正射投影视景体
这个函数的前4个参数定义了近裁剪平面的左下角点和右上角点的三维空间坐标,即(left,bottom,-near)和(right,top,-near)。
- 特殊正射投影视景体
视口
视口就是计算器屏幕的一块矩形区域,三维物体投影为二维平面,而这个二维平面就将显示在视口上。
裁剪变换
在OSG中,可以设置裁剪平面,将没必要显示的物体去除掉,裁剪平面法线是哪个方向就留下哪个部分。
使用osg::ClipPlane类
来进行裁剪。
也可以使用 osg::Scissor类
来进行裁剪。
相机节点
osg::Camera类
可以用于获得三维物体的自定义角度和方向的二维映射(拍照)。
该类方法除了上面视口,投影的相关函数还有如下:
图形环境
为了更方便的控制图形渲染,可以设置各种图形渲染的属性,也可以直接采用默认的。
通过osg::GraphicsContext::Traits类
来设置图形环境特性。
通过osg::GraphicsContext类
的createGraphicsContext()
函数来创建图形环境特性。
图形环境属性:
宽屏变形实例
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osg/Camera>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <iostream>
int main()
{
//创建Viewer对象, 场景浏览器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
//创建场景节点
osg::ref_ptr<osg::Group> root = new osg::Group();
//读取模型
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile("cow.osg");
root->addChild(node.get());
//设置图形环境特性
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
traits->x = 0;
traits->y = 0;
traits->width = 1000;
traits->height = 800;
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
//场景图形环境特性
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext( traits.get());
if (gc.valid())
{
osg::notify(osg::INFO)<< "GraphicsWindow has been created successfully."<< std::endl;
//清楚窗口颜色及清楚颜色和深度缓存
gc->setClearColor(osg::Vec4f(0.2f, 0.2f, 0.6f, 1.0f));
gc->setClearMask(GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);
}
else
{
osg::notify(osg::NOTICE)<< "GraphicsWindow has not been created successfully."<< std::endl;
}
//根据分辨率确定合适的投影来保证显示的图形不变形
double fovy, aspectRatio, zNear, zFar;
viewer->getCamera()->getProjectionMatrixAsPerspective(fovy, aspectRatio, zNear, zFar);
double newAspectRatio = double(traits->width) / double(traits->height);
double aspectRatioChange = newAspectRatio / aspectRatio;
if (aspectRatioChange != 1.0)
{
//设置投影矩阵
viewer->getCamera()->getProjectionMatrix() *= osg::Matrix::scale(1.0 / aspectRatioChange, 1.0, 1.0);
}
//设置视口
viewer->getCamera()->setViewport(new osg::Viewport(0, 0, traits->width, traits->height));
//设置图形环境
viewer->getCamera()->setGraphicsContext(gc.get());
//优化场景
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
viewer->run();
return 0;
}
单窗口多相机渲染实例
// stdafx.h
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/MatrixTransform> //移动节点的矩阵类,最常用的移动节点的类。可随动、旋转控制节点
#include <osg/PositionAttitudeTransform> //位置变换节点类,提供模型的位置变换、大小缩放、原点位置的设置、坐标系的变换
#include <osg/Camera> //相机节点,管理OSG中的模型——视图矩阵,相机的管理主要是通过各种变换实现的
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osgViewer/Viewer> //只允许单视图,单视图可以同时包含多个相机渲染,也可以在多窗口中渲染
#include <osgViewer/ViewerEventHandlers> //事件监听
//.cpp
/*
*单窗口多相机渲染
*
*创建图形环境的步骤:
*通过WindowingSystemInterface类得到系统窗口接口,该系统接口主要是为了管理窗口系统与图形环境
*根据需要设置图形环境特性的参数
*通过图形环境特性创建图形环境
*通过图形环境创建窗口
*
*建立视口的关键步骤:
*创建图形环境(创建一个设备上下文对象)
*将该对象和一个相机关联
*设置视口位置
*添加相机到窗口中
*/
void singleWindowMultipleCameras(osg::ref_ptr<osgViewer::Viewer> viewer)
{
//创建窗口系统接口(调用底层API得到绘图设备、窗口特性)
osg::ref_ptr<osg::GraphicsContext::WindowingSystemInterface> wsi = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsi)
{
osg::notify(osg::NOTICE) << "Error, no WindowSystemInterface available, cannot create windows." << std::endl;
return;
}
//得到当前窗口分辨率
unsigned int width, height;
wsi->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), width, height);
//设置图形环境特性(设置要创建的图形设备上下文)
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits();
//设置图形设备的尺寸
traits->x = 0;
traits->y = 0;
traits->width = width;
traits->height = height;
//显示标题栏
traits->windowDecoration = true;
traits->doubleBuffer = true;
traits->sharedContext = 0;
//创建图形环境(创建图形设备上下文)
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if (gc->valid())
{
osg::notify(osg::INFO) << "GraphicsWindow has been created successfully." << std::endl;
gc->setClearColor(osg::Vec4f(0.2f, 0.2f, 1.6f, 1.0f));
gc->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}
else
{
osg::notify(osg::NOTICE) << "GraphicsWindow has not been created successfully." << std::endl;
}
//设置第一个视口(创建cameraMaster主相机)
osg::ref_ptr<osg::Camera> cameraMaster = viewer->getCamera();
//设置图形环境
cameraMaster->setGraphicsContext(gc.get());
//根据分辨率确定合适的投影来保证显示的图形不变形
double fovy, aspectRatio, zNear, zFar;
cameraMaster->getProjectionMatrixAsPerspective(fovy, aspectRatio, zNear, zFar);
double newAspectRatio = double(traits->width) / double(traits->height);
double aspectRatioChange = newAspectRatio / aspectRatio;
if (aspectRatioChange != 1.0)
{
cameraMaster->getProjectionMatrix() *= osg::Matrix::scale(1.0 / aspectRatioChange, 1.0, 1.0);
}
//设置视口位置
cameraMaster->setViewport(new osg::Viewport(0, 0, width, height));
//根据是否使用双缓存,确定绘制哪个缓存,如果使用双缓存,先绘制前缓存
GLenum bufferMaster = traits->doubleBuffer ? GL_BACK : GL_FRONT;
//设置缓冲区(设置每帧开始绘制时,绘制哪个缓存)
cameraMaster->setDrawBuffer(bufferMaster);
cameraMaster->setReadBuffer(bufferMaster);
//设置第二个视口(创建从属相机)
osg::ref_ptr<osg::Camera> cameraClient = new osg::Camera();
//设置图形环境
cameraClient->setGraphicsContext(gc.get());
//设置视口位置,视口的坐标为左下角为坐标原点
cameraClient->setViewport(new osg::Viewport(0, 0, 400, 400));
//根据是否使用双缓存,确定绘制哪个缓存,如果使用双缓存,先绘制前缓存
GLenum bufferClient = traits->doubleBuffer ? GL_BACK : GL_FRONT;
//设置缓冲区(设置每帧开始绘制时,绘制哪个缓存)
cameraClient->setDrawBuffer(bufferClient);
cameraClient->setReadBuffer(bufferClient);
//添加从属相机
viewer->addSlave(cameraClient, osg::Matrix::scale(aspectRatio, 1.0, 1.0), osg::Matrix());
}
int main()
{
osg::ref_ptr<osg::Node> root = osgDB::readNodeFile("cow.osg");
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();
//启用单视图多相机渲染
singleWindowMultipleCameras(viewer.get());
//优化场景数据
osgUtil::Optimizer optimizer;
optimizer.optimize(root.get());
viewer->setSceneData(root.get());
viewer->realize();
return viewer->run();
}
效果图:文章来源:https://www.toymoban.com/news/detail-823122.html
文章来源地址https://www.toymoban.com/news/detail-823122.html
到了这里,关于OpenSceneGraph 相机与视图的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!