看了视频教程https://www.bilibili.com/video/av74285647/
感谢up主,
我试图简易地描述下,如果渲染到场景中,最简单的主摄像机
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Group> grp = new osg::Group;
std::string strFileName = "f:/test/pot.FBX";
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strFileName);
grp->addChild(node);
viewer->setSceneData(grp);
viewer->run();
}
现在不想直接绘制到场景了,绘制到面片上;或者进行后处理后再次投影到全屏中,比如hdr
这就用到了FBO,
这里不考虑放缩,直接用屏幕大小的面片,以后即使贴到屏幕上,也是一样的。
1,先考虑面片,
osg::Texture2D *createFloatTextureRectangle(int width, int height)
{
osg::ref_ptr<osg::Texture2D> tex2D = new osg::Texture2D;
tex2D->setTextureSize(width, height);
tex2D->setInternalFormat(GL_RGBA16F_ARB);
tex2D->setSourceFormat(GL_RGBA);
tex2D->setSourceType(GL_FLOAT);
return tex2D.release();
}
先获取系统分辨率,确定帧缓冲区大小
unsigned int screenWidth, screenHeight;
{
osg::GraphicsContext::WindowingSystemInterface *wsInterface = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsInterface)
{
return -1;
}
wsInterface->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), screenWidth, screenHeight);
}
int texWidth = screenWidth;
int texHeight = screenHeight;
// 然后设置离屏表面的纹理,FBO涉及到3种缓冲区:深度模板,颜色和法线
这里只考虑关联颜色缓冲区的纹理
osg::Texture2D *textureSample = createFloatTextureRectangle(texWidth, texHeight);
设置完后呢,需要关联颜色缓冲区,把颜色缓冲区的内容放在这个纹理上。这用到了采样摄像机,这里仍然采用铺满该纹理。其实,主摄像机和采样摄像机都是一样的。
// 采样像机1设置:
{
osg::Camera *cameraSample = new osg::Camera;// 各种采样
grp->addChild(cameraSample);// 加入场景
cameraSample->addChild(node);// 加入模型
cameraSample->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);// 这句使内容不渲染到屏幕上
cameraSample->attach(osg::Camera::COLOR_BUFFER0, textureSample);// 关联采样贴图
cameraSample->setViewport(0, 0, screenWidth, screenHeight);//
}
然后这张纹理贴到哪里呢?先贴到面片上,面片直接加载到场景中
osg::Geode * panel1 = createTexturePanel();
panel1->getOrCreateStateSet()->setTextureAttributeAndModes(0, textureSample);
grp->addChild(panel1);
目前现在这步算是ok了,颜色缓冲区的东西放到面片上了,
可是后处理不是这么玩的,我还想放在全屏上,咋办呢?
那就把颜色缓冲区的纹理,再放到最终的摄像机上。这个摄像机要用到设备规格化
//最终的摄像机
osg::ref_ptr<osg::Camera> finalCamera = new osg::Camera;
finalCamera->addChild(panel1);
grp->addChild(finalCamera);
finalCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
osg::Geode* createTexturePanel()
{
osg::Vec3Array *vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(-1.0, -1.0, 0.0));
vertices->push_back(osg::Vec3(1.0, -1.0, 0.0));
vertices->push_back(osg::Vec3(1.0, 1.0, 0.0));
vertices->push_back(osg::Vec3(-1.0, 1.0, 0.0));
osg::Vec2Array *texcoord = new osg::Vec2Array;
texcoord->push_back(osg::Vec2(0.0, 0.0));
texcoord->push_back(osg::Vec2(1.0, 0.0));
texcoord->push_back(osg::Vec2(1.0, 1.0));
texcoord->push_back(osg::Vec2(0.0, 1.0));
osg::Geometry *geom = new osg::Geometry;
geom->setVertexArray(vertices);
geom->setTexCoordArray(0, texcoord);
geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
osg::Geode *geode = new osg::Geode;
geode->addDrawable(geom);
osg::StateSet *set1 = geode->getOrCreateStateSet();
//geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
set1->setMode(GL_LIGHTING, osg::StateAttribute::OFF);// 设置不受光照影响, 不然太暗了就看不清
return geode;
}
结果如下:
贴上去了,看到这里,估计有人想,这不是在逗我吧。我怎么知道这是主摄像机的,还是采样摄像机的纹理贴上去的?
这个简单,在采样摄像机中,再把背景颜色涂成黑色。
cameraSample->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
我们再把颜色缓冲区放到面片上。
看清楚,黑了啊,和主摄像机不同了。
我们再把它当做全屏摄像机的纹理贴上去。
ok,说明确实是把颜色缓冲区的纹理贴到了全屏摄像机上。
类似地,深度模板和法线应该同样,具体以后再试。
这里总结下,实际上归为两个问题
1,主摄像机为什么把物体直接绘制到屏幕上,而FBO没有
2,fbo如何绘制到面片上,如何绘制到全屏?
先看第一个问题。
FBO除了关联三个缓冲区外(这里只写了颜色缓冲区),还设置了下
cameraSample->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);// 这句使内容不渲染到屏幕上
cameraSample->attach(osg::Camera::COLOR_BUFFER0, textureSample);// 关联采样贴图
这两句起到了作用,当然,还有第三句容易误导
cameraSample->setViewport(0, 0, screenWidth, screenHeight);//
这是把离屏表面填满,与主摄像机的视口没任何关系。
或许,应该直接写为
cameraSample->setViewport(0, 0, texWidth, texHeight);
第二个问题,如何绘制到面片和全屏,
这里规整后代码对比下
osg::Geode * panel1 = createTexturePanel();
panel1->getOrCreateStateSet()->setTextureAttributeAndModes(0, textureSample);
.//面片直接添加到场景中
{
grp->addChild(panel1);
}
//全屏
{
osg::ref_ptr<osg::Camera> finalCamera = new osg::Camera;
finalCamera->addChild(panel1);
grp->addChild(finalCamera);
finalCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
}
由此可见,似乎是
finalCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
在起作用。
把全屏的注释掉这句代码。
果然成面片了。
看看API,
默认状态是RELATIVE_RF
即:
1,FBO需要
cameraSample->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);// 这句使内容不渲染到屏幕上
cameraSample->attach(osg::Camera::COLOR_BUFFER0, textureSample);// 关联采样贴图
2,如果想将FBO放到全屏,则需要新建一个摄像机,摄像机添加该面片,并设置该摄像机为
finalCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
最后上代码
#include "common.h"
#include <iostream>
osg::Geode* createTexturePanel()
{
osg::Vec3Array *vertices = new osg::Vec3Array;
vertices->push_back(osg::Vec3(-1.0, -1.0, 0.0));
vertices->push_back(osg::Vec3(1.0, -1.0, 0.0));
vertices->push_back(osg::Vec3(1.0, 1.0, 0.0));
vertices->push_back(osg::Vec3(-1.0, 1.0, 0.0));
osg::Vec2Array *texcoord = new osg::Vec2Array;
texcoord->push_back(osg::Vec2(0.0, 0.0));
texcoord->push_back(osg::Vec2(1.0, 0.0));
texcoord->push_back(osg::Vec2(1.0, 1.0));
texcoord->push_back(osg::Vec2(0.0, 1.0));
osg::Geometry *geom = new osg::Geometry;
geom->setVertexArray(vertices);
geom->setTexCoordArray(0, texcoord);
geom->addPrimitiveSet(new osg::DrawArrays(GL_QUADS, 0, 4));
osg::Geode *geode = new osg::Geode;
geode->addDrawable(geom);
osg::StateSet *set1 = geode->getOrCreateStateSet();
//geode->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
set1->setMode(GL_LIGHTING, osg::StateAttribute::OFF);// 设置不受光照影响, 不然太暗了就看不清
return geode;
}
osg::Texture2D *createFloatTextureRectangle(int width, int height)
{
osg::ref_ptr<osg::Texture2D> tex2D = new osg::Texture2D;
tex2D->setTextureSize(width, height);
tex2D->setInternalFormat(GL_RGBA16F_ARB);
tex2D->setSourceFormat(GL_RGBA);
tex2D->setSourceType(GL_FLOAT);
return tex2D.release();
}
int main()
{
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
osg::ref_ptr<osg::Group> grp = new osg::Group;
std::string strFileName = "f:/test/pot.FBX";
osg::ref_ptr<osg::Node> node = osgDB::readNodeFile(strFileName);
//获取系统分辨率
unsigned int screenWidth, screenHeight;
{
osg::GraphicsContext::WindowingSystemInterface *wsInterface = osg::GraphicsContext::getWindowingSystemInterface();
if (!wsInterface)
{
return -1;
}
wsInterface->getScreenResolution(osg::GraphicsContext::ScreenIdentifier(0), screenWidth, screenHeight);
}
int texWidth = screenWidth;
int texHeight = screenHeight;
// 采样贴图设置:
osg::Texture2D *textureSample = createFloatTextureRectangle(texWidth, texHeight);
// 采样像机1设置:
{
osg::Camera *cameraSample = new osg::Camera;// 各种采样
grp->addChild(cameraSample);// 加入场景
cameraSample->addChild(node);// 加入模型
cameraSample->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f));
cameraSample->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT);// 这句使内容不渲染到屏幕上
cameraSample->attach(osg::Camera::COLOR_BUFFER0, textureSample);// 关联采样贴图
cameraSample->setViewport(0, 0, texWidth, texHeight);//
}
osg::Geode * panel1 = createTexturePanel();
panel1->getOrCreateStateSet()->setTextureAttributeAndModes(0, textureSample);
#if 0
{
grp->addChild(panel1);
}
#endif
#if 1
//最终的摄像机
{
osg::ref_ptr<osg::Camera> finalCamera = new osg::Camera;
finalCamera->addChild(panel1);
grp->addChild(finalCamera);
finalCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
}
#endif
viewer->setSceneData(grp);
viewer->run();
}