How to render sphere using open GL, c++ and not Glut/Glu
我一般来说对OpenGL和C还是比较陌生,我一直在研究一个自定义框架,要求我在其中渲染球体。我已经进行了研究,并设法生成了一个由点组成的球形。如果可能,我希望对其进行改造,这样我就可以制作一个由具有半径等属性的三角形制成的实际球体。我用来生成由点组成的球体的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | Mesh* Mesh::GenerateSphere() { const int Y_SEGMENT= 10; const int X_SEGMENT = 20; //const float count; Mesh*m = new Mesh(); m->numVertices = (X_SEGMENT+1)*(Y_SEGMENT+1); m->type = GL_POINTS; m->vertices = new Vector3[m->numVertices]; //s->GenerateTriangle(); for (int i = 0; i < Y_SEGMENT+1;i++) { float angleY = PI*i / Y_SEGMENT; float y = cos(angleY); float xz = sin(angleY); for (int j = 0; j < X_SEGMENT+1; j++) { float angleX = 2*PI*j / X_SEGMENT; float x = xz*cos(angleX); float z = xz*sin(angleX); Vector3 v(x,y,z); m->vertices[i * (X_SEGMENT+1)+j] = v; } } m->BufferData(); return m; } |
在我的渲染课程中,我被教导要把球想象成一个圆角的网格。因此,首先您要实现一个以x和y为维度1,以位置(0,0,0)为中心的网格的实现,该位置将被n行(rowMax)和m列(colMax)细分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | // Aux function inline int index (int i, int j) { return i + j*(m_colMax + 1); } float numCoords = 3*rowMax*colMax; // Array size float *coordData = new float[numCoords]; // Array with coordinate positions // Fill coordinate positions [to change] for (int j = 0; j <= rowMax; j++) { for (int i = 0; i <= colMax; i++) { int k = index(i, j); coordData[k] = (float)i / m_colMax - (0.5f); coordData[k + 1] = (float)j / m_rowMax - (0.5f); coordData[k + 2] = 0; } } // Fill index int k = 0; GLuint *indexData = new GLuint[numCoords]; // Array with indexing data for (int j = 0; j < rowMax; j++) { for (int i = 0; i < colMax; i++) { indexData[k++] = index (i, j); indexData[k++] = index (i + 1, j + 1); indexData[k++] = index (i, j + 1); indexData[k++] = index (i, j); indexData[k++] = index (i + 1, j); indexData[k++] = index (i + 1, j + 1); } } |
对于这些数据,请记住使用DrawElements()和GL_TRIANGLES来考虑索引编制(第二个链接对此方法具有清晰的印象)。由于您是OpenGL新手,因此以下两个链接可以总结您需要学习的内容:
[1]使用不带着色器的OGL 2.1:http://www.songho.ca/opengl/gl_vertexarray.html
[2]使用更高级的技术(aka,带有Core / Compatibility Profile的OGL 3.3)http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-9-vbo-indexing/
现在,要执行所需的操作,只需在第一个for循环中更改代码即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 | // Fill coordinate positions // Pi variable can be a define or declared somewhere in your code float radius = 1.0f; for (int j = 0; j <= rowMax; j++) { for (int i = 0; i <= colMax; i++) { int k = index(i, j); float teta = ((float)i / m_colMax) * 2 * (float)Pi; float fi = ((float)j / m_rowMax)*(float)Pi; coordData[k] = radius*(cos (teta))*(sin (fi)); coordData[k + 1] = radius*(cos (fi)); coordData[k + 2] = 1.0f*(sin (teta))*(sin (fi)); } } |
您将获得带有索引的球体坐标。使用for循环将其绘制在OpenGL的旧管道(2.1或兼容性配置文件)上,或在新的OpenGL管道(Core Profile)上设置缓冲区(VAO,VBO)。
这是我很久以前写的一些代码。我已经用三角扇画出了极周围的区域,并用四条纹画出了球体的其余部分。当然,您可以使用三角形而不是四边形,但是由于成对的三角形仍将是平面的,因此看起来不会有任何不同,除非我弄错了-自从接触任何GL以来已经很长时间了。
正如molbdnilo指出的那样,通过以不同的方式计算点,您将获得一个更好的球体。如果要对球体进行纹理贴图,则对多维数据集进行细分和平滑会再次获??得更好的结果,因为这样可以避免在两极之间"捏住"。
这是一篇很好的文章,对此进行了讨论:http://www.iquilezles.org/www/articles/patchedsphere/patchedsphere.htm
我还应该指出,计算法线或在旋转时进行变换的方式存在问题-我过去在旋转球体时会获得时髦的照明效果。 (我认为这与常态是一致的)
另外,现在看看刚才的代码,我不确定我是否已经正确计算了所需的顶点数量-您必须仔细检查。好像我没有存储顶点对于阵列中的任意一个极点。
编辑:
这是输出的图片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | typedef struct { GLfloat x, y, z; }vec3; void myGlutBall(float radius, int numStacks, int numSides) { // vec3 points[sides * (sides-1)]; GLfloat curRadius, curTheta, curRho, deltaTheta, deltaRho, curX,curY,curZ; int curStack, curSlice, numVerts = (numStacks-1)*numSides; vec3 points[numVerts]; int curVert = 0; int t; deltaTheta = (2*M_PI) / numSides; deltaRho = M_PI / numStacks; for (curStack=1; curStack<numStacks; curStack++) { curRho = (3.141/2.0) - curStack*deltaRho; curY = sin(curRho) * radius; curRadius = cos(curRho) * radius; for (curSlice=0; curSlice<numSides; curSlice++) { curTheta = curSlice * deltaTheta; curX = curRadius * cos(curTheta); curZ = -curRadius * sin(curTheta); points[curVert++] = vec3{curX,curY,curZ}; } } // option 1 - points only /* glBegin(GL_POINTS); glNormal3d(0,1,0); glVertex3d(0,radius,0); for (t=0; t<numVerts; t++) { curX = points[t].x; curY = points[t].y; curZ = points[t].z; glNormal3d(curX, curY, curZ); glVertex3d(curX, curY, curZ); } glNormal3d(0,-1,0); glVertex3d(0,-radius,0); glEnd(); */ /////////////////////////////// // option 2 - solid /////////////////////////////// // part A - draw the top 'lid' (tris) glBegin(GL_TRIANGLE_FAN); glNormal3d(0,1,0); glVertex3d(0,radius,0); for (t=0; t<numSides; t++) { curX = points[t].x; curY = points[t].y; curZ = points[t].z; glNormal3d(curX, curY, curZ); glVertex3d(curX, curY, curZ); } curX = points[0].x; curY = points[0].y; curZ = points[0].z; glNormal3d(curX, curY, curZ); glVertex3d(curX, curY, curZ); glEnd(); // part B - draw the 'sides' (quads) int vertIndex; for (curStack=0; curStack<numStacks-2; curStack++) { vertIndex = curStack * numSides; glBegin(GL_QUAD_STRIP); for (curSlice=0; curSlice<numSides; curSlice++) { glNormal3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z); glVertex3d(points[vertIndex+curSlice].x, points[vertIndex+curSlice].y, points[vertIndex+curSlice].z); glNormal3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z); glVertex3d(points[vertIndex+numSides+curSlice].x, points[vertIndex+numSides+curSlice].y, points[vertIndex+numSides+curSlice].z); } glNormal3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z); glVertex3d(points[vertIndex].x, points[vertIndex].y, points[vertIndex].z); glNormal3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z); glVertex3d(points[vertIndex+numSides].x, points[vertIndex+numSides].y, points[vertIndex+numSides].z); glEnd(); } // part C - draw the bottom 'lid' (tris) glBegin(GL_TRIANGLE_FAN); glNormal3d(0,-1,0); glVertex3d(0,-radius,0); for (t=0; t<numSides-1; t++) { curX = points[numVerts-1-t].x; curY = points[numVerts-1-t].y; curZ = points[numVerts-1-t].z; glNormal3d(curX, curY, curZ); glVertex3d(curX, curY, curZ); } curX = points[numVerts-1].x; curY = points[numVerts-1].y; curZ = points[numVerts-1].z; glNormal3d(curX, curY, curZ); glVertex3d(curX, curY, curZ); glEnd(); } |