Есть ли какие-нибудь учебные пособия, в которых объясняется, как я могу нарисовать сферу в OpenGL без необходимости использования gluSphere()
?
Многие учебники по 3D для OpenGL написаны только на кубах. Я искал, но большинство решений для рисования сферы нужно использовать gluSphere()
. Существует также сайт , который имеет код для рисования сферы на этом сайте , но это не объясняет математику за рисунок сферы. У меня также есть другие версии того, как рисовать сферу в многоугольнике вместо четырехугольника в этой ссылке. Но опять же, я не понимаю, как с помощью кода рисуются сферы. Я хочу иметь возможность визуализировать, чтобы при необходимости изменить сферу.
Ответы:
Один из способов сделать это - начать с платонового твердого тела с треугольными сторонами - например, октаэдра . Затем возьмите каждый треугольник и рекурсивно разбейте его на более мелкие треугольники, например:
Как только у вас будет достаточное количество точек, вы нормализуете их векторы, чтобы все они находились на постоянном расстоянии от центра твердого тела. Это приводит к тому, что стороны выступают в форму, напоминающую сферу, с увеличением гладкости по мере увеличения количества точек.
Нормализация здесь означает перемещение точки так, чтобы ее угол по отношению к другой точке был таким же, но расстояние между ними другое. Вот двухмерный пример.
A и B находятся на расстоянии 6 единиц. Но предположим, что мы хотим найти точку на линии AB, удаленную на 12 единиц от A.
Мы можем сказать, что C - это нормализованная форма B относительно A с расстоянием 12. Мы можем получить C с помощью такого кода:
#returns a point collinear to A and B, a given distance away from A. function normalize(a, b, length): #get the distance between a and b along the x and y axes dx = b.x - a.x dy = b.y - a.y #right now, sqrt(dx^2 + dy^2) = distance(a,b). #we want to modify them so that sqrt(dx^2 + dy^2) = the given length. dx = dx * length / distance(a,b) dy = dy * length / distance(a,b) point c = new point c.x = a.x + dx c.y = a.y + dy return c
Если мы сделаем этот процесс нормализации для множества точек, все относительно одной и той же точки A и с одинаковым расстоянием R, тогда все нормализованные точки будут лежать на дуге окружности с центром A и радиусом R.
Здесь черные точки начинаются на линии и «выпирают» в дугу.
Этот процесс можно расширить до трех измерений, и в этом случае вы получите сферу, а не круг. Просто добавьте компонент dz к функции нормализации.
Если вы посмотрите на сферу в Epcot , вы увидите, как работает эта техника. это додекаэдр с выпуклыми гранями, чтобы он выглядел более округлым.
источник
Далее я объясню популярный способ создания сферы с использованием широты и долготы (другой способ, икосферы , уже объяснялся в самом популярном ответе на момент написания этой статьи).
Сфера может быть выражена следующим параметрическим уравнением:
F ( u , v ) = [cos (u) * sin (v) * r, cos (v) * r, sin (u) * sin (v) * r]
Где:
Затем создание сферы включает в себя оценку параметрической функции через фиксированные интервалы.
Например, чтобы сгенерировать 16 линий долготы, будет 17 линий сетки вдоль оси u с шагом π / 8 (2π / 16) (17-я линия оборачивается).
Следующий псевдокод генерирует треугольную сетку, оценивая параметрическую функцию через равные промежутки времени (это работает для любой параметрической функции поверхности, а не только для сфер).
В псевдокоде ниже UResolution - это количество точек сетки по оси U (здесь - линии долготы), а VResolution - это количество точек сетки по оси V (здесь, линии широты).
var startU=0 var startV=0 var endU=PI*2 var endV=PI var stepU=(endU-startU)/UResolution // step size between U-points on the grid var stepV=(endV-startV)/VResolution // step size between V-points on the grid for(var i=0;i<UResolution;i++){ // U-points for(var j=0;j<VResolution;j++){ // V-points var u=i*stepU+startU var v=j*stepV+startV var un=(i+1==UResolution) ? EndU : (i+1)*stepU+startU var vn=(j+1==VResolution) ? EndV : (j+1)*stepV+startV // Find the four points of the grid // square by evaluating the parametric // surface function var p0=F(u, v) var p1=F(u, vn) var p2=F(un, v) var p3=F(un, vn) // NOTE: For spheres, the normal is just the normalized // version of each vertex point; this generally won't be the case for // other parametric surfaces. // Output the first triangle of this grid square triangle(p0, p2, p1) // Output the other triangle of this grid square triangle(p3, p1, p2) } }
источник
Код в примере быстро объясняется. Вы должны изучить функцию
void drawSphere(double r, int lats, int longs)
:void drawSphere(double r, int lats, int longs) { int i, j; for(i = 0; i <= lats; i++) { double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats); double z0 = sin(lat0); double zr0 = cos(lat0); double lat1 = M_PI * (-0.5 + (double) i / lats); double z1 = sin(lat1); double zr1 = cos(lat1); glBegin(GL_QUAD_STRIP); for(j = 0; j <= longs; j++) { double lng = 2 * M_PI * (double) (j - 1) / longs; double x = cos(lng); double y = sin(lng); glNormal3f(x * zr0, y * zr0, z0); glVertex3f(r * x * zr0, r * y * zr0, r * z0); glNormal3f(x * zr1, y * zr1, z1); glVertex3f(r * x * zr1, r * y * zr1, r * z1); } glEnd(); } }
Параметры
lat
определяют, сколько горизонтальных линий вы хотите иметь в своей сфере иlon
сколько вертикальных линий.r
это радиус вашей сферы.Теперь выполняется двойная итерация по
lat
/lon
и координаты вершины вычисляются с помощью простой тригонометрии.Вычисленные вершины теперь отправляются на ваш графический процессор с использованием в
glVertex...()
качестве aGL_QUAD_STRIP
, что означает, что вы отправляете каждые две вершины, которые образуют четырехугольник с двумя ранее отправленными.Все, что вам теперь нужно понять, это то, как работают тригонометрические функции, но я думаю, вы легко сможете это понять.
источник
См. Красную книгу OpenGL: http://www.glprogramming.com/red/chapter02.html#name8 Он решает проблему путем разбиения на многоугольники.
источник
Если вы хотите быть хитрым, как лис, вы можете сократить код из GLU на полдюйма. Ознакомьтесь с исходным кодом MesaGL (http://cgit.freedesktop.org/mesa/mesa/).
источник
Мой пример того, как использовать «полосу треугольников» для рисования «полярной» сферы, состоит в рисовании точек попарно:
const float PI = 3.141592f; GLfloat x, y, z, alpha, beta; // Storage for coordinates and angles GLfloat radius = 60.0f; int gradation = 20; for (alpha = 0.0; alpha < GL_PI; alpha += PI/gradation) { glBegin(GL_TRIANGLE_STRIP); for (beta = 0.0; beta < 2.01*GL_PI; beta += PI/gradation) { x = radius*cos(beta)*sin(alpha); y = radius*sin(beta)*sin(alpha); z = radius*cos(alpha); glVertex3f(x, y, z); x = radius*cos(beta)*sin(alpha + PI/gradation); y = radius*sin(beta)*sin(alpha + PI/gradation); z = radius*cos(alpha + PI/gradation); glVertex3f(x, y, z); } glEnd(); }
Первая введенная точка (glVertex3f) представляет собой параметрическое уравнение, а вторая точка сдвигается на один шаг альфа-угла (от следующей параллели).
источник
Хотя принятый ответ решает вопрос, в конце есть небольшое заблуждение. Додекаэдры - это (или могут быть) правильные многогранники, у которых все грани имеют одинаковую площадь. Похоже, так обстоит дело с Эпкотом (который, кстати, не является додекаэдром ). Поскольку решение, предложенное @Kevin, не обеспечивает этой характеристики, я подумал, что могу добавить подход, который это делает.
Хороший способ сгенерировать N-гранный многогранник, где все вершины лежат в одной сфере и все его грани имеют одинаковую площадь / поверхность, - это начать с икосаэдра и итеративно подразделить и нормализовать его треугольные грани (как предлагается в принятом ответе. ). Додекаэдры, например, на самом деле являются усеченными икосаэдрами .
Обычные икосаэдры имеют 20 граней (12 вершин) и могут быть легко построены из 3 золотых прямоугольников; просто нужно использовать это в качестве отправной точки вместо октаэдра. Вы можете найти здесь пример .
Я знаю, что это немного не по теме, но я считаю, что это может помочь, если кто-то придет сюда в поисках этого конкретного случая.
источник
Один из способов - создать четырехугольник, обращенный к камере, и написать шейдер вершин и фрагментов, который рендерит что-то похожее на сферу. Вы можете использовать уравнения для круга / сферы, которые вы можете найти в Интернете.
Приятно то, что силуэт шара выглядит одинаково под любым углом. Однако, если сфера не находится в центре перспективного вида, она, возможно, больше похожа на эллипс. Вы можете разработать уравнения для этого и поместить их в штриховку фрагмента. Затем необходимо изменять световое затенение по мере движения игрока, если у вас действительно есть игрок, перемещающийся в трехмерном пространстве вокруг сферы.
Может ли кто-нибудь прокомментировать, пробовали ли они это или это было бы слишком дорого, чтобы быть практичным?
источник
Адаптация Python ответа @Constantinius:
lats = 10 longs = 10 r = 10 for i in range(lats): lat0 = pi * (-0.5 + i / lats) z0 = sin(lat0) zr0 = cos(lat0) lat1 = pi * (-0.5 + (i+1) / lats) z1 = sin(lat1) zr1 = cos(lat1) glBegin(GL_QUAD_STRIP) for j in range(longs+1): lng = 2 * pi * (j+1) / longs x = cos(lng) y = sin(lng) glNormal(x * zr0, y * zr0, z0) glVertex(r * x * zr0, r * y * zr0, r * z0) glNormal(x * zr1, y * zr1, z1) glVertex(r * x * zr1, r * y * zr1, r * z1) glEnd()
источник
void draw_sphere() { // z // | // __ // /| // | // | // | * \ // | _ _| _ _ _ | _y // / \c |n / p3 --- p2 // / \o |i | | // / \s|s z=sin(v) p0 --- p1 // |/__ y=cos(v) *sin(u) // x=cos(v) *cos(u) // / // x // double pi = 3.141592; double di =0.02; double dj =0.04; double du =di*2*pi; double dv =dj*pi; for (double i = 0; i < 1.0; i+=di) //horizonal for (double j = 0; j < 1.0; j+=dj) //vertical { double u = i*2*pi; //0 to 2pi double v = (j-0.5)*pi; //-pi/2 to pi/2 double p[][3] = { cos(v) * cos(u) ,cos(v) * sin(u) ,sin(v), cos(v) * cos(u + du) ,cos(v) * sin(u + du) ,sin(v), cos(v + dv)* cos(u + du) ,cos(v + dv)* sin(u + du) ,sin(v + dv), cos(v + dv)* cos(u) ,cos(v + dv)* sin(u) ,sin(v + dv)}; //normal glNormal3d(cos(v+dv/2)*cos(u+du/2),cos(v+dv/2)*sin(u+du/2),sin(v+dv/2)); glBegin(GL_POLYGON); glTexCoord2d(i, j); glVertex3dv(p[0]); glTexCoord2d(i+di,j); glVertex3dv(p[1]); glTexCoord2d(i+di,j+dj); glVertex3dv(p[2]); glTexCoord2d(i, j+dj); glVertex3dv(p[3]); glEnd(); } }
источник