osgVerse学习:1、使用GPU烘焙处理倾斜摄影顶层合并时的接缝问题
以下代码是学习osgVerse的源码后,笔者自己实现的
#include <osg/Node>
#include <osg/Texture>
#include <osg/Program>
#include <osg/FrameBufferObject>
#include <osg/Geometry>
// GpuBaker.h
class GpuBaker {
public:
virtual ~GpuBaker() {}
// 烘焙纹理图像
virtual osg::ref_ptr<osg::Image> bakeTextureImage(osg::Node* node) = 0;
// 烘焙几何体
virtual osg::Geometry* bakeGeometry(osg::Node* node) = 0;
// 设置烘焙参数
void setTextureResolution(int width, int height) {
_textureWidth = width;
_textureHeight = height;
}
void setGeometryResolution(int width, int height) {
_geometryWidth = width;
_geometryHeight = height;
}
void setSimplifyRatio(float ratio) {
_simplifyRatio = ratio;
}
protected:
int _textureWidth = 1024;
int _textureHeight = 1024;
int _geometryWidth = 128;
int _geometryHeight = 128;
float _simplifyRatio = 0.2f;
};
// DefaultGpuBaker.h
class DefaultGpuBaker : public GpuBaker {
public:
virtual osg::ref_ptr<osg::Image> bakeTextureImage(osg::Node* node) override;
virtual osg::Geometry* bakeGeometry(osg::Node* node) override;
private:
osg::ref_ptr<osg::HeightField> createHeightField(osg::Node* node, int width, int height);
};
/**
* @file GPUBaker.cpp
* @brief GPU烘焙实现文件,用于生成纹理和几何体
*/
#include "GPUBaker.h"
#include <osgUtil/LineSegmentIntersector>
#include <osg/ComputeBoundsVisitor>
#include <osgUtil/Simplifier>
#include <osgViewer/Viewer>
/**
* @brief 创建用于渲染到纹理的相机
* @param image 目标图像
* @param bbox 场景包围盒
* @return 配置好的相机对象
*/
static osg::ref_ptr<osg::Camera> createImageCamera(osg::Image* image, const osg::BoundingBox& bbox)
{
// 创建相机并设置基本属性
osg::ref_ptr<osg::Camera> camera = new osg::Camera;
camera->setClearColor(osg::Vec4(0.0f, 0.0f, 0.0f, 0.0f)); // 设置透明背景
camera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
camera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT); // 使用FBO
camera->setRenderOrder(osg::Camera::PRE_RENDER);
camera->setViewport(0, 0, image->s(), image->t());
camera->attach(osg::Camera::COLOR_BUFFER0, image);
// 设置投影矩阵参数
float zn = bbox.zMin(), zf = bbox.zMax();
if (zf <= zn) zf = zn + 10.0f;
osg::Vec3 center = bbox.center();
center.z() = zf;
// 配置相机变换
camera->setReferenceFrame(osg::Transform::ABSOLUTE_RF);
camera->setProjectionMatrix(osg::Matrix::ortho(
bbox.xMin() - center.x(), bbox.xMax() - center.x(),
bbox.yMin() - center.y(), bbox.yMax() - center.y(),
zn, zf));
camera->setViewMatrix(osg::Matrix::lookAt(center, bbox.center(), osg::Y_AXIS));
// 创建图形上下文配置
osg::ref_ptr<osg::GraphicsContext::Traits> traits = new osg::GraphicsContext::Traits;
traits->x = 0;
traits->y = 0;
traits->width = image->s();
traits->height = image->t();
traits->doubleBuffer = false; // 单缓冲模式
traits->pbuffer = true; // 使用PBuffer进行离屏渲染
// 创建并设置图形上下文
osg::ref_ptr<osg::GraphicsContext> gc = osg::GraphicsContext::createGraphicsContext(traits.get());
if (!gc.valid()) {
OSG_WARN << "Failed to create graphics context!" << std::endl;
return NULL;
}
camera->setGraphicsContext(gc.get());
return camera;
}
/**
* @brief 烘焙纹理图像
* @param node 要烘焙的场景节点
* @return 生成的纹理图像
*/
osg::ref_ptr<osg::Image> DefaultGpuBaker::bakeTextureImage(osg::Node* node) {
// 计算场景包围盒
osg::ComputeBoundsVisitor cbv;
node->accept(cbv);
osg::BoundingBox bbox = cbv.getBoundingBox();
// 创建目标图像
osg::ref_ptr<osg::Image> image = new osg::Image;
image->allocateImage(_textureWidth, _textureHeight, 1, GL_RGB, GL_UNSIGNED_BYTE);
image->setInternalTextureFormat(GL_RGB8);
// 设置渲染场景
osg::ref_ptr<osg::Camera> camera = createImageCamera(image, bbox);
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(node);
camera->addChild(root.get());
// 创建并配置查看器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
viewer->setSceneData(root.get());
viewer->addSlave(camera, false);
viewer->realize();
// 执行渲染
while (viewer->checkNeedToDoFrame()) {
viewer->frame();
}
return image;
}
/**
* @brief 烘焙几何体
* @param node 要烘焙的场景节点
* @return 生成的几何体
*/
osg::Geometry* DefaultGpuBaker::bakeGeometry(osg::Node* node) {
// 创建新的几何体对象
osg::ref_ptr<osg::Geometry> geom = new osg::Geometry;
geom->setUseDisplayList(false);
geom->setUseVertexBufferObjects(true);
// 创建高度场并转换为几何体
osg::ref_ptr<osg::HeightField> hf = createHeightField(node, _geometryWidth, _geometryHeight);
osg::BuildShapeGeometryVisitor bsgv(geom.get(), NULL);
hf->accept(bsgv);
return geom.release();
}
/**
* @brief 创建高度场
* @param node 要烘焙的场景节点
* @param width 高度场宽度
* @param height 高度场高度
* @return 生成的高度场对象
*/
osg::ref_ptr<osg::HeightField> DefaultGpuBaker::createHeightField(osg::Node* node, int width, int height) {
// 计算场景包围盒
osg::ComputeBoundsVisitor cbv;
node->accept(cbv);
osg::BoundingBox bbox = cbv.getBoundingBox();
// 创建深度图纹理
osg::ref_ptr<osg::Image> image = new osg::Image;
image->allocateImage(width, height, 1, GL_LUMINANCE, GL_FLOAT);
image->setInternalTextureFormat(GL_LUMINANCE32F_ARB);
// 设置渲染场景
osg::ref_ptr<osg::Camera> camera = createImageCamera(image.get(), bbox);
osg::ref_ptr<osg::Group> root = new osg::Group;
root->addChild(node);
camera->addChild(root.get());
// 配置深度渲染着色器
{
const char* vsCode = {
"#version 110\n"
"varying float depth;\n"
"void main() {\n"
" depth = -(gl_ModelViewMatrix * gl_Vertex).z;\n"
" gl_Position = ftransform();\n"
"}\n"
};
const char* fsCode = {
"#version 110\n"
"varying float depth;\n"
"void main() {\n"
" gl_FragColor = vec4(depth, 1.0, 1.0, 1.0);\n"
"}\n"
};
osg::ref_ptr<osg::Shader> vs = new osg::Shader(osg::Shader::VERTEX, vsCode);
osg::ref_ptr<osg::Shader> fs = new osg::Shader(osg::Shader::FRAGMENT, fsCode);
osg::ref_ptr<osg::Program> prog = new osg::Program;
prog->addShader(vs.get());
prog->addShader(fs.get());
prog->addBindAttribLocation("osg_Vertex", 0);
camera->getOrCreateStateSet()->setAttributeAndModes(prog.get());
}
// 创建并配置查看器
osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer;
viewer->setThreadingModel(osgViewer::Viewer::SingleThreaded);
viewer->setSceneData(root.get());
viewer->addSlave(camera, false);
viewer->realize();
// 执行渲染
while (viewer->checkNeedToDoFrame()) {
viewer->frame();
}
// 创建高度场并设置基本属性
osg::ref_ptr<osg::HeightField> hf = new osg::HeightField;
hf->allocate(width, height);
hf->setOrigin(bbox._min);
hf->setXInterval((bbox.xMax() - bbox.xMin()) / (float)(width - 1));
hf->setYInterval((bbox.yMax() - bbox.yMin()) / (float)(height - 1));
// 从深度图提取高度值
float* ptr = (float*)image->data();
float zRange = bbox.zMax() - bbox.zMin();
for (int y = 0; y < height; ++y)
for (int x = 0; x < width; ++x) {
float eyeZ = *(ptr + (y * width) + x);
if (eyeZ <= 0.0f)
hf->setHeight(x, y, 0.0f);
else
hf->setHeight(x, y, zRange - eyeZ);
}
return hf;
}
#include <osgViewer/Viewer>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Group>
#include <osgDB/ReadFile>
#include <osgDB/WriteFile>
#include <osgUtil/Optimizer>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <string>
#include <iostream>
#include <osgDB/ConvertUTF>
#ifdef _WIN32
#include <Windows.h>
#endif
#include <osg/ComputeBoundsVisitor>
#include <osg/MatrixTransform>
using namespace std;
#include "GPUBaker.h"
int main(int argc, char** argv)
{
// 解决输出时中文乱码问题
#ifdef _WIN32
SetConsoleOutputCP(CP_UTF8);
#else
setlocale(LC_ALL, "en_US.UTF-8");
#endif // _WIN32
// 加载OSGB模型
osg::ref_ptr<osg::Node> model1 = osgDB::readNodeFile(R"(E:\BaiduNetdiskDownload\qingxieshuju\qingxie\Data\Data\Tile_+030_+031\Tile_+030_+031_L22_0003300.osgb)");
osg::ref_ptr<osg::Node> model2 = osgDB::readNodeFile(R"(E:\BaiduNetdiskDownload\qingxieshuju\qingxie\Data\Data\Tile_+030_+031\Tile_+030_+031_L22_0003310.osgb)");
osg::ref_ptr<osg::Node> model3 = osgDB::readNodeFile(R"(E:\BaiduNetdiskDownload\qingxieshuju\qingxie\Data\Data\Tile_+030_+031\Tile_+030_+031_L22_0003320.osgb)");
osg::ref_ptr<osg::Node> model4 = osgDB::readNodeFile(R"(E:\BaiduNetdiskDownload\qingxieshuju\qingxie\Data\Data\Tile_+030_+031\Tile_+030_+031_L22_0003330.osgb)");
osg::ref_ptr<osg::Group> group = new osg::Group;
group->addChild(model1);
group->addChild(model2);
group->addChild(model3);
group->addChild(model4);
// 创建烘焙器
DefaultGpuBaker baker;
// 设置烘焙参数
baker.setTextureResolution(512, 512);
baker.setGeometryResolution(128, 128);
baker.setSimplifyRatio(0.3f);
// 烘焙纹理
osg::ref_ptr<osg::Image> bakedImage = baker.bakeTextureImage(group.get());
// 烘焙几何体
osg::ref_ptr<osg::Geometry> bakedGeometry = baker.bakeGeometry(group.get());
osg::ref_ptr<osg::Texture2D> texture = new osg::Texture2D;
texture->setImage(bakedImage);
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
texture->setWrap(osg::Texture::WRAP_S, osg::Texture::REPEAT);
texture->setWrap(osg::Texture::WRAP_T, osg::Texture::REPEAT);
bakedGeometry->getOrCreateStateSet()->setTextureAttributeAndModes(0, texture, osg::StateAttribute::ON);
// 保存结果
osgDB::writeImageFile(*bakedImage, "baked_texture.png");
// 创建新节点并保存
osg::ref_ptr<osg::Geode> geode = new osg::Geode;
geode->addDrawable(bakedGeometry.get());
osgDB::writeNodeFile(*geode, "baked_geometry.osgb");
return 0;
}
结果: