这是根据视频整理的项目,视频中的讲解有一点小问题,所有作了如下整理,视频链接是:球体
一、编程环境
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。