OsgEarth3基础3D图形实现

这里尝试在通过OsgEarth提供的各种图形绘制方法,实现基础的3D图形。每个图形除了基础的设置外,尝试提供:位置(Position)姿态(Posture)、**填充色(FillColor)、描边色(ProfileColor/LineColor)的设置,在具体的框架性代码环境中也可以尝试抽象出基类,这里主要关注具体实现。
在这里插入图片描述

主要难点

Geometry能力

其中osg::Geometry提供的顶点绘制能力,可以满足对各种形状的实现,所以这里也是为了绘制需要图形的描边线,进行了简单封装,通过提供顶点数组、顶点坐标、图形样式、线颜色、线宽完成绘制。

事实上它可以包含:

  1. Statetset信息:比如设置透明度支持:
m_pGeom->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
m_pGeom->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
  1. Primitive列表和顶点数组:确定顶点索引,添加多个几何体
    提供的几何体类型也很丰富,具体描述可以详见该博客

https://blog.csdn.net/qq_36881934/article/details/107912706

 enum Mode
 {
      POINTS = GL_POINTS,
      LINES = GL_LINES,
      LINE_STRIP = GL_LINE_STRIP,
      LINE_LOOP = GL_LINE_LOOP,
      TRIANGLES = GL_TRIANGLES,
      TRIANGLE_STRIP = GL_TRIANGLE_STRIP,
      TRIANGLE_FAN = GL_TRIANGLE_FAN,
      QUADS = GL_QUADS,
      QUAD_STRIP = GL_QUAD_STRIP,
      POLYGON = GL_POLYGON,
      LINES_ADJACENCY = GL_LINES_ADJACENCY,
      LINE_STRIP_ADJACENCY = GL_LINE_STRIP_ADJACENCY,
      TRIANGLES_ADJACENCY = GL_TRIANGLES_ADJACENCY,
      TRIANGLE_STRIP_ADJACENCY = GL_TRIANGLE_STRIP_ADJACENCY,
      PATCHES = GL_PATCHES
};

有特殊顺序的可以通过顶点索引,设置顶点坐标的方式添加:

osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry;//创建一个几何体对象

osg::ref_ptr<osg::Vec3Array> v = new osg::Vec3Array;
v->push_back(osg::Vec3(0.0f, 0.0f, 0.0f));
v->push_back(osg::Vec3(2.0f, 0.0f, 0.0f));
v->push_back(osg::Vec3(1.0f, -1.0f, 1.0f));
v->push_back(osg::Vec3(2.0f, -2.0f, 0.0f));
v->push_back(osg::Vec3(0.0f, -2.0f, 0.0f));
geometry->setVertexArray(v);//设置几何体顶点数据

osg::ref_ptr<osg::DrawElementsUInt> quad = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, 0);//指定绘图基元为绘制多段三角形
quad->push_back(0); quad->push_back(1); quad->push_back(2); 
quad->push_back(3); quad->push_back(4);  
quad->push_back(0); quad->push_back(2);
geometry->addPrimitiveSet(quad);//添加到几何体

简单的顶点连接顺序,可以直接通过geometry->addPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, 0, vcArray->size()));添加。

  1. 颜色/纹理数组列表
    颜色可以是渐变色,也可以是纯色,主要在于设置的颜色数组,以及颜色绑定模式。
    如渐变色可以根据顶点:

osg::ref_ptr<osg::Vec4Array> vc = new osg::Vec4Array;//创建颜色数组
vc->push_back(osg::Vec4(1.0f, 0.0f, 0.0f, 1.0f));
vc->push_back(osg::Vec4(0.0f, 1.0f, 0.0f, 1.0f));
vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
vc->push_back(osg::Vec4(1.0f, 1.0f, 0.0f, 1.0f));
vc->push_back(osg::Vec4(0.0f, 0.0f, 1.0f, 1.0f));
geometry->setColorArray(vc);
geometry->setColorBinding(osg::Geometry::BIND_PER_VERTEX);//设置颜色绑定模式

在这里插入图片描述

姿态支持

对于3D图形的属性,姿态肯定也是重点:俯仰、横滚、航向。
这里通过osg::MatrixTransform的rotate方式实现。
首先将Matrix加入到osgEarth::GeoTransform节点,Drawable加入到Matrix节点作为Child,就可以控制Drawable图形的姿态了。

// GeoTransform
	m_pGeoTransform = new osgEarth::GeoTransform();
	m_pMatrix = new osg::MatrixTransform;
	m_pGeoTransform->addChild(m_pMatrix);
	m_pMatrix->addChild(m_pDrawable);
m_pMatrix->setMatrix(
		osg::Matrix::rotate(
			osg::DegreesToRadians(vPos.x()), osg::X_AXIS,
			osg::DegreesToRadians(vPos.y()), osg::Y_AXIS,
			osg::DegreesToRadians(vPos.z()), osg::Z_AXIS));
}

在这里插入图片描述

任意立方体 PolygonCube

使用osgEarth::FeatureNode实现,它对矢量的点线面绘制提供了很好的解决方案,包括与地形的贴合方式(osgEarth::AltitudeSymbol::Clamping),与矢量图层的结合方式等。这里主要通过设置一组坐标信息,绘制立方体。
在这里插入图片描述

矩形立方体 Box

虽然osgEarth::FeatureNode通过设置顶点坐标,也可以画出矩形立方体,但是在实践的过程中发现,高度实现,更准确讲是实现的面向上“挤出”后的效果,而且地面没有封顶,也没有描边线。

m_Style.getOrCreate<ExtrusionSymbol>()->height() = dHeightMeter;

为了实现一个“像样的”矩形立方体,使用了osg::Box结合osg::ShapeDrawable实现,但是osg的绘制体没有描边色,所以也是通过osg::Geometry实现了Box的边线。
在这里插入图片描述

圆锥体 Cone

使用了osg::Cone结合osg::ShapeDrawable实现,也是通过osg::Geometry实现了底部圆形描边+分段线描边。
注意中心点在高度的1/3处,所以如果要控制中心点在底部,可以通过让osgEarth::GeoTransform->addChild(),加入一个Relative的osg::MatrixTransform去偏移到底部。
在这里插入图片描述

圆柱体 Cylinder

使用了osg::Cylinder结合osg::ShapeDrawable实现,也是通过osg::Geometry实现了上下圆形描边+分段线描边。
另一个重点是,Cylinder的实现最开始我是尝试的osgEarth::CircleNode,也可以达到圆柱的效果,但是没办法自己控制分段线的条数,也不能直接根据Matrix修改姿态,高度也是“挤出”形式,也是为了统一内部实现,改为了现在的osg::Cylinder,虽然分段线多一点点实现过程,但效果会更好。
注意中心点在高度的1/2处,所以如果要控制中心点在底部,可以通过让osgEarth::GeoTransform->addChild(),加入一个Relative的osg::MatrixTransform去偏移到底部。
在这里插入图片描述

四棱锥 Pyramid

这个图形,可以说是“纯手工”绘制,因为图形和描边线,都是用的osg::Geometry实现,osg::Geometry通过addPrimitiveSet两个顶点数组索引,完成实现。
最开始没有理解到osg::Geometry的精髓,使用了两个osg::Geometry分别实现三角面和连线,事实上addPrimitiveSet已经给出了解决方案,已经把图形组的接口给抽象好了。
重要代码:

	{
		osg::ref_ptr<osg::DrawElementsUInt> quad = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP, 0);//指定绘图基元为绘制多段三角形
		quad->push_back(0); quad->push_back(1); quad->push_back(2);
		quad->push_back(3); quad->push_back(0); quad->push_back(4);
		quad->push_back(1); quad->push_back(4); quad->push_back(2); quad->push_back(3);
		m_pGeometry->addPrimitiveSet(quad);//添加到几何体
	}


	{
		osg::ref_ptr<osg::DrawElementsUInt> quad = new osg::DrawElementsUInt(osg::PrimitiveSet::LINE_LOOP, 0);//指定绘图基元为绘制闭合线
		quad->push_back(0); quad->push_back(1); quad->push_back(2);
		quad->push_back(3); quad->push_back(4); quad->push_back(0); 
		quad->push_back(4); quad->push_back(1); quad->push_back(2); quad->push_back(4); quad->push_back(3);
		m_pGeometry->addPrimitiveSet(quad);//添加到几何体
	}

	// Color
	m_cSurface = Color::White;
	m_cLine = Color(0, 0, 0, 0);
	m_vcColor = new osg::Vec4Array;
	m_vcColor->push_back(m_cSurface);
	m_vcColor->push_back(m_cLine);
	m_pGeometry->setColorArray(m_vcColor, osg::Array::BIND_PER_PRIMITIVE_SET);
	m_pGeometry->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
	m_pGeometry->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);

void CPyramidObject3D::Rebuild()
{
	if (NULL == m_vcGeom)
	{
		return;
	}

	m_vcGeom->clear();

	m_vcGeom->push_back(osg::Vec3d(-m_dX/2, -m_dY/2, 0.0));
	m_vcGeom->push_back(osg::Vec3d(-m_dX/2, m_dY/2, 0.0));
	m_vcGeom->push_back(osg::Vec3d(m_dX/2, m_dY/2, 0.0f));
	m_vcGeom->push_back(osg::Vec3d(m_dX/2, -m_dY/2, 0.0));
	m_vcGeom->push_back(osg::Vec3d(0.0, 0.0, m_dH));

	m_pGeometry->setVertexArray(m_vcGeom);
}

球体 Sphere

使用了osg::Sphere结合osg::ShapeDrawable实现,也是通过osg::Geometry实现了横竖的分段线绘制。主要是对于球体,需要有分段线才能体现立体感,否则和2D圆形的区别在某些角度就不明显了。
在这里插入图片描述


源码示例

3D图形的实现源码之前分享过一次,这次为了描边线和姿态又尝试修改了一些实现,具体的环境搭建,可以看上一篇文章。
有一些实现不足的多多指教,对于OsgEarth的学习也在摸索中。

  1. 封装基于Osg+OsgEarth3实现的3D基础图元类,每个类提供各个图元的基础参数设置。
  2. 封装的图元类:PolygonCubeObject3D(任意立方体)、CylinderObject3DEx(圆柱)、SphereObject3D(球体)、ConeObject3D(圆锥)、PyramidObject3D(四棱锥)、BoxObject3D(矩形立方体)。
  3. OsgEarthMapViewer内包含响应按钮事件(hand函数),以动态修改图元属性的测试。注意测试指定图元属性修改时,需要打开指定handle的注释,并对应switch内的按键进行操作。
  4. 建议自行建立工程后,编译源码后进行测试(内含main.cpp),随时修改以及时看到变化情况,了解各个参数对绘制的影响。
    (相比上面的2D图元绘制的代码,3D图元绘制的资源内,封装了对绘制属性的设置修改,即封装成类,提供到接口操作)
    在这里插入图片描述

这里提供ElementGeometry和Cone的示例实现,其它的就是依葫芦画瓢了。

ElementGeometry

为了其它图元内部更好地实现描边线而进行的封装

#ifndef ElementGeometry_h__
#define ElementGeometry_h__

#include <osg/Node>
#include <osgEarth/SpatialReference>
#include <osgEarth/Color>
#include <osgEarth/GeoTransform>
#include <osg/Geometry>
#include <osg/LineWidth>

class CElementGeometry
{
public:
	CElementGeometry();
	~CElementGeometry();

	osg::ref_ptr<osg::Geometry> GetGeometry() const;

	void SetVertexArray(osg::Vec3Array* array);

	void AddPrimitiveSet(osg::PrimitiveSet* quad);

	void RemovePrimitiveSet(int nIndex);

	void SetColor(const osgEarth::Color& c);

	void SetColorArray(osg::Array* array, osg::Array::Binding binding);

	void SetWidth(float fWidth);

private:

	osg::ref_ptr<osg::Geometry> m_pGeom;
	osg::ref_ptr<osg::LineWidth> m_pLineWidth;
};

#endif // ElementGeometry_h__

#include "ElementGeometry.h"

CElementGeometry::CElementGeometry()
{
	m_pGeom = new osg::Geometry;
	m_pGeom->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
	m_pGeom->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
}

CElementGeometry::~CElementGeometry()
{
	m_pGeom = NULL;
}

osg::ref_ptr<osg::Geometry> CElementGeometry::GetGeometry() const
{
	return m_pGeom;
}

void CElementGeometry::SetVertexArray(osg::Vec3Array* array)
{
	m_pGeom->setVertexArray(array);
}

void CElementGeometry::AddPrimitiveSet(osg::PrimitiveSet* quad)
{
	m_pGeom->addPrimitiveSet(quad);
}

void CElementGeometry::RemovePrimitiveSet(int nIndex)
{
	m_pGeom->removePrimitiveSet(nIndex);
}

void CElementGeometry::SetColor(const osgEarth::Color& c)
{
	osg::Vec4Array* vecColorSurface = new osg::Vec4Array;
	vecColorSurface->push_back(c);
	m_pGeom->setColorArray(vecColorSurface, osg::Array::BIND_OVERALL);
}

void CElementGeometry::SetColorArray(osg::Array* array, osg::Array::Binding binding)
{
	m_pGeom->setColorArray(array, binding);
}

void CElementGeometry::SetWidth(float fWidth)
{
	if (fWidth > 0.0)
	{
		if (!m_pLineWidth.valid())
		{
			m_pLineWidth = new osg::LineWidth(1.0f);
			m_pGeom->getOrCreateStateSet()->setAttribute(m_pLineWidth, osg::StateAttribute::OFF);
		}

		m_pLineWidth->setWidth(fWidth);
		m_pGeom->setNodeMask(0xff);
	}
	else
	{
		m_pGeom->setNodeMask(0);
	}

	m_pGeom->dirtyDisplayList();
}


圆锥Cone

#ifndef ConeObject3D_h__
#define ConeObject3D_h__

#include <osg/Node>
#include <osg/ShapeDrawable>
#include <osg/Shape>
#include <osgEarth/SpatialReference>
#include <osgEarth/Color>
#include <osgEarth/GeoTransform>

class CElementGeometry;
class CConeObject3D
{
public:
	CConeObject3D(const osgEarth::SpatialReference* mapSRS);
	~CConeObject3D();

	osg::ref_ptr<osg::Node> GetObjectNode() const;

	void SetCenter(const osg::Vec3d& vPt);

	void SetRadius(double dRadiusMeter);

	void SetHeight(double dHeightMeter);

	void SetPosture(const osg::Vec3d& vPos);

	void SetFillColor(const osgEarth::Color& c);

	void SetProfileColor(const osgEarth::Color& c);

	void SetProfileWidth(float dWidth);

	void SetSegmentation(int nNum);

private:

	void BuildProfile(double dRadius, double dHeight, int nNum);

private:
	const osgEarth::SpatialReference* m_pSRS;

	osg::ref_ptr<osgEarth::GeoTransform> m_pGeoTransform;
	osg::ref_ptr<osg::MatrixTransform> m_pMatrix;
	osg::ref_ptr<osg::MatrixTransform> m_pRelativeMatrix;
	osg::Vec3d m_vPosture;

	osg::ShapeDrawable* m_pDrawable;
	osg::Cone* m_pCone;

	double m_dRadius;
	double m_dHeight;
	osgEarth::Color m_lineColor;
	float m_fLineWidth;

	std::vector<CElementGeometry*> m_vecGeometry;
};

#endif // ConeObject3D_h__

#include "ConeObject3D.h"
#include "osg/Math"
#include "ElementGeometry.h"

using namespace osgEarth;

CConeObject3D::CConeObject3D(const osgEarth::SpatialReference* mapSRS)
	: m_pSRS(mapSRS)
	, m_dRadius(0.0)
	, m_dHeight(0.0)
	, m_fLineWidth(0.0)
{
	m_pCone = new osg::Cone();

	m_pDrawable = new osg::ShapeDrawable(m_pCone);
	m_pDrawable->getOrCreateStateSet()->setMode(GL_BLEND, osg::StateAttribute::ON);
	m_pDrawable->getOrCreateStateSet()->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);

	m_pGeoTransform = new osgEarth::GeoTransform();
	m_pMatrix = new osg::MatrixTransform;
	m_pRelativeMatrix = new osg::MatrixTransform;
	m_pGeoTransform->addChild(m_pMatrix);
	m_pMatrix->addChild(m_pDrawable);
	m_pMatrix->addChild(m_pRelativeMatrix);

	SetRadius(5000);
	SetHeight(1000);
}

CConeObject3D::~CConeObject3D()
{
	SetSegmentation(-1);
	m_pGeoTransform = NULL;
}

osg::ref_ptr<osg::Node> CConeObject3D::GetObjectNode() const
{
	return m_pGeoTransform;
}

void CConeObject3D::SetCenter(const osg::Vec3d& vPt)
{
	m_pGeoTransform->setPosition(osgEarth::GeoPoint(m_pSRS, vPt, osgEarth::ALTMODE_RELATIVE));
}

void CConeObject3D::SetRadius(double dRadiusMeter)
{
	m_pCone->setRadius(dRadiusMeter);

	m_pDrawable->dirtyDisplayList();
	m_pGeoTransform->dirtyBound();

	m_dRadius = dRadiusMeter;

	SetSegmentation(m_vecGeometry.size() - 1);
}

void CConeObject3D::SetHeight(double dHeightMeter)
{
	m_dHeight = dHeightMeter;
	m_pCone->setHeight(dHeightMeter);

	// 原始中心点在图形中间 h的1/4处
	m_pRelativeMatrix->setMatrix(
		osg::Matrix::rotate(0.0, osg::X_AXIS,
			0.0, osg::Y_AXIS,
			0.0, osg::Z_AXIS)
		*osg::Matrix::translate(osg::Vec3d(0, 0, -dHeightMeter / 4)));
	SetPosture(m_vPosture);

	m_pDrawable->dirtyDisplayList();
	m_pGeoTransform->dirtyBound();


	SetSegmentation(m_vecGeometry.size() - 1);
}

void CConeObject3D::SetPosture(const osg::Vec3d& vPos)
{
	m_vPosture = vPos;

	m_pMatrix->setMatrix(
		osg::Matrix::rotate(
			osg::DegreesToRadians(vPos.x()), osg::X_AXIS,
			osg::DegreesToRadians(vPos.y()), osg::Y_AXIS,
			osg::DegreesToRadians(vPos.z()), osg::Z_AXIS)
		*osg::Matrix::translate(osg::Vec3d(0, 0, m_dHeight / 4)));
}

void CConeObject3D::SetFillColor(const osgEarth::Color& c)
{
	m_pDrawable->setColor(c);
}

void CConeObject3D::SetProfileColor(const osgEarth::Color& c)
{
	for (int i = 0; i < m_vecGeometry.size(); i++)
	{
		m_vecGeometry[i]->SetColor(c);
	}
	m_lineColor = c;
}

void CConeObject3D::SetProfileWidth(float dWidth)
{
	for (int i = 0; i < m_vecGeometry.size(); i++)
	{
		m_vecGeometry[i]->SetWidth(dWidth);
	}
	m_fLineWidth = dWidth;
}

void CConeObject3D::SetSegmentation(int nNum)
{
	for (int i = 0; i < m_vecGeometry.size(); i++)
	{
		m_pRelativeMatrix->removeChild(m_vecGeometry[i]->GetGeometry());
	}
	m_vecGeometry.clear();

	BuildProfile(m_dRadius, m_dHeight, nNum);
	SetProfileWidth(m_fLineWidth);
	SetProfileColor(m_lineColor);
}

// ===== Private

void CConeObject3D::BuildProfile(double dRadius, double dHeight, int nNum)
{
	if (nNum < 0)
	{
		return;
	}

	// circle
	{
		osg::ref_ptr<osg::Vec3Array> vcArray = new osg::Vec3Array;
		int n = pow(2.0, 7);
		double dAngle = 360.0 / n;
		for (int i = 0; i < n; i++)
		{
			double dTmpAngle = dAngle * i;
			double dRad = dTmpAngle * (osg::PI / 180);

			double dX = dRadius * cos(dRad);
			double dY = dRadius * sin(dRad);
			vcArray->push_back(osg::Vec3d(dX, dY, 0.0));
		}

		CElementGeometry* pElement = new CElementGeometry;
		pElement->SetVertexArray(vcArray);
		pElement->AddPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_LOOP, 0, vcArray->size()));
		m_vecGeometry.push_back(pElement);
		m_pRelativeMatrix->addChild(pElement->GetGeometry());
	}

	// lines
	if (nNum > 0)
	{
		double dAngle = 360.0 / nNum;
		for (int i = 0; i < nNum; i++)
		{
			double dTmpAngle = dAngle * i;
			double dRad = dTmpAngle * (osg::PI / 180);
			double dX = dRadius * cos(dRad);
			double dY = dRadius * sin(dRad);

			osg::ref_ptr<osg::Vec3Array> vcArray = new osg::Vec3Array;
			vcArray->push_back(osg::Vec3d(0.0, 0.0, dHeight));
			vcArray->push_back(osg::Vec3d(dX, dY, 0.0));

			CElementGeometry* pElement = new CElementGeometry;
			pElement->SetVertexArray(vcArray);
			pElement->AddPrimitiveSet(new osg::DrawArrays(osg::PrimitiveSet::LINE_STRIP, 0, vcArray->size()));
			m_vecGeometry.push_back(pElement);
			m_pRelativeMatrix->addChild(pElement->GetGeometry());
		}
	}
}


  • 7
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值