当前位置: 首页 > news >正文

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;
}

结果:
在这里插入图片描述
在这里插入图片描述

相关文章:

  • js 对象深拷贝的五种方法
  • java-IO流笔记
  • android 一步完成 aab 安装到手机
  • 【Qt】QByteArray详解
  • 【HTML 基础教程】HTML 属性
  • antd-vue Table组件翻页后保留上一页已选的数据
  • 比特币等虚拟货币实时价格使用说明,数字货币价格获取,k线获取,实时价格获取
  • Java 8-17核心特性全景解析之Java12
  • Spring IOC/DI的依赖注入方式及示例
  • 索引(重点)
  • 地理信息可视化技术大全【WebGIS 技术文档大全】
  • 科大讯飞语音转文字STT--unity
  • mysql死锁排查解决
  • Mysql 回表查询,什么是回表查询,如何拒绝sql查询时的回表问题
  • 【Django】教程-3-数据库相关介绍
  • C++中的判断与循环
  • Python 魔术方法功能分类指南
  • 十一、JavaScript简单数据类型和复杂数据类型
  • 材料科学基础:空间群与点群(1)
  • flutter 获取设备的唯一标识
  • 做网站应该拿多少提成/国际军事新闻最新消息视频
  • asp大型网站开发/现在做百度快速收录的方法
  • 武汉建设委员会网站/电子商务
  • 免费域名网站的/优化推广方案
  • 体育新闻网站的建设/图片搜索图片识别
  • 微信网站 微信支付/惠州seo关键词