Рисование сферы в OpenGL без использования gluSphere()?
Существуют ли какие-либо учебные пособия, которые объясняют, как я могу нарисовать сферу в OpenGL без необходимости использования gluSphere()
?
Многие учебники по 3D для OpenGL просто на кубах. Я искал, но большинство решений для рисования сферы заключается в использовании gluSphere()
. Есть также сайт, на котором есть код для рисования сферы на этом сайте, но он не объясняет математику рисования сферы. У меня есть и другие версии того, как рисовать сферу в многоугольнике вместо четырехугольников в этой ссылке. Но опять же, я не понимаю, как сферы нарисованы с помощью кода. Я хочу иметь возможность визуализировать, чтобы я мог изменить сферу, если мне нужно.
Ответы
Ответ 1
Один из способов сделать это - начать с платонического твердого тела с треугольными сторонами - например, octahedron. Затем возьмите каждый треугольник и рекурсивно разложите его на более мелкие треугольники, например:
![recursively drawn triangles]()
Как только у вас будет достаточное количество очков, вы нормализуете их векторы, чтобы они находились на постоянном расстоянии от центра тела. Это заставляет стороны выпячиваться в форму, напоминающую сферу, с повышением гладкости при увеличении количества очков.
Нормализация здесь означает перемещение точки так, чтобы ее угол относительно другой точки был одинаковым, но расстояние между ними различно.
Вот двухмерный пример.
![enter image description here]()
A и B разделены на 6 единиц. Но предположим, что мы хотим найти точку на линии AB, что 12 единиц от A.
![enter image description here]()
Можно сказать, что 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.
![bulging line segment]()
Здесь черные точки начинаются на линии и "выпучиваются" в дугу.
Этот процесс может быть расширен в три измерения, и в этом случае вы получаете сферу, а не круг. Просто добавьте компонент dz в функцию нормализации.
![normalized polygons]()
![level 1 bulging octahedron]()
![level 3 bulging octahedron]()
Если вы посмотрите на сферу в Epcot, вы можете увидеть эту технику на работе. это додекаэдр с выпуклыми лицами, чтобы он выглядел круглым.
Ответ 2
Далее я объясню популярный способ создания сферы с использованием широты и долготы (другой
путь, icospheres, был уже объяснен в самом популярном ответе на момент написания этой статьи.)
Сфера может быть выражена следующим параметрическим уравнением:
F (u, v) = [cos (u) * sin (v) * r, cos (v) * r, sin (u) * sin (v) * r]
Где:
- r - радиус,
- u - долгота, от 0 до 2 & pi;; и
- v - широта, от 0 до & pi;.
Создание сферы затем включает в себя оценку параметрической функции с фиксированными интервалами.
Например, для генерации 16 строк долготы вдоль оси u будет 17 линий сетки, причем шаг
& pi;/8 (2 & pi;/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)
}
}
Ответ 3
Код в образце объясняется быстро. Вы должны взглянуть на функцию 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...()
как GL_QUAD_STRIP
, что означает, что вы отправляете каждые две вершины, которые образуют квад, с двумя ранее отправленными.
Все, что вам нужно понять сейчас, это как работают тригонометрические функции, но я думаю, вы можете легко это понять.
Ответ 4
Если вы хотите быть хитрым, как лиса, вы можете сделать половину кода от GLU. Проверьте исходный код MesaGL (http://cgit.freedesktop.org/mesa/mesa/).
Ответ 5
См. красную книгу OpenGL: http://www.glprogramming.com/red/chapter02.html#name8
Он решает проблему с помощью многоугольника.
Ответ 6
В моем примере, как использовать "треугольную полосу" для рисования "полярной" сферы, она состоит в рисовании точек парами:
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) является следующим параметрическим уравнением, а вторая сдвинута на один шаг альфа-угла (из следующей параллели).
Ответ 7
Хотя принятый ответ решает вопрос, в конце есть небольшое заблуждение. Додекаэдры являются (или могут быть) регулярным многогранником, где все грани имеют одинаковую площадь. Это похоже на Epcot (который, кстати, вообще не является додекаэдром). Поскольку решение, предложенное @Kevin, не дает этой характеристики, я думал, что могу добавить подход, который делает.
Хороший способ создания многогранника с N-гранью, где все вершины лежат в одной и той же сфере, и все его грани имеют сходную площадь/поверхность, начиная с икосаэдра и итеративно разделяя и нормализуя его треугольные грани (как это предлагается в принятом ответе). Додекаэдры, например, являются фактически усеченными икосаэдрами.
Обычные икосаэдры имеют 20 граней (12 вершин) и могут быть легко построены из 3 золотых прямоугольников; это просто вопрос о том, чтобы это было отправной точкой вместо октаэдра. Вы можете найти пример здесь.
Я знаю, что это немного не по теме, но я считаю, что это может помочь, если кто-то попадет сюда в поисках этого конкретного случая.
Ответ 8
Один из способов - создать квадрат, который обращен к камере, и написать вершинный и фрагментарный шейдер, который отображает нечто похожее на сферу. Вы можете использовать уравнения для круга/сферы, которые вы можете найти в Интернете.
Хорошо, что силуэт сферы выглядит одинаково под любым углом. Однако, если сфера не находится в центре перспективного вида, то она, по-видимому, скорее напоминает эллипс. Вы могли бы выработать уравнения для этого и поместить их в затенение фрагмента. Затем светлая затенение должна изменяться по мере продвижения игрока, если у вас действительно есть игрок, движущийся в трехмерном пространстве вокруг сферы.
Может кто-нибудь прокомментировать, если они пробовали это или если было бы слишком дорого, чтобы быть практичным?
Ответ 9
struct v3
{
double x,y,z;
v3(double _x=0, double _y=0, double _z=0){x=_x;y=_y;z=_z; }
v3 operator + ( v3 v) {return { x+v.x, y+v.y, z+v.z };}
v3 operator * ( double k) {return { x*k, y*k, z*k };}
v3 operator / ( double k) {return { x/k, y/k, z/k };}
v3 normalize(){
double L=sqrt( x*x+y*y+z*z);
return { x/L , y/L , z/L };}
};
void draw_spheree(double r,int adim)
{
// z
// |
// __
// /|
// |
// |
// | * \
// | _ _| _ _ _ | _y
// / \c |n / a4 --- a3
// / \o |i | |
// / \s|s z=sin(v) a1 --- a2
// |/__ y=cos(v) *sin(u)
// x=cos(v) *cos(u)
// /
// x
//
//glEnable(GL_LIGHTING);
//glEnable(GL_LIGHT0);
//glEnable(GL_TEXTURE_2D);
double pi=3.141592;
double d=pi/adim;
for(double u=-pi ; u<pi ; u+=d) //horizonal xy düzlemi Longitude -180 -180
for(double v=-pi/2; v<pi/2; v+=d) //vertical z aks Latitude -90 90
{
v3 a1 = { cos(v)*cos(u) ,cos(v)*sin(u) ,sin(v) },
a2 = { cos(v)*cos(u+d) ,cos(v)*sin(u+d) ,sin(v) },
a3 = { cos(v+d)*cos(u+d) ,cos(v+d)*sin(u+d) ,sin(v+d) },
a4 = { cos(v+d)*cos(u) ,cos(v+d)*sin(u) ,sin(v+d) };
v3 normal=(a1+a2+a3+a4)/4.0; //normal vector
a1=a1*r;
a2=a2*r;
a3=a3*r;
a4=a4*r;
double tu=(u+pi) / (2*pi); //0 to 1 horizonal
double tv=(v+pi/2)/ pi; //0 to 1 vertical
double td=1.0/2./adim;
glNormal3dv((double *)&normal);
glBegin(GL_POLYGON);
glTexCoord2d(tu ,tv ); glVertex3dv((double *) &a1);
glTexCoord2d(tu+td ,tv ); glVertex3dv((double *) &a2);
glTexCoord2d(tu+td ,tv+2*td ); glVertex3dv((double *) &a3);
glTexCoord2d(tu ,tv+2*td ); glVertex3dv((double *) &a4);
glEnd();
}
}