使用 Visual C++ 在 Opengl 中创建 3D 球体
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5988686/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Creating a 3D sphere in Opengl using Visual C++
提问by Lloyd
I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.
我无法使用 C++ 中的 OpenGL 库函数 glutSolidSphere() 创建一个简单的 3D 球体。
Here's what I tried:
这是我尝试过的:
#include<GL/glu.h>
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glLoadIdentity();
glutSolidSphere( 5.0, 20.0, 20.0);
glFlush();
}
void myInit()
{
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(1.0,0.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,499.0,0.0,499.0);
glMatrixMode(GL_MODELVIEW);
}
void main(int argc,char **argv)
{
qobj = gluNewQuadric();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutCreateWindow("pendulum");
glutDisplayFunc(display);
myInit();
glutMainLoop();
}
回答by datenwolf
In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.
在 OpenGL 中你不创建对象,你只是绘制它们。一旦它们被绘制出来,OpenGL 就不再关心你发送了什么几何图形。
glutSolidSphere
is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?
glutSolidSphere
只是向 OpenGL 发送绘图命令。然而,它并没有什么特别之处。而且由于它与 GLUT 相关联,因此我不会使用它。相反,如果您的代码中确实需要一些领域,那么为自己创建 if 怎么样?
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>
// your framework of choice here
class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
public:
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = x;
*n++ = y;
*n++ = z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
void draw(GLfloat x, GLfloat y, GLfloat z)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x,y,z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
}
};
SolidSphere sphere(1, 12, 24);
void display()
{
int const win_width = …; // retrieve window dimensions from
int const win_height = …; // framework of choice here
float const win_aspect = (float)win_width / (float)win_height;
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#ifdef DRAW_WIREFRAME
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
sphere.draw(0, 0, -5);
swapBuffers();
}
int main(int argc, char *argv[])
{
// initialize and register your framework of choice here
return 0;
}
回答by Victor Zamanian
It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.
到目前为止,似乎没有人解决您原始代码的实际问题,所以我想我会这样做,即使这个问题在这一点上已经很老了。
The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.
问题最初与与球体的半径和位置相关的投影有关。我想你会发现问题并不太复杂。该程序实际上工作正常,只是正在绘制的内容很难看到。
First, an orthogonal projection was created using the call
首先,使用调用创建了一个正交投影
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustumhas a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.
这“相当于用near = -1和far = 1调用glOrtho。”这意味着视锥体的深度为2。因此半径大于1(直径= 2)的球体将不完全适合在视锥体内。
Then the calls
然后电话
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.
使用,它加载模型视图矩阵的单位矩阵,然后“ [r] 渲染一个以指定半径的建模坐标原点为中心的球体。”意思是,球体在原点处渲染,(x, y, z) = (0, 0, 0),半径为 5。
Now, the issue is three-fold:
现在,问题是三个方面:
- Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually
pi*5^2/499^2
, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2. - Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually
pi*5^2/(4*499^2)
, which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look. - Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothingon the screen, even if part of the sphere is indeed within the viewing frustum.
- 由于窗口为 500x500 像素,视锥体的宽度和高度几乎为 500 (499.0),球体的小半径 (5.0) 使其投影面积仅略高于尺寸的五十分之一 (2*5/499)每个维度的窗口。这意味着球体的表观大小大约
pi*5^2/499^2
是整个窗口的 1/2,500 (实际上,更接近 1/3170),因此可能很难看到。这是假设整个圆都绘制在窗口区域内。然而,它并不像我们将在第 2 点中看到的那样。 - 由于视锥体的左平面位于 x = 0 处,底平面位于 y = 0 处,因此球体将以其几何中心在窗口的左下角进行渲染,因此只有一个象限的投影球体可见!这意味着所看到的甚至更小,大约
pi*5^2/(4*499^2)
是窗口大小的1/10,000(实际上更接近 1/12,682)。这将使它更加难以看到。特别是因为球体被渲染得非常靠近屏幕的边缘/角落,你可能不会想到去那里看。 - 由于视锥体的深度明显小于球体的直径(小于一半),因此只有球体的一小部分位于视锥体内,仅渲染该部分。因此,与实心球体/圆相比,您在屏幕上会更像一个空心圆。碰巧的是,该条子的厚度在屏幕上可能代表不到 1 个像素,这意味着我们甚至可能在屏幕上看不到任何东西,即使球体的一部分确实在视锥体内。
The solution is simply to change the viewing frustum and radius of the sphere. For instance,
解决方案是简单地改变视锥体和球体的半径。例如,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
renders the following image.
呈现以下图像。
As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,
如您所见,半径为 5 的球体的“赤道”周围只有一小部分可见。(我更改了投影以用球体填充窗口。)另一个示例,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
renders the following image.
呈现以下图像。
The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.
上图显示了视锥体内更多的球体,但球体仍比视锥体大 0.2 个深度单位。如您所见,球体的“冰帽”不见了,无论是北面还是南面。因此,如果我们希望整个球体适合深度为 2 的视锥体,我们必须使半径小于或等于 1。
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
renders the following image.
呈现以下图像。
I hope this has helped someone. Take care!
我希望这对某人有所帮助。小心!
回答by coin
I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:
我不明白 datenwolf 的索引生成怎么可能是正确的。但我仍然觉得他的解决方案相当清楚。这是我经过一些思考后得到的:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + (s+1));
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
push_indices(indices, sectors, r, s);
}
}
}
回答by sarah
Here's the code:
这是代码:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
回答by user430788
Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:
Datanewolf 的代码几乎是正确的。我不得不反转绕组和法线,使其与固定管道正常工作。以下对我来说在开启或关闭剔除时正常工作:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = -x;
*n++ = -y;
*n++ = -z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++) {
/*
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*/
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:
编辑:有一个关于如何绘制这个的问题......在我的代码中,我将这些值封装在一个 G3DModel 类中。这是我设置框架、绘制模型并结束它的代码:
void GraphicsProvider3DPriv::BeginFrame()const{
int win_width;
int win_height;// framework of choice here
glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
float const win_aspect = (float)win_width / (float)win_height;
// set lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
GLfloat lightpos[] = {0, 0.0, 0, 0.};
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// set up world transform
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
}
void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
G3DModelPriv* privModel = (G3DModelPriv *)model;
glPushMatrix();
glLoadMatrixf(transform.GetOGLData());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);
glEnable(GL_TEXTURE_2D);
//glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, privModel->texname);
glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void GraphicsProvider3DPriv::EndFrame()const{
/* Swap front and back buffers */
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
回答by Harald
I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:
我喜欢硬币的答案。它很容易理解并适用于三角形。然而,他的程序的索引有时会超出范围。所以我在这里发布了他的代码,并进行了两个微小的更正:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
int nextS = (s+1) % sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + nextS);
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
if(r < rings-1)
push_indices(indices, sectors, r, s);
}
}
}