상세 컨텐츠

본문 제목

OpenGL 이동,회전,확대축소 관련

프로그래밍 관련/3D,2D DRAW 관련

by AlrepondTech 2020. 9. 10. 04:39

본문

반응형

 

 

 

=======================

=======================

=======================

 

 

 

출처: http://www.gpgstudy.com/forum/viewtopic.php?t=3537

 

 

[Open GL] PopMatrix PushMatrix에 대한 개념이 이해가 잘 안가

전체글 글쓴이: 비회원 » 2004-06-12 15:17

안녕하세요

제가 보는 오픈지엘 책에 나오는 예제중에

태양계 만드는 것이 있는데.. 그중에 궁금한 점이

태양을 가운데 놓고, 주변에 지구,달, 금성, 목성같은것을

만들어서 태양을 중심으로 도는 태양계 만드는 것이 있습니다.

코드중에서 태양계를 만들때 PushMatrix와 PopMatrix를 많이 사용하는데

그 개념이 잘 잡히지 않네요.. 이것을 이용해서

glRotate(xRot , 1.0 , 0.0, 0.0);
glRotate(yRot , 0.0 , 1.0, 0.0);
를 태양그리는 코드쪽에 집어넣어서 키보드로 태양만 돌리게 만들고

마우스를 움직여서

태양계전체를 이동하면서 볼수있는 네비게이션을 만들고자 할때도

푸쉬 팝 메트릭스를 이용해서 만들수 있나요?

푸쉬, 팝을 어디에 배치시켜야하는지를 잘 몰라서 

마우스조작시 주위 달이나 지구 같은것들만 빨리 돌고

둘러볼수가 없는데 뭘 더 생각해서 짜야할까요..^^

도와주시면 감사하겠습니다.

상위

비회원

전 오픈지엘을 먼저 공부했더래서

전체글 글쓴이: 비회원 » 2004-06-13 13:37

다이렉트 3D 방식이 잘 이해가 안갔었더라는...

오픈지엘은 그냥 써있는데로 순서대로 실행된다라고 이해하시면 알수 있을듯

상위

 

douner전체글: 35가입일: 2002-04-13 09:00

 

전체글 글쓴이: douner » 2004-06-13 16:03

예를 들어서 설명해 드릴께요.^^

컴퓨터 프로그래밍에서 함수를 호출하면 컴퓨터는 내부적으로 어떻게 합니까?

기존에 실행하던 코드 위치(주소)를 스택에 저장하고 나서 호출한 함수를 실행합니다. 그리고 나서 함수 실행이 끝나면 이전

 위치로 돌아와서 실행하던 코드를 계속해서 실행해야 합니다. 이때 스택에 보관해 두었던 주소값을 꺼내서 사용하면 됩니다. 

스택이 없었다면 이전 위치로 돌아오는 코드가 복잡해 졌겠죠?

그럼 'Y' 자 모양을 그린다고 생각해 봅시다.

우서 | 를 그립니다. 쉽죠. 수직선을 그리면 됩니다. 근데 이제 골치아파 집니다. V 를 그려야 하는데 어떻게 그려야할까요? 

우선 왼쪽으로 30도(V 자 사이가 60도라고 가정) 회전하고 \ 를 그립니다. 그리고 나서 오른쪽으로 60도 회전한다음 /

 를 그립니다. 이대로 하면

/
\
|

이렇게 됩니다. 아~ V 자의 중심으로 다시 돌아오는 것을 실수로 빼먹은 것입니다.! 어떻게 다시 V 의 중심으로 돌아 올 수 있을까요? 

스택을 이용하면 쉽겠죠? \ 를 그리기 전에 즉 왼쪽으로 30도 회전하기 전에 현재 위치를 스택에 보관합니다.(push) 그리고 

나서 \ 를 그리고 V 의 중심으로 돌아가 위해서 스택에서 이전 위치를 꺼내옵니다.(pop) 그럼 오른쪽으로 30도 회전한다음 / 를 

그리면 되겠죠. 그럼 아래처럼 Y 자가 그려질 것입니다.

\/
|

이것을 openGL 에서 생각해 봤을 때 glPushMatrix() 와 glPopMatrix() 는 현재 행렬을 스택에 보관하는 것입니다. 행렬의 역할을 

무엇입니까? 바로 변환입니다. 벡터를 변환하는데 행렬을 사용합니다. 벡터의 이동, 회전, 크기 변환 등을 말하죠.

위의 예제를 들면

코드: 모두 선택

drawLine(); //직선을 그린다. glPushMatrix(); //|를 그리고 난 다음의 현재 위치(변환) 정보를 담고 있는 행렬을 스택에 저장한다. rotate(30); //왼쪽으로 30도 회전한다. drawLine(); // \ 직선을 그린다. glPopMatrix() // / 를 그리기 위해서 | 를 그린 다음 위치로 다시 돌아온다. rotate(-30); //오른쪽으로 30도 회전한다. | 그리고 난 후의 위치로 돌아 왔으므로 60 회전을 할 필요가 없습니다. drawLine(); // / 직선을 그린다.

그럼 책에 나오는 태양계를 그린다고 할 때

(태양) (지구) (달)

이라고 했을 때 우선 태양을 그리기 위해서 원점(전역 좌표)에서 오른쪽으로 3만큼 움직인 후 그려봅시다.

---(태양)------------

이제 지구를 그립니다. 다시 원점부터 그려야할 까요? 아니죠. 태양의 위치에서 위치를 재서 그리는게 더 편하겠죠? 지구가 

태양으로부터 4 위치만큼 떨어져 있다고 하면

코드: 모두 선택

glTranslate3f(3.0f, 0.0f, 0.0f); drawSun(); glPushMatrix(); glTranslate3f(4.0f, 0.0f, 0.0f); drawEarth(); glPopMatrix();

---(태양)----(지구)----------

이제 달을 그려야합니다. 이 역시도 지구의 위치에서 시작해서 그리는게 편하겠죠. 달이 지구로부터 1 만큼 떨어져 있다고 하면

코드: 모두 선택

glTranslate3f(3.0f, 0.0f, 0.0f); drawSun(); glPushMatrix(); glTranslate3f(4.0f, 0.0f, 0.0f); drawEarth(); glPushMatrix(); glTranslate3f(1.0f, 0.0f, 0.0f); drawMoon(); glPopMatrix(); glPopMatrix();

---(태양)----(지구)-(달)---------

이렇게 할 수 있겠죠. 다 그렸는데 태양 주위에 화성을 다시 그려야 한다면(코드를 수정할 수 없고 추가할 수 있다고만

 가정) 그냥 끝에다 추가하면 되겠죠.

코드: 모두 선택

glTranslate3f(3.0f, 0.0f, 0.0f); drawSun(); glPushMatrix(); glTranslate3f(4.0f, 0.0f, 0.0f); drawEarth(); glPushMatrix(); glTranslate3f(1.0f, 0.0f, 0.0f); drawMoon(); glPopMatrix(); glPopMatrix(); //여기는 태양을 그리고 난 다음의 변환 행렬이 적용됩니다. glTranslate3f(1.0f, 0.0f, 0.0f); drawMars();

이게 가능한 이유는 glPushMatrix() 가 행렬을 스택에 push 할 때 스택의 top 에 있던 행렬과 곱하기 때문입니다. 행렬의 곱은 

변환의 합성입니다. 그러니 이전에 변환했던 내용을 계속 유지해가며 쉽게 쉽게 그릴 수 있습니다.

열혈.도우너~^^

상위

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://m.blog.naver.com/kzh8055/140041841823

 

 

흠...
앞서 3D의 좌표 체계와 변환에 대해 허접스레 다뤄봤었다.
뭐 이건 3D 프로그래밍을 다룬 책이라면
OpenGL 이건 Direct3D 관련이건 어디에서나 책 첫머리에 나와있으니
참고해 보길 바란다.

하여간 이제부터 OpenGL의 변환에 대해서 살펴 보겄다.
OpenGL 에서의 변환을 제대로 파악하기 위해 반드시 짚고 넘어가야 할 부분이 있는데 
그건 실제 내부적으로 이뤄지는 변환과 그를 위한 명령어 호출 순서가 완전히 반대라는 것이다.
그러니까 실제로 정점을 처리는 하는 순서는

 

모델 좌표 --[모델 변환]--> 월드 좌표 --[뷰 변환]--> 시점 좌표

 

인데 반해 동일한 작업을 위한 OpenGL의 명령어 호출 순서는

 

시점좌표계 --[뷰 변환]--> 월드 좌표계 --[모델 변환]--> 모델 좌표계

 

이렇게 된다는 것이다.

본인의 판단으로는 아마도 바로 이 부분이
초보 OpenGL 프로그래머가 변환에 대해 가장 혼란스러워하는 부분이 아닐까한다.

아니 그냥 Rendering Pipe Line 순서 대로 명령어를 호출하면 될껄 
왜 구지 반대로 호출하게 만들어 왜 일을 크게 벌이냐!

라고 따질수도 있겠지만 이는 나름대로 이유가 있고 일단 이 방식에 적응하면
그런 불만은 금새 수그러들것이다.

 

일단 알아둬야 할 중요한 개념은
OpenGL에서의 변환 Command들은 각 정점이 아닌 좌표축에 가해진다는 것이다.

 

 

 

 

위의 그림에서 왼쪽이 현재의 좌표 축이었다면 여기서 변환 Command(glTranslatef())를

호출하면 좌표축 자체가 이동 변환된다.

비단, glTranlatef()와 같은 이동 변환 뿐만아니라 회전(glRotatef()),

크기 변환(glScalef()) 또한 마찬가지다.

 

이 처럼 OpenGL에서의 변환은 정점에 가해지는 것이 아닌 좌표축에 가해진다. 

 

그러면 이를 바탕으로 좌표계의 변환에 대해 알아 보겄다. 

 

 

 

 

glLoadIdentity();

 

gluLookAt(...);

 

glTranslatef(...);
glRotatef(...);

 

glBegin();
 glVertex3f(...);
 glVertex3f(...);
 ....
glEnd();

 

 

 

 

위 소스에서 glLoadIdentity() 함수는 모델 뷰 행렬을 단위(항등) 행렬로 Setting하라는

명령어 인데 일단, 이렇게만 알아둬라.

모든 좌표축,즉,시점 좌표축,월드좌표 축,모델 좌표축을 동일하게 만드는 것이라고.

 

다음의 gluLookAt(...) 함수는 간단히 말해 시점 (카메라) 좌표축 의 위치와 방향을

설정해주는 함수다.여기엔 총 9가지의 인자가 들어가는데

처음 3가지는 시점 좌표계의 원점의 위치 좌표이고 가운데는 시점 방향 Vector

그리고 나머지 3개는 위 방향을 가르키는 Vector(보통 Up Vector라한다)다.

 

gluLookAt(...)함수에 첫번째 3인자가 시점 좌표축의 원점 위치를 나타낸다고 했는데

그럼 어떤 좌표 축을 기준으로 둬서 시점 좌표축의 원점 위치를 잡는다는 말인가? 

그건 바로 월드 좌표축 기준이다.

 

이것을 역으로 시점 좌표축 원점을 기준으로 월드 좌표축의 위치를 정한다고 해보면

gluLookAt(...) 함수의 처음 3개 인자의 반대(각 값에 -1을 곱한)에 위치 하게될것이다.

 

즉, glLoadIdentity() 를 호출해 3계의 좌표축이 모두 동일 공간상에 있는 상황에서

gluLookAt()함수를 호출하면 World 좌표계가 분리 되는 것이다.

이때의 World좌표축 원점의 위치는 위에서 언급한대로다.

 

OpenGL에서의 좌표 변환에 대한 순서를 다시 보자면 

 

시점좌표계 --[뷰 변환]--> 월드 좌표계 ...

 

위에서 보다시피 시점 좌표계에 [월드 변환]이 가해지면 월드 좌표계가 된다.

바로 gluLookAt() 함수를 호출함으로써 [월드 변환]을 가하는 것이다.

 

하여간 다음 줄로 내려가 보자.

 

glTranslatef(...);
glRotatef(...);

 

앞서 잠깐 언급했다시피 glTranslatef()는 이동변환, glRotatef()는 회전 변환을 뜻한다.

또 그 변환의 대상은 좌표축이라고 말했었다.

그리고 다음 줄을 보면  glBegin()이 나오고 그 다음줄을 보면 glVertex3f()함수가 나오는데

이것은 정점위치를 지정해주는 함수다.

 

현재 기준 좌표축은 월드 좌표축이다. 이 상태에서 좌표축을 이동(glTranslatef())하고

회전(glRotatef())한 후 그 변환된 좌표축을 기준으로 정점 위치를 표시(glVertex3f())한다. 

 

glLookAt()함수 이후(시점 좌표축에서 월드 좌표축으로 변환뒤) 부터

glBegin() 전까지( 정점을 정의(모델링)하기 직전) 까지의 변환을 모델 변환이라 할수 있다.

 

또 다시 OpenGL에서의 좌표 변환에 대한 순서를 보자면 

 

... 월드 좌표계 --[모델 변환]--> 모델 좌표계

 

즉, 월드 좌표축에서 모델 좌표축으로 변환하기 위해서 모델 변환이 가해진다.

이 상태가 마지막 종착역(?)인 모델 좌표축이다.

여기서 glVertex3f()함수를 통해 현재 기준 좌표축인

모델 좌표축 기준으로 정점들을 지정한다.

 

 

 

glLoadIdentity();

 

<시점 좌표계 기준>

 

gluLookAt(...);

 

<월드 좌표계 기준> 

 

glTranslatef(...);
glRotatef(...);

 

<모델 좌표계 기준>

 

glBegin();
 glVertex3f(...);
 glVertex3f(...);
 ....
glEnd();

 

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: http://m.blog.naver.com/buzzjy/20121737629

 

 

svn checkout --username anonsvn https://dev.naver.com/svn/gl2d/trunk/GL2D

password는 anonsvn

 

 

 

이번엔

좌표이동, 회전, 확대축소를 해보겟습니다.

 

 

 

 

 

1.우선 여지껏 쓰던 프로젝트에서 변경할곳이 두군데 있습니다.

2D용으로 glOrtho2D라는게 있지만 아이폰에선 사용할수없어서 몰랐는데

glOrthof에 뷰포트좌표를 입력하면 2D좌표 그대로 쓸수있더군요;

밑에처럼 바꾸면 굳이 좌표계산을 할필요가 없습니다 ㅎ;(과감히 삭제)

이번에 설정한수치는 정중앙이 아니고 화면 왼쪽상단이 원점입니다.

(-160,160,-240,240으로 설정하면 정중앙이 원점)

 

그리고 버텍스의 크기에서 렌더링좌표를 빼고 순수하게 크기만 설정합니다.

좌표는 렌더링할때 새롭게 설정합니다.

 

 

 

 

 

 

 

 

 

2.새롭게 추가한것은

glPushMatrix(),glPopMatrix() 의 셋트와

glTranslatef()이동용

glRotatef()회전용

glScalef()확대축소용

딱 5개만 추가하면 됩니다만 

아마 설명이 더길듯 합니다.

 

 

 

 

 

 

기본적으로 오브젝트(모델)는

glMatrixMode(GL_MODELVIEW)를 설정하고

glLoadIdentity()로 매트릭스를 초기화 합니다.

그이후에 오브젝트에 아핀변환(좌표이동,회전,확대축소)을 사용하게되는데

매트릭스가 하나밖에 존재하지않아서

이동을 하던 축소를 하던 모든 계산이 축적이 되버려 다른오브젝트까지 영향을 미치게 됩니다.(355,358라인)

 

그래서 필요한게 glPushMatrix()와 glPopMatrix()입니다.

이 두개의 함수안에서 설정한 아핀변환은 GL_MODELVIEW에 영향을 끼치지 않습니다.

그래서 보통은 위의 예저처럼 1개의 오브젝트당 변환전에push해주고 렌더링 끝나면 pop해줘서 삭제시켜버립니다.

 

그럼 이번엔 아핀변환을 해봅시다.

이론적인 순서는

1.확대축소

2.회전

3.이동

인데 gl에선 꺼꾸로 적어야합니다.(사실은 DirectX가 거꾸로겟죠 ㅋ_ㅋ)

매트릭스의 성질중에 하나인 ( AB  

 BA )가 이유인데

자세한건 위키페디아나 다른싸이트를 참조하세요 ㅎㅎ;

대게 함수에서 파라메터가 3개가 나열되면 이변이 없는한 순서대로 x, y, z 입니다.

 

이동은 2D좌표를 쓰니 그대로 쓰시면 됩니다.

일반 모니터와같은 화면 왼쪽위에가 원점입니다~

 

회전은 회전각도와 어느축으로 회전할것이냐를 설정합니다.

2D에서 말하는 회전은 모두 Z축회전이기에 마지막에 1.0을 써줍니다.

회전각도는 radian이 아닌 angle이므로 변환해줄필요는 없습니다!!!

 

스케일은 1.0이 그대로 이고 2.0이면 2배 0.5이면 1/2배 를 나타냅니다.

그닥 사용빈도는 적습니다만 기본값은 0.0이 아니고 1.0이라는건 기억해둡시다!

 

이걸로 좌표이동,회전,확대축소는 끝입니다만

이상태론 게임에선 써먹을수가 없네요

회전할때 원점이 각각 왼쪽모서리기 때문이죠

 

다음번엔 텍스쳐의 정가운데를 원점으로 해서 회전을 해보겟습니다.

 

 

 

잡담.

gl의 마지막에 붙어있는 f 는 OpenGL의 규칙으로 파라메터의 형을 나타냅니다.

f는 float, i는 integer, d는 double, v는 포인터

게임에선 double쓸일은 거의 없으니 항상 f를 써주시고

숫자뒤엔 항상 0.0f, 1.0f 이런식으로 f를 붙혀주세요

f가 없으면 컴파일러는 숫자를 double로 인식을 하여 쓸모없는 형변환을 거치게 됩니다.

똑똑한 컴파일러는 자동으로 float로 바꿔주는지는 몰겟네요 ㅎㅎ;

 

 

 

 

반응형

 

728x90

 

 

 

 

=======================

=======================

=======================

 

 

 

 

 

출처: http://m.blog.naver.com/utez/90115408354

 

 

 

 

오늘 포스팅할 내용은 변환입니다.

면접은 다떨어지고 취업도 못하고 이렇게 블로그에 포스팅만 하면서 사는 내 인생을 변환 시켜볼까 해서 포스팅 합니다.

T.T...

 

변환은 상당히 어렵습니다...

어렵다기 보다는 이해하기가 힘이 듭니다.

OpenGL에서 변환은 행렬의 곱을 통해서 이루어 집니다.

골치가 아프죠.

일반적으로 버텍스를 포함하는 벡터 행렬에 변환 방식을 담고 있는 행렬을 곱해서 나타낼 수 있습니다.

 

 

 

다양한 행렬들..

 

예를 들어 x,y,z로 표현할 수 있는 지점에 점이있다고 할 때 이 점을 지정된 각도 만큼 회전시키거나 방향을 바꾼뒤에 어떻게 변하는지 알고 싶다면 행렬을 이용하면 됩니다.

이렇듯 간단한 방정식으로는 표현하기 힘들며 행렬은 이를 표현해주는 강력한 도구로 사용이 됩니다.

 

저 수학 잘 못하는데요?

 

겁먹을 필요 없습니다.

대부분은 변환에 대한 각 기능을 하는 함수가 OpenGL에서 제공되기 때문입니다.

그럼에도 불구하고 변환을 이해하기 위해서 개념을 배우는 것은 앞으로 이 배운 내용을 가지고 응용하여 사용하기 때문입니다.

 

이번 포스팅에서는 개념적인 부분은 설명하지 않겠습니다.

앞으로도 그럴것 같습니다.

개념에 대한 설명은 OpenGL 변환 이라고 검색만 하시면 아주 훌륭하게 포스팅된 자료들을 금방 찾으실 수 있기 때문이고 처음 제가 포스팅하는 목적과도 거리가 멀기 때문입니다.

(어려운 것도 있고.. 실력이 부족한 것도 있고...;;)

참고로 제가 OpenGL을 포스팅하는 가장 큰 목적은 공부했던 내용을 정리하고, 개념적인 이해 없이도 가볍고 쉽게 OpenGL을 다루어 보자 라는 있습니다. ^^;;;;;;

 

업로드 된 예제를 중심으로 설명하겠습니다.

지난 예제에서 바뀐 부분이 거의 없습니다.

Vecter 클래스도 그대로 포함되어 있고 단지 벡터를 시각적으로 표현해 주기 위해서 라인을 그려준 부분이 있는데 그 부분 삭제 하였습니다.

그리고 큐브가 2개 더 추가 되었습니다.

 

void display()
{
 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);



 glPushMatrix();
  GLfloat temp_matrix[16];
  glRotatef((float)mouse_dx*0.3f,0,1,0);
  glRotatef((float)mouse_dy*0.3f,1,0,0);
  glGetFloatv(GL_MODELVIEW_MATRIX,temp_matrix);
 glPopMatrix();



 glPushMatrix();
  glLoadMatrixf(temp_matrix);
  glMultMatrixf(matrix);
  glGetFloatv(GL_MODELVIEW_MATRIX,matrix);

  
 glPushMatrix();
   glTranslatef(3,0,0);
   colorcube();
  glPopMatrix();



  glPushMatrix();
   glTranslatef(-3,0,0);
   glScalef(0.5,0.5,0.5);
   colorcube();
  glPopMatrix();



  glPushMatrix();
   glRotatef(60,0,0,1);
   colorcube();
  glPopMatrix();



  Draw_Axis();
 glPopMatrix();


 glutSwapBuffers();
}

먼저 OpenGL에서 모든 모델은 4X4 행렬인 모델뷰 행렬로 표현된다고 생각하시면 될것 같습니다.

이 모델뷰 행렬은 매 프레임마다 누적이 됩니다.(초기화하지 않는 이상)

**모델뷰 행렬은 프로그래머가 따로 선언해 주는 것이 아니라 OpenGL API안에 숨어? 있습니다.

따라서 그 누적된 모델뷰 행렬에 모델의 버텍스가 곱해짐으로 최종적인 버텍스의 위치가 표현 되는 것입니다.

복잡한가요?.. ㅎ_ㅎ

 

위 소스를 이해하기 위해서 알아두어야 할 함수가 3가지 있습니다.

바로 glTranslatef()와 glRotatef(), 그리고 glScalef() 입니다.

하나의 버텍스가 어느 방향으로 이동 혹은 회전을 하기 위해서는 그려기지 전에 적절한 모델뷰 행렬과 곱하는 과정을 거쳐야 하는데 이 함수를 이용하면 간단하게 해결 됩니다.

 

glTranslatef(float x,float y,float z)

예전에도 설명한 적이 있는듯 한데...

이 함수는 x,y,z,로 이동할 만큼의 거리를 인자로 받아서 이동시켜 주는 함수입니다.

한 물체에만 적용되는 것이 아니라 이 함수 밑으로 나오는 모든 모델에 적용됩니다.

 

glRotatef(float angle, float x, float y, float z)

x,y,z 로 표현되는 벡터(혹은 축) 중심으로 지정된 각도만큼 회전 시켜 줍니다.

이 역시 한 모델에만 적용되는 것이 아니라 모든 모델에 적용됩니다.

 

glScalef(float x, float y, float z)

각 축에 대해서 지정된 배율만큼 늘리거나 줄일 수 있습니다.

역시 한 모델이 아닌 모든 모델에 적용됩니다.

 

위 3개의 함수 전부 밑으로 그려지는 모든 모델에 적용된다고 했는데 모델뷰 행렬이 누적되기 때문입니다.

예를 들어 translate와 rotate 그리고 translate를 사용한다고 했을때

 

 

 

현재 모델뷰 행렬에 순서대로 각 변환에 대한 행렬이 곱해져 나중에는 모델뷰 행렬이 누적되게 되어 밑에 그려지는 모든 모델에 적용이 되는 것입니다.

어때요? 참 쉽죠? --;;

 

glPushMatrix(), glPopMatrix()는 예전 3D 큐브 그리기에서 포스팅 한 적이 있습니다.

glPushMatrix()는 현재 모델뷰 행렬을 저장합니다.

그리고 그 밑으로 그려지는 것들에 대해서 현재 모델뷰 행렬을 가지고 다시 누적시켜 가면서 표현합니다.

마지막에 glPopMatrix()를 만나면서 그동안의 누적된 행렬은 없어지고 glPushMatrix()에서 저장했던 행렬을 다시 불러오게 됩니다.

즉 glPushMatrix()와 glPopMatrix() 사이에 그려지는 모델들은 외부에 그려지는 모델들의 매트릭스에 독립적으로 그려지게 되는 것입니다.

 

 

 

 

 

 

이제 슬슬 코드를 분석해 볼까요?

 glPushMatrix();
  GLfloat temp_matrix[16];
  glRotatef((float)mouse_dx*0.3f,0,1,0);
  glRotatef((float)mouse_dy*0.3f,1,0,0);
  glGetFloatv(GL_MODELVIEW_MATRIX,temp_matrix);
 glPopMatrix();

먼저 이부분을 보겠습니다.

glPushMatrix()를 통해서 현재 행렬을 저장합니다.

GLfloat 형으로 배열을 선언합니다.

다음으로 glRotatef를 통해서 y축만큼 회전, x축만큼 회전 시키고 있죠?

여기가 바로 마우스 드래그를 통해서 모델을 회전 시키는 부분입니다.

이렇게 모델뷰 행렬에 회전된 값을 누적시킨다음

glGetFloatv()를 통해서 현재 모델뷰 행렬을 temp_matrix에 저장합니다.

여기까지는 실질적으로 모델을 회전시키는 것이 아니라 현재 모델뷰에 회전 행렬을 누적시키고 그 값을 temp_matrix에 저장한다음 glPopMatrix()를 통해서 다시 원래 모델뷰를 불러오게 됩니다.

 

  glLoadMatrixf(temp_matrix);
  glMultMatrixf(matrix);
  glGetFloatv(GL_MODELVIEW_MATRIX,matrix);

glLoadMatrixf()는 인자로 전달되는 행렬을 모델뷰행렬로 불러옵니다.

위에서 회전 연산에 의해 누적된 행렬이 저장된 temp_matrix를 다시 불러오고 있습니다.

glMultMatrixf()는 현재 모델뷰 행렬에 인자로 전달된 행렬을 곱합니다.

마지막으로 glGetFloatv를 통해서 현재 모델뷰 행렬을 matrix에 저장합니다.

즉 저 위에서 생성된 회전이 누적된 행렬(temp_matrix)을 현재 행렬(matrix)과 곱해서 그 값을 누적시켜 밑에 나오는 모델들을 누적된 행렬에 맞춰서 회전 시켜 주는 역할을 담당하는 것입니다.

   

  glPushMatrix();
   glTranslatef(3,0,0);
   colorcube();
  glPopMatrix();

 
  glPushMatrix();
   glTranslatef(-3,0,0);
   glScalef(0.5,0.5,0.5);
   colorcube();
  glPopMatrix();

 
  glPushMatrix();
   glRotatef(60,0,0,1);
   colorcube();
  glPopMatrix();

이제 Push, Pop은 지겹게 보셨으니깐 아시겠죠?

위 코드에서 Push와 Pop 사이의 Translate와 rotate, Scale은 전부 독립적으로 사용되기 때문에 다른 모델에 영향을 미치지 않습니다.

그렇기 때문에 3개의 큐브는 각각의 변환에 따라 최종적으로 출력이 됩니다.

 

정리를 해보자면 OpenGL이라는 세계에는 모델뷰라는 행렬이 존재하고 이 행렬에 모델의 버텍스를 곱함으로써 모델의 위치가 표현됩니다.

이 모델뷰 행렬은 OpenGL API안에 숨어 있으며 사용자가 원할때는 그 값을 불러오거나 변경할 수 있습니다.

모델뷰 행렬은 glTranslate나 glRotate, glscale에 의해서 누적되며 그 누적된 결과는 밑에 나오는 모든 모델들에 적용됩니다.

glPushmatrix와 glPopMatrix는 현재 행렬을 저장하고 불러오기 때문에 그 사이에서는 독립적인 행렬공간을 만들 수 있으며 이 둘은 항상 한쌍으로 이용되어야 합니다.

 

이 변환과 모델뷰 행렬에 대한 이해는 중요합니다.(맨날 중요하데... --;;)

이번 포스팅을 통해서 이해를 못하겠다면 다른 블로그를 뒤져서라도 꼭 이해하시기 바랍니다.

특히 Push, Pop, 그리고 Translate, Rotate의 사용은 꼭 익혀두시는 것이 좋고 Translate와 Rotate의 사용 순서가 다르면 결과도 다르게 나온다는 것도 꼭 상기하시기 바랍니다.

위 내용을 연습하기 위해 가장 좋은 방법중에 하나는 태양계를 구현하는 것입니다.

가운데 태양을 두고 수금지화목토천혜명을 순서대로 그린다음 자전과 공전을 구현하는 것이죠.

제가 수업시간에 만들었던 예제와 태양계 예제도 함께 올리도록 하겠습니다.

 

질문과 지적을 환영합니다~ ^^/~

 

 

 

=======================

=======================

=======================

 

 

 

 

출처: https://libsora.so/posts/text-billboard/

 

3차원 공간에 2차원 객체 렌더링

2013/04/21 By @if1live

 

 

 

 

디버깅 용도(예를 들면, 물체의 좌표값, 게임객체의 상태등을 표시)로 3차원 공간상에 2차원 객체(텍스트 포함)를 그냥 출력하고 싶다. 이렇게 사용하기 위해서는 세가지 조건을 만족해야한다.

첫번째는 카메라의 위치가 달라지더라도 빌보드처럼 기울어지지 않아야한다. 카메라 방향따라서 텍스트 방향이 달라지면 기울어지거나 뒤집때도 있을텐데 이걸 어떻게 읽을 수 있겠는가?

두번째는 거리에 상관없이 동일한 크기로 나와야한다. 디버깅 용도인데 멀리있는 텍스트라고 작게나오고 가까이에 있다고 크게 나올 필요가 있는가? 읽기 쉬우려면 멀리있건 가까이에있건 동일한 크기로 나오는편이 좋다

세번째는 3차원 공간상의 Z좌표까지 유지되어야한다는 점이다. Z좌표를 씹고 렌더링하면 텍스트의 깊이감이 사라져서 제대로된 좌표에 렌더링된것인지 확인하기 어렵다. (상황에 따라서는 깊이를 무시하고 렌더링하는게 필요할때도 있겠지만 그것을 기본값으로 하는건 좀 어색하다)

이를 적절히 구현한 예제소스를 정리해서 공개한다.

Repo

설명

준비사항

  • Projection / View / Model Matrix를 적절히 관리한다. 좌표계산이 이것이 필요하다. 행렬을 따로 관리하지 않고 고정파이프라인 함수호출로 바로 사용한 경우, glGetFloatv(GL_PROJECTION_MATRIX, Persmatrix); glGetFloatv(GL_MODELVIEW_MATRIX, MVmatrix);를 통해서 행렬을 얻어올수 있지만, 속도가 많이 느려지기 때문에 추천하지 않는다

단계별 설명

  1. Projection / View / Model Matrix를 합쳐서 Projection-View-Model Matrix(이하 MVP Matrix를 만든다. 좌표계산에서 각각의 행렬을 독립적으로 사용하진 않으니까 묶어놓는게 간단하다)
  2. 3차원 좌표를 최종 렌더링 결과 좌표로 바꾼다. MVP Matrix * vec4(x, y, z, 1)을 하면 구할 수 있다.
  3. 동차좌표계 변환. w속성은 달고있어봣자 복잡하기만 하다. w=1로 바꿔주기 위해서 변환된 3차원 좌표의 각 속성을 w로 나눠준다.
  4. OpenGL 좌표계는 화면에서 나오는 방향이 +Z이다. 그런데 Projection 좌표계에서는 화면에서 나오는 방향이 -Z이다. OpenGL 좌표계를 가지고 2차원 객체를 렌더링할거니까 z값을 뒤집는다
  5. Projection 좌표계에서 보여질 영역은 x=-1~+1, y=-1~+1, z=-1~+1이다. 하지만 2차원 객체는 x=0~ScreenWidth, y=0~ScreenHeight의 영역에서 렌더링한다. (그래야 2차원 객체의 크기를 픽셀좌표계로 맞출수있으니까) z값의 경우는 ortho의 NearZ와 FarZ만 -1~1로 Projection 좌표계와 동일한 범위로 유지시켜주면 건드릴 필요 없다.
  6. 계산된 Projection Matrix / 좌표는 다음과 같다. 이를 이용해서 적절히 렌더링하면 된다
  7. ortho
    • left = 0
    • width = ScreenWidth
    • bottom = 0
    • height = ScreenHeight
    • nearZ = -1
    • farZ = 1
  8. pos
    • pos_x = (x + 1) * 0.5 * ScreenWidth
    • pos_y = (y + 1) * 0.5 * screenHeight
    • pos_z = z

구현 핵심 코드

 

void RenderBillboardLabel(haruna::gl::Label &label, float x, float y, float z){ 
	mat4 mvp = g_proj_mat * g_view_mat * g_model_mat; //billboard 같은 느낌으로 글자 쓰기 
    
    //기울어지는거 없이 항상 글자가 뜨도록 적절히 만들기 
    // rendering pipeline통과 후 좌표값 
    vec4 cliping_pos = mvp * vec4(x, y, z, 1); 
    cliping_pos /= cliping_pos.w; 
    cliping_pos.z = -cliping_pos.z; //보정된 좌표계는 z방향 다르다 
    
    // -1~+1로 보정된 좌표를 윈도우좌표로 변환 
    vec3 win_coord( (cliping_pos.x+1) * kWidth/2.0f, 
    (cliping_pos.y+1) * kHeight/2.0f, cliping_pos.z ); 
    
    glMatrixMode(GL_PROJECTION); glLoadIdentity(); 
    glOrtho(0, kWidth, 0, kHeight, -1, 1); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glTranslatef(win_coord.x, win_coord.y, win_coord.z); 
    
    glVertexPointer(2, GL_FLOAT, sizeof(FontVertex), &label.vertex_list()[0].p); 
    glTexCoordPointer(2, GL_FLOAT, sizeof(FontVertex), &label.vertex_list()[0].uv); 
    glDrawElements(GL_TRIANGLES, label.index_count(), GL_UNSIGNED_SHORT, label.index_data()); 
    
    { 
    	// restore 3d matrix 
        glMatrixMode(GL_PROJECTION); 
        glLoadIdentity(); 
        glLoadMatrixf(glm::value_ptr(g_proj_mat)); 
        glMatrixMode(GL_MODELVIEW); 
        glLoadIdentity(); 
        
        glm::mat4 modelview = g_view_mat * g_model_mat; 
        glLoadMatrixf(glm::value_ptr(modelview)); 
     } 
     
}

 

 

 

=======================

=======================

=======================

 

 

반응형


관련글 더보기

댓글 영역