osg純手工畫球+貼紋理


手動計算球面頂點的坐標,紋理坐標,來畫球並貼紋理

其中createSphereGeom()函數的的二個參數為18,意思是在經緯度上每10度設一個點,因為經度一共是180度,180/18=10,相當於橫着把球分為18份,每一份相當於一個圓盤,然后再把圓盤分為18*2=36份,每一份相當於一個扇形。

由於要貼紋理,所以在實現的時候,最上面的一個頂點是36+1=37個頂點重合了,最下面的一個頂點一樣,中間每一圈的最開始的頂點和最后一個頂點重合,每一圈都有36+1=37個頂點,總共有(36+1)*(18+1)=703個頂點。

為什么每一圈是37個頂點而不是36,其實如果不貼紋理就只是畫球,那36個頂點就可以了,但是紋理是一張矩形的圖片,你要把一個矩形上的每個點映射到球面上,那每一圈就需要多一個點。

如下圖,把一張紙分為4份,上面有5個點,再把這張紙卷成圓筒,最左邊和最右邊的點重合了,看起來就只有4個點了。當然如果不需要貼紋理,那就不用重合的點了。

紋理的坐標原點是在左下角,為了和物體位置的坐標xyz區分,紋理坐標叫做str,s相當於x,t相當y。s是橫坐標,t是縱坐標。

下面看完整的代碼,想要運行,你需要安裝好osg

#include <math.h>

#include <osg/Geode>
#include <osg/Group>
#include <osg/Vec3>
#include <osg/Geometry>
#include <osg/ref_ptr>
#include <osg/Vec2>
#include <osg/Texture2D>

#include <osgDB/ReadFile>

#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>

#include <osgGA/StateSetManipulator>

/**
@brief : 創建一個球的幾何節點
@param : [in] dRadius 球的半徑
@param : [in] iHint 每180/iHint度設置一個點,默認18
@return: 一個球的osg::Geometry*
*/
osg::Geometry* createSphereGeom(double dRadius = 1., int iHint = 18)
{
	osg::ref_ptr<osg::Geometry>  rpGeom     = new osg::Geometry;
	osg::ref_ptr<osg::Vec3Array> rpVertexes = new osg::Vec3Array;//頂點數組
	osg::ref_ptr<osg::Vec3Array> rpNormal   = new osg::Vec3Array;//法線數組
	osg::ref_ptr<osg::Vec2Array> rpTexCoord = new osg::Vec2Array;//紋理數組

	for (int i = 0; i <= iHint; i++)//從上到下添加點,有等於號是為了之后貼紋理時可以和圖片的點一一對應
	{
		for (int j = 0; j <= iHint * 2; j++)//逆時針添加點
		{
			osg::Vec3 vec3VertexT(
				sin(osg::PI*i / iHint)*cos(osg::PI*j / iHint),
				sin(osg::PI*i / iHint)*sin(osg::PI*j / iHint),
				cos(osg::PI*i / iHint));//球面坐標公式
			rpVertexes->push_back(vec3VertexT * dRadius);//添加頂點
			rpNormal->push_back(vec3VertexT);//添加法線
			rpTexCoord->push_back(osg::Vec2(double(j) / 2.0 / iHint
				, 1 - double(i) / iHint));//添加紋理坐標
		}
	}

	osg::ref_ptr<osg::Vec4Array> rpColors = new osg::Vec4Array;
	rpColors->push_back(osg::Vec4(1.0, 1.0, 1.0, 1.0));

	rpGeom->setVertexArray(rpVertexes);
	rpGeom->setNormalArray(rpNormal);
	rpGeom->setNormalBinding(osg::Geometry::BIND_PER_VERTEX);
	rpGeom->setColorArray(rpColors);
	rpGeom->setColorBinding(osg::Geometry::BIND_OVERALL);
	rpGeom->setTexCoordArray(0, rpTexCoord);

	//添加圖元
	osg::ref_ptr<osg::DrawElementsUInt> rpFace = new osg::DrawElementsUInt(osg::PrimitiveSet::TRIANGLE_STRIP);
	for (int i = 0; i < iHint; i++) {
		for (int j = 0; j <= iHint * 2; j++) {
			rpFace->push_back(i*(iHint * 2 + 1) + j);
			rpFace->push_back((i + 1)*(iHint * 2 + 1) + j);
		}
	}
	rpGeom->addPrimitiveSet(rpFace);

	return rpGeom.release();
}

/**
@brief : 給球貼紋理
@param : [in/out] pNode 球的節點
@return: 
*/
void setTex(osg::Node* pNode)
{
	osg::ref_ptr<osg::Image> rpImage = osgDB::readImageFile("../star/moon.jpg");
	osg::ref_ptr<osg::Texture2D> rpTexture = new osg::Texture2D();

	rpTexture->setImage(rpImage);
	rpTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);//設置S方向的環繞模式
	rpTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);//設置R方向的環繞模式
	//rpTextureT->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR_MIPMAP_NEAREST);
	//rpTextureT->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);

	osg::ref_ptr<osg::StateSet> pState = pNode->getOrCreateStateSet();
	pState->setTextureAttributeAndModes(0, rpTexture, osg::StateAttribute::ON);
}

int main(int argc, char** argv)
{
	osg::ref_ptr<osgViewer::Viewer> rpViewer = new osgViewer::Viewer();
	osg::ref_ptr<osg::Group> rpRoot = new osg::Group();

	osg::ref_ptr<osg::Geode> rpGeode = new osg::Geode;
	rpGeode->addChild(createSphereGeom(1., 45));
	setTex(rpGeode);//在球上貼紋理

	rpRoot->addChild(rpGeode);
	rpViewer->setSceneData(rpRoot);

	rpViewer->addEventHandler(new osgGA::StateSetManipulator(rpViewer->getCamera()->getOrCreateStateSet()));//添加w事件處理
	rpViewer->addEventHandler(new osgViewer::StatsHandler);//添加s事件處理

	rpViewer->run();
	return 0;
}

程序運行截圖

未貼紋理的球:

隱藏表面,只顯示線的球,即按w鍵后的球:

只顯示點的球:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM