program tip

gluSphere ()를 사용하지 않고 OpenGL에서 구 그리기?

radiobox 2020. 10. 13. 07:37
반응형

gluSphere ()를 사용하지 않고 OpenGL에서 구 그리기?


OpenGL에서 사용하지 않고도 구를 그릴 수있는 방법을 설명하는 튜토리얼이 gluSphere()있습니까?

OpenGL에 대한 많은 3D 튜토리얼은 큐브에 있습니다. 나는 검색했지만 구를 그리는 대부분의 솔루션은 gluSphere(). 이 사이트에 구를 그리는 코드가있는 사이트도 있지만 구를 그리는 이면의 수학을 설명하지 않습니다. 해당 링크의 쿼드 대신 다각형으로 구를 그리는 방법에 대한 다른 버전도 있습니다. 그러나 다시 말하지만 코드로 구체가 어떻게 그려 지는지 이해하지 못합니다. 필요한 경우 구를 수정할 수 있도록 시각화 할 수 있기를 원합니다.


이를 수행 할 수있는 한 가지 방법은 삼각형면이있는 플라토닉 솔리드 ( : 팔면체 )로 시작하는 것 입니다. 그런 다음 각 삼각형을 재귀 적으로 더 작은 삼각형으로 나눕니다.

재귀 적으로 그려진 삼각형

충분한 양의 점이 있으면 벡터가 모두 솔리드 중심에서 일정한 거리에 있도록 벡터를 정규화합니다. 이렇게하면 점의 수를 늘릴수록 부드러움이 증가하면서 측면이 구와 유사한 모양으로 튀어 나옵니다.

여기서 정규화는 다른 점에 대한 각도가 같지만 그 사이의 거리가 달라 지도록 점을 이동하는 것을 의미합니다. 다음은 2 차원의 예입니다.

여기에 이미지 설명 입력

A와 B는 6 단위 떨어져 있습니다. 그러나 우리가 A에서 12 단위 떨어진 라인 AB에서 한 지점을 찾고 싶다고 가정 해 보겠습니다.

여기에 이미지 설명 입력

C는 거리가 12 인 A에 대해 B의 정규화 된 형태라고 말할 수 있습니다. 다음과 같은 코드로 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을 갖는 원의 호에 놓일 것입니다.

불룩한 선 세그먼트

여기에서 검은 색 점은 선에서 시작하여 호로 "불룩 해집니다".

이 과정은 3 차원으로 확장 될 수 있으며,이 경우 원이 아닌 구를 얻게됩니다. 정규화 함수에 dz 구성 요소를 추가하기 만하면됩니다.

정규화 된 다각형

레벨 1 부풀어 오르는 팔면체 레벨 3 부풀어 오르는 팔면체

Epcot 에서 구를 보면이 기술이 작동하는 것을 볼 수 있습니다. 그것은 더 둥글게 보이게하기 위해 튀어 나온 얼굴을 가진 십이 면체입니다.


위도와 경도를 사용하여 구를 생성하는 인기있는 방법을 추가로 설명하겠습니다 (또 다른 방법 인 icospheres 는이 글을 쓰는 당시 가장 인기있는 답변에서 이미 설명되었습니다.)

구는 다음과 같은 파라 메트릭 방정식으로 표현할 수 있습니다.

F ( u , v ) = [cos (u) * sin (v) * r, cos (v) * r, sin (u) * sin (v) * r]

어디:

  • r 은 반경입니다.
  • u 는 0에서 2π까지의 경도입니다.
  • v 는 0에서 π까지의 위도입니다.

그런 다음 구를 생성하려면 고정 된 간격으로 파라 메트릭 함수를 평가해야합니다.

예를 들어, 16 개의 경도 라인을 생성하려면 u 축을 따라 π / 8 (2π / 16) 간격 으로 17 개의 그리드 라인이 있어야 합니다 (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간단한 삼각법을 사용하여 정점 좌표가 계산됩니다.

계산 된 정점이 이제를 사용하여 GPU glVertex...()GL_QUAD_STRIP전송됩니다. 즉, 이전에 전송 된 두 정점과 함께 쿼드를 형성하는 각 정점 두 개를 전송합니다.

이제 이해해야 할 것은 삼각 함수가 작동하는 방식 뿐이지 만 쉽게 이해할 수있을 것 같습니다.


여우처럼 교활하고 싶다면 GLU의 코드를 반 인치로 만들 수 있습니다. MesaGL 소스 코드 (http://cgit.freedesktop.org/mesa/mesa/)를 확인하세요.


OpenGL red book : http://www.glprogramming.com/red/chapter02.html#name8 다각형 세분화로 문제를 해결합니다.


'삼각형 스트립'을 사용하여 "극성"구를 그리는 방법의 예는 쌍의 드로잉 포인트로 구성됩니다.

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)은 다음과 같은 파라 메트릭 방정식이고 두 번째 점은 알파 각도의 단일 단계 (다음 평행선에서)만큼 이동합니다.


받아 들여지는 대답으로 질문이 해결되지만 끝에 약간의 오해가 있습니다. 정 십이 면체 는 모든면이 같은 면적을 갖는 정다면체입니다. 그것은 Epcot의 경우 인 것 같습니다 (그런데, 이것은 십이 면체 가 아닙니다 ). @Kevin이 제안한 솔루션은 이러한 특성을 제공하지 않기 때문에 이러한 특성을 추가 할 수 있다고 생각했습니다.

A good way to generate an N-faced polyhedron where all vertices lay in the same sphere and all its faces have similar area/surface is starting with an icosahedron and the iteratively sub-dividing and normalizing its triangular faces (as suggested in the accepted answer). Dodecahedrons, for instance, are actually truncated icosahedrons.

Regular icosahedrons have 20 faces (12 vertices) and can easily be constructed from 3 golden rectangles; it's just a matter of having this as a starting point instead of an octahedron. You may find an example here.

I know this is a bit off-topic but I believe it may help if someone gets here looking for this specific case.


One way is to make a quad that faces the camera and write a vertex and fragment shader that renders something that looks like a sphere. You could use equations for a circle/sphere that you can find on the internet.

One nice thing is that the silhouette of a sphere looks the same from any angle. However, if the sphere is not in the center of a perspective view, then it would appear perhaps more like an ellipse. You could work out the equations for this and put them in the fragment shading. Then the light shading needs to changed as the player moves, if you do indeed have a player moving in 3D space around the sphere.

Can anyone comment on if they have tried this or if it would be too expensive to be practical?


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();                
    } 
 }

참고URL : https://stackoverflow.com/questions/7687148/drawing-sphere-in-opengl-without-using-glusphere

반응형