[英]Creating a 3D sphere in Opengl using Visual C++
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();
}
In OpenGL you don't create objects, you just draw them. 在OpenGL中,您不创建对象,只需绘制它们即可。 Once they are drawn, OpenGL no longer cares about what geometry you sent it.
绘制完成后,OpenGL不再关心您发送的几何体。
glutSolidSphere
is just sending drawing commands to OpenGL. glutSolidSphere
只是向OpenGL发送绘图命令。 However there's nothing special in and about it. 然而,它并没有什么特别之处。 And since it's tied to GLUT I'd not use it.
因为它与GLUT联系在一起我不会使用它。 Instead, if you really need some sphere in your code, how about create if for yourself?
相反,如果你的代码中确实需要一些球体,那么如何为自己创造呢?
#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;
}
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 frustum has 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: 现在,问题是三方面的:
pi*5^2/499^2
, which is closer to about 1/3170th) of the entire window, so it might be difficult to see . pi*5^2/499^2
2/499 pi*5^2/499^2
,接近约1/3170),因此可能很难看到 。 This is assuming the entire circle is drawn within the area of the window. pi*5^2/(4*499^2)
, which is closer to 1/12,682nd) of the window size. pi*5^2/(4*499^2)
,接近1 / 12,682)。 This would make it even more difficult to see . 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. 上面的图像显示了视锥体内部的更多球体,但是球体仍然比观察平截头体大0.2个深度单位。 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.
因此,如果我们希望整个球体适合具有深度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!
照顾自己!
I don't understand how can datenwolf`s index generation can be correct. 我不明白datenwolf的索引生成怎么可能是正确的。 But still I find his solution rather clear.
但我仍然觉得他的解决方案很清楚。 This is what I get after some thinking:
这是我在思考之后得到的:
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);
}
}
}
Here's the code: 这是代码:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
Datanewolf's code is ALMOST right. Datanewolf的代码几乎是正确的。 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:
以下工作正常,对我来说剔除或关闭:
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. 编辑:有一个关于如何绘制这个问题的问题...在我的代码中,我将这些值封装在G3DModel类中。 This is my code to setup the frame, draw the model, and end it:
这是我设置框架,绘制模型并结束它的代码:
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();
}
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);
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.