Func 3: osg 绘制太阳系恒星及其公转轨道(包括地月系、星球纹理)

/*
***本项目包含第三天和第四天的大作业

***第三天 大作业内容
	模拟太阳系,绘制十个球体,包括太阳、八大行星和月球,为八大行星和月
球绘制公转轨道线。
    要求:
    1) 球体、轨道线用使用图元绘制,不能直接调用 OSG 提供的球、线图
    形方法;
    2) 对于无法求出球体顶点坐标的学习者,可以参照《绘制球体》文档
    3) 实现时尽可能满足 C++封装的特性,给出包括公转半径、球体半径、
    公转自转速度、纹理路径等基本信息,输出星球的节点指针。
    4) 基于封装的生成球体类或函数,在木星和火星之间创建一百个球体
    半径大小随机的小行星,形成小行星带,无须尾迹和纹理,但要求小行星分
    布均匀,公转速度一直,球体半径不同。

***第四天 大作业内容
	基于上次的大作业,给各个星球贴上纹理,修改大作业中球体轨道颜色、线
宽、线型,此次作业无需动态修改图元属性,只需展示图元的纹理和颜色等属性
即可。
	要求:
	1) 纹理坐标需要手动设置,不能调用 OSG 提供的自动生成纹理坐标功能;
*/

/**
@brief : 函数:创建轨道线
@param : [in] center 中心点
@param : [in] radius 半径
@param : [in] segments 拆分线数
@return:轨迹线 osg::ref_ptr<osg::Geometry>
*/
osg::ref_ptr<osg::Geometry> createOrbit(const osg::Vec3& center, float radius, int segments, const osg::Vec4& color) {

	osg::ref_ptr<osg::Geometry> geometry = new osg::Geometry();
	osg::ref_ptr<osg::Vec3Array> vertices = new osg::Vec3Array();

	for (int i = 0; i < segments; ++i) {
		float angle = 2 * osg::PI * i / segments;
		float x = center.x() + radius * cos(angle);
		float y = center.y() + radius * sin(angle);
		float z = center.z();
		vertices->push_back(osg::Vec3(x, y, z));
	}

	geometry->setVertexArray(vertices);
	geometry->addPrimitiveSet(new osg::DrawArrays(GL_LINE_LOOP, 0, vertices->size()));

	osg::ref_ptr<osg::StateSet> stateSet = geometry->getOrCreateStateSet();
	osg::ref_ptr<osg::LineWidth> lineWidth = new osg::LineWidth(2.0f);
	stateSet->setAttributeAndModes(lineWidth, osg::StateAttribute::ON);
	stateSet->setMode(GL_BLEND, osg::StateAttribute::ON);
	stateSet->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
	osg::ref_ptr<osg::Material> material = new osg::Material;
	material->setAmbient(osg::Material::FRONT, color);
	material->setDiffuse(osg::Material::FRONT, color);
	stateSet->setAttribute(material);
	stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);

	geometry->setName("orbitGeometry"); // 确保名称正确设置
	return geometry.release();
}

/**
@brief : 创建一个球的几何节点
@param : [in] dRadius 球的半径
@param : [in] iHint 每180/iHint度设置一个点,默认18
@return: 一个球的osg::Geometry* 
*/
osg::Geometry* createSphereGeom(const osg::Vec3& center, double dRadius = 1.0, 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(
				center.x() + sin(osg::PI*i / iHint)*cos(osg::PI*j / iHint),
				center.y() + sin(osg::PI*i / iHint)*sin(osg::PI*j / iHint),
				center.z() + 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, std::string path)
{
	osg::ref_ptr<osg::Image> rpImage = osgDB::readImageFile(path);
	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方向的环绕模式

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

	//关闭光照确保纹理可见
	pState->setMode(GL_LIGHTING, osg::StateAttribute::OFF); // 禁用光照,以便纹理可见
}
/**
@brief : 创建包含轨道、轨道颜色、星球的Group
@param : [in] name 名字
@param : [in] texturePath 纹理路径
@param : [in] place 相对于太阳的位置
@param : [in] radius 半径
@param : [in] cenetr 轨道中心
@return:  osg::Group *
note:过多参数 用 结构体
*/
osg::Group * makePlanetGroup(std::string name, std::string texturePath, osg::Vec3 place, double radius, osg::Vec3 center = osg::Vec3(0, 0, 0))
{
	osg::ref_ptr<osg::MatrixTransform> planetTransform = new osg::MatrixTransform;
	planetTransform->setMatrix(osg::Matrix::translate(place));

	osg::ref_ptr<osg::Geode> planetGeode = new osg::Geode;
	planetGeode->setName(name);
	planetTransform->setName(name);

	// 创建星球球体
	osg::ref_ptr<osg::Geometry> planetGeometry = createSphereGeom(osg::Vec3(0, 0, 0), radius, 45);
	planetGeometry->setName(name);
	planetGeode->addChild(planetGeometry);

	//当是地球时,添加月球
	if (name == "earth")
	{

		planetTransform->addChild(makePlanetGroup("moon", R"(E:\xx\newOSG\Data\Star\moon.jpg)", osg::Vec3(6, 0, 0), 0.3));
	}


	// 设置星球纹理
	setTex(planetGeode, texturePath); // 在球上贴纹理
	planetTransform->addChild(planetGeode);

	// 轨道设置
	osg::ref_ptr<osg::Geode> orbitGeode = new osg::Geode;
	orbitGeode->setName(name.append("orbitGeometry"));
	osg::ref_ptr<osg::Geometry> orbitGeometry = createOrbit(center, place.x() - center.x() < 0 ? center.x() - place.x() : place.x() - center.x(), 50, osg::Vec4(0.5f, 0.7f, 0.8f, 1.f));
	orbitGeode->addChild(orbitGeometry);

	// 默认不显示
	osg::ref_ptr<osg::Switch> orbitControl = new osg::Switch();
	orbitControl->setAllChildrenOff();
	orbitControl->setName(name.append("0rbit"));
	orbitControl->addChild(orbitGeode);

	// 添加到星球组节点
	osg::ref_ptr<osg::Group> planetGroup = new osg::Group;
	planetGroup->setName(name);
	planetGroup->addChild(planetTransform);
	planetGroup->addChild(orbitControl);

	return planetGroup.release();
}

/**
@brief : 在离圆心(太阳中心点,即(0,0,0)点)nearadius到farradius 随机生成100个陨石
@param : [in] nearadius 近轨道
@param : [in] farradius 远轨道
@return: 组节点
*/
osg::ref_ptr<osg::Group> makeStonesBetween(float nearadius, float farradius)
{

	// 创建一个组来容纳所有的陨石
	osg::ref_ptr<osg::Group> stonesGroup = new osg::Group();

	// 设置随机数种子
	srand(static_cast<unsigned int>(time(nullptr)));

	// 生成100个陨石
	for (int i = 0; i < 100; ++i) {
		// 随机生成石头的半径
		float radius = static_cast<float>(nearadius + (farradius - nearadius) * (rand() / static_cast<float>(RAND_MAX)));

		// 随机生成石头的角度
		float angle = static_cast<float>(rand() / static_cast<float>(RAND_MAX) * 2 * osg::PI);

		// 计算石头的x和y坐标
		float x = radius * cos(angle);
		float y = radius * sin(angle);
		float z = 0.0f; // z坐标固定为0

						
		std::random_device rd;  // 用于获取随机数种子
		std::mt19937 gen(rd()); // 伪随机数生成器
		std::uniform_real_distribution<> dis(0.1, 0.5); // 定义分布范围
		float radiusst = dis(gen);// 生成随机浮点数

		// 将陨石节点添加到组中
		stonesGroup->addChild(makePlanetGroup("stones" + std::to_string(i), R"(E:\xx\newOSG\Data\Star\moon.jpg)", osg::Vec3(x, y, z), radiusst));
	}

	return stonesGroup;
}

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::Group> sunGroup = new osg::Group;
	sunGroup->setName("sun");
	// 新建太阳
	osg::ref_ptr<osg::Geode> sun = new osg::Geode;
	sun->setName("sun");
	sun->addChild(createSphereGeom(osg::Vec3(0, 0, 0), 10., 45));
	setTex(sun, R"(E:\xx\newOSG\Data\Star\sun.jpg)");//在球上贴纹理

														   //创建太阳的matrixTransform
	osg::ref_ptr<osg::MatrixTransform> earthTransform = new osg::MatrixTransform();
	earthTransform->setName("sun");
	earthTransform->addChild(sun);
	sunGroup->addChild(earthTransform);

	// 将行星的变换节点添加到太阳节点中
	sunGroup->addChild(makePlanetGroup("venus", R"(E:\xx\newOSG\Data\Star\venus.jpg)", osg::Vec3(25, 0, 0), 2.));

	sunGroup->addChild(makePlanetGroup("mercury", R"(E:\xx\newOSG\Data\Star\mercury.jpg)", osg::Vec3(15, 0, 0), 0.6));

	sunGroup->addChild(makePlanetGroup("earth", R"(E:\xx\newOSG\Data\Star\earth.jpg)", osg::Vec3(45, 0, 0), 4));

	sunGroup->addChild(makePlanetGroup("mars", R"(E:\xx\newOSG\Data\Star\mars.jpg)", osg::Vec3(60, 0, 0), 1.3));

	sunGroup->addChild(makePlanetGroup("jupiter", R"(E:\xx\newOSG\Data\Star\jupiter.jpg)", osg::Vec3(80, 0, 0), 1.5));

	sunGroup->addChild(makePlanetGroup("saturn", R"(E:\xx\newOSG\Data\Star\saturn.jpg)", osg::Vec3(90, 0, 0), 1.6));

	sunGroup->addChild(makePlanetGroup("uranus", R"(E:\xx\newOSG\Data\Star\uranus.jpg)", osg::Vec3(96, 0, 0), 1.7));

	sunGroup->addChild(makePlanetGroup("neptune", R"(E:\xx\newOSG\Data\Star\neptune.jpg)", osg::Vec3(107, 0, 0), 1.8));

	sunGroup->addChild(makeStonesBetween(65.f, 75.f));

	//将太阳系节点添加到根节点
	rpRoot->addChild(sunGroup);

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

	rpViewer->run();
	return 0;
	
}

最后别忘记替换贴图路径哦!

osg blender func是OpenSceneGraph中的一个函数,用于设置材质混合函数。混合函数指定了绘制时两个物体像素的颜色是如何混合的。在使用osg blender func之前,我们通常需要先创建一个osg::StateSet对象,并将其与需要设置混合的节点或几何体相关联。 osg blender func函数接受两个参数:osg::StateAttribute::Values source和osg::StateAttribute::Values destination。source参数表示源颜色的颜色值将如何与目标颜色的颜色值混合,destination参数表示目标颜色的颜色值将如何混合。 source参数和destination参数可以接受以下值之一: - osg::StateAttribute::ZERO:表示使用0.0作为混合因子。 - osg::StateAttribute::ONE:表示使用1.0作为混合因子。 - osg::StateAttribute::SRC_COLOR:表示使用源颜色的颜色值作为混合因子。 - osg::StateAttribute::ONE_MINUS_SRC_COLOR:表示使用1.0减去源颜色的颜色值作为混合因子。 - osg::StateAttribute::DST_COLOR:表示使用目标颜色的颜色值作为混合因子。 - osg::StateAttribute::ONE_MINUS_DST_COLOR:表示使用1.0减去目标颜色的颜色值作为混合因子。 - osg::StateAttribute::SRC_ALPHA:表示使用源颜色的透明度值作为混合因子。 - osg::StateAttribute::ONE_MINUS_SRC_ALPHA:表示使用1.0减去源颜色的透明度值作为混合因子。 - osg::StateAttribute::DST_ALPHA:表示使用目标颜色的透明度值作为混合因子。 - osg::StateAttribute::ONE_MINUS_DST_ALPHA:表示使用1.0减去目标颜色的透明度值作为混合因子。 通过使用不同的混合函数,我们可以实现各种效果,例如混合透明度、加法混合、减法混合等。osg blender func函数提供了一种简单而灵活的方法来控制渲染的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值