手動計算球面頂點的坐標,紋理坐標,來畫球並貼紋理
其中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鍵后的球:

只顯示點的球:

