따뜻한 대한민국 겨울만들기 외규장각 도서 환수 모금 캠페인

gluLookAt()

iPhoneDev/OpenGL ES 2009/07/03 14:51 |

OpenGL ES는 모바일 환경을 고려해서 만들어졌기 때문에 많은 함수들이 빠져 있습니다.

그중 카메라(시점)와 관계가 있는 gluLookAt()함수가 빠져 있기 때문에 시점 변환 등에 에로사항이 꽃피게 됩니다.
이런 초대박 불편함을 해결 하기 위해서 아래 처럼 스스로 gluLookAt()함수를 만들어서 사용하는 방법이 있습니다.

라이선스는 후리(free)이지만, 코드를 작성해주신 MIT 능자님들에 대한 감탄, 존경, 경외, 배려, 존중의 의미로 주석을 넣어줍시다!

gluLookAt.h
/*
*  gluLookAt.h
*
*  This is a modified version of the function of the same name from
*  the Mesa3D project ( http://mesa3d.org/ ), which is  licensed
*  under the MIT license, which allows use, modification, and
*  redistribution
*
*  In order to work under OpenGL ES, all instances of GLdouble
*  had to be changed to GLfloat, and all "d" function calls had
*  to be changed to the "f" versions.
*
*  Original developer's comments have been left in place.
*
*  Out of respect for the original authors, this is licensed under
*  the Mesa (MIT) license. Original license follows:

*  -----------------------------------------------------------------------
*
*  Copyright (C) 1999-2007  Brian Paul   All Rights Reserved.

*  Permission is hereby granted, free of charge, to any person obtaining a
*  copy of this software and associated documentation files (the "Software"),
*  to deal in the Software without restriction, including without limitation
*  the rights to use, copy, modify, merge, publish, distribute, sublicense,
*  and/or sell copies of the Software, and to permit persons to whom the
*  Software is furnished to do so, subject to the following conditions:

*  The above copyright notice and this permission notice shall be included
*  in all copies or substantial portions of the Software.

*  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
*  OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
*  BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
*  AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
*  CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
*/

#import <Foundation/Foundation.h>
#import <OpenGLES/EAGL.h>
#import <OpenGLES/ES1/gl.h>
#import <OpenGLES/ES1/glext.h>

void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez,
            GLfloat centerx, GLfloat centery, GLfloat centerz,
            GLfloat upx, GLfloat upy, GLfloat upz);





gluLookAt.m
/*
 *  gluLookAt.c
 */

#include "gluLookAt.h"
void gluLookAt(GLfloat eyex, GLfloat eyey, GLfloat eyez, GLfloat centerx, GLfloat centery, GLfloat centerz, GLfloat upx, GLfloat upy, GLfloat upz) { GLfloat m[16]; GLfloat x[3], y[3], z[3]; GLfloat mag; /* Make rotation matrix */ /* Z vector */ z[0] = eyex - centerx; z[1] = eyey - centery; z[2] = eyez - centerz; mag = sqrt(z[0] * z[0] + z[1] * z[1] + z[2] * z[2]); if (mag) { /* mpichler, 19950515 */ z[0] /= mag; z[1] /= mag; z[2] /= mag; } /* Y vector */ y[0] = upx; y[1] = upy; y[2] = upz; /* X vector = Y cross Z */ x[0] = y[1] * z[2] - y[2] * z[1]; x[1] = -y[0] * z[2] + y[2] * z[0]; x[2] = y[0] * z[1] - y[1] * z[0]; /* Recompute Y = Z cross X */ y[0] = z[1] * x[2] - z[2] * x[1]; y[1] = -z[0] * x[2] + z[2] * x[0]; y[2] = z[0] * x[1] - z[1] * x[0]; /* mpichler, 19950515 */ /* cross product gives area of parallelogram, which is < 1.0 for * non-perpendicular unit-length vectors; so normalize x, y here */ mag = sqrt(x[0] * x[0] + x[1] * x[1] + x[2] * x[2]); if (mag) { x[0] /= mag; x[1] /= mag; x[2] /= mag; } mag = sqrt(y[0] * y[0] + y[1] * y[1] + y[2] * y[2]); if (mag) { y[0] /= mag; y[1] /= mag; y[2] /= mag; } #define M(row,col) m[col*4+row] M(0, 0) = x[0]; M(0, 1) = x[1]; M(0, 2) = x[2]; M(0, 3) = 0.0; M(1, 0) = y[0]; M(1, 1) = y[1]; M(1, 2) = y[2]; M(1, 3) = 0.0; M(2, 0) = z[0]; M(2, 1) = z[1]; M(2, 2) = z[2]; M(2, 3) = 0.0; M(3, 0) = 0.0; M(3, 1) = 0.0; M(3, 2) = 0.0; M(3, 3) = 1.0; #undef M glMultMatrixf(m); /* Translate Eye to Origin */ glTranslatef(-eyex, -eyey, -eyez); }

원본 출처는 아래와 같습니다:
http://iphonedevelopment.blogspot.com/2008/12/glulookat.html

저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

Landscape 모드로 개발중에 코드에서 현재 Landscape중인지 아닌지 확인을 해야하는 상황이 생겼다.
아래와 같이 Info.plist에 있는 Landscape 모드 정보를 확인하려면 UIApplication을 이용해야한다.


아래와 같이 UIApplication 인스턴스를 만들어 사용하면 현재 Info.plist에 있는 정보를 확인할 수 있다.

UIApplication * application = [UIApplication sharedApplication];

if(application.statusBarOrientation == UIInterfaceOrientationLandscapeRight){
    NSLog(@"landscapeRight");
}



저작자 표시
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요

※이 예제는 Simon Maurice의 블로그에 있는 내용을 의역(?)한 내용입니다. 본문에 대한 저작권은 Simon Maurice에게 있습니다. 해석이 후져서 이해가 어려우신 분들은 원본의 내용을 참고하시기 바랍니다.

원본
http://web.me.com/smaurice/AppleCoder/iPhone_OpenGL/Entries/2009/4/1_OpenGL_ES_06_-_Objects_in_3D.html


OpenGL ES 06 - Objects in 3D

 지금까지 두개의 2D 객체를 그리는 일을 했다. 이제 3D객체를 그릴 시간이다. 3D객체를 그리는일은 크게 어려운일이 아니다. 단지 조금 더 많은 버택스를 필요로 하거나(버택스 배열을 사용할 경우), 더 많은 변환(transformaions)을 사용할 뿐이다.(사각형을 복사해서 상자를 만들 경우)

 점, 선에 관해서 먼저 이야기를 하긴 해야하는데, 삼각형, 사각형을 그리고 택스쳐 매핑을 하는등, 이미 너무 멀리 와버렸다. 그다지 재미도 없는 도형들(점,선) 때문에 진도를 천천히 나갈 필요는 없다.

 뿐만아니라, 변환(transformation)으로 돌아가서 회전(rotation)에 관해서 조금 더 자세하게 다루는 것이 필요하다. 그리고, 이런 것들은 내가 지금까지 자세히 말하지 않은 것들 중에서는 아주 기본적인 내용들이다. 사실 이런 말들이 의미하는건 아직 튜토리얼이 더 많이 남아있다는 말이다.

시작은 drawView매소드로

 지금까지 하드코딩했던 작업물들과는 결별하고, 이제 drawView매소드를 처음의 홀가분한 상태로 바꾸자.

 drawView매소드를 아래와 같이 바꾸자:

- (void)drawView {
// Our new object definition code goes here
    [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
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
    [self checkGLError:NO];
}

 이쯤오면 지금까지 3차원 공간에서 작업을 했던것, 달리 말하면 깊이버퍼를 사용해서 작업을 했던것에 대해서 고맙게 생각해야한다. 위의 코드가 낮설지는 않을 것이다.

 3D객체의 정의

 지금부터 상자를 만들 것이다. 상자를 만드는 이유는 상자가 3D에서 일반적으로 사용되고, 택스쳐 매핑을 하고 회전시키는 일이 쉽기 때문이다. 상자를 그리기 전에 상자에 대해서 다시한번 생각해보자. 상자를 만들려먼 우리가 익히 그려왔던 사각형을 6개만 그리기면 된다. 이건 아주 쉬운일이다.
아래에 상자를 앞면부터 새로 정의하자:

    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,             // top left (at front)
        1.0, 1.0, 1.0,              // top right (at front)
        1.0, 1.0, -1.0,             // top right (at rear)

 앞면의 사각형과 방향 뿐 아니라 시작점도 같다. X축으로 상자를 90도 회전시켜보면 윗면이 앞면으로 오게된다. 이렇게 돌려놓고보면 원래의 앞면과 윗면이 삭가형을 그리는 순서가 같다는 것을 알 수 있다.

 다음은 뒷면이다:

        // 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,            // Bottom left front
        1.0, -1.0, 1.0,             // Bottom right front
        1.0, -1.0, -1.0,            // Bottom right rear
        -1.0, -1.0, -1.0,           // Bottom left rear

 마음속으로 상자를 그리고 회전시켜서 어느 순서로 그려지지는지 생각해보자.

 마지막으로 남은 왼면과 오른면을 그린다:

           // Left face
        -1.0, 1.0, -1.0,            // top left
        -1.0, 1.0, 1.0,             // top right
        -1.0, -1.0, 1.0,            // bottom right
        -1.0, -1.0, -1.0,           // bottom left

        // Right face
        1.0, 1.0, 1.0,              // top left
        1.0, 1.0, -1.0,             // top right
        1.0, -1.0, -1.0,            // right
        1.0, -1.0, 1.0              // left

 위내용에 대한 완전한 코드는 아래와 같다:

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

 좌표계에 대해서 문제가 있다면 마음속으로 상자를 그려서 구체화 해본다. 이래도 이해가 잘 안가면 종이에 상자를 그려서 이해해 보려고 해보자. 지금부터는 3D객체에 대한 이해가 정말 필요하다. (하지만 난 아무리 생각해도 그림이 아래처럼밖에 안그려진다;; 그래서 위에 주석을 이해할 수 없다.-_-;;)


 내가 Our new object definition code goes here 라고 주석 달아놓은 곳에 이 버택스 배열을 집어넣는다.

 상자 그리기

 상자를 그리는 쉬운 방법은 이전처럼 코드를 이용해서 상자를 그리게 하는 것이다. 지금은 그냥 3D객체를 그리는 방법만 알려줄 것이고, 나중에 더 고차원적인 방법으로 그림을 그려보도록 하자.

 우선 익숙한 코드를 통해 그리기를 시작해보자.
Our new drawing code goes here 라고 주석 달린곳에 아래 코드를 넣자:

    glLoadIdentity();
    glTranslatef(0.0, 0.0, -6.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);

 이것 역시 전혀 새롭지 않을 것이다. OpenGL에게 사각형을 그리고 사각형의 색갈을 빨간색으로 그리라고 알려주었다. 이제 다음 4개의 버택스로 상자의 윗면을 그려보자:

// Draw the top face in green
glColor4f(0.0, 1.0, 0.0, 1.0);
glDrawArrays(GL_TRIANGLE_FAN, 4, 4);

glDrawArray()함수를 모자. 함수의 파라미터가 어떤 일을 하는지 기억한다면 두번째 파라미터가 도형을 그리는 첫번째 버택스를 지정한다는 것을 알고 있을 것이다. 두번째 사각형을 그리는 버택스가 버택스 배열의 4번째에 있으므로(cubeVertices[4]) 이렇게 값을 넘긴다.

 뒷면을 그리자:

    // Draw the rear face in Blue
    glColor4f(0.0, 0.0, 1.0, 1.0);
    glDrawArrays(GL_TRIANGLE_FAN, 8, 4);

 같은 방법으로, cubeVertices[8]이 뒷면을 이루는 사각형의 첫 버택스가 있는 곳이다. 나머지 세 사각형도 같은 방법으로 그린다.

    // 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);

 우리가 한 것은 컬러셋을 바꾸고, glDrawArray()함수의 두번째 파라미터를 바꾼것밖에 없다.

 빌드를 하고 실행을 하면, 상자는 보이지 않고 빨간색 사각형만 보일 것이다. glLoadIdentity()함수 위로 가서 아래 코드를 추가하자:

 rota += 0.5;

 전에 만들어놨던 rota 변수이다. 뿐만아니라 전에 사용했었던 glRoataef()함수도 필요하다. glTranslatef()함수 뒤에 아래 코드를 추가하자:

         glRotatef(rota, 1.0, 1.0, 1.0);

 빌드 하고 실행하면 아래와 같은 결과를 볼 수 있다:


 3D객체의 세계에 온것을 환영한다.

 택스쳐를 입쳐보면 어떨까?

 지금까지 우리가 한 작업은 객체에 색만입히는 아주 간단한 일이었다. 지난 예제처럼 택스쳐를 사용해서 상자에 6면 모두에 택스쳐를 입혀서 상자를 더 재미있게 만들어보자.

 아직 지난 시간에 했던 택스쳐 로딩 코드를 건드리지 않았으니, drawView 매소드를 수정하기만 하면 된다. 이제 얼마나 빨리 텍스쳐 매핑을 할 수 있는지 한번 봤으면 한다.

 일단 지난 시간에 했던 내용을 기억해보자:

     const GLshort squareTextureCoords[] = {
        // Front face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right

  한 면에 택스쳐를 매핑하는데는 괜찮았었다. 이 내용을 조금 더 확장해야 할 필요가 있다. 어렵지 않다. 전에 어떻게 좌표계를 계산했는지 되짚어보자. 그리고 상자를 정의할때도 그리는 순서도 똑같다. 아마 어떻게 할지에 대한 방법을 찾았을 것이다.

 상자에 택스쳐를 입힐때의 택스쳐좌표의 오프셋과 상자의 버택스의 오프셋은 같아야 한다. 결과적으로 상자의 여섯면에 택스쳐를 입히기 위해서는 아래처럼 4개의 좌표를 5번씩 더 입력해 주어야만 한다:

     const GLshort squareTextureCoords[] = {
        // Front face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
      
        // Top face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
      
        // Rear face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
      
        // Bottom face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
      
        // Left face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
      
        // Right face
        0, 1,       // top left
        0, 0,       // bottom left
        1, 0,       // bottom right
        1, 1,       // top right
    };

 코드에 붙여놓자.

 이제 그림을 그리는 코드 몇줄만 더 추가하면 택스쳐를 상자에 매핑할 준비가 끝난다.

 먼저 아래의 코드를 추가한다:

     glTexCoordPointer(2, GL_SHORT, 0, squareTextureCoords);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);

 어렵지 않다. 앞에서 상자를 그리는데 사용했던 glColor4f()함수를 지우지 말고, 빌드후에 실행해보자:


 택스쳐 매핑된 3D객체가 회전하고 있다.

 택스쳐 맵 이미지의 크기

이 튜토리얼에서 말을 한다는 것을 까먹고 있었다. 택스쳐의 크기가 2의 제곱 크기어야 한다는 것을 말하지 않았다. 택스쳐의 가로, 세로 크기는 1, 2, 4, ... 32, ... 512, 1025 이어야 한다. 가로, 세로의 크기는 같을 필요는 없지만 어찌되었건 2의 배수 이어야 한다. 그래서 32 x 512는 64x64와 같아서 허용되지만, 30x30은 허용되지 않는다.

glRoatatef()와 니가 만든 객체

 다시말하지만, 예제 말고 다른것으로 객체를 만들어보길 바란다. 객체의 중점은 항상 (0, 0, 0) 이라는 것에 눈치 챘나? 예제에서
두 객체의 중점은 항상 (0,0,0) 에 위치했다. 이런 이유는 OpenGL이 객체를 회전시킬 때 모델매트릭스의 중점으로 회전을 시키기 때문이다. OpenGL은 객모델의 중점을 (0,0,0)으로 조정하지 않고 회전시킨다. 그래서 만약 객체가 모델매트릭스의 중앙에 위치하지 않으면 객체는 한쪽으로 기울여진체로 회전할 것이다.

오늘할일은 여기까지!

 오늘 할 예제는 끝났다. 재미있고, 유익한 시간이었길 바란다. 더 궁금한게 있으면 메일을 보내기 바란다.

 지나고나서 보니 상자의 버택스의 순서를 반시계 방향으로 바꿔야 했다. 신경쓰지말자. 다음 예제에서는 반시계 방향과 시계뱡향에 대해서 조금 더 자세히 다루고, 사각형을 반시계 방향으로 그려볼 것이다.

아래에 이번튜토리얼의 예제가 있다:


저작자 표시 비영리 변경 금지
크리에이티브 커먼즈 라이선스
Creative Commons License

댓글을 달아 주세요