OpenGL自己编码画球体——网格入门小例子

4 篇文章 0 订阅

这是根据视频整理的项目,视频中的讲解有一点小问题,所有作了如下整理,视频链接是:球体

一、编程环境

     Win 10 + VS 2013 +OpenGL

     采用C语言进行编写

二、结构体定义以及全局变量

enum Ref_Plain { XOY, YOZ, XOZ };

struct Vertex{
	float x, y, z;
};

struct HalfQuarterSphere{
	Vertex topVertex;// 1/8圆的穹顶点
	Vertex **pVertexs;//其余顶点
	int numCircles;//纬线数量
	int numLine;//经线数量,连接南北两极
	float radius;//半径
};

HalfQuarterSphere hqSphere;

三、相关函数

void generateHalfQuarterSphere(int numCir/*纬线数*/, int numLin/*经线数*/, HalfQuarterSphere &hqSphere, float R){//生成1/8球体所有的顶点
	hqSphere.numCircles = numCir;
	hqSphere.numLine = numLin;
	hqSphere.radius = R;
	hqSphere.topVertex.z = R;
	hqSphere.topVertex.x = hqSphere.topVertex.y = 0;
	//申请内存块
	hqSphere.pVertexs = new Vertex*[numCir];
	for (int i = 0; i < numCir; i++){
		hqSphere.pVertexs[i] = new Vertex[numLin];
	}
	float cirAngleSegment = HALF_PI / (numLin - 1);//每道纬线的间距
	float cirAngleOffset = 0;
	float zOffset = 0;
	float angleOffset = 0;
	float linAngleSegment = HALF_PI / (numCir - 1);//每道相邻经线在XOY平面投影线的夹角


	//初始化纬线与XOZ平面的交点
	float curR = 0;
	int boundLine = numLin - 1;
	for (int i = numCir - 1; i >= 0; i--){
		zOffset = R*sin(angleOffset);
		//初始化当前纬线与始经线,末经线的交点
		curR = hqSphere.pVertexs[i][0].x = hqSphere.pVertexs[i][boundLine].y = sqrt(R*R - zOffset*zOffset);
		hqSphere.pVertexs[i][0].y = hqSphere.pVertexs[i][boundLine].x = 0;
		hqSphere.pVertexs[i][0].z = hqSphere.pVertexs[i][boundLine].z = zOffset;
		cirAngleOffset = cirAngleSegment;
		//初始化当前纬线与其余经线的交点
		for(int j = 1; j < boundLine; j++){
			hqSphere.pVertexs[i][j].x = curR*cos(cirAngleOffset);
			hqSphere.pVertexs[i][j].y = curR*sin(cirAngleOffset);
			hqSphere.pVertexs[i][j].z = zOffset;
			cirAngleOffset += cirAngleSegment;
		}
		angleOffset += linAngleSegment;
	}
}

        

        cirAngleSegment = HALF_PI / (numLin - 1);// 1/8球体中任意一条纬线的两端点与球心构成的角度是90度(如∠YOX),                                      //numLin是经线条数,由于纬线与每一条经线都有交点,因此这个计算结果就是两相邻交点与球心构成的角                                 //度(如∠BOX),这个地方一定要减1。如OY,OB,OX是三条经线,与纬线XBY只有两个夹角

        linAngleSegment = HALF_PI / (numCir - 1);//与上面同理,是经线上两相邻交点与球心之间的夹角,穹点(NS极)也算一                                  //条纬线,上图有三条纬线,Z,AC所在平面的纬线,XBY。 

        左图中,λ是纬线之间的夹角,即cirAngleSegment,φ是经线之间的夹角,即linAngleSegment。右图中可以看到经线有10条,纬线有6条。左图中的1/8半球有三条经线,三条纬线,所以在代码中的numCir,numLin均为3,由于数组下标从0开始,因此存储所有顶点的二位数组hqSphere.pVertexs的下标从一开始,第一维坐标为纬线,第二维为经线,最后一个点的下标为[2,2]。

         代码解释:

        Step1:

         for (int i = numCir - 1; i >= 0; i--){} //该重for循环是为了依次求出纬线i上与经线的交点坐标。i的初值为 numCir - 1,因此是                           //从后往前求解,即XBY(hqSphere.pVertexs[2][...])                                                                                                                           //AC所在平面的纬线(hqSphere.pVertexs[1][...]),      Z(hqSphere.pVertexs[0][...])。

        Step2:

         zOffset = R*sin(angleOffset);

         angleOffset += linAngleSegment;

         angleOffset初值为0,后来的增量为linAngleSegment,即角度φ,从左图中可以看出R*sinφ是平行于Z轴的分量,即Z坐标,如OC*sinφ=BC(OC为半径,长度为R),故点C的Z坐标为OC*sinφ。即球面上任意一点的Z坐标均为zOffset。

  Step3:

        curR = hqSphere.pVertexs[i][0].x = hqSphere.pVertexs[i][boundLine].y = sqrt(R*R - zOffset*zOffset);
        hqSphere.pVertexs[i][0].y = hqSphere.pVertexs[i][boundLine].x = 0;
        hqSphere.pVertexs[i][0].z = hqSphere.pVertexs[i][boundLine].z = zOffset;

        hqSphere.pVertexs[i][0] 即纬线i与经线ZX的交点(因此y坐标为0),hqSphere.pVertexs[i][boundLine]  即纬线i与经线ZY的交点(因此x坐标为0),根据Step1可知,球面上任意一点的Z坐标均为zOffset。sqrt(R*R - zOffset*zOffset)为XOY平面上的投影长度,如sqrt(OC*OC-CB*CB)=OB,我们把点C移动到纬线i与经线ZX的交点上,就会发现sqrt(OC*OC-CB*CB)是hqSphere.pVertexs[i][0] 的X坐标(点B落在X轴上),而将点C移动到纬线i与经线ZY的交点上,就会发现sqrt(OC*OC-CB*CB)是hqSphere.pVertexs[i][boundLine] 的Y坐标(点B落在Y轴上)。

       Step4:

       cirAngleOffset = cirAngleSegment;
        //初始化当前纬线与其余经线的交点
        for(int j = 1; j < boundLine; j++){
            hqSphere.pVertexs[i][j].x = curR*cos(cirAngleOffset);
            hqSphere.pVertexs[i][j].y = curR*sin(cirAngleOffset);
            hqSphere.pVertexs[i][j].z = zOffset;
            cirAngleOffset += cirAngleSegment;
        }

        cirAngleOffset初值为λ,增量也为λ。因为纬线与经线ZX、ZY的交点都已经求出来了,所有for循环的初始化为j=1,循环条件为j<boundLine。由Step3可知curR是点在XOY平面上的投影长度,如OB,所有初始时OB*cosλ即为B的X坐标,OB*sinλ即为B的Y坐标(可以看左图或者自己重新画一个XOY的平面图)。之后cirAngleOffset增加λ,即纬线i与下一条经线的交点,在左图中即为与Y轴的交点(按左图设置参数会跳出循环)。

       在视频中Step2与Step4的变量设置有问题,经纬线的偏移角度设置的是错误的,此处一定要注意。

                                                     

void flipHalfQuarterSphere(HalfQuarterSphere &hqSphere, Ref_Plain rp){
	switch (rp){
	case XOY://Z坐标取反
		hqSphere.topVertex.z = -hqSphere.topVertex.z;
		for (int i = 0; i < hqSphere.numCircles; i++){
			for (int j = 0; j < hqSphere.numLine; j++){
				hqSphere.pVertexs[i][j].z = -hqSphere.pVertexs[i][j].z;
			}
		}
		break;
	case YOZ://X坐标取反
		hqSphere.topVertex.x = -hqSphere.topVertex.x;
		for (int i = 0; i < hqSphere.numCircles; i++){
			for (int j = 0; j < hqSphere.numLine; j++){
				hqSphere.pVertexs[i][j].x = -hqSphere.pVertexs[i][j].x;
			}
		}
		break;
	case XOZ://Y坐标取反
		hqSphere.topVertex.y = -hqSphere.topVertex.y;
		for (int i = 0; i < hqSphere.numCircles; i++){
			for (int j = 0; j < hqSphere.numLine; j++){
				hqSphere.pVertexs[i][j].y = -hqSphere.pVertexs[i][j].y;
			}
		}
		break;
	}
}

void renderHalfQuarterSphere(const HalfQuarterSphere &hqSphere){//绘制1/8球
	glBegin(GL_TRIANGLE_FAN);//以穹点为中心画三角形扇
	glNormal3f(hqSphere.topVertex.x, hqSphere.topVertex.y, hqSphere.topVertex.z);
	glVertex3f(hqSphere.topVertex.x, hqSphere.topVertex.y, hqSphere.topVertex.z);
	for (int i = 0; i < hqSphere.numLine; i++){
		glNormal3f(hqSphere.pVertexs[0][i].x, hqSphere.pVertexs[0][i].y, hqSphere.pVertexs[0][i].z);
		glVertex3f(hqSphere.pVertexs[0][i].x, hqSphere.pVertexs[0][i].y, hqSphere.pVertexs[0][i].z);
	}
	glEnd();

	for (int i = 1; i < hqSphere.numCircles; i++){//剩下的顶点
		glBegin(GL_TRIANGLE_STRIP);
		for (int j = 0; j < hqSphere.numLine; j++){
			glNormal3f(hqSphere.pVertexs[i - 1][j].x, hqSphere.pVertexs[i - 1][j].y, hqSphere.pVertexs[i - 1][j].z);
			glVertex3f(hqSphere.pVertexs[i - 1][j].x, hqSphere.pVertexs[i - 1][j].y, hqSphere.pVertexs[i - 1][j].z);
			glNormal3f(hqSphere.pVertexs[i][j].x, hqSphere.pVertexs[i][j].y, hqSphere.pVertexs[i][j].z);
			glVertex3f(hqSphere.pVertexs[i][j].x, hqSphere.pVertexs[i][j].y, hqSphere.pVertexs[i][j].z);
		}
		glEnd();
	}
}

void renderSphere(HalfQuarterSphere &hqSphere){
	renderHalfQuarterSphere(hqSphere);//X轴正向Y轴正向Z轴正向的1/8
	flipHalfQuarterSphere(hqSphere, YOZ);
	renderHalfQuarterSphere(hqSphere);//X轴负向Y轴正向Z轴正向的1/8
	flipHalfQuarterSphere(hqSphere, XOZ);
	renderHalfQuarterSphere(hqSphere);//X轴负向Y轴负向Z轴正向的1/8
	flipHalfQuarterSphere(hqSphere, YOZ);
	renderHalfQuarterSphere(hqSphere);//X轴正向Y轴负向Z轴正向的1/8
	flipHalfQuarterSphere(hqSphere, XOY);
	renderHalfQuarterSphere(hqSphere);//X轴正向Y轴负向Z轴负向的1/8
	flipHalfQuarterSphere(hqSphere, XOZ);
	renderHalfQuarterSphere(hqSphere);//X轴正向Y轴正向Z轴负向的1/8
	flipHalfQuarterSphere(hqSphere, YOZ);
	renderHalfQuarterSphere(hqSphere);//X轴负向Y轴正向Z轴负向的1/8
	flipHalfQuarterSphere(hqSphere, XOZ);
	renderHalfQuarterSphere(hqSphere);//X轴负向Y轴负向Z轴负向的1/8
}

四、相关配置及源码

相关库文件配置请参考链接:配置OpenGL

源码:https://download.csdn.net/download/qq_33742119/11014712

本文为作者原创,转载请注明出处:https://blog.csdn.net/qq_33742119/article/details/88422059

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值