Interesting/iPhone | Posted by hyena0 2010. 1. 20. 01:18

[iPhone] OpenGL 프로그래밍 ...3


  [iPhone] OpenGL 프로그래밍 ...3

이전 포스트에서 drawView 메소드 부분을 다루어 보았습니다.

이 drawView에서 3차원 도형을 그리는 방법을 크게 3가지로

분류해서 다루어 보겠습니다.

1. glVertexPointer, glDrawArrays 이용

2. glVertexPointer, glNormalPointer, glDrawArrays 이용

3. glVertexPointer, glNormalPointer, glTexCoordPointer, glDrawArrays 이용

우선 첫번째 부분을 보면 아래와 같은 예를 볼 수 있겠습니다.

- (void)drawView {

// Our new object definition code goes here

    const GLfloat cubeVertices[] = {

        // Define the front face

        -1.0, 1.0, 1.0,             // top left

        -1.0, -1.0, 1.0,            // bottom left

        1.0, -1.0, 1.0,             // bottom right

        1.0, 1.0, 1.0,              // top right

      

        // Top face

        -1.0, 1.0, -1.0,            // top left (at rear)

        -1.0, 1.0, 1.0,             // bottom left (at front)

        1.0, 1.0, 1.0,              // bottom right (at front)

        1.0, 1.0, -1.0,             // top right (at rear)     

        // Rear face

        1.0, 1.0, -1.0,             // top right (when viewed from front)

        1.0, -1.0, -1.0,            // bottom right

        -1.0, -1.0, -1.0,           // bottom left

        -1.0, 1.0, -1.0,            // top left


        // bottom face

        -1.0, -1.0, 1.0,

        -1.0, -1.0, -1.0,

        1.0, -1.0, -1.0,

        1.0, -1.0, 1.0,

  

        // left face

        -1.0, 1.0, -1.0,

        -1.0, 1.0, 1.0,

        -1.0, -1.0, 1.0,

        -1.0, -1.0, -1.0,

  

        // right face

        1.0, 1.0, 1.0,

        1.0, 1.0, -1.0,

        1.0, -1.0, -1.0,

        1.0, -1.0, 1.0

    };

    [EAGLContext setCurrentContext:context];    

    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

    glViewport(0, 0, backingWidth, backingHeight);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glMatrixMode(GL_MODELVIEW);

// Our new drawing code goes here

rota += 0.5; // 회전각도

glLoadIdentity();

    glTranslatef(0.0, 0.0, -6.0);

glRotatef(rota, 1.0, 1.0, 1.0);

glVertexPointer(3, GL_FLOAT, 0, cubeVertices);

    glEnableClientState(GL_VERTEX_ARRAY);

// Draw the front face in Red

glColor4f(1.0, 0.0, 0.0, 1.0);

    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);

 

// Draw the top face in green

glColor4f(0.0, 1.0, 0.0, 1.0);

glDrawArrays(GL_TRIANGLE_FAN, 4, 4);

// Draw the rear face in Blue

glColor4f(0.0, 0.0, 1.0, 1.0);

glDrawArrays(GL_TRIANGLE_FAN, 8, 4);

// Draw the bottom face

glColor4f(1.0, 1.0, 0.0, 1.0);

glDrawArrays(GL_TRIANGLE_FAN, 12, 4);


// Draw the left face

glColor4f(0.0, 1.0, 1.0, 1.0);

glDrawArrays(GL_TRIANGLE_FAN, 16, 4);

// Draw the right face

glColor4f(1.0, 0.0, 1.0, 1.0);

glDrawArrays(GL_TRIANGLE_FAN, 20, 4);

    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

    [context presentRenderbuffer:GL_RENDERBUFFER_OES];

[self checkGLError:NO];

}


정육면체가 회전하게 되는 예제로 glVertexPointer 함수에서 

배열 cubeVertices 의 값을 이용하여 정육면체를 그리게 됩니다.

여기서 배열  cubeVertices 의 값을 보면 각 면의 값을 모두 가지고 있다는 것을 알게 되지요.

그래서 각 면에 해당하는 부분을 glDrawArrays 함수에서 4 행씩 증가시켜가면서

그리게 됩니다.

사실 이런 방식으로 도형을 그리게 되면 정육면체 혹은 정사면체 등의 우리가 아는 도형만

그릴 수 있고 3D 게임을 만들거나 하기 위한 복잡한 도형을 만들기는 사실상 불가능합니다.

그래서 두 번째 및 세 번째 방법을 이용해야한다는 것을 알게 됩니다.

다음 방법은 다음 포스트에서 봅시다.





Interesting/iPhone | Posted by hyena0 2010. 1. 19. 22:00

[iPhone] OpenGL 프로그래밍..2


  

  [iPhone] OpenGL 프로그래밍..2


  오랜만에 아이폰 포스팅을 하게 되었습니다.

  그간 작업하던 것을 마무리 하느라 어쩔 수 없었군요.

  이전 포스팅에서 참고하고 있던 simon maurice 의 블로그는 계정이

  만료되었는지 더이상 접근하기 어렵습니다.

  참고할 만한 사이트가 없어진게 좀 아쉬운 부분이지요.

  이번 포스팅에서는 GLGravity 프로젝트를 기준으로 설명해 보겠습니다.

  이 프로젝트 파일은 구글 개발자 사이트에서 다운로드 받을 수 있습니다.

  GLGravityAppDelegate.h
  GLGravityAppDelegate.m
  GLGravityView.h
  GLGravityView.m

  네 개의 클래스로 구성되는 이 프로젝트는 가속도 센서 값을 받아서 
 
  주전자 모양의 3차원 그래픽을 아이폰을 회전할때마다 중력반대방향으로

  세워놓는 구성을 가집니다. 물론 시뮬레이터일 경우는 빙글빙글 회전만 합니다.



  GLGravityAppDelegate 클래스는 센서값을 주기적으로 가져오도록 설정되어 있고,

  GLGravityView 클래스에서 다루려고 하는 3차원 도형을 그리게 됩니다.

  기존에 다루고 있는 tutorial 에서는 대부분 정육면체를 보여주기 때문에 이해가 가지 않는

  부분도 존재합니다. 물론 정육면체부터 이해하고 넘어가는게 순서겠지요.

  어쨌건, 해당 파일을 보면 다음의 메소드를 가집니다.

  - (id)initWithCoder:(NSCoder*)coder 
  - (void)setupView
  - (void)drawView
  - (void)layoutSubviews
 - (BOOL)createFramebuffer
 - (void)destroyFramebuffer 
  - (NSInteger) animationFrameInterval
 - (void) setAnimationFrameInterval:(NSInteger)frameInterval
 - (void) startAnimation
 - (void)stopAnimation
 - (void)dealloc

  이 메소드들 중에서 다뤄볼 부분은 drawView 입니다. 경우에 따라서는 다른 메소드를 수정할 수 있겠지만

  현재 3차원 도형을 다루는 부분을 볼 것이므로 이 메소드만 보겠습니다.

  해당 메소드를 그대로 옮겨보면 아래와 같습니다.

// Updates the OpenGL view

- (void)drawView

{

// Make sure that you are drawing to the current context

[EAGLContext setCurrentContext:context];

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

GLfloat matrix[4][4], length;

//Make sure we have a big enough acceleration vector

length = sqrtf(accel[0] * accel[0] + accel[1] * accel[1] + accel[2] * accel[2]);

//Setup model view matrix

glLoadIdentity();

glTranslatef(0.0, -0.1, -1.0);

glScalef(kTeapotScale, kTeapotScale, kTeapotScale);

if(length >= 0.1)

{

//Clear matrix to be used to rotate from the current referential to one based on the gravity vector

bzero(matrix, sizeof(matrix));

matrix[3][3] = 1.0;

//Setup first matrix column as gravity vector

matrix[0][0] = accel[0] / length;

matrix[0][1] = accel[1] / length;

matrix[0][2] = accel[2] / length;

//Setup second matrix column as an arbitrary vector in the plane perpendicular to the gravity vector {Gx, Gy, Gz} defined by by the equation "Gx * x + Gy * y + Gz * z = 0" in which we arbitrarily set x=0 and y=1

matrix[1][0] = 0.0;

matrix[1][1] = 1.0;

matrix[1][2] = -accel[1] / accel[2];

length = sqrtf(matrix[1][0] * matrix[1][0] + matrix[1][1] * matrix[1][1] + matrix[1][2] * matrix[1][2]);

matrix[1][0] /= length;

matrix[1][1] /= length;

matrix[1][2] /= length;

//Setup third matrix column as the cross product of the first two

matrix[2][0] = matrix[0][1] * matrix[1][2] - matrix[0][2] * matrix[1][1];

matrix[2][1] = matrix[1][0] * matrix[0][2] - matrix[1][2] * matrix[0][0];

matrix[2][2] = matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];

//Finally load matrix

glMultMatrixf((GLfloat*)matrix);

// Rotate a bit more so that its where we want it.

glRotatef(90.0, 0.0, 0.0, 1.0);

}

// If we're in the simulator we'd like to do something more interesting than just sit there

// But if we're on a device, we want to just let the accelerometer do the work for us without a fallback.

#if TARGET_IPHONE_SIMULATOR

else

{

static GLfloat spinX = 0.0, spinY = 0.0;

glRotatef(spinX, 0.0, 0.0, 1.0);

glRotatef(spinY, 0.0, 1.0, 0.0);

glRotatef(90.0, 1.0, 0.0, 0.0);

spinX += 1.0;

spinY += 0.25;

}

#endif

// Draw teapot. The new_teapot_indicies array is an RLE (run-length encoded) version of the teapot_indices array in teapot.h

for(int i = 0; i < num_teapot_indices; i += new_teapot_indicies[i] + 1)

{

glDrawElements(GL_TRIANGLE_STRIP, new_teapot_indicies[i], GL_UNSIGNED_SHORT, &new_teapot_indicies[i+1]);

}

 

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

[context presentRenderbuffer:GL_RENDERBUFFER_OES];

}


  주석에서 설명이 되어있으므로 주요한 설명은 안해도 될 것 같습니다.


  여기서 3차원 도형을 그리기 위한 주요한 내용만 다시 보겠습니다.


  단순히 도형만 그린다면 아래와 같이 고쳐볼 수 있습니다.


- (void)drawView

{

        [EAGLContext setCurrentContext:context];

glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


//Setup model view matrix

glLoadIdentity();

glTranslatef(0.0, -0.1, -1.0);

glScalef(kTeapotScale, kTeapotScale, kTeapotScale);


// Draw teapot. The new_teapot_indicies array is an RLE (run-length encoded) version of the teapot_indices array in teapot.h

for(int i = 0; i < num_teapot_indices; i += new_teapot_indicies[i] + 1)

{

glDrawElements(GL_TRIANGLE_STRIP, new_teapot_indicies[i], GL_UNSIGNED_SHORT, &new_teapot_indicies[i+1]);

}

 

glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);

[context presentRenderbuffer:GL_RENDERBUFFER_OES];

}


  위에서 glDrawElements 함수가 도형정보를 그리는 부분입니다. 


  주석에서처럼 teapot.h 파일에 num_teapot_indices, new_teapot_indices[i] 가 정의되어 있지요.


  이 부분이 어떻게 정의되는 지는 다음 포스트에서 봅시다.



 [Next] OpenGL 프로그래밍..3


  OpenGL 로 원하는 도형 만들기

  android로 OpenGL ES 를 이용하는 예제는

  Dev site 에서 찾을 수 있습니다.

  예제는 Cube.java 와 CubeRenderer.java 입니다.

  그런데 여기서 Cube 만 다루다 보니 

  다른 도형을 어떻게 해야 할지 좀 막막하더군요.

  Web 상의 OpenGL 문서를 뒤져보고 해도 기초만 나오기 때문에

  좀처럼 이해하기 어려웠습니다.

  원하는 도형을 한번에 만들어서 Renderer 에서 그리면 좋을텐데,

  꼭지점을 이용하여 그리는 방식을 이해하기 어렵더군요.

  그간 고민한게 효과가 있었던지, 드디어 알게 되었습니다.

  일단 Cube.java 파일을 봅시다.

class Cube

{

    public Cube()

    {

        int one = 0x10000;

        int vertices[] = {

               //x,    y,    z,

                -one, -one, -one, // index #0

                one, -one, -one,  // index #1

                one,  one, -one,  // index #2

                -one,  one, -one, // index #3

                -one, -one,  one, // index #4

                one, -one,  one,  // index #5

                one,  one,  one,  // index #6

                -one,  one,  one, // index #7

        };


        int colors[] = {

              //R, G, B, A

                0,    0,    0,  one,

                one,    0,    0,  one,

                one,  one,    0,  one,

                0,  one,    0,  one,

                0,    0,  one,  one,

                one,    0,  one,  one,

                one,  one,  one,  one,

                0,  one,  one,  one,

       

        };


        byte indices[] = {

                // Triangle 로 쪼개는 인덱스 번호들

                0, 4, 5,    0, 5, 1,

                1, 5, 6,    1, 6, 2,

                2, 6, 7,    2, 7, 3,

                3, 7, 4,    3, 4, 0,

                4, 7, 6,    4, 6, 5,

                3, 0, 1,    3, 1, 2,

        

        };


        // Buffers to be passed to gl*Pointer() functions

        // must be direct, i.e., they must be placed on the

        // native heap where the garbage collector cannot

        // move them.

        //

        // Buffers with multi-byte datatypes (e.g., short, int, float)

        // must have their byte order set to native order


        ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length*4);

        vbb.order(ByteOrder.nativeOrder());

        mVertexBuffer = vbb.asIntBuffer();

        mVertexBuffer.put(vertices);

        mVertexBuffer.position(0);


        ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length*4);

        cbb.order(ByteOrder.nativeOrder());

        mColorBuffer = cbb.asIntBuffer();

        mColorBuffer.put(colors);

        mColorBuffer.position(0);


        mIndexBuffer = ByteBuffer.allocateDirect(indices.length);

        mIndexBuffer.put(indices);

        mIndexBuffer.position(0);

        

    }


    public void draw(GL10 gl)

    {

        gl.glFrontFace(gl.GL_CW);

        gl.glVertexPointer(3, gl.GL_FIXED, 0, mVertexBuffer);

        gl.glColorPointer(4, gl.GL_FIXED, 0, mColorBuffer);

        gl.glDrawElements(gl.GL_TRIANGLES, 36, gl.GL_UNSIGNED_BYTE, mIndexBuffer);

        

    }

    

    private IntBuffer   mVertexBuffer;

    private IntBuffer   mColorBuffer;

    private ByteBuffer  mIndexBuffer;

}


그리고 anddev.org 사이트에서 찾아보면 나오는 설명에서 아래의 그림처럼
꼭지점들(vertices)을 정하고, 색을 정하고, 그림을 그리기 위한 조각들을 결정합니다.

 

위의 그림처럼 꼭지점이 인덱스가 "0" 이면 (-1,-1,-1) 이고,  "1"이면 (1,-1,-1)  의 순으로 지정됩니다.
vertices[] 내의 좌표 순서가 바로 인덱스 순서라고 보면 되지요.
그리고 glDrawElements 함수로 그리기 위해 GL_TRIANGLES를 이용했는데 아래 그림처럼 삼각형으로 잘라지지요.

 

삼각형의 번호는 인덱스의 번호로 3개씩 짝을 짓고 있고 glFrontFace 함수에서 시계방향을 선택했으므로
인덱스 번호의 순서를 결정할 수 있습니다.

그러면 다른 도형의 형태를 어떻게 할 것인가? 감이 오시는지...

안오신다면 아래의 그림을 다시 한번 보면 이해가 될 것 같네요.


만약 집모양을 만든다고 하면 좌표값의 크기를 정하고, 좌표값에 맞는 인덱스를 정한 후 

도형의 면을 그릴 수 있도록 삼각형, Loop 등의 방법을 결정해 주면 됩니다.