Создание 3D-сферы в Opengl с использованием Visual C++
Я не могу создать простую трехмерную сферу, используя библиотечную функцию OpenGL glutSolidSphere() в C++.
Вот что я попробовал:
#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();
}
6 ответов
В OpenGL вы не создаете объекты, вы просто рисуете их. После того, как они нарисованы, OpenGL больше не заботится о том, какую геометрию вы отправили.
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;
}
Не похоже, чтобы кто-то до сих пор обращался к реальной проблеме с вашим исходным кодом, поэтому я подумал, что сделаю это, хотя вопрос довольно старый на данный момент.
Первоначально проблема была связана с проекцией относительно радиуса и положения сферы. Я думаю, вы обнаружите, что проблема не слишком сложна. Программа на самом деле работает правильно, просто то, что рисуется, очень трудно увидеть.
Во-первых, ортогональная проекция была создана с помощью вызова
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
который " эквивалентен вызову glOrtho с ближним = -1 и дальним = 1. " Это означает, что глубина усечения для просмотра равна 2. Таким образом, сфера с радиусом, превышающим 1 (диаметр = 2), не будет полностью соответствовать в пределах усмотрения усмотрения.
Тогда звонки
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
используются, который загружает единичную матрицу матрицы вида модели, а затем " [r] вводит сферу, центрированную в начале координат моделирования указанного радиуса. " Это означает, что сфера отображается в начале координат, (x, y, z) = (0, 0, 0) и с радиусом 5.
Теперь проблема в три раза:
- Поскольку окно имеет размер 500x500 пикселей, а ширина и высота поля обзора составляет почти 500 (499,0), малый радиус сферы (5,0) делает ее проецируемую площадь лишь чуть более одной пятидесятой (2*5/499) размера окна в каждом измерении. Это означает, что видимый размер сферы будет примерно 1/2500-й (на самом деле
pi*5^2/499^2
, что ближе к 1/3170th) всего окна, поэтому может быть трудно увидеть. Это при условии, что весь круг нарисован в пределах области окна. Однако это не так, как мы увидим в пункте 2. - Поскольку область просмотра имеет левую плоскость в точке x = 0 и нижнюю плоскость в точке y = 0, сфера будет визуализироваться с ее геометрическим центром в самом нижнем левом углу окна, так что будет виден только один квадрант проецируемой сферы.! Это означает, что то, что было бы видно, еще меньше, около 1/10000 (на самом деле
pi*5^2/(4*499^2)
, что ближе к 1/12 682-й) размера окна. Это сделает это еще более трудно увидеть. Тем более, что сфера отображается так близко к краям / углу экрана, где вы, возможно, даже не подумаете посмотреть. - Поскольку глубина поля зрения значительно меньше диаметра сферы (меньше половины), в пределах области обзора будет только полоса сферы, отображающая только эту часть. Таким образом, вы получите больше похожий на полый круг на экране, чем на сплошную сферу / круг. Когда это происходит, толщина этого осколка может составлять менее 1 пикселя на экране, что означает, что мы можем даже ничего не видеть на экране, даже если часть сферы действительно находится в пределах области просмотра.
Решение состоит в том, чтобы просто изменить угол обзора и радиус сферы. Например,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
оказывает следующее изображение.
Как видите, вокруг "экватора" видна лишь небольшая часть сферы с радиусом 5. (Я изменил проекцию, чтобы заполнить окно сферой.) Другой пример,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
оказывает следующее изображение.
Изображение выше показывает большую часть сферы внутри усеченной области просмотра, но все еще сфера на 0,2 единицы глубины больше, чем усеченная область просмотра. Как видите, "ледяные шапки" сферы отсутствуют как на севере, так и на юге. Итак, если мы хотим, чтобы вся сфера вписывалась в усеченный зрачок глубиной 2, мы должны сделать радиус меньше или равным 1.
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
оказывает следующее изображение.
Я надеюсь, что это помогло кому-то. Береги себя!
Я не понимаю, как генерация индекса 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);
}
}
}
Код 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;
}
Изменить: Возник вопрос о том, как нарисовать это... в моем коде я инкапсулирую эти значения в классе 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();
}
Мне нравится ответ монеты. Это просто понять и работает с треугольниками. Однако индексы его программы иногда выходят за границы. Поэтому я публикую здесь его код с двумя небольшими исправлениями:
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);
}
}
}
Вот код:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();