상세 컨텐츠

본문 제목

OpenGL 오픈지엘 텍스쳐, 화질 개선 밉맵 필터링 Texture Filtering and Mipmapping 관련

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

by AlrepondTech 2020. 9. 15. 13:33

본문

반응형

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://gogorchg.tistory.com/entry/Android-Opengl-%EC%82%AC%EC%9A%A9-%EC%8B%9C-Antialiasing-%EC%9E%91%EC%9A%A9

1. 아래에 두 옵션을 먼저 넣어보세요.
   조금이나마 선이 부드럽게 바뀝니다.

gl.glEnable(GL10.GL_LINE_SMOOTH);
gl.glHint(GL11.GL_LINE_SMOOTH_HINT, GL11.GL_NICEST); 



2. 만약 위와 같은 옵션을 줬어도 변함이 없는 경우
혹시, Texture에 Bitmap을 넣을 경우 Bitmap.createScaledBitmap이 함수를 사용하셨는지 확인해보세요.
 
Bitmap.createScaledBitmap(src, dstWidth, dstHeight, filter);

src : 원본 Bitmap
dstWidth : 변경할 가로 사이즈
dstHeight : 변경할 세로 사이즈
filter : Bitmap에 부드럽게 보여주기 위한 필터 매개 변수 입니다. (boolean) 

이 네번째 매개 변수를 true로 해보세요.
정말 뭔가 Texture 이미지에 깔끔함을 볼 수 있으실껍니다.

아마도 Opengl뿐만 아니라 Bitmap적용되는 곳에서는 다 사용되겠죠^^
참고하세요.

이건 제 경험으로 알게 된겁니다. ㅎ 


출처: http://gogorchg.tistory.com/entry/Android-Opengl-사용-시-Antialiasing-작용 [항상 초심으로]

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://lazyfoo.net/tutorials/OpenGL/28_antialiasing_and_multisampling/index.php

Antialiasing and Multisampling

 

 

Last Updated 8/09/12

Using OpenGL's built antialiasing and multisampling functionality, you can smooth out the edges of your polygon when they rasterize.

-------------------------------------------------------------------------------------------------------

From main.cpp

    //Create Double Buffered Window with multisampled buffer

    glutInitDisplayMode( GLUT_DOUBLE | GLUT_MULTISAMPLE );

    glutInitWindowSize( SCREEN_WIDTH, SCREEN_HEIGHT );

    glutCreateWindow( "OpenGL" );

In order to use multisampling, you have to request a multisampled buffer from your windowing API. For freeGLUT, that means passing in GLUT_MULTISAMPLE to glutInitDisplayMode(). For other windowing APIs, look in your documentation on how to request a multisampled buffer.

 

--------------------------------------------------------------------------------------------------------

From LUtil.h

//Aliasing methods

enum AliasMode {
    ALIAS_MODE_ALIASED, ALIAS_MODE_ANTIALIASED, ALIAS_MODE_MULTISAMPLE
};

In this tutorial there's 3 ways we're going to render the triangle on the screen. Aliased means we're going to render the triangle with plain rasterization which leads to aliasing (the technical term for jaggies). Antialiased means OpenGL will smooth out the edges of the triangle. Multisampling is another type of antialiasing where we render with multiple samples per pixel.

From LUtil.cpp

//Aliasing

AliasMode gMode = ALIAS_MODE_ALIASED;

At the top of LUtil.cpp, we have a global variable that controls how we're rendering the triangle.

From LUtil.cpp

    //Set blending

    glEnable( GL_BLEND );

    glDisable( GL_DEPTH_TEST );

    glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );

 

    //Set antialiasing/multisampling

    glHint( GL_LINE_SMOOTH_HINT, GL_NICEST );

    glHint( GL_POLYGON_SMOOTH_HINT, GL_NICEST );

    glDisable( GL_LINE_SMOOTH );

    glDisable( GL_POLYGON_SMOOTH );

    glDisable( GL_MULTISAMPLE );

In order to use antialiasing and multisampling, you have to enable blending. This isn't much of an issue because we've been using blending for a while now.

 

Using glHint() we can tell the OpenGL driver how to do polygon smoothing antialiasing. Here we're telling the OpenGL driver to use the nicest line and polygon smoothing. Whether the driver will listen to our request is all up to the card vendor's (AMD, nVidia, Intel, etc) driver implementation.

 

Finally, we disable line/polygon smoothing and multisampling in our initGL() function. For this demo we want aliased polygons to be our default behavior.

From LUtil.cpp

bool loadMedia() {
    return true;
}
void update() {}

Since we're rendering a plain polygon, the loadMedia() and update() functions don't do much.

From LUtil.cpp

void render() { // Clear color
    glClear(GL_COLOR_BUFFER_BIT);
    // Start alias mode
    switch (gMode) {
        case ALIAS_MODE_ALIASED:
            glDisable(GL_LINE_SMOOTH);
            glDisable(GL_POLYGON_SMOOTH);
            glDisable(GL_MULTISAMPLE);
            break;
        case ALIAS_MODE_ANTIALIASED:
            glEnable(GL_LINE_SMOOTH);
            glEnable(GL_POLYGON_SMOOTH);
            glDisable(GL_MULTISAMPLE);
            break;
        case ALIAS_MODE_MULTISAMPLE:
            glDisable(GL_LINE_SMOOTH);
            glDisable(GL_POLYGON_SMOOTH);
            glEnable(GL_MULTISAMPLE);
            break;
    }

At the top of the render() function, we clear the screen as usual and then set the aliasing mode.

 

If we want the polygon to be aliased, smoothing and multisampling are disabled. If we want the polygon to be smoothed, we enable smoothing and disable multisampling. If we want to render things multisampled, we disable smoothing and enable multisampling.

From LUtil.cpp

// Render Triangle
glColor3f(1. f, 1. f, 1. f);
glBegin(GL_TRIANGLES);
glVertex2f(SCREEN_WIDTH, 0. f);
glVertex2f(SCREEN_WIDTH, SCREEN_HEIGHT);
glVertex2f(0. f, SCREEN_HEIGHT);
glEnd();

For the sake of simplicity, we're using the slow immediate mode to render the triangle.

From LUtil.cpp

    //End alias mode

switch(gMode) {
    case ALIAS_MODE_ANTIALIASED: glDisable(GL_LINE_SMOOTH);
    glDisable(GL_POLYGON_SMOOTH);
    break;
    case ALIAS_MODE_MULTISAMPLE: glDisable(GL_MULTISAMPLE);
    break;
}
// Update screen
glutSwapBuffers();
}

After we're done rendering the triangle, we disable whatever smoothing/multisampling was used.

 

Finally, we swap the buffers to update the screen.

From LUtil.cpp

void handleKeys(unsigned char key, int x, int y) { // If the user presses q
    if (key == 'q') { // Cycle alias mode
        switch (gMode) {
            case ALIAS_MODE_ALIASED:
                printf("Antialiased\n");
                gMode = ALIAS_MODE_ANTIALIASED;
                break;
            case ALIAS_MODE_ANTIALIASED:
                printf("Multisampled\n");
                gMode = ALIAS_MODE_MULTISAMPLE;
                break;
            case ALIAS_MODE_MULTISAMPLE:
                printf("Aliased\n");
                gMode = ALIAS_MODE_ALIASED;
                break;
        }
    }
}

 

And in our handleKeys() function we cycle through the different aliasing modes.

 

When you run the program, you can zoom in to see how your OpenGL driver handles the polygon rendering.

 

This is rendering with aliased edges.

 

 

 

This is rendering with multisampled buffer.

 

---------------------------------------------------------------------------------------------------------------------------------------

If you're wondering how using polygon smoothing looks, that's.... something I would like to know too. See, antialiasing via polygon smoothing was one of the earlier methods of antialiasing in OpenGL. Then multisampling came along. While it's part of the OpenGL spec, polygon smoothing isn't always there and my home rig doesn't support it. When we make a call with glHint, it's handled as a request, not an order.

 

These days, it's generally not a good idea to rely on polygon smoothing for antialiasing. If you want antialiasing done for you, you can use multisampling. Many developers today don't rely on API level multisampling and use their own methods via shaders. One method I've seen is using a gaussian blur on edges.

 

Start looking up image processing is you want to use your own method for antialiasing.

Download the media and source code for this tutorial here. 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

함수원형 : void glDisable(GLenum 기능), void glEnable(GLenum 기능)

ex ) glEnable(GL_LIGHTING); // 이런식으로 상태를 설정 가능하다

 

기능

설명 

 GL_BLEND  색상 블렌딩
 GL_CULL_FACE  폴리곤 추려내기
 GL_DEPTH_TEST  깊이 테스트
 GL_DITHER  디더링
 GL_FOG  OpenGL 안개 모드
 GL_LIGHTING  OpenGL 조명
 GL_LIGHTx  x번째 OpenGL 조명(최소값:8)
 GL_POINT_SMOOTH  점 안티알리아싱
 GL_LINE_SMOOTH  선 안티알리아싱
 GL_LINE_STIPPLE  선 스티플링
 GL_POLYGON_SMOOTH  폴리곤 안티알리아싱
 GL_SCISSOR_TEST  시서링
 GL_STENCIL_TEST  스텐실 테스트

 GL_TEXTURE_xD

 x차원의 텍스처링(x는 1,2,3 중 하나)
 GL_TEXTURE_CUBE_MAP  큐브 맵 텍스처링
 GL_TEXTURE_GEN_x  x에 대한 texgen(x는 S,T,R,Q 중 하나)


출처: http://bsh0608.tistory.com/entry/glEnable-glDisalble [내 인생 코딩 발자취]

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

네모난 사각형 POLYGON 또는 GL_QUADS 으로 그려줄때 GL_POLYGON_SMOOTH 안티 엘리어싱 적용후 텍스쳐에 깨진 대각선 선이 보이는경우

 

ex) 이런식으로 

glBegin(GL_QUADS);

glTexCoord2d(0.0f, 0.0f);  glVertex2d(0, 0);

glTexCoord2d(1.0f, 0.0f);  glVertex2d(width, 0);

glTexCoord2d(1.0f, -1.0f); glVertex2d(width, -height);

glTexCoord2d(0.0f, -1.0f); glVertex2d(0, -height);

glEnd();

 

또는

 

glBegin(GL_POLYGON);

glTexCoord2d(0.0f, 0.0f);  glVertex2d(0, 0);

glTexCoord2d(1.0f, 0.0f);  glVertex2d(width, 0);

glTexCoord2d(1.0f, -1.0f); glVertex2d(width, -height);

glTexCoord2d(0.0f, -1.0f); glVertex2d(0, -height);

glEnd();

 

GL_POLYGON_SMOOTH 안티 엘리어싱 적용후 텍스쳐가 대각선 선이 보이는경우

glEnable(GL_POLYGON_SMOOTH);

 

------------------------------------------------------------------------------------------------------------------------------------------------------해결방안

 

아래와 같이 GL_QUAD_STRIP 그려준 도형에 텍스쳐를 넣어준다.

 

glBegin(GL_QUAD_STRIP);

glTexCoord2d(0.0f, 0.0f); glVertex2d(0, 0);

glTexCoord2d(1.0f, 0.0f); glVertex2d(width, 0);

glTexCoord2d(1.0f, -1.0f); glVertex2d(width, -height);

glTexCoord2d(0.0f, -1.0f); glVertex2d(0, -height);

glEnd();

 

-----------------------------------------------------------------------------------------------------------------------------------------------------해결방안

glClearColor(1.0, 1.0, 1.0, 0.0); <- 이런식으로 알파값이 "0" 인경우  GL_POLYGON_SMOOTH  적용시 텍스쳐에 깨진 대각선이

보이는경우가 있다 

"" glClearColor(0.5, 0.5, 1.0, 1.0);  "" 이런식으로 4번째 값의 알파값을 모두 채운채로 옵션을 설정해주면

깨진 대각선이 해결되는 경우가 있다.

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: https://www.opengl.org/discussion_boards/showthread.php/123473-Diagonal-line-across-transluscent-rectangle

Diagonal line across transluscent rectangle

Hello,
This is probably a newbie question, but... I am drawing a white semi-transparent rectangle over a background, and I am seeing a visible diagonal line from (left, bottom) corner to (right, top) corner of my rectangle. Here are the things I am doing:

- Using a pixel format supports multi-sampling (WGL_SAMPLE_BUFFER_ARB = GL_TRUE) on windows XP, NVIDIA GeForce FX5200
- glEnable(GL_BLEND)
- glBlend(GL_ONE_MINUS_SRC_ALPHA, GL_SRC_ALPHA)
- glPolygonMode(GL_FRONT, GL_FILL)

And to draw the rectangle:

w_div_2 = width / 2.0f;
h_div_2 = height / 2.0f;
glBegin(GL_QUADS);
glColor4ub(255, 255, 255, 150);
glVertex3f(-w_div_2, -h_div_2, 0.0f);
glVertex3f( w_div_2, -h_div_2, 0.0f);
glVertex3f( w_div_2, h_div_2, 0.0f);
glVertex3f(-w_div_2, h_div_2, 0.0f);
glEnd();

Would anyone care to guess what's making a white diagonal line appear across my semi-transparent rectangle?
Thanks in advance,

Steve

 

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Re: Diagonal line across transluscent rectangle

I actually could get rid of the diagonal line by turnning on multi-sampling. (I had it off, and was only enabling it for parts of the scene that needs anti-aliasing).

So, it seems that if I have multi-sampling buffer but disable multi-sampling, the lines along tessalation seems to appear.

This is interesting and I'd be interested in hearing anybody's further insight on this.

Thanks, good night.

-Steve
------------------------------------------------------------------------------------------------------------------------------------------------------

Re: Diagonal line across transluscent rectangle

The multisample fact irritates me a little, but this problem normally appears with depth buffered antialiased polygons when using glEnable(GL_POLYGON_SMOOTH).

Do you actually have multisampling enabled and a buffer with multiple samples? If yes, that would change the rasterization rules for antialiased primiteves and smoothing should not have an effect.

Does it change with depth buffering disabled?

(edit: Nice double post...)

If this is rendering a full screen quad, it could be that you have position imprecisions and there is actually another sliver triangle generated. I had such a case about 8 years ago. ;-)

Use a perfect gluOrtho2D setup using (0, 0, width, height) and render the quad from (0, 0), to (width, height) to cover pixel perfectly.

Nitpick: You should move the color call before the begin.
-----------------------------------------------------------------------------------------------------------------------------------------------------

Re: Diagonal line across transluscent rectangle

I am seeing a similar problem. Code is basically the same, using glOrtho to look at z=0 plane. Depth buffer and multisampling disabled:

glEnable(GL_BLEND);
glEnable(GL_POLYGON_SMOOTH);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

and to draw:
glColor(...)

// tried this
glRectf(x1, y1, x2, y2);

// and this
glBegin(GL_QUADS);
glVertex2f(x1, y1);
glVertex2f(x2, y1);
glVertex2f(x2, y2);
glVertex2f(x1, y2);
glEnd();

// and this
glBegin(GL_TRIANGLES);
glEdgeFlag(true); glVertex2f(x1, y1);
glEdgeFlag(true); glVertex2f(x2, y1);
glEdgeFlag(false); glVertex2f(x2, y2);
glEdgeFlag(true); glVertex2f(x2, y2);
glEdgeFlag(true); glVertex2f(x1, y2);
glEdgeFlag(false); glVertex2f(x1, y1);
glEnd();

I get the diagonal line no matter which way I draw it. Seems like it is incorrectly treating the diagonal as boundary edges and so blending, but I want them to be non-edge boundaries.

Is this a standard problem and if so how to fix it? I have worked around it by drawing the rectangle twice, specifying the corners in opposite order, which masks the worst of the problem.

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Re: Diagonal line across transluscent rectangle

 Edge flags do nothing in fill mode.

Yes, polygon smoothing has this problem. It basically sucks due to a number of restrictions.

Normally you use it with depth buffering disabled, front to back sorted polygons, destination alpha channel in the pixel format(!), and blending set to saturate on the destination like describe in the OpenGL Programming Guide.

I've never tried this on multisample formats. Antialiasing should better be done with the multisampling itself.

 

--------------------------------------------------------------------------------------------------------------------------------------------------------------

 

Re: Diagonal line across transluscent rectangle

By the way, depth buffer doesn't seem to affect the diagonal line when using multisample buffer; that is, the line is still visible with depth test off. 

But with multisampling enabled, it goes away, which is kind of a pity since I wanted to render most of my scene without multisampling, and only enable multisampling as needed. (e.g. when rendering outlined font).

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://cafe382.daum.net/_c21_/bbs_search_read?grpid=wmlM&fldid=hrY&datanum=22&openArticle=true&docid=wmlMhrY2220051129003715

 

Texture Mapping

 

이번 장에서는 컴퓨터 그래픽의 꽃이라고 할수있는 텍스쳐 맵핑에 대해서 알아보도록하자. 컴퓨터 그래픽에 있어서 가장 발전한 기술이 바로 이 텍스쳐 맵핑이라고 한다. 텍스쳐 맵핑이 무엇인지 잠깐 설명하도록 하겠다. 둠이라는 게임을 해 보았는가? 벽으로 막힌 건물 안에서 사악한 주인공이 조용이 살고 싶어하는 착한 괴물을 죽이고 다니는 아주 불건전한 겜이다. 여기서 둠에 나오는 벽을 생각해 보자. 그 벽은 원래 그냥 폴리곤(면)에 불과 했다. 그런데 그 면위에 그림을 입혀 마치 진짜 벽처럼 보이게 한 것이다. 바로 그림을 폴리곤에 입히는 것을 텍스쳐 맵핑이라고 한다. 그 둠이라는 게임에서 사용한 텍스쳐 맵핑 기술은 아주 원시적인 기술이다. 둠의 텍스쳐 맵핑은 단지 평평한 면에만 그림을 입힐수있다. 하지만 OpenGL에서는 곡면에도 그림을 입힐수있다.

 

대강 텍스쳐 맵핑에 대한 소개는 이쯤에서 접고 OpenGL에서 텍스쳐 맵핑 가운데 2차원 텍스쳐 맵핑에 대해서 알아보도록 하자. 2차원 텍스쳐 맵핑을 설명하기 위해 필자가 준비한 실습 예제는 우리가 지난장에서 만든 정육면체의 각면들에 세가지 그림을 입히는 것, 즉 그림(텍스쳐)를 맵핑하는 것으로 하겠다. 시작에 앞서 그 결과를 먼저 보여주겠다.

 

 

그 결과를 살며 보았으면 이제 어떻게 이런 결과를 얻었는지의 그 과정을 설명하기 위해 5장에서 사용한 소스를 기본으로 해서 제작해도록 하겠다. 자! 5장의 소스를 비주얼씨++로 열어 보기 바란다.

텍스쳐 맵핑을 하기 위해 준비해야될 것들은 무엇인가? 바로 텍스쳐, 즉 그림이다. 이 장에서는 3개의 그림이 필요하겠다. OpenGL에서는 간단하게 그림 파일(BMP)을 바로 텍스쳐 맵핑 소스로 사용할 수 없다. 이 그림 파일을 텍스쳐 맵핑 소스로 변환해 주는 루틴이 필요한다. 다음이 바로 그 루틴이다.

 

AUX_RGBImageRec *LoadBMPFile(char *filename)
{
FILE *hFile = NULL;
if(!filename) return NULL;
   
hFile = fopen(filename, "r");
if(hFile) {
fclose(hFile);
return auxDIBImageLoad(filename);
}
   
return NULL;
}

 

AUX_RGBImageRec 구조체는 AUX 라이브러리의 함수로 BMP 파일의 크기와 그림 데이타 포인터를 가지고 있다. 위의 함수는 파일 이름 인자를 가지는데 그 인자에 해당하는 것이 BMP 파일이다. 중요한 함수는 auxDIBImageLoad 함수이고 나머지 모든 루틴은 파일이 존재하는지을 참조하기 위함이다. 모든 오류에 있어서 NULL 값을 반환한다.

이제 그림 파일에서 맵핑 소스를 읽어 들이는 루틴이 완성되었다. 그렇다면 텍스쳐 맵핑을 OpenGL에서 사용할수있도록 Setting을 해줘야 하는데 그에 해당되는 코드들을 살펴보자.

 

AUX_RGBImageRec *texRec[3]; //<1>
memset(texRec, 0, sizeof(void *)*3); //<2>
   
if((texRec[0]=LoadBMPFile("img.bmp")) && //<3>
(texRec[1]=LoadBMPFile("img2.bmp")) &&
(texRec[2]=LoadBMPFile("img3.bmp"))) {
glGenTextures(3, &tex[0]); //<4>
for(int i=0; i<3; i++) { //<5>
glBindTexture(GL_TEXTURE_2D, tex[i]); //<6>
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //<7>
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //<8>
glTexImage2D(GL_TEXTURE_2D, 0, 3, texRec[i]->sizeX, texRec[i]->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE,texRec[i]->data); //<9>
}
} else return FALSE;

for(int i=0; i<3; i++) { //<10>
if(texRec[i])
{
if(texRec[i]->data) free(texRec[i]->data);
free(texRec[i]);
} else return FALSE;
}
   
glEnable(GL_TEXTURE_2D); //<11>
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); //<12>

 

자, 하나 하나 살펴 보도록하자.

<1> 코드는 우리가 3개의 텍스쳐 맵핑 소스를 만들어야 하기 때문에 3개의 Cell을 갖는 배열을 선언한 것이다. AUX_RGBImageRec 구조체 타입은 이미 앞에서 살펴보았는데 바로 그림 파일에서 맵핑 소스를 얻기 위해 사용할 함수의 반환값을 저장할 목적으로 쓰인다.

 

<2> 코드는 메모리 상의 변수를 0으로 만드는 코드인데 굳이 필요치 않는 코드이나 깔끔을 떠는 프로그래머들이 즐겨 사용하는 스타일로 어떤 변수를 사용하기에 앞서 0으로 초기화를 시켜본 경험이 있는 독자도 있을 것이다. 바로 그것이다.

 

<3> 코드는 IF 문인데 앞에서 살펴본 그림파일에서 텍스쳐 맵핑 소스를 생성하는 함수를 사용해서 각각의 3가지 그림 파일을 실제 읽어 들이는 루틴이다. 오류(파일이 없다)가 발생하지 않는 다면 실행점이 IF문의 TRUE 블럭으로 진입한다.

 

<4> 코드에서 새로운 함수가 등장했다. 앞으로 OpenGL의 새로운 함수가 많이 등장할 것이다. glGenTextures 함수 2개의 인자를 갖는데 첫째는 우리가 몇개의 텍스쳐 맵핑 소스를 생성할 것인지를 지정하는 것이고 두번째는 그중 가장 첫번째 텍스쳐 맵핑 소스의 식별자의 주소값이다. 여기서 텍스쳐 맵핑 소스의 식별자라 함은 일단 텍스쳐 맵핑 소스가 생성되었으면 나중에 사용하기 위해서 그 맵핑 소스에 이름을 붙이게 되는데 그 이름으로써 정수값을 사용하며 그 정수값을 식별자라 부른다. 그런데 뜸금없이 tex라는 변수가 튀어 나왔는데 이 변수는 전역 변수 선언부에 다음과 같이 얌전하게 선언되어 있다.

 

GLuint tex[3];

 

3개의 텍스쳐를 사용할 것이므로 역시 3개의 Cell을 갖는 배열을 선언하였다.

 

<5>번 코드는 FOR 문인데 3번 반복하게 되어 있는데 한번의 반복당 하나의 텍스쳐 맵핑 소스에 대한 특성을 지정하게 된다.

 

<6>코드에 나오는 함수인 glBindTexture는 앞으로 설정할 텍스쳐의 식별자를 지정하는 것으로 첫번째 인자는 텍스쳐가 1차원(GL_TEXTURE_1D)인지, 2차원(GL_TEXTURE_2D)인지를 지정한다. 두번째 인자는 다음에 설정할 텍스쳐 맵핑의 식별자가 온다. 이 함수 이후로 이루어지는 텍스쳐 맵핑의 특성에 대한 설정은 모두 앞서 지정된 식별자에 해당하는 텍스쳐 맵핑에 대한 것이다.

 

<7>, <8>번 코드를 설명하기 전에 먼저 알아야 할 것이 있다. 실제 텍스쳐 맵핑의 크기와 맵핑되어질 물체의 크기가 똑같다면 간단하게 맵핑을 시키면 되겠으니 작거나 크다면 어떤 식으로 텍스쳐 맵핑 소스를 물체의 크기에 맞게 줄이거나 키울 것인가? 바로 그 어떻게 줄이거나 키울 것에 대한 방법으로 대표적으로 2가지가 있는데 하나는 보간법(GL_LINEAR)과 최근점법(GL_NEAREST)가 있다. 이해가 되었다면 glTexParameteri에 대해서 살펴보자. 세개의 인자를 취하는데 첫번째는 1차원인지, 2차원인지를 지정하는 것이고 두번째 인자는 만약 텍스쳐 맵핑 소스가 맵핑되어질 물체보다 클 경우에 어떤 식으로 축소시켜야 하는지를 지정하는 GL_TEXTURE_MIN_FILTER와 텍스쳐 맵핑 소스가 맵핑되어질 물체보다 작을 경우 어떤 식으로 확대시켜야 하는지를 지정하는 GL_TEXTURE_MAG_FILTER가 온다. 세번째 인자는 실제 그 축소, 확대시키는 방법을 기술하는데 대표적으로 GL_LINEAR와 GL_NEAREST가 온다. GL_LENEAR이 GL_NEAREST보다 나은 장면을 얻을 수 있으나 속도가 느리다. 위의 코드에서는 나은 장면을 얻기위해 모든 텍스쳐 맵핑 소스에 대해서 보간법을 사용하였다.

 

<9>번 코드는 꽤 많은 인자를 요구하는 glTexImage2D라고 하는 놈이다. 모두 9개의 인자를 갖는다.

첫번째는 텍스쳐 이미지가 1차원인지 2차원인지를 지정하는 것이고

두번째 인자는 항상 0으로 지정하게 된다. 사실 이 인자는 Detail Level을 나타내는 놈으로 보다 나은 Midmap 텍스쳐 맵핑을 구현할때 사용하는 것이다. 이장에서는 사용법에 대해 언급하지 않겠지만 간략하게 소개하자면 앞에서 우리는 텍스쳐 맵핑 소스가 물체보다 크거나 작을때 맵핑 소스를 줄이거나 키운다고 했다. 하지만 Midmap 텍스쳐 맵핑은 처음부터 미리 큰 맵핑 소스와 그리고 중간 맵핑 소스, 작은 맵핑 소스등 많은 맵핑 소스를 준비해서 보다 나은 장면을 얻을 수 있는데 이런 Midmap 맵핑 소스 이미지들은 프로그래머가 처음부터 그림 파일로 만들어 놓고 지정하는 경우와 컴퓨터가 자동으로 생성하는 경우가 있다. 이제 계속 인자에 대해 알아보자.

세번째 인자는 생상 구성 요소의 갯수인데 BMP 파일은 RGB의 세가지 색상요소가 있으므로 3이 온다.

네번재와 다섯번째는 이미지의 가로 길이와 세로 길이를 나타내게 되고

여섯번째는 이미지의 경계에 대한 픽셀두깨를 지정하게 된다. 이 예제에서 사용하는 그림들은 모두 불필요한 경계가 없으므로 0이 된다.

일곱번째는 텍스쳐 맵핑 소스의 구성 요소를 분명하게 지정하는 것으로 GL_COLOR_INDEX, GL_RED, GL_GREEN, GL_BLUE, GL_RGB 등 여러 값이 올수있으나 여기서는 GL_RGB로 이미 우리는 그림파일에서 텍스쳐 맵핑을 준비하는 것이고 그 그림파일은 RGB값을 갖는 구조이기 때문이다.

여덜번째는 색상 구성 요소들이 갖는 값의 타입을 나타내는데 모든 BMP파일에 있어서 RGB의 세가지 구성요소들은 부호 없는 Byte형이므로 GL_UNSIGNED_BYTE가 온다.

마지막으로 아홉번째는 텍스쳐 맵핑 소스의 실제 데이타값(그림 데이타)에 대한 포인터가 온다.

여기까지가 텍스쳐 맵핑 소스를 생성하고 그 맵핑 소스에 대한 특성을 설정하는 것들이다. 이후로 이제 불필요한 할당된 메모리를 해제하고 텍스쳐 맵핑을 사용한다고 지정하는 것들이 남았다.

 

<10>번 코드가 바로 이제는 더이상 필요치 않는 할당된 메모리를 해제하는 코드이다.

 

<11>은 2차원 텍스쳐 맵핑을 사용하겠노라고 OpenGL에게 알린다.

 

<12>에 나오는 함수인 glTexEnvi는 세개의 인자를 갖는데 중요한 것은 세번째 인자로써 올수있는 값은 GL_MODULATE와 GL_BLEND와 GL_DECAL이 올수있다. GL_MODULATE는 화면상의 픽셀 색과 텍스춰의 픽셀 색을 서로 곱하게 된다. <12>번 코드를 생략하면 자동으로 GL_MODULATE로 지정된다. GL_DECAL은 화면상의 픽셀 색을 텍스춰의 픽셀 색으로 대체한다. 이 경우 빛에 대해서 전혀 텍스쳐가 영향을 받지 않는다. GL_BLEND는 화면상의 픽셀 색을 텍스쳐의 픽색 색과 섞고 특정한 색과 결합시키게 된다.

 

자!! 이렇게 해서 텍스쳐 맵핑 소스를 만드는 것에 대한 것들을 알아 보았다. 그렇다면 이 코드들은 어디에 위치해야 하는가? 간단하게 OpenGL을 초기화는 곳에 위치하도록 하자. 즉 void InitGL(GLvoid) 함수에 위치 시키자.

 

이제 우리는 이 텍스쳐 맵핑 소스를 물체에 적용하는 방법에 대해 알아볼 차례이다. 적용은 실제 물체를 그리는 코드에서 이루어진다. 다음의 DrawGLScene 함수를들어다 보자.

 

int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
   
glTranslatef(0.0f, 0.0f, -7.0f);
   
glColor3f(0.8f, 0.8f, 0.8f);
   
glRotatef(rot, 1.0f, 1.0f, 0.0f);
glBindTexture(GL_TEXTURE_2D, tex[0]); //<1>
glBegin(GL_QUADS);
glNormal3f(0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); 
glEnd();
   
glBindTexture(GL_TEXTURE_2D, tex[1]); //<1>
glBegin(GL_QUADS);
glNormal3f(1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
glEnd();
   
   .
   .
   .
   
glBindTexture(GL_TEXTURE_2D, tex[0]); //<1>
glBegin(GL_QUADS);
glNormal3f(0.0f, 1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, -1.0f);
glEnd();
   
rot+=1.0f;
if(rot>359.0f) rot=0.0f;
   
return TRUE;
}

 

5장에서의 정육면체를 그릴때의 방법을 약간 변형했는데 5장에서는 한번의 glBegin과 glEnd를 사용하여 모든 면을 그려주었다.

하지만 이 장에서는 각각의 면에 대해서 glBegin와 glEnd를 사용하여 그려주었다. 그 이유는 각 면에 대해서 서로 다른 텍스쳐 맵핑을 시켜야 하기 때문이다. 살펴 볼것은 면에 대해서 어떤 텍스쳐 맵핑을 사용할 것인지는 앞서 언급한 텍스쳐 맵핑 식별자를 사용하는데 <1>번 코드가 바로 그것이다. 그리고 실제 텍스쳐 맵핑을 지정하는 것에 대한 것인데 위의 코드를 주의 깊게 살펴보면 면에 대한 각 점들을 지정해 줄때 glTexCoord2f라는 함수를 통해서 텍스쳐 맵핑 좌표를 같이 지정해 주었다는 점이다. 이것은 무엇을 의미하는가? 텍스쳐 맵핑의 소스는 크기에 상관없는 S-T 자표계를 사용하는데 S-T좌표계라는 시작점은 0으로 하고 끝점은 1로 한다는 점이다. 아래의 그림과 같이 말이다.

 

 

즉 glTexCoord2f를 이용하여 glVertex3f로 지정한 점에 대해 텍스쳐 맵핑 좌표를 설정해 줄때 glVertex3f로 지정한 위치에 대해 glTexCoord2f 좌표가 서로 일치시켜 그림을 맵핑하게 되는 것이다.

각각의 면들에 대해서 동일한 방법으로 텍스쳐 맵핑 좌표를 설정하였다.

이로써 텍스쳐 맵핑에 대한 것들을 마칠까 한다.

 

 

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://sadiles.blog.me/10089582820

 

 

   mipmap texture mapping을 사용할 때에, mipmap을 만드는 것 외에도 한 가지 주의할 점이 있다.  바로 GL_TEXTURE_MIN_FILTER를 제대로 적용하여야 한다는 것이다.  예를 들어, GL_TEXTURE_MIN_FILTER 값으로 GL_NEAREST와 GL_LINEAR를 사용할 경우에는 mipmap textureing이 적용되지 않게 된다 . 

 

  그러므로 mipmap을 사용할 때는, GL_TEXTURE_MIN_FILTER를 꼭 다음 아래의 4가지 값 중 하나로 설정하도록 하자.  

  

< GL_TEXTURE_MIN_FILTER >

GL_NEAREST_MIPMAP_NEAREST

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the GL_NEAREST criterion (the texture element nearest to the center of the pixel) to produce a texture value.

 

GL_LINEAR_MIPMAP_NEAREST

Chooses the mipmap that most closely matches the size of the pixel being textured and uses the GL_LINEAR criterion (a weighted average of the four texture elements that are closest to the center of the pixel) to produce a texture value.

 GL_NEAREST_MIPMAP_LINEAR

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the GL_NEAREST criterion (the texture element nearest to the center of the pixel) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

 GL_LINEAR_MIPMAP_LINEAR

Chooses the two mipmaps that most closely match the size of the pixel being textured and uses the GL_LINEAR criterion (a weighted average of the four texture elements that are closest to the center of the pixel) to produce a texture value from each mipmap. The final texture value is a weighted average of those two values.

 

< Reference >

1.Site: http://www.khronos.org/opengles/sdk/docs/man/

 

[출처] [ ES API ] glTexParameter - mipmap 사용시 주의할 점 |작성자 

 

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

 

출처: http://docs.mcneel.com/rhino/5/help/ko-kr/options/view/opengl.htm

 

앤티앨리어스

없음

2x

4x

8x

밉 맵 필터링

밉 팹 필터링(Mip Map filtering)은 차지하고 있는 공간 또는 거리를 기준으로 텍스처 화질을 향상시킵니다. 텍스처 이미지가 처음 로드되었을 때 밉 맵이 생성되며, 원래 이미지 크기에 따라 여러 개의 맵이 만들어집니다. 예: 크기가 1024x1024 인 이미지를 로드하면 Rhino는 원래 이미지를 바탕으로 다음의 해상도를 가진 비슷한 샘플 이미지를 만듭니다. 512x512, 256x256, 128x128, 64x64, 32x32, 16x16, 8x8, 4x4, 2x2, 1x1 (단일 픽셀). 크기와 거리를 기준으로, 텍스처 모음에서 텍스처가 선택되어 더욱 부드럽고 덜 거친 이미지와 모아레(moiré) 패턴이 만들어집니다.

가장 가까운

선형

이방성 필터링

이방성 필터링(Anisotropic filtering)은 텍스처 처리된 개체를 더욱 선명하게 표시하며 카메라에 상대적으로 낮은 각도에서 보이는 텍스처 화질을 향상시킵니다.

 

 


이방성 필터링 꺼진 상태 (왼쪽), 켜진 상태 (오른쪽).

낮음

중간

높음

 

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://cafe.sayclub.com/cb_board.nwz?tbtype=&act=read&clubsrl=3998807&bsrl=2&page=16&aseq=3758204

 

 

ati_npatch 0 // ATI VGA 카드에서 플레이어나 무기 모델을 둥글게 만드는 것과 관련. 퍼포먼스 향상을 위해서는 ATI 카드가 없어도 ‘0' 권장.
ati_subdiv 0
bgmvolume 0 // 배경음을 끕니다.
cd_fps 1 // C-D Test 관련 명령어입니다. 퍼포먼스 향상을 위해서는 ‘1’ 권장.
cl_allowdownload 1 // 서버로 부터 파일들을 다운 가능 유무에 관련. '0' 으로 설정 시 맵도 다운받을 수 없다.
cl_allowupload 1 // 자신의 고유한 spray 파일 같은 것을 올리는 것에 관련. ‘0’ 으로 설정시 연결시간이 조금 절약됩니다. 단, 다른 사람들에게 자신의 고유한 스프레이를 보여줄 수는 없다.
// cl_bob 0.01
// cl_bobcycle 0.8 // '0' 으로 설정 시 탄착이 조금 아래쪽에 형성된다.
// cl_bobup 0.5 // 캐릭터가 뛸 때 흔들리는 주기와 정도에 관한 컨픽 명령어 입니다. 퍼포먼스를 위해서는 모두 '0' 권장하지만 탄착형성과도 관련이 있는 만큼 주의가 필요합니다.
// cl_cmdbackup // 손실 대비를 위해 업로드 되는 데이터를 얼마만큼 복제하는가에 관련됩니다. 인터넷 연결이 좋지 못하면 이 수치를 높이면 됩니다. 적정 수치는 인터넷 환경에 따라 틀립니다만, 특별히 조절할 필요는 없을 것으로 사료됩니다.
// cl_cmdrate // 서버로의 초당 갱신 횟수를 정합니다. 이 값은 ping, recoil, 보이스 사운드 퀄러티에 관련됩니다. 적정 수치는 인터넷 환경에 따라 틀립니다.
cl_corpsestay 1 // 시체가 남아있는 시간을 정합니다. ‘0’ 의 경우 계속 남아있습니다. ‘1’ 은 거의 즉시 사라집니다.
cl_download_ingame 0 // 게임 내에서 잡다한 decals, sounds 파일을 받지 않습니다.
// cl_dynamiccrosshair 0 // 이걸 적용시 크로스헤어가 벌어지지 않고 고정됩니다.
cl_dynamiclights 0 // DoD. 동적광원 사용 유무를 결정합니다.
cl_himodels 0 // 고사양 모델을 사용치 않습니다.
cl_identiconmode 2 // DoD. 머리위의 팀 아이콘 관련 명령어입니다. ‘0~2’
cl_lb 1 // 적이 총에 맞았을 때 즉시 피를 흘립니다. 단, 핑에 따라서 약간의 시간차가 존재합니다. ‘1’ 권장.
cl_lc 1 // ---> config.cfg 에 존재. Lag compensation. 랙으로 인한 시각차를 보상. ‘1’ 권장.
cl_lw 1 // ---> config.cfg 에 존재. Fire Prediction. 즉각 사격반응에 관련. ‘1’ 권장.
cl_minmodels 1 // 모델을 각 팀 한 가지로 통일.
cl_nopred 1 // 클라이언트 측의 예보와 관련된 명령어입니다. LAN 게임이 아닌 한 이것을 OFF 하면 안됩니다.
cl_particlefx 0 // DoD. 파티클 효과 설정(Particle Effects).
// cl_rate // 초당 서버로 보낼 데이터의 크기(byte)를 정합니다. CS 에서는 보통 9999 수치로 초기화 되는 경향이 있습니다. 수치는 인터넷 환경에 따라 틀립니다.
cl_shadows 0 // CS 에 새로 추가된 플레이어 그림자의 유무를 결정합니다. ‘0’ 권장.
// cl_updaterate // 초당 서버로부터 클라이언트의 갱신 횟수를 정합니다. choke 값이 있을 때는 이 수치를 낮추면 됩니다. 최대값은 100. 적정 수치는 인터넷 환경에 따라 틀립니다.
cl_weather 0 // 날씨효과를 끕니다. ‘0~4’ 디폴트 보다 높이는 것도 가능합니다. 이 경우 당연히 퍼포먼스가 하락합니다.
d_spriteskip 2 // Sprite Skipping. 스프라이트의 애니메이션 이미지(수류탄 폭발 같은 것) 관련. 최근의 VGA 에는 별 상관이 없습니다.
fastsprites 2 // Sprites(수류탄이나 스모크탄의 가스 같은 것)를 뭉갭니다. '0~2'
fs_lazy_precache 1 // 게임 조인시 문제(Time out 문제등)가 있다면 ‘1’. HL Toolz 권장도 ‘1’.
// gl_alphamin // 그래픽 카드의 최소 블렌딩레벨을 설정하는 명령어로 추정합니다. 최적화에 대한 정보가 없습니다.
gl_clear 0 // Fill Texturegaps. 텍스쳐 사이의 빈틈을 채웁니다. '0'이 OFF, '1'이 ON입니다. '0' 권장.
gl_cull 1 // Draw visible only. 보이는 물체만 렌더링합니다. '0'이 OFF, '1'이 ON 입니다. '1' 권장.
// gl_d3dflip 1 // 몇몇 그래픽카드에서 발생하는 핑에 상관없이 버튼을 누른지 2~3초 후에 움직이는 현상을 없애줍니다. ---> 개인적으로 주석이 잘못된 것으로 생각됩니다. Direct 3D 관련 명령어로 추정합니다.
gl_dither 1 // Dither Textures. 디더(링)(중간색(조)의 표현을 기정색(조)의 pixel과 짜맞추어 실현하는 기법)에 관련된 명령어입니다. ‘1’ 권장. 텍스쳐가 흐려집니다.
gl_fog 0 // Condition Zero 전용. 안개효과를 끕니다.
gl_keeptjunctions 0 // Fill Texturegaps. 텍스쳐 사이의 빈틈을 채웁니다. '0' 으로 설정시 계단 현상이 일어납니다. '0' 권장.
gl_lightholes 0 // ‘Light holes‘ 를 켜고 끕니다. ‘0’ 권장.
gl_max_size 64 // 텍스처의 최대 사이즈를 결정하는 명령어로 값이 낮을수록 프레임에 도움이 됩니다 '64-512'까지 설정 가능 합니다.
// gl_monolights 0 // ---> 정보 없음. config.cfg 에 존재.
gl_palette_tex 1 // 정적(靜的) (재생하지 않아도 기억 내용이 유지되는) 컬러 팔레트를 사용합니다. 어느 것이 퍼포먼스 향상에 도움이 되는지 알려지지 않았습니다.
gl_picmip 1 // 텍스쳐의 질(Texture Quality)을 조절합니다. '0~2'. ‘1’ 정도를 권장. ‘2’ 의 경우 플레이에 심각한 영향이 있습니다.
gl_playermip 2 // 플레이어 캐릭터의 텍스쳐를 뭉갭니다. '0~2'. ‘2’ 권장.
gl_round_down 4 // 텍스쳐의 정확도(Texture Accuracy)를 조절합니다. '0~4'. ‘4’ 권장.
// gl_smoothmodels 0 // 모델을 덜 부드럽게 해줍니다.
gl_spriteblend 1 // 줌인 크로스헤어를 뚜렷하게 만듭니다. 퍼포먼스와는 별 관계가 없는 듯합니다.
gl_texturemode GL_LINEAR_MIPMAP_NEAREST
// GL_TEXTUREMODE GL_NEAREST ---> CS, DoD 에 적용되지 않는 것으로 알고 있음.
// GL_TEXTUREMODE GL_LINEAR ---> CS, DoD 에 적용되지 않는 것으로 알고 있음.
// GL_TEXTUREMODE GL_NEAREST_MIPMAP_NEAREST, OpenGL 사용 시 저화질. 권장.
// GL_TEXTUREMODE GL_NEAREST_MIPMAP_LINEAR
// GL_TEXTUREMODE GL_LINEAR_MIPMAP_NEAREST (Default), Direct3D 사용 시 저화질. 권장.
// GL_TEXTUREMODE GL_LINEAR_MIPMAP_LINEAR, 최고화질.
// 텍스쳐 필터링(Texture Filtering)의 설정입니다. 적정 설정은 사용하는 Video Mode 에 따라서 틀립니다. OpenGL 에서 실험결과 적용되지 않는 것으로 알고 있는 위의 두 가지를 제외하고, 위쪽의 것 일수록 저화질(모자이크화)로 나타났습니다. 
gl_wateramp 0 // 물의 웨이브 셋팅입니다. '0'이 OFF, '1'이 ON 입니다. '0' 권장.
gl_ztrick 0 // ZTrikc Rendering. 3dfx사 그래픽카드(Voodoo 카드)의 랜더링 고속화 관련 명령어 입니다. 최근의 VGA 카드라면 ‘0’ 권장. Voodoo 카드나 ztrick 을 지원하는(오래된) 카드라면 ‘1’ 이 퍼포먼스 향상에 도움될 겁니다.
hisound 0 // 음악의 질에 관한 설정입니다. 소리를 8비트로 바꾸는 것입니다. '1' 이 16bit, '0' 이 8bit.
hpk_maxsize 1 // custom.hpk 파일 사이즈를 정합니다. MB 단위입니다. ‘0’ 의 경우 무한대.
// loadas8bit 1 // 오디오를 8비트로 바꿉니다. ---> 'hisound' 와 같은 명령어로 추정. 적용 가능성의 유무에 대해서는 확인되지 않았습니다.
max_rubble 1 // DoD. 폭발시 생기는 파편 수를 정합니다. ‘0’ 은 무한대.
max_shells 1 // CS. 한 번에 보이는 탄피 수(탄피방출효과)에 관련된 명령어입니다. ‘0’ 은 무한대.
max_smokepuffs 1 // CS. 한 번에 보이는 연기 수에 관련된 명령어입니다. '0' 은 무한대.
max_wallpuffs 1// DoD. 벽에 사격 시 생기는 벽 파편 수를 정합니다. ‘0’ 은 무한대.
// mp_decals // 한 번에 보이는 decals 숫자를 정합니다. 100 이하로 정하시는 것을 권장.
precache 1 // 필요한 파일들을 미리 로드합니다. 퍼포먼스 향상에 도움.
r_detailtextures 0
r_detailtexturessupported 0 // 위의 명령어와 함께 디테일 텍스쳐 관련 명령어입니다. ‘0’ 권장.
// r_drawviewmodel 0 // 1인칭 무기모델을 보이지 않게 합니다. 상당한 FPS 상승이 있습니다만, 플레이에 상당한 영향을 끼치므로 비추천합니다.
// r_dynamic 0 // 메모리와 관련된 명령어 입니다. 자세한 정보는 없습니다.
r_mirroralpha 0 // 반사에 관한 설정입니다. 반사되는 이미지 끕니다. '1'이 ON, '0'이 OFF입니다.
r_mmx 1 // 'Intel CPU'의 'MMX'기능을 활성화 시킵니다. 인텔사의 'cpu'를 사용하는 경우 게임의 원할한 진행에 도움을 줍니다. '1' 이 ON, '0'이 OFF입니다.
r_shadows 0 // 하프라이프의 그림자를 사용합니다. 약간 엉성해 보이지만, 15%까지 성능을 끌어올릴 수 있다고 합니다.
// rate // 초당 서버로부터 받을 데이터의 크기(byte)를 정합니다. 최대값은 20000. 너무 높거나 낮을 경우 choke 발생. 적정 수치는 인터넷 환경에 따라 틀립니다.
s_a3d 0 // 사운드 카드의 Areal 3D 서포트 관련입니다. 3D 사운드 효과에 관계. ‘0’ 권장.
s_eax 0 // 사운드 카드의 EAX 서포트 관련입니다. 3D 사운드 효과에 관계. ‘0’ 권장.
s_reverb 0 // 메아리 효과(반향음)의 유무와 관련합니다. ‘0’ 권장.
violence_ablood 0 // 플레이어 피와 관련.
violence_agibs 0 
violence_hblood 1 // 플레이어 피와 관련.
violence_hgibs 0 // 다른 값은 '0'으로 맞추고 'hbood'만 '1'로 하면 피 튀는게 확실히 보일 것입니다. 렉 감소에도 약간의 도움이 있다고 합니다.
voice_dsound 0 // 보이스 사운드 퀄러티 관련. 퍼포먼스를 위해서는 ‘0’ 권장.
voice_enable 1 // 보이스를 사용할지 유무. 퍼포먼스를 위해서는 ‘0’ 권장. 하지만 다른 사람의 보이스를 못 듣게 된다.

 

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://skyfe.tistory.com/entry/iOS-OpenGL-ES-%ED%8A%9C%ED%86%A0%EB%A6%AC%EC%96%BC-11%ED%8E%B8

 

 

 

iOS OpenGL|ES 튜토리얼 11편에서는 glTexParameter 함수를 조금 깊게 다뤄 보겠습니다. glTexParameter함수로 설정할 수 있는 항목은 GL_TEXTURE_MIN_FILTER, GL_TEXTURE_MAG_FILTER, GL_TEXTURE_WRAP_S, GL_TEXTURE_WRAP_T, GL_GENERATE_MIPMAP 입니다. 이 항목들은 무엇을 뜻하는 것일까요? 이 질문에 대한 답은 glTexParameter  함수가 하는 일을 이해하면 알 수 있습니다.

 

텍스춰맵핑은 2차원 이미지를 3차원 기하에 맵핑하는 작업입니다. 10편 튜토리얼에서 3차원 기하는 깊이와 곡률이 있기 때문에 2차원 이미지를 3차원 기하에 1:1로 픽셀 맵핑을 할 수 없다고 했습니다. 따라서 2차원 이미지에서 어떤 픽셀을 어떻게 가져와 3차원 기하에 맵핑할 것인가에 대한 방법이 필요합니다. 이를 샘플링이라고 한다면 glTexParameter함수는 3차원 기하에 필요한 화소를 2차원 이미지에서 샘플링하는 방법을 설정하는 함수입니다. 샘플링 방법과 샘플링을 위한 최적화 방법 등을 glTexParameter 함수를 통해 설정합니다.

 

GL_TEXTURE_MIN_FILTER와 GL_TEXTURE_MAG_FILTER

2차원 이미지를 같은 3차원 기하에 텍스춰맵핑하더라도 3차원 기하의 깊이에 따라 크기가 달라집니다. 즉, 화면에 가까우면 크게 보이고 화면에서 멀면 작게 보입니다.

 

 

 위의 그림처럼 가까이 있는 사각형은 크게 보이고 멀리 있는 사각형은 작게 보입니다. 만약 1:1로 2D이미지를 맵핑한다면 가까운 사각형에는 빈칸이 많을 것이고 멀리 있는 사각형에는 픽셀 뭉침이 발생할 것입니다. 이렇게 기하의 확대 및 축소에 따라 이미지의 어떤 부분에서 화소를 가져올 것인가를 선택하는 것이 GL_TEXTURE_MIN_FILTER와 GL_TEXTURE_MAG_FILTER 입니다.

 

 

 

GL_TEXTURE_MIN_FILTER

위는 기하(폴리곤)가 텍스춰(2차원 이미지에 S,T 좌표로 추출한 이미지 데이터)보다 작을 때 적용되는 GL_TEXTURE_MIN_FILTER 를 나타낸 그림입니다. GL_TEXTURE_MIN_FILTER 의 값이 GL_NEAREST이면 기하의 픽셀 중점인 빨간색점에 가까운 텍스춰의 화소를 선택하여 17번을 사용하고 GL_LINEAR이면 17, 18, 12, 13 화소의 평균값을 사용합니다. 

 

 

 

GL_TEXTURE_MAG_FILTER

 

위는 기하가 텍스춰보다 클 때 적용되는 GL_TEXTURE_MAG_FILTER를 나타낸 것입니다. GL_TEXTURE_MAG_FILTER의 값이 GL_NEAREST이면 기하의 픽셀 중점인 빨간색점에 가까운 텍스춰의 화소를 선택하여 10번을 사용하고 GL_LINEAR이면 13, 14, 9, 10 화소의 평균값을 사용합니다.

 

OGLTexture.m 파일을 열어 generateTexture 메소드를 아래와 같이 수정합니다.

 

view plaincopy to clipboardprint?

 

-(BOOL)generateTexture {
    ...
    // : 텍스춰 파라미터를 설정한다
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    ...return YES;
}

 

 

 

]컴파일하고 실행하면 왼쪽 그림과 같이 앨리어싱이 심하게 발생한 결과를 볼 수 있습니다. 값을 GL_LINEAR로 주고 실행하면 오른쪽 그림과 같이 결과가 부드럽게 생성됩니다.

 


GL_NEAREST

 

 


GL_LINEAR

GL_TEXTURE_MIN_FILTER와 GL_TEXTURE_MAG_FILTER를 항상 같은 값으로 설정해야 하는 것은 아닙니다. 축소될 때는 앨리어싱이 크게 상관 없으므로 GL_NEAREST를 사용하고 확대되었을 때에는 부드럽게 나오도록 GL_LINEAR를 사용할 수 있습니다. 

 

view plaincopy to clipboardprint?

  1. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  
  2. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  

 

 

 

 

 

GL_TEXTURE_WRAP_S와 GL_TEXTURE_WRAP_T

10편에선 (u,v) 또는 (s,t)좌표의 범위는 [0.0, 1.0] 이라고 했습니다. 하지만 만약 이 범위를 벗어나게 설정하면 이미지의 어느 부분에서 화소를 가져와야 할까요? 이럴 경우에 샘플링 방법을 GL_TEXTURE_WRAP_S와 GL_TEXTURE_WRAP_T로 설정할 수 있습니다. 이미지 자체를 반복(GL_REPEAT) 또는 이미지의 테두리 값 반복(GL_CLAMP_TO_EDGE) 중 한 가지로 설정하면 됩니다. 예제를 통해 차이점을 알아보겠습니다.

OGLTexture.m 파일을 열어 generateTexture 메소드를 아래와 같이 수정합니다.

view plaincopy to clipboardprint?

-(BOOL)generateTexture {
    ...
    // : 텍스춰 파라미터를 설정한다
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    ...return YES;
}

위의 코드는 가로 범위(s)가 [0.0, 1.0]을 넘으면 이미지 자체를 반복하고 세로 범위(t)가 [0.0, 1.0]을 넘으면 이미지의 테두리 값을 반복하도록 설정한 것입니다.

 

그리고 TextureMappedPolygonView.m 파일을 열어 아래와 같이 텍스춰좌표를 설정합니다.

 

view plaincopy to clipboardprint?

GLfloat verticesForGL_TRIANGLE_STRIP[] = {
    0.2,
    1.05,
    0.0, // v1
    0.0,
    2.0, // UV1
    0.2,
    0.45,
    0.0, // v2
    0.0,
    0.0, // UV2
    0.8,
    1.05,
    0.0, // v3
    2.0,
    2.0, // UV3
    0.8,
    0.45,
    0.0, // v4
    2.0,
    0.0, // UV4
};

 

컴파일하고 실행하면 아래와 같은 결과를 볼 수 있습니다.

 

 

아래는 가로, 세로를 모두 같은 값으로 주어 실행한 결과입니다.


GL_REPEAT


GL_CLAMP_TO_EDGE

 

GL_GENERATE_MIPMAP

밉맵은 빠른 렌더링을 위해서 여러벌의 텍스춰를 미리 만들어 놓는 것입니다. 밉맵에 대한 자세한 내용은 위키피디아(http://en.wikipedia.org/wiki/Mipmap)에서 확인할 수 있습니다. 만약 64x64 크기의 이미지를 사용할 때, 밉맵을 생성하게 하면 32x32, 16x16, 8x8, 4x4, 1x1 크기의 이미지를 생성합니다. 64x64 텍스춰 이미지를 맵핑한 3차원 기하가 화면에서 멀어 4x4  크기를 갖는다면 4x4 텍스춰 이미지를 맵핑하는 것이 더 빠르고 더 효율적일 것입니다. 밉맵을 사용할 때는 GL_TEXTURE_MIN_FILTER에 설정하는 값이 4개 더 추가 됩니다.

 

  • GL_NEAREST_MIPMAP_NEAREST
    현재 텍스춰 크기와 비슷한 밉맵을 선택한 다음 NEAREST방법으로 화소 추출
  •  
  • GL_LINEAR_MIPMAP_NEAREST
    현재 텍스춰 크기와 비슷한 밉맵을 선택한 다음 LINEAR방법으로 화소 추출
  •  
  • GL_NEAREST_MIPMAP_LINEAR
    현재 텍스춰 크기와 비슷한 두 개의 밉맵을 선택한 다음 각각의 밉맵에서 NEAREST 방법으로 두 개의 화소를 추출한 다음 평균을 내어 화소값으로 사용
  •  
  • GL_LINEAR_MIPMAP_LINEAR
    현재 텍스춰 크기와 비슷한 두 개의 밉맵을 선택한 다음 각각의 밉맵에서 LINEAR방법으로 두 개의 화소를 추출한 다음 평균을 내어 화소값으로 사용

 

아래와 같이 밉맵 사용을 활성화 하고 GL_TEXTURE_MIN_FILTER에 GL_LINEAR_MIPMAP_LINEAR 값을 설정합니다.

 

view plaincopy to clipboardprint?

-(BOOL)generateTexture {
    ...
    // : 텍스춰 파라미터를 설정한다
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    ...return YES;
}

 

 

그리고 밉맵을 사용하기 위해서 renderView메서드에서 glScalef를 사용해 폴리곤을 축소합니다.(아직 변환을 다루지는 않았지만 밉맵적용을 위해 사용합니다.)

 

view plaincopy to clipboardprint?

-(void)renderView { // : 배경을 검은색으로 지운다
    glClearColor(0.0, 0.0, 0.0, 1.0);
    glClear(GL_COLOR_BUFFER_BIT);
    // : 행렬 모드는 모델뷰 행렬로 변경한다
    glMatrixMode(GL_MODELVIEW);
    // : 모델뷰 행렬을 초기화한다
    glLoadIdentity();
    // : 밉맵 테스트
    glScalef(0.3, 0.3, 1.0);
    ...
}

 

설정값을 변경하여 실행해 보면 아래와 같은 결과를 얻을 수 있습니다. 이미지가 작아서 잘 분간이 안되지만 자세히 보면 하얀선의 표현이 조금씩 다른 것을 확인할 수 있습니다.

 

 

GL_NEAREST_MIPMAP_NEAREST

 

 

GL_LINEAR_MIPMAP_NEAREST

 

 

GL_NEAREST_MIPMAP_LINEAR

 

 

GL_LINEAR_MIPMAP_LINEAR

 

 

텍스춰맵핑에 대한 기초 부분은 여기서 마치고 다음 시간부터는 3차원 기하 표현을 다루겠습니다. 텍스춰 고급은 그 후에 다시 다룰게요^^ 긴 글 읽어 주셔서 감사합니다.

Tutorial11.zip
0.06MB

출처: http://skyfe.tistory.com/entry/iOS-OpenGL-ES-튜토리얼-11편 [철이의 컴노리]

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://m.blog.naver.com/gomdev/220139425618

OpenGL ES 3.0 Programming Guide
Chapter 9. Texturing 

 

Texture Filtering and Mipmapping 

  • Texture Filtering
    • Filtering type
      • Minification
        • Polygon size가 texture size보다 작을 경우
        • GL_NEAREST, GL_LINEAR
        • GL_NEAREST_MIPMAP_NEAREST
        • GL_NEAREST_MIPMAP_LINEAR
        • GL_LINEAR_MIPMAP_NEAREST
        • GL_LINEAR_MIPMAP_LINEAR 사용 가능
      • Magnification
        • Polygon size가 texture size보다 클 경우
        • Mipmap은 사용 불가능하다.
        • GL_NEAREST, GL_LINEAR 만 사용 가능
    • Filtering mode
      • glTexParameter[i|f][v]를 사용해서 정할 수 있다. 
      • GL_NEAREST
        • Texture coordinate가 가리키는 single texel을 가져온다. 
        • Visual artifact가 생길 수 있다. 
          • Aliasing
        • Visual artifact에 대한 solution
          • mipmapping
      • GL_LINEAR
        • Bilinear filtering
      • GL_NEAREST_MIPMAP_NEAREST
        • 가장 가까운 mipmap level에서 single point sample을 가져온다. 
      • GL_NEAREST_MIPMAP_LINEAR
        • 가장 가까운 두 개의 mipmap level에서 single point sample을 가져와서 이 두개를 보간해서 가져온다. 
      • GL_LINEAR_MIPMAP_NEAREST
        • 가장 가까운 mipmap level에서 bilinear filtering을 해서 가져온다. 
      • GL_LINEAR_MIPMAP_LINEAR
        • 가장 가까운 두 개의 mipmap level에서 bilinear filtering을 해서 가져온 두 값을 보간해서 가져온다. 
        • Trilinear filtering
  • glTexParameter[i|f][v]

 

 

  • Mipmapping
    • Mipmap chain을 생성해서 polygon size에 맞는 texture를 적용할 수 있도록 한다. 
    • Mipmap chain
      • 원본 이미지를 시작으로 width와 height를 반으로 줄이면서 1x1이 될때까지 이미지를 만든다. 
    • The mip levels can be generated programmatically, typically by computing each pixel in a mip level as an average of the four pixels at the same location in the mip level above it (box filtering)
    • Mipmap 적용 방법
      • Mipmap chain 생성 및 texture 생성
      • Mipmap 사용하도록 filtering mode 설정
  • Example

 

 

    • GenMipMap2D
      • Mipmap chain 생성
  • Example - GenMipmap2D

 

 

 

    • Box filtering 사용
  • Nearest Versus Trilinear Filtering

 

  • Performance
    • 대부분의 경우에 mipmap filtering mode는 best choice이다. 
      • Mipmap을 사용하지 않으면 texture cache를 잘 활용할 수 없다. 
    • Higher filtering mode를 사용하면 performance cost가 커진다. 
      • 대부분의 hardware에서 bilinear filtering을 사용하는 것이 trilinear filtering을 사용하는 것보다 cost가 작다.
    • Performance에 큰 영향이 없이 원하는 quality를 얻을 수 있는 filtering mode를 선택해야 한다. 
    • 특정 hardware에서는 high-quality filtering을 성능 저하 없이 사용할 수 있다. 
    • 결국은 tunning을 통해서 최적의 성능을 얻을 수 있는 filtering mode를 찾아야 한다. 

 

Seamless Cubemap Filtering 

  • OpenGL ES 3.0에서부터 seamless cubemap filtering이 추가되었다.
  • 추가적인 작업없이 내부적으로 적용이 된다. 
  • OpenGL ES 2.0
    • Cubemap 사용시 각 face가 만나는 edge에서 artifact가 발생할 수 있었다.
    • Face 단위로 filtering이 적용되기 때문에 edge부분에서 seam이 생길 수 있었다. 
  • OpenGL ES 3.0
    • Filtering이 face단위로 적용되지 않고 edge 부분은 인접하는 face간에 filtering이 적용되기 때문에 seam이 생기지 않는다. 

 

Automatic Mipmap Generation 

  • OpenGL ES 3.0에서부터는 mipmap chain을 자동으로 생성해 준다.
  • glGenerateMipmap

 

  • OpenGL ES 3.0에서는 mipmap을 생성할 때 어떤 algorithm을 사용하도록 강제하지 않는다. 
  • Automatic mipmap generation은 framebuffer object를 사용해서 texture에 rendering 할 때 매우 중요하게 된다. 
    • Mipmap을 생성하기 위해서 texture의 content를 다시 읽어올 필요 없이 glGenerateMipmap을 호출하면 되기 때문에 성능상 훨씬 유리하다. 

 

Texture Coordinate Wrapping 

  • Texture wrap mode는 texture coordinate가 [0.0, 1.0] 범위 밖에 있을 때 동작을 정의한다. 
  • glTexParameter[i|f][v]를 사용해서 설정한다. 
  • s-coordinate, t-coordinate, r-cooridnate에 대해서 각각 설정한다. 
    • GL_TEXTURE_WRAP_S
    • GL_TEXTURE_WRAP_T
    • GL_TEXTURE_WRAP_R
  • Texture wrap modes
  •  

 

  •  
  • Texture wrap mode는 filtering에도 영향을 미친다. 
    • 예를 들어, Texture edge에서 bilinear filtering을 적용할 경우, 어떤 texel을 가져올지는 wrap mode에 따라 바뀐다. 
  • Example
  •  

 

    •  
    •  
    • GL_REPEAT, GL_CLAMP_TO_EDGE, GL_MIRRORED_REPEAT
    • Texture coordinate [-1.0, 2.0]
    • Result

 

 

 

 

 

Texture Swizzels 

  • Texture swizzle은 R, RG, RGB, RGBA texture의 component를 shader에서 texture를 fetch할 때 가져오는 component에 어떻게 mapping 시킬지를 정한다.
  • 예를 들어, application에서 GL_RED를 default인 (R, 0, 0, 1)이 아닌 (0, 0, 0, R) 또는 (R, R, R, 1)로 mapping 시킬 수 있다. 
  • glTexParameter[i|f][v]를 사용해서 할 수 있다. 
  • 조절하고자 하는 component는 GL_TEXTURE_SWIZZLE_R, GL_TEXTURE_SWIZZLE_G, GL_TEXTURE_SWIZZLE_B, GL_TEXTURE_SWIZZLE_A로 설정하고, texture에서 가져오고자 하는 값은 GL_RED, GL_BLUE, GL_GREEN, GL_ALPHA를 사용해서 명시한다. 
  • 추가적으로 application은 component에 대한 값으로 GL_ZERO  또는 GL_ONE을 사용할 수 있다. 



Texture Level of Detail

  • 특정 application에서는 모든 texture mipmap level이 사용 가능하기 전에 scene을 rendering 할 수 있도록 하는 것이 유용하다.  
    • 예를 들어, texture image를 data connection을 통해서 다운받는 GPS application은 lowest-level mipmap로 먼저 보여주고 higher level이 사용 가능해지면 higher level로 보여주도록 할 수 있다. 
  • OpenGL ES 3.0에서는 glTexParameter[i|f][v]를 사용해서 가능하다. 
    • GL_TEXTURE_BASE_LEVEL
      • Largest mipmap level을 설정한다.
      • 기본값은 0 이다.
      • Mipmap level이 아직 사용가능하지 않다면 사용 가능한 higher value가 설정된다. 
    • GL_TEXTURE_MAX_LEVEL
      • Smallest mipmap level을 설정한다. 
      • 기본값은 1000 이다.
    • GL_TEXTURE_MIN_LOD
    • GL_TEXTURE_MAX_LOD
      • Minimum, maximum lod 값을 설정한다. 
      • Smooth transition을 위해 필요하다. 
        • 새로운 mipmap level이 사용 가능할 때  popping artifact가 발생하는 것을 막을 수 있다. 
  • LOD value
    • OpenGL ES에서 level of detail (LOD) 값을 자동으로 계산해서 rendering 할 때 사용할 mipmap level을 선택한다. 
    • LOD 값 (floating-point value)은 filtering하기 위해서 어떤 mipmap level을 사용할지 결정한다. 



Depth Texture Compare (Percentage Closest Filtering)

  • PCF 필요성
    • Shadow mapping을 수행할 때, fragment shader는 fragment가 shader 내부에 있는지 밖에 있는지 결정하기 위해서 depth texture에 있는 depth value와 current depth value를 비교해야 한다. 
    • Depth texture에서 bilinear filtering을 적용하면 shader edge를 smooth하게 보여줄 수 있다. 
    • Depth texture를 filtering할 때 정상적인 결과를 얻기 위해서는 comparison 후에 filtering이 적용되어야 한다. 
    • PCF를 사용하면 원하는 결과(correct filtering)을 얻을 수 있다. Sampling된 각 depth 값은 referemce depth (current depth value) 와 비교되고 comparison의 결과는 averaging 된다. 
  • GL_TEXTURE_COMPARE_MODE
    • 기본값은 GL_NONE 이다.
    • GL_COMPARE_REF_TO_TEXTURE
      • (s, t, r) texture coordinate에서 r-coordinate는 depth texture의 값과 비교될 것이다. 
      • Comparison의 결과는 shadow texture fetch의 결과가 된다. 
        • 만약 texture filtering이 사용되지 않는다면, 0 or 1
        • 만약 texture filtering이 사용된다면, averaging 값
  • GL_TEXTURE_COMPARE_FUNC
    • GL_LEQUAL
    • GL_EQUAL
    • GL_LESS
    • GL_GREATER
    • GL_NOTEQUAL
    • GL_ALWAYS
    • GL_NEVER

Mipmapping 관련 shader code와 동작은 다음 app에서 확인 가능

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://lab.gamecodi.com/board/zboard.php?id=GAMECODILAB_QnA_etc&page=2&page_num=40&select_arrange=headnum&desc=&sn=off&ss=on&sc=on&keyword=&category=&no=1643

Iphone3D Programming(Using OpenGL ES) 라는 책을 보고 공부중입니다. 책중에서 와이어프레임뷰어부터 시작해서 오브젝트뷰어, 텍스쳐 붙이는것까지 보고 간단하게 사각형에 텍스쳐 붙여보려고 시도를 해봤습니다. 

책에서 뷰어에 텍스쳐를 붙이는 방식 그대로 한것 같은데 도무지 그림은 코배기도 안보이네요... 이틀동안 보다가 도저히 해결이 안되어서 ㅠㅠ 여기에 소스 올려봅니다 ㅠㅠㅠㅠ

pixels에 텍스쳐 파일을 담는 ResourceManager란 놈의 정의는 다음과 같습니다. (이놈도 읽어오는 정보 출력해보니 텍스쳐 발라지는 예제랑 같은걸 보면.. 파일은 제대로 불러오는 것 같은데.. 그림이 안뜨네요 ㅠ;)

OpenGL ES를 차근차근 따라가면서 공부할만 책 없을까요? 무턱대고 이책 샀는데 책의 진행이 참 들죽 날죽 저자가 만든 소스 설명만 많은것 같고... 체계적인 설명이 없는 것 같습니다;

어디 물어볼 곳도 없어서 이렇게 소스 올려봅니다 ㅠㅠ; 어떤 조언이라도 감사히 받겠습니다 ㅠ;


===================================ResourceManager 클래스=======================
using namespace std;
namespace Darwin {
    
class ResourceManager : public IResourceManager {
public:
  string GetResourcePath() const
  {
   NSString* bundlePath =[[NSBundle mainBundle] resourcePath];
   return [bundlePath UTF8String];
  }
  void LoadPngImage(const string& name)
  {
   NSString* basePath = [[NSString alloc] initWithUTF8String:name.c_str()];
   NSBundle* mainBundle = [NSBundle mainBundle];
   NSString* fullPath = [mainBundle pathForResource:basePath ofType:@"png"];
   UIImage* uiImage = [[UIImage alloc] initWithContentsOfFile:fullPath];
   CGImageRef cgImage = uiImage.CGImage;
   m_imageSize.x = CGImageGetWidth(cgImage);
   m_imageSize.y = CGImageGetHeight(cgImage);
   m_imageData = CGDataProviderCopyData(CGImageGetDataProvider(cgImage));
   [uiImage release];
   [basePath release];
  }
  void* GetImageData()
  {
   return (void*) CFDataGetBytePtr(m_imageData);
  }
  ivec2 GetImageSize()
  {
   return m_imageSize;
  }
  void UnloadImage()
  {
   CFRelease(m_imageData);
  }
private:
  ivec2 m_imageSize;
  CFDataRef m_imageData;
};

IResourceManager* CreateResourceManager()
{
  return new ResourceManager();
}

}
=================================================================

==========================렌더링 엔진 부분===============================
struct Vertex {
    float Position[3];
    float Color[4];
    float Texture[2];
};

// Define the positions and colors of two triangles.


const Vertex Vertices[] = {
    {{-1.0, -1.0, 0.5}, {1, 1, 0.5f, 1}, {1.0,1.0} }, 
    {{-1.0, 1.0, 0.5},  {1, 1, 0.5f, 1}, {0.0,1.0} }, 
    {{1.0, 1.0, 0.5},   {1, 1, 0.5f, 1}, {1.0,1.0} },
    {{1.0, -1.0, 0.5},  {1, 1, 0.5f, 1}, {1.0,0.0} },
    {{-1.0, -1.0, 0.5},  {1, 1, 0.5f, 1}, {0.0,0.0} },
    {{1.0, 1.0, 0.5},         {1, 1, 0.5f}, {1.0,1.0} },
    {{-0.5, 0.5, 0.4}, {1.0f, 1.0f, 1.0f}, {0.0,1.0} },
    {{-0.5, -0.5, 0.4 }, {1.0f, 1.0f, 1.0f}, {0.0,0.0} },
    {{0.5, 0.5, 0.4 }, {1.0f, 1.0f, 1.0f}, {1.0,1.0} },
    {{0.5, -0.5, 0.4 }, {1.0f, 1.0f, 1.0f}, {1.0,0.0} },
    {{-0.5, -0.5, 0.4 }, {1.0f, 1.0f, 1.0f}, {0.0,0.0} },
    {{0.5, 0.5, 0.4}, {1.0f, 1.0f, 1.0f}, {1.0,1.0} },
};

IRenderingEngine* CreateRenderer1(IResourceManager* resourceManager)
{
    return new RenderingEngine1(resourceManager);
}

RenderingEngine1::RenderingEngine1(IResourceManager* resourceManager)
{
m_resourceManager = resourceManager;
    // Create & bind the color buffer so that the caller can allocate its space.
    glGenRenderbuffersOES(1, &m_renderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);
}

void RenderingEngine1::Initialize(int width, int height)
{
// VBO
glGenBuffers(1, &m_vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, m_vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, 
     12 * sizeof(Vertices[0]),
     &Vertices[0],
     GL_STATIC_DRAW); 

/* // Extract width and height from the color buffer.
int width1, height1 ; 
glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                    GL_RENDERBUFFER_WIDTH_OES, &width1);
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES,
                                    GL_RENDERBUFFER_HEIGHT_OES, &height1);  */

// Create the depth buffer.
    glGenRenderbuffersOES(1, &m_depthRenderbuffer);
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_depthRenderbuffer);       
    glRenderbufferStorageOES(GL_RENDERBUFFER_OES,
                             GL_DEPTH_COMPONENT16_OES,
                             width,
                             height); 

    // Create the framebuffer object and attach the color buffer.
    glGenFramebuffersOES(1, &m_framebuffer);
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, m_framebuffer);
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                                 GL_COLOR_ATTACHMENT0_OES,
                                 GL_RENDERBUFFER_OES,
                                 m_renderbuffer);

glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES,
                                 GL_DEPTH_ATTACHMENT_OES,
                                 GL_RENDERBUFFER_OES,
                                 m_depthRenderbuffer);  
   glBindRenderbufferOES(GL_RENDERBUFFER_OES, m_renderbuffer);

   glViewport(0, 0, width, height);
   

   glRotatef(180, 0, 1, 0);
   glMatrixMode(GL_PROJECTION);  


    m_resourceManager->LoadPngImage("Grid18");
    void* pixels = m_resourceManager->GetImageData(); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
    m_resourceManager->UnloadImage(); 


    // Initialize the projection matrix.
    const float maxX = 2;
    const float maxY = 3;
    glOrthof(-maxX, +maxX, -maxY, +maxY, -1, 1);

/*glFrustumf(-maxX, +maxX, -maxY, +maxY, 0, 10);
glTranslatef(0, 0, 5);*/
    glMatrixMode(GL_MODELVIEW);

    // Initialize the rotation animation state.
    OnRotate(DeviceOrientationPortrait);
    m_currentAngle = m_desiredAngle;


printf("%d", pixels);

glEnable(GL_DEPTH_TEST); 
glEnable(GL_TEXTURE_2D);

glGenTextures(1, &m_texture);
    glBindTexture(GL_TEXTURE_2D, m_texture);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);   


}

void RenderingEngine1::Render() const
{
glClearColor(0.5f, 0.5f, 0.5f, 1);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnable(GL_TEXTURE_2D);
glRotatef(m_currentAngle+2, 0, 1, 0);
glPushMatrix();
glRotatef(m_currentAngle, 0, 0, 1);


/* glMatrixMode(GL_TEXTURE);
glTranslatef(0.01f, 0, 0);*/
int stride = sizeof(Vertices[0]);
const GLvoid* texCoordOffset = (const GLvoid*) (sizeof(Vertices[0].Position) + sizeof(Vertices[0].Color));


const GLvoid* colorOffset = (GLvoid*)sizeof(Vertices[0].Position);
glVertexPointer(3, GL_FLOAT, stride, 0);
    
    glColorPointer(4, GL_FLOAT, stride, colorOffset);
    glTexCoordPointer(2, GL_FLOAT, stride, texCoordOffset);

    

    GLsizei vertexCount = sizeof(Vertices) / sizeof(Vertex);
    glDrawArrays(GL_TRIANGLES, 0, 12);
   
glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
    glPopMatrix();
}
===================================================

 

------------------------------------------------------------------------------------------------------------------------------------------------------답변

 

 

일단 결과 스크린샷 정도 올려주시는게 좋을거 같고... 전체적으로 슥 훑어본봐로는

glBindTexture 한 이후에 glTexImage2D 를 호출해주셔야 했는데 순서가 반대로 보이는군요.

어떤 texture handle 로 pixel 의 값이 로딩될지 gl 쪽에서 알수가 없는 상황입니다.

그리고 gles 1.1 (gles 1.0 ) 이라면 굳이 gles 책보다 opengl 1.5 의 sub-spec 이므로 opengl super bible 류의 책을 추천합니다. 한글화 잘 되어있구요.

gles 2.0 이라면 desktop gl 책이 별 도움 안되겠지만 1.1 (1.0) 이라면 desktop gl 책으로 공부하셔도 크게 차이는 없습니다.

( 여러가지 제약이 따르겠지만.. )

2011-06-30
11:06:16


문제가 해결되었습니다. 텍스쳐버퍼를 바인딩 한후에 이미지를 올려야하는게 당연하군요 ㅠㅠ;; 답변 너무 감사드립니다!

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://www.technoa.co.kr/news/articleView.html?idxno=43968


Using GL_EXT_texture_filter_anisotropic

OpenGL 의 확장을 통한 anisotropic texture filtering 의 지원은 의외로 간단하다. max_anisotropy 파라미터를 이용하는 방법이 그것이다. 즉, 디폴트 max_anisotropy 파라미터는 1.0의 상수값으로 정해져 있다. 값이 1일 경우 isotropic filtering 과 완전히 동일한, 그러니까 변화없는 기존의 필터링 데이터가 넘어온다. 하지만 nVIDIA 의 지포스 제품군의 OpenGL 드라이버를 이용하면 이 값을 최대 2까지 사용할 수 있다. 즉, largest_supported_anisotropy 변수를 선언해서 사용하면 그것으로 그만이다.

아래는 오픈지엘에서 largest_supported_anisotropy 를 사용하기 위한 간단한 함수선언을 보여준다.

Glfloat largest_supported_anisotropy;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, &largest_supported_anisotropy);

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: https://www.opengl.org/discussion_boards/printthread.php?t=175567&pp=10&page=1

 

jianliang79

using GL_LINEAR_MIPMAP_LINEAR filter cause crash

I have recently encountered an annoying crash problem when using GL_LINEAR_MIPMAP_LINEAR filter to sample texture image(only with my ATI card, my nVidia card works fine), to demonstrate this problem I wrote a sample program to reproduce it, the code snippet is listed below:

 

Code :

static char *g_vertexShader =

    "#version 150\n"

    "uniform mat4 mvp;\n"

    "in vec2 vertex;\n"

    "in vec2 texCoord;\n"

    "out vec2 fragTexCoord;\n"

    "void main()\n"

    "{\n"

    "fragTexCoord = texCoord;\n"

    "gl_Position = mvp * vec4(vertex, 0, 1);\n"

    "}\n";

 

static char *g_fragmentShader =

    "#version 150\n"

    "uniform sampler2D texSampler;\n"

    "in vec2 fragTexCoord;\n"

    "out vec4 fragColor;\n"

    "void main()\n"

    "{\n"

    "fragColor = texture(texSampler, fragTexCoord);\n"

    "}\n";

 

struct SVertex {

    GLfloat x, y;

    GLfloat s, t;

};

 

static void crash()

{

    //

    // prepare shader program

    //

    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);

    GLint shaderLen = -1;

    glShaderSource(vertexShader, 1, (const GLchar **)&amp;g_vertexShader, &amp;shaderLen);

    glCompileShader(vertexShader);

 

    GLint compileStatus = GL_FALSE;

    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &amp;compileStatus);

 

    GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(fragmentShader, 1, (const GLchar **)&amp;g_fragmentShader, &amp;shaderLen);

    glCompileShader(fragmentShader);

 

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &amp;compileStatus);

 

    GLuint shaderProgram = glCreateProgram();

    glAttachShader(shaderProgram, vertexShader);

    glAttachShader(shaderProgram, fragmentShader);

    glLinkProgram(shaderProgram);

 

    GLint linkStatus = GL_FALSE;

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &amp;linkStatus);

 

    //

    // create VAO

    //

    GLuint vao = 0;

    glGenVertexArrays(1, &amp;vao);

    glBindVertexArray(vao);

 

    //

    // create VBO

    //

    GLuint vbo = 0;

    glGenBuffers(1, &amp;vbo);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);

 

    SVertex vtx[4];

    vtx[0].x = 0;

    vtx[0].y = 1;

    vtx[0].s = 0;

    vtx[0].t = 1;

    vtx[1].x = 0;

    vtx[1].y = 0;

    vtx[1].s = 0;

    vtx[1].t = 0;

    vtx[2].x = 1;

    vtx[2].y = 1;

    vtx[2].s = 1;

    vtx[2].t = 1;

    vtx[3].x = 1;

    vtx[3].y = 0;

    vtx[3].s = 1;

    vtx[3].t = 0;

 

    glBufferData(GL_ARRAY_BUFFER, sizeof(SVertex) * 4, vtx, GL_STATIC_DRAW);

 

    GLint attribIdx;

    attribIdx = glGetAttribLocation(shaderProgram, "vertex");

    glEnableVertexAttribArray(attribIdx);

    glVertexAttribPointer(attribIdx, 2, GL_FLOAT, GL_TRUE, sizeof(GLfloat) * 4, (const GLvoid *)0);

 

    attribIdx = glGetAttribLocation(shaderProgram, "texCoord");

    glEnableVertexAttribArray(attribIdx);

    glVertexAttribPointer(attribIdx, 2, GL_FLOAT, GL_TRUE, sizeof(GLfloat) * 4, (const GLvoid *)(sizeof(GLfloat) * 2));

 

    glBindBuffer(GL_ARRAY_BUFFER, 0);

 

    //

    // create FBO

    //

    GLuint fbo = 0;

    glGenFramebuffers(1, &amp;fbo);

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo);

 

    //

    // create textures

    //

    GLuint tex[2] = {0, 0};

    glGenTextures(2, tex);

 

    glBindTexture(GL_TEXTURE_2D, tex[0]);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_2D, 0);

 

    glBindTexture(GL_TEXTURE_2D, tex[1]);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1920, 1080, 0, GL_BGRA, GL_UNSIGNED_BYTE, 0);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

    glBindTexture(GL_TEXTURE_2D, 0);

 

    glViewport(0, 0, 1920, 1080);

 

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[0], 0);

    glClearColor(1, 0, 0, 1);

    glClear(GL_COLOR_BUFFER_BIT);

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);

 

    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2D, tex[0]);

    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

 

    glUseProgram(shaderProgram);

 

    //

    // setup uniform

    //

    GLint uniformLoc = glGetUniformLocation(shaderProgram, "mvp");

    // model/view/projection matrix

    GLfloat matUniform[16] = {2, 0, 0, 0, 0, 2, 0, 0, 0, 0, -1, 0, -1, -1, 0, 1};

    glUniformMatrix4fv(uniformLoc, 1, GL_FALSE, matUniform);

 

    uniformLoc = glGetUniformLocation(shaderProgram, "texSampler");

    glUniform1i(uniformLoc, 0);

 

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex[1], 0);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);    // crash in this line!

    glFramebufferTexture2D(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 0, 0);

 

    glUseProgram(0);

    glBindTexture(GL_TEXTURE_2D, 0);

 

    glDeleteTextures(2, tex);

 

    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

    glDeleteFramebuffers(1, &amp;fbo);

 

    glDeleteBuffers(1, &amp;vbo);

    glBindVertexArray(0);

    glDeleteVertexArrays(1, &amp;vao);

 

    glDeleteShader(vertexShader);

    glDeleteShader(fragmentShader);

    glDeleteProgram(shaderProgram);

}



 

I run this code in windows 7 x64 and I have an ATI radeon 5770 card with catalyst 11.8 driver installed in my system, It will crash in

Code :

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

But this code works fine in my nVidia card. This crash will NOT happen if I don't use a FBO to draw to the first texture which I will sample later or if I don't set its filter mode to GL_LINEAR_MIPMAP_LINEAR. Is anything wrong with my code ?

09-08-2011, 09:53 PMstrattonbrazil

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

This may help you. 

 

http://www.opengl.org/discussion_boa...;Number=251072

09-09-2011, 12:10 AMjianliang79

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

I have read that post, but I think that case is not very similar to my case.

1) In that post, that program crashed when generating mipmap for texutre, but in my case the crash is happened when I was issuing GL draw command.

2) In that post, that program didn't draw something to the texture through FBO before generating mipmap for it; In my case I clear the texture through FBO before generating mipmap for it, and then use it as an input texture for the next drawing command.

3) In that post they used some tricks to workaround the crash, for example, call glEnable(GL_TEXTURE_2D); or glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); before generating mipmap, but that thing has been deprecated in opengl 3 (I used a opengl 3.2 core profile context)

09-09-2011, 02:58 AMnsdev

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

Could you try a quick workarround: before calling glGenerateMipmap() function set first the mipmap filtering mode for the texture to LINEAR_MIPMAP_LINEAR. 

 

So instead of:

Quote:

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, tex[0]);

glGenerateMipmap(GL_TEXTURE_2D);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);

you will have:

Quote:

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, tex[0]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, 

GL_LINEAR_MIPMAP_LINEAR);

glGenerateMipmap(GL_TEXTURE_2D);

09-09-2011, 06:53 AMjianliang79

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

hi, nsdev:

In fact when I wrote this sample program for the first time I call glTexParameteri(GL_TEXTURE_2D, L_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); before generating mipmap for the texture, in this case the program will crash in glGenerateMipmap(GL_TEXTURE_2D); To avoid this crash I place the GL_LINEAR_MIPMAP_LINEAR mode setting code after mipmap generating deliberately, but this time the crash happens in glDrawArrays();

 

I think the point of this issue is that: render something to a texture through FBO before generating mipmap to this texture and use it as an input texture to draw something later will always lead to crash in ATI card (sometimes when generating mipmap and sometimes when issue draw command)

09-09-2011, 07:10 AMnsdev

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

If it's like you say it means that this bug I already raised a few months before is not yet fixed at ATI side: post 

Check that post. There is a workaround suggested for this.

 

I was under the impression that they fixed it already.

09-09-2011, 01:31 PMAlfonse Reinheart

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

I don't know why it is that you constantly feel the need to flip the filtering mode. Just set it to GL_LINEAR_MIPMAP_LINEAR when you create it and be done with it. The filtering mode has no effect on how rendering to the image via FBOs work. You do need to set a proper filtering mode to make the texture complete.

 

Yes, setting GL_LINEAR_MIPMAP_LINEAR on a texture with no mipmaps can make the texture incomplete. But the easiest way to do that is to generate the mipmap pyramid by hand beforehand. There's a reason why we just got new functions to allocate a mipmap pyramid all in one shot: because drivers like it that way.

 

The first thing you should always do when creating a texture is to allocate the mipmaps for it. You don't need data; you can pass NULL for those mipmaps. But you should always generate storage for them with glTexImage* if you intend to use mipmaps.

 

After that, the next thing you should do is set the GL_TEXTURE_BASE/MAX_LEVEL correctly. Note that it is not a half-open range; it is an inclusive range. So if you have 10 mipmaps, then the base level is 0 and the max level is 9. Never leave these at the default settings.

 

Note: this doesn't excuse the crash. But you're more likely to not get a crash if you're as up-front with the driver as possible.

09-10-2011, 12:58 AMnsdev

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

Alfonse, from where did you deduce that I "constantly feel the need to flip the filtering mode"? Please read carefully before jumping to conclusions.

 

I've just said that after an year I would have expected that bug to be fixed. Especially by such an important provider like AMD. Or maybe you consider this kind of driver problems not important? It is in small details where a software's quality lies.

09-12-2011, 09:07 PMjianliang79

Re: using GL_LINEAR_MIPMAP_LINEAR filter cause crash

Hi, Alfonse, by allocating mipmap storage before calling glGenerateMipmap() to generate mipmap for texture I have successfully avoid the crash. Thank you!

 

But this behaviour of AMD driver is a bug, where can I report bug to AMD openGL driver?

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://www.androidpub.com/2272636

 

 

gl.glGenTextures(1, mTextures, 0);

2.gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextures[0]);

3.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);

4.gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);

5.GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);

6.bitmap.recycle();



gl은 GL10객체, bitmap은 Bitmap객체, mTextures는 int array(int [])일 때

하나의 텍스쳐를 위와 같이 로드할 수 있는건 알 것 같습니다.

 

그런데 매번 이미지를 위 소스를 통해 로드하는건 아무래도 효율적이지 못하고 그렇게 사용하라고 만든건 아닌것 같아서요.

로드해두고 해당 로드값마다 id를 줘서 빠르게 로딩할 수 있을 것 같은데(SoundPool 사용과 비슷하게) 방법을 모르겠네요.

영어가 안되니 원문 레퍼런스는 해석조차 안되니 더더욱이요 ㅠㅠ

 

제가 함수명과 파라미터로 추측하는건 

glGenTextures : 로드된 텍스쳐의 index id를 mTextures에 저장하겠다

glBindTexture : 2D Type으로 mTextures[0]번째 id를 사용하겠다

glTexParameterf : 기타 설정 세팅

GLUtils.texImages2D : 현재 바인드되어 있는 texture에 bitmap을 세팅

 

당장 이렇게 어거지로 해석하고 있습니다.

만약 여러개의 텍스쳐를 로딩해두고 사용한다면 초기에 중복으로 해줘야할 일과 중복을 제거해서 해줘야 할일

마지막으로 로딩할 때 해줘야 할 일까지 구분해서 테스트 해보고 싶습니다.

 

아시는 분은 도와주시면 감사하겠습니다!

엮인글 주소 : http://www.androidpub.com/index.php?document_srl=2272636&act=trackback&key=ef3

2012.08.15 22:21:44

즐거운인생

(추천: 1 / 0)

아래 코드를 참고하세요.

int[] texture = new int[TEX_NUM];
...

/* 초기화 함수내 */
gl.glGenTextures(TEX_NUM, texture, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
bitmap = BitmapFactory.decodeStream(mContext.getResources().openRawResource(R.drawable.background));
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();

gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[1]);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
bitmap = BitmapFactory.decodeStream(mContext.getResources().openRawResource(R.drawable.player));
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();

gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[2]);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexParameterx(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
bitmap = BitmapFactory.decodeStream(mContext.getResources().openRawResource(R.drawable.box));
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
bitmap.recycle();

 

/* 실제 사용하는 부분 */
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glBindTexture(GL10.GL_TEXTURE_2D, texture[0]); /* background */
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, fb[1]);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, fb[0]);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);

2012.08.15 23:33:30

저임ㅋ

정말 감사드립니다!

바로 복사만 해서 사용할 수 있을정도로 정리가 되어있네요 ㅠㅠ

몇번 작성해보고 익혀야갰네요 ^^

다시 한번 감사드립니다~~!

2012.08.16 00:03:41

저임ㅋ

궁금한 점이 하나 더 생겼는데 괜찮으시면 답변 부탁드립니다.

텍스쳐 로딩을 구현하는 위치와 실행하는 위치가 궁금합니다.

일단 제가 블로그 등으로 공부한 곳에서 로딩은 하나의 물체를 그려주는 곳(vertex를 가지고 사각형 혹은 큐브 등을 그리는)에서 각각 로딩을 구현했고

Renderer를 구현한 클래스의 onSurfaceCreated에서 실행을 했는데 효율적인 방법 하나만 추천해주실 수 있나요?

2012.08.16 10:14:28

즐거운인생

(추천: 1 / 0)

말씀하신대로 하면 될 것 같은데요? ^^

저는.. onSurfaceCreated에서 texture 로딩 해놓고, 각 3D 객체, 예를 들면 Player 등에서 textureId를 변수로 두고, draw 함수에서 textureId로 바인딩해서 쓰고 있습니다.

2012.08.16 11:34:40

저임ㅋ

감사합니다^.^
덕분에 답답함이 싹 가신것 같네요
책없이 공부하니 힘이드네요ㅎㅎㅎ
좋은 하루되세요!!

2013.02.06 00:01:27

adb

정말 감사합니다ㅠ 몇일동안 찾아도 없었는데 드디어 여기서 찾는군요ㅠㅠ;;

사용법까지 상세해서 정말 얼마 안걸렸네요.

다시 한번 감사드리고요 언제나 행복하세요!!

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://codeonwort.tistory.com/302

텍스처

- 셰이더가 읽고 쓸 수 있는 구조화된 저장 형태

- 주로 이미지 데이터를 저장하는 데 쓰임

 

사용법

1. 이름을 생성한다

2. 텍스처 타겟에 그 이름을 바인딩한다

3. 원하는 이미지 크기를 명시한다

 

종류 - 1D, 2D, 3D, rectangle

 

텍스처 읽기

- 텍스처는 샘플러 변수(sampler variable)로 표현된다

- 2D 텍스처 => sampler2D

 

uniform sampler2D s;

out vec4 color;

void main() {

  color = texelFetch(s, ivec2(gl_FragCoord.xy), 0);

}

 

sampler1D, sampler2D, ... => floating-point

prefix i => integer(signed)

prefix u => unsigned integer

 

텍스처 읽기 제어 - 래핑wrapping, 필터링filtering 모드

 

매개변수들은 sampler 개체에 저장된다

glGenSamplers(GLsizei n, GLuint *samplers)

glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)

glSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param)

sampler 개체를 타겟에 바인딩하지 않아도 직접 수정할 수 있다

 

샘플러 개체를 텍스처 유닛에 바인딩하기

glBindSampler(GLuint uint, GLuint sampler)

 

각 텍스처는 샘플러 개체를 내장한다

void glTexParameterf(GLenum target, GLenum pname, GLfloat param)

void glTexParameteri(GLenum target, GLenum pname, GLint param)

 

여러 텍스처 사용

- 샘플러 유니폼을 여러 개 생성한다

- 각각 다른 텍스처 유닛을 참조하도록 설정한다

- 몇 개까지 지원하는가?

  GLint maxUnits;

  glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxUnits);

  => 셰이더 스테이지들 중 임의의 한 시점에서 접근 가능한 텍스처 유닛 최대 개수

- 텍스처를 텍스처 유닛에 바인딩하기: active texture unit selector를 변경해야 한다

  glActiveTexture(GL_TEXTURE0 + offset)

- GL_TEXTURE1 ~ 31까지는 정의되어 있음

- 샘플러 유니폼들이 다른 유닛들을 참조하게 하는 방법

  1. glUniform1i() with glGetUniformLocation()

  2. layout(binding = 0) uniform sampler2D foo // 추천

 

텍스처 필터링: 실제로는 텍셀과 픽셀이 일대일로 매칭될 일이 거의 없다

- texelFetch(): 특정 정수 텍스처 좌표에서 단일 텍셀을 가져온다

- 더 유연한 함수로 texture()가 있다

- 텍스처 늘이기 = maginifcation -> GL_TEXTURE_MAG_FILTER

- 텍스처 줄이기 = minification -> GL_TEXTURE_MIN_FILTER

- 기본 필터: GL_NEAREST, GL_LINEAR

  * 기본 필터링은 밉맵 없이는 작동하지 않는다

- glSamplerParameteri(sampler, GL_TEXTURE_MIN_FILTER, GL_NEAREST)

 

밉매핑

- 일반적인 텍스처 매핑의 흔한 문제점

  1. scintillation (계단현상)

  2. performance (띄엄띄엄 하는 추출)

  물체는 작을 때 텍스처는 클 때

- 밉매핑 수준 결정

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0)

  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4)

 

밉맵 필터링

- minification의 경우

  - GL_NEAREST_MIPMAP_NEAREST - nearest mipmap, nearest neighbor sampling

  - GL_NEAREST_MIPMAP_LINEAR - 밉맵간 보간, 최단 이웃 샘플링

  - GL_LINEAR_MIPMAP_NEAREST

  - GL_LINEAR_MIPMAP_LINEAR

- magnification의 경우 GL_NEAREST와 GL_LINEAR만 가능

- GL_LINEAR_MIPMAP_LINEAR를 trilinear 밉매핑이라고 부르기도 한다

 

밉맵 얻는 방법

- 모든 밉맵 수준을 직접 그린다

- OpenGL이 생성하도록 한다: glGenerateMipmap(GLenum target)

 

텍스터 wrap

- glSamplerParameteri(param, value)

  - param: GL_TEXTURE_WRAP_S,T,R

  - value: GL_REPEAT, MIRRORED_REPEAT, CLAMP_TO_EDGE, CLAMP_TO_BORDER

  - S는 1D,2D,3D에, T는 2D,3D에, R은 3D에 영향을 준다

  - clamp to border의 경우 색상 설정은 glSamplerParameterfv()에 GL_TEXTURE_BORDER_COLOR를 인자로

 

array texture: 여러 개의 1D, 2D, 큐브맵 이미지를 하나의 텍스처 개체에 적재

- 둘 이상의 이미지를 하나의 텍스처에 담기는 새로운 개념이 아니다

  - 밉매핑: 각 밉 수준은 별개의 이미지

  - 큐브매핑: 각 면이 고유의 이미지와 밉 수준을 가진다

- 그럼 텍스처 배열은 무엇인가?

  - 텍스처 이미지의 배열이 하나의 텍스처 개체에 바인딩되고 셰이더는 인덱스를 통해 접근한다

  - 종류: 1D, 2D, 큐브맵 배열 텍스처. 단 3D 배열 텍스처는 없음

- 2D 배열 텍스처와 3D 텍스처의 차이점

  - 배열 텍스처의 레이어 사이에는 필터링이 적용되지 않는다

 

- 2D 배열 텍스처 불러오기

GLuint tex;

glGenTextures(1, &tex);

glBindTexture(GL_TEXTURE_2D_ARRAY, tex);

glTexStorage3D(GL_TEXTURE_2D_ARRAY, 8, GL_RGBA8, 256, 256, 100);

for(int i=0; i<100; i++){

  glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, 256, 256, 1, GL_RGBA, GL_UNSIGNED_BYTE, image_data[i]);

}


출처: http://codeonwort.tistory.com/302 [CODEONWORT]

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

[OpenGL] MIP map 사용법

출처: http://m.blog.daum.net/purume77/7243527

 

크게 두 가지만 해주면 된다..

 

첫째 할일...

텍스쳐를 로드하는 함수를

 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);  이것을 사용하는 대신에.. 아래 라인을 사용해야한다.

                                                  ||

                                                  ||

                                                  |/

 gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data );

둘째할일..
TexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );

                                ||

                                        ||

                |/

glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); 
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );


아래는 예제코드............

#include <GL/gl.h>
#include <GL/glut.h>
#include <windows.h>
#include <stdio.h>

GLuint texture; //the array for our texture

GLfloat angle = 0.0;

GLuint LoadTexture( const char * filename, int width, int height )
{
    GLuint texture;
    unsigned char * data;
    FILE * file;

    //The following code will read in our RAW file
    file = fopen( filename, "rb" );
    if ( file == NULL ) return 0;
    data = (unsigned char *)malloc( width * height * 3 );
    fread( data, width * height * 3, 1, file );
    fclose( file );

    glGenTextures( 1, &texture ); //generate the texture with the loaded data
    glBindTexture( GL_TEXTURE_2D, texture ); //bind the texture to it's array
    glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); //set texture environment parameters

    //here we are setting what textures to use and when. The MIN filter is which quality to show
    //when the texture is near the view, and the MAG filter is which quality to show when the texture
    //is far from the view.

    //The qualities are (in order from worst to best)
    //GL_NEAREST
    //GL_LINEAR
    //GL_LINEAR_MIPMAP_NEAREST
    //GL_LINEAR_MIPMAP_LINEAR

    //And if you go and use extensions, you can use Anisotropic filtering textures which are of an
    //even better quality, but this will do for now.
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR );

    //Here we are setting the parameter to repeat the texture instead of clamping the texture
    //to the edge of our shape.
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
    glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

    //Generate the texture with mipmaps
    gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height, GL_RGB, GL_UNSIGNED_BYTE, data );
    free( data ); //free the texture
    return texture; //return whether it was successfull
}

void FreeTexture( GLuint texture )
{
  glDeleteTextures( 1, &texture ); 
}

void square (void) {
    glBindTexture( GL_TEXTURE_2D, texture ); //bind our texture
    glPushMatrix();
    glRotatef( angle, 1.0f, 1.0f, 1.0f );
    glBegin (GL_QUADS);
    glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
    glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
    glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
    glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
    glEnd();
    glPopMatrix();
    glPushMatrix();
    glTranslatef( 0,0,-5 );
    glBegin (GL_QUADS);
    glTexCoord2d(0.0,0.0); glVertex2d(-1.0,-1.0);
    glTexCoord2d(1.0,0.0); glVertex2d(+1.0,-1.0);
    glTexCoord2d(1.0,1.0); glVertex2d(+1.0,+1.0);
    glTexCoord2d(0.0,1.0); glVertex2d(-1.0,+1.0);
    glEnd();
    glPopMatrix();
}

void display (void) {
    glClearColor (0.0,0.0,0.0,1.0);
    glClear (GL_COLOR_BUFFER_BIT);
    glLoadIdentity();  
    glEnable( GL_TEXTURE_2D ); //enable texturing
    gluLookAt (0.0, 0.0, 5.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    square();
    glutSwapBuffers();
    angle ++;
}
void reshape (int w, int h) {
    glViewport (0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode (GL_PROJECTION);
    glLoadIdentity ();
    gluPerspective (60, (GLfloat)w / (GLfloat)h, 1.0, 100.0);
    glMatrixMode (GL_MODELVIEW);
}

int main (int argc, char **argv) {
    glutInit (&argc, argv);
    glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowSize (500, 500);
    glutInitWindowPosition (100, 100);
    glutCreateWindow ("A basic OpenGL Window");
    glutDisplayFunc (display);
    glutIdleFunc (display);
    glutReshapeFunc (reshape);

    texture = LoadTexture( "texture.raw", 256, 256 ); //load our texture

    glutMainLoop ();

    FreeTexture( texture );

    return 0;
}

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://m.blog.daum.net/boy7407/17462780?categoryId=995731

 

06 그래픽스 기본요소의 속성 (2).txt
0.01MB
06.pdf
3.21MB

 

★ 학습 개요


그래픽 기본요소들에는 각각에 맞는 여러 가지 속성이 지정되며, 이 속성에 따라 원하는 모습으로 디스플레이 된다. 이번 강의에서는 도형의 내부 영역을 채우기 위한 속성 및 영역의 내부를 판별하여 채우기 속성을 적용하는 알고리즘에 대하여 학습한다. 또한 래스터 그래픽스 영상에서는 유한한 크기의 점을 이용하여 그림을 표현하기 때문에 에일리어싱이 발생하여 그림의 질을 떨어뜨린다. 이를 완화하기 위한 기술이 안티에일리어싱이며, 안티 에일리어싱을 구현하는 기술들에 대해 소개한다.


★ 목표


1. 비트맵 마스크를 적용하는 과정을 설명할 수 있다.
2. 주사선 다각형 채우기 방식에서 다각형의 내부와 외부를 판별하는 방법을 설명할 수 있다.
3. 경계선 채우기 및 범람 채우기 알고리듬을 설명할 수 있다.
4. 래스터 그래픽스 방식에서 사용할 수 있는 안티 에일리어싱 기술들을 설명할 수 있다.

 

 

★ 목차
1. 영역 채우기 속성
2. 영역 채우기 알고리듬
3. 안티 에일리어싱

 

 


○ 영역 채우기
* 채우기 속성

* 영역의 정의

 


○ 비트맵 패턴 채우기
◇ 타일 붙이기
* 직사각형 배열 형태의 마스크로 패턴 정의

 


* 대상 영역을 모두 채울 때까지 수평 및 수직 방향으로 반복적으로 복제


◇ 타일 붙이기의 기준 위치
① 윈도 원점 기준

 


 

 


② 도형의 경계상자 모서리 기준

 


 

 


○ OpenGL의 채우기 속성 함수


◇ 다각형 모드 설정


void glPolygonMode(GLenum face, GLenum mode);


☞ face : 모드를 설정할 면 선택
- GL_FRONT : 앞면
- GL_BACK : 뒷면
- GL_FRONT_AND_BACK : 양면
☞ mode : 다각형 그리기 방법
- GL_POINT : 꼭짓점만 그림
- GL_LINE : 변만 그림
- GL_FILL : 다각형 내부를 채움


◇ 비트맵 마스크 설정


void glPolygonStipple(const GLubyte* mask);


☞ mask : 32 x 32 비트맵 마스크 

 


◇ 비트맵 마스크 적용 예)


void drawBitmapPolygon()
{
   GLubyte fillPattern[] = {
      ......
   };
   glClearCOlor(GL_COLOR_BUFFER_BIT);
   glEnable(GL_POLYGON_STIPPLE);
   glPolygonStipple(fillPattern);
   glPolygonMode(GL_FRONT, GL_FILL);
   glBegin(GL_POLYGON); // 아래 다시 소스코드 있음
      ......
   glEnd();
   glFlush();
}


* 소스코드


void drawBitmapPolygon()
{
   ......
   glBegin(GL_POLYGON);
      glColor3f(0,0,1);
      glVertex2i(100,200);
      glColor3f(0,1,1);
      glglVertex2i(50,50);
      glClor3f(0,1,0);
      glVertex2i(350,50);
      glColor3f(1,0,0);
      glVertex2i(250,250);
   glEnd();
   glFlush();
}

 


● 2 영역 채우기 알고리듬

 

○ 주사선 다각형 채우기


◇ 홀 - 짝 규칙

◇ 래스터 좌표와 픽셀 영역
* 직사각형 (0,0) - (4,3)
11
◇ 래스터 좌표를 고려한 주사선 다각형 채우기
12
13
◇ 다각형 선분 테이블
14
◇ 활동 선분 목록
15
16
17
18
19
20
21
22
23
24
○ 불규칙한 경계로 정의되는 영역 채우기
25
○ 경계선 채우기 알고리듬


◇ 이웃의 정의
26
◇ 경계선이 4방향 연결로 정의된 경우의 채우기
27
◇ 경계선이 8방향 연결로 정의된 경우의 채우기
28


// x, y : 픽셀좌표, fCol : 채우기 색, bCol : 경계선 색
void boundaryFill4(int x, int y, int fCol, int bCol){
{
   int intCol;
   getPixel(x, y, intCol); // (x, y) 위치의 픽셀 색을 읽음
   // 경계선 색이나 채우기 색이 아닌 경우
   if (intCol != bCol && intCol != fCol){
      setPixel(x, y); // 현재 위치를 채우기 색으로 그림
      // 4방향 연결성에 따라 이웃 픽셀에 경계선 채우기를 적용
      boundaryFill4(x+1, y, fCol, bCol);
      boundaryFill4(x-1, y, fCol, bCol);
      boundaryFill4(x, y+1, fCol, bCol);
      boundaryFill4(x, y-1, fCol, bCol);
   }
}


○ 범람 채우기 알고리듬


◇ 동일 색 영역의 지정된 채우기 속성에 따라 채움
29
// x, y : 픽셀 좌표, fCol : 채우기 색, intCol : 내부 색
void floodFill4(int x, int y, int fCol, int intCol) {
{
   int currCol;
   getPixel(x, y, currCol); // (x, y)의 위치의 픽셀 색을 읽음
   // 현 위치의 색이 영역 내부 색인 경우
   if (currCol == intCol) {
      setPixel(x, y); // 현재 위치를 채우기 색으로 그림
      // 4방향 연결성에 따라 이웃 픽셀에 경계선 채우기를 적용
      floodFill4(x+1, y, fCol, intCol);
      floodFill4(x-1, y, fCol, intCol);
      floodFill4(x, y+1, fCol, intCol);
      floodFill4(x, y-1, fCol, intCol);
   }
}


● 3 안티 에일리어싱


○ 에일리어싱


◇ 낮은 해상도로 인한 정보 왜곡
30
31
○ 안티 에일리어싱


◇ 전 필터링 (prefiltering)
* 물체가 차지하는 픽셀 면적을 기준으로 픽셀의 밝기를 조절하는 방법
* 영역 샘플링 (area sampling)이라고도 함
32
◇ 슈퍼 샘플링 (supersampling)
* 하나의 픽셀을 여러 개의 작은 픽셀로 분할
* 물체 영역이 차지하는 서브픽셀의 수에 따라 픽셀 색 결정
33
◇ 후 필터링 (postfiltering)
* 이웃 샘플들의 가중치 평균으로 픽셀 값을 결정
34
35
○ OpenGL의 안티 에일리어싱 함수


◇ 색 혼합 활성화


glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);


◇ 안티 에일리어싱 활성화


void glEnable(GLenum aaPrimitive);


☞ aaPrimitive : 안티 에일리어싱을 적용할 그래픽스 기본요소의 유형
   GL_POINT_SMOOTH
   GL_LINE_SMOOTH
   GL_POLYGON_SMOOTH


◇ 안티 에일리어싱 적용 예)


void drawPolygonAntiAlias(void)
{
   glClear(GL_COLOR_BUFFER_BIT); // 윈도우 색으로 지움


   glEnable(GL_BLEND);          // 색 혼합 활성하
   glBlendFunc(GL_SRC_ARPHA, GL_ONE_MINUS_SRC_ALPHA);
   glEnable(GL_LINE_SMOOTH);   // 선 안티 에일리어싱 활성화
   glEnable(GL_POINT_SMOOTH);  // 점 안티 에일리어싱 활성화
   glColor3f(0.5, 0.5, 1.0);   // 그리기 색상 지정
   glLineWidth(10);            // 선 폭 지정
   glPolygonMode(GL_FRONT, GL_LINE); // 다각형의 변을 그림
   glBegin(GL_POLYGON);        // 다각형 그리기
      glVertex2i(100, 200);
      glVertex2i(50, 50);
      glVertex2i(350, 50);
      glVertex2i(250, 250);
   glEnd();
   ......
}

36


★ 연습문제


1. 범람 채우기는 어떠한 영역을 채울 때 사용하는가 ?


① 동일한 경계색으로 둘러싸인 영역
② 곡선으로 둘러싸인 영역
③ 동일한 색으로 이루어진 영역
④ 변화하는 색으로 채워진 영역


2. 32 X 32 크기의 비트맵 마스크를 디스플레이 윈도 좌측 하단의 모서리인 (0, 0)을 기준으로 타일 붙이기 하려고 한다. 좌표 (x, y)에 대한 마스크 좌표 (xm, ym)을 계산하는 수식은 무엇인가 ?


3. 주사선 다각형 채우기 알고리듬의 처리 과정 중 어느 주사선에서 활동 선분 목록이 다음과 같을 때 픽셀을 채우는 x좌표 구간은 ?
37
4. 다음은 범람 채우기 알고리듬이다. (ㄱ)에 넣을 적절한 것은 ?


// x,y : 픽셀 좌표, fCol : 채우기 색, intCol : 내부 색
void floodFill4(int x, int y, int fCol, int intCol) {
{
   int currCol;
   getPixel(x, y, currCol);   // (x, y)위치의 픽셀 색을 읽음
   // 현 위치의 색이 영역 내부 색인 경우
   if (   (ㄱ)   ) {
      setPixel(x, y);   // 현재 위치를 채우기 색으로 그림
      // 4방향 연결성에 따라 이웃 픽셀에 경계선 채우기를 적용
      floodFill4(x+1, y, fCol, intCol);
      floodFill4(x-1, y, fCol, intCol);
      floodFill4(x, y+1, fCol, intCol);
      floodFill4(x, y-1, fCol, intCol);
   }
}


① currCol == intCol
② currCol == fCol
③ x < y
④ x == y


5. 안티 에일리어싱 기법을 적용하여 얻을 수 있는 효과는 ?


① 배경이 투명하게 비쳐 보이게 한다.
② 대각선 방향의 선분이 계단처럼 거칠게 보이는 것을 부드럽게 디스플레이되도록 한다.
③ 다각형의 내부를 지정된 비트맵으로 채운다.
④ 도형이 복제된다.


정답 :
1. ③ 범람 채우기는 동일한 색으로 둘러싸인 영역 내부의 한 점을 지정하면 정의된 연결성 규칙에 따라 동일한 내부색 이웃 픽셀들을 채우는 과정을 반복하는 영역 채우기 알고리듬이다.
2. xm = x mod 32, ym = y mod 32
(0, 0)을 기준으로 타일 붙이기를 하므로 각각의 좌표 성분을 마스크의 크기로 나누어 나머지를 구하면 마스크 내에서의 좌표를 구할 수 있다.
3. 5 ≤ x ≤ 7, 10 ≤ x ≤ 17
활동선분 목록에 저장되는 내용은 각각의 변이 주사선과 교차하는 x 좌표, 그 변의 최대 y 주사선 좌표, 기울기의 역수, 다음 항목을 가리키는 포인터이다. 홀-짝 규칙에 따라 두 항목씩 짝을 이루어 내부를 채우는데, 첫 번째 항목의 x좌표가 x1, 둘째 항목의 x 좌표가 x2라면 [x1]부터 [x2]-1까지의 픽셀을 그린다.
4. ① 범람 채우기 알고리듬은 동일한 내부색으로 지정된 영역을 채우는 알고리듬이다.
5. ② 에일리어싱 현상은 래스터 그래픽스 디스플레이 시스템에서 픽셀 단위로 그림을 출력하기 때문에 발생하는 현상으로 대각선 경계의 경우 계단형으로 거칠게 표현된다. 안티 에일리어싱을 적용하면 이러한 현상을 완화하여 부드러운 시각적 효과를 낼 수 있다.


★ 내용정리


1. 비트맵 패턴의 채우기는 정해진 크기의 마스크를 반복적으로 이어 붙이는 타일 붙이기 형식으로 처리된다.
2. 타일 붙이기는 윈도의 원점이나 도형 경계상자의 모서리점을 기준으로 적용한다.
3. 주사선 다각형 채우기
- 각각의 주사선 단위로 홀-짝 규칙을 적용한다.
- 주사선이 꼭짓점을 만나는 경우의 처리를 위해 각 변에서 y축 좌표의 값이 큰 꼭짓점 좌표는 제외한다. 또한 수평 선분은 처리에서 제외한다.
- 다각형 선분 테이블에는 각 주사선 단위로 그 주사선 y좌표에서 시작되는 변들의 리스트를 x좌표 순서에 따라 저장한다.
- 활동 선분 목록은 현재 처리중인 주사선과 교차하는 변들의 리스트를 저장한다.
4. 경계선 채우기 알고리듬은 정해진 색으로 되어 있는 경계로 둘러싸인 영역 내부를 지정된 색으로 채운다.
5. 범람 채우기 알고리듬은 동일한 색으로 되어 있는 영역을 지정된 색으로 채운다.
6. 에일리어싱은 낮은 해상도로 인한 정보 왜곡이다. 래스터 그래픽스 시스템에서 선분이나 영역 경계가 계단형으로 출력되는 현상이 나타난다.
7. 안티 에일리어싱은 에일리어싱 현상을 완화하기 위한 기술로서, 전 필터링은 물체가 차지하는 픽셀 면적에 따라 픽셀의 값을 결정한다.
8. OpenGL의 속성 채우기 관련 함수
void glPolygonMode(GLenum face, GLenum mode); - 다각형을 그리는 방법 선택
void glEnable(GL_POLYGON_STIPPLE); - 다각형 패턴 채우기 활성화 
void glDisable(GL_POLYGON_STIPPLE); - 다각형 패턴 채우기 해제
void glPolygonStipple(const GLubyte*mask); - 32 X 32 크기의 비트맵 마스크 정의

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://blog.naver.com/crazygats/60017606630

* OpenGL 상태 기계 ( OpenGL state machine ) 는 렌더링의 여러 측면들에 영향을 주는 수 백개의 설정들로 구성된다. 상태기계는 OpenGL 의 모든 측면들을 관장한다.

상태들을 조회하거나 설정하는 함수들 
glGet() 함수들 : 상태기계의 현재 상태를 조회 

void glGetBooleanv( GLenum pname, GLboolean *params );
void glGetDoublev( GLenum pname, GLdouble *params );
void glGetFloatv( GLenum pname, GLfloat *params );
void glGetIntegerv( GLenum pname, GLint *params );

이 함수들을 호출하면 조회하고자 하는 상태에 관련된 값이 param 인자에 배정된다.
pname 은 조회하고자 하는 상태를 지정한다.

glIsEnabled() 함수 : 어떤 특정한 OpenGL 기능성의 활성화 여부를 알아낼때 사용한다. ( 참 거짓 만을 알아내는 경우 )

GLboolean glIsEnabled( GLenum cap );

이 함수는 인자가 가리키는 기능성이 활성화되어 있으면 GL_TRUE, 그렇지 않으면 GL_FALSE 를 돌려준다.

 

< 기본 도형 그리기 >

OpenGL 에서 기본 도형( primitive )은 가장 기본적인 기하학적인 객체들, 즉 점과 선, 삼각형 등을 가리킨다.
도형을 그리기 위해 가장많이 사용되는 함수는 glBegin(), glEnd()

glBegin() 함수 : 기본도형을 그리기 위한 선언

void glBegin( GLenum mode );

glBegin()의 인자에 사용할 수 있는 값들

상수 설명
GL_POINTS 개별적인 점들
GL_LINES 연결되지 않는 선분들
GL_LINE_STRIP 연결된 일련의 선분들
GL_LINE_LOOP 연결된 선분들 - 처음과 끝이 연결되어 하나의 닫힌 선분들이 된다
GL_TRIANGLES 개별적인 삼각형들
GL_TRIANGLE_STRIP 연결된 일련의 삼각형들
GL_TRIANGLE_FAN 하나의 꼭지점을 공유하는 일련의 삼각형들
GL_QUADS 개별적인 사각형들
GL_QUAD_STRIP 연결된 일련의 사각형들
GL_POLYGON 임의의 개수의 꼭지점들을 가진 다각형

 

 

 

 

 

 

 

 

 

 

 

glBegin() 을 호출해서 기본도형을 시작했으면 반드시 glEnd()로 끝내야 한다.

void glEnd();

여기서 명심해야 할것은, glBegin() / glEnd() 쌍이 중첩되어서는 안된다. 이 블럭안에서 또 다른 glBegin() / glEnd() 를 호출해서는 안된다.  

glBegin() / glEnd() 쌍안에서 사용할수 있는 OpenGL 함수들 :

glVertex(), glColor(), glIndex(), glNormal(), glTexcoord(), glEvalCoord(). glEvalPoint(), glMaterial(), glEdgeFlag(), glCallList(), glCallLists() 의 변형함수들

 

  1. 점찍기

    glBegin( GL_POINTS );
        glVertex3f( 0.0, 0.0, 0.0 );
    glEnd();


    점크기 변경 : void glPointSize( GLfloat size );

    현재 점의 크기 확인 : glGet( GL_POINT_SIZE );

    점의 안티얼라이어싱( antialiasing ) : 변을 부드럽게 표현하는 하나의 수단. 활성화 
             활성화 :     glEnable( GL_POINT_SMOOTH );

             비활성화 :      glDisable( GL_POINT_SMOOTH );

             현재 활성화여부 확인 :      glGet( GL_POINT_SMOOTH );  or  glIsEnabled( GL_POINT_SMOOTH );

             안티얼라이어싱 지원크기 확인 :      
                           GLfloat sizes[ 2 ];
                           GLfloat granularity;
                           
                           glGetFloatv( GL_POINT_SIZE_RANGE, sizes );
                           GLfloat minPointSize = sizes[ 0 ];
                           GLfloat maxPointSize = sizes[ 1 ];
                           
                           glGetFloatv( GL_POINT_SIZE_GRANULARITY, &granularity );

  2. 선 긋기

    glBegin( GL_LINES );
        glVertex3f( -2.0, -1.0, 0.0 );
        glVertex3f( 3.0, 1.0, 0.0 );
    glEnd();


    선굵기변경 :  기본굵기는 1.0      void glLineWidth( GLfloat width );

    현재의 선굵기 확인 :  glGet( GL_LINE_WIDTH );

    선의 안티얼라이어싱 : 점의 안티얼라이어싱과 동일 ( GL_POINT_SIZE_RANGE  -->  GL_LINE_SIZE_RANGE, 
                                                                            GL_POINT_SIZE_GRANULARITY -->  GL_LINE_WIDTH_GRANULARITY,
                                                                            GL_POINT_SMOOTH  -->  GL_LINE_SMOOTH,
                                                                            minLineWidth, maxLineWidth )

  3. 다각형 그리기 

    OpenGL 은 다각형의 전면( front face - 카메라를 바라보는 면 )과 후면( back face - 카메라 반대쪽을 바라보는 면 )을 개별적으로 취급

    void glPolygonMode( GLenum face, GLenum mode );

    face 인자 : GL_FRONT, GL_BACK, GL_FRONT_AND_BACK
    mode 인자 : GL_POINT, GL_LINE, GL_FILL

    예) glPolygonMode( GL_FRONT, GL_FILL );          전면 색칠
          glPolygonMode( GL_BACK, GL_LINE );            후면 와이어 프레임( 테두리만 )

    현재 그리기 모드 확인 : glGet( GL_POLYGON_MODE );

    다각형의 면제거( face culling ) :
         glEnable( GL_CULL_FACE );
         glCullFace( GL_BACK );

    OpenGL은 다각형을 감는 방법( polygon winding )에 기반해서 전면과 후면 판단.
    반시계 방향으로 감긴 다각형 : 전면
    시계 방향으로 감긴 다각형 : 후면 
    형식 변경 : void glFrontFace( GLenum mode );
                       mode 인자 : GL_CCW ( 반시계방향을 전면으로 간주 ), GL_CW ( 시계 방향을 전면으로 간주 )

    다각형 변숨기기 : void glEdgeFlag( GLboolean isEdge );    or    void glEdgeFlag( const GLboolean *isEdge );
                 isEdge : GL_TRUE ( 기본 설정 ), GL_FALSE ( 그리지 않는다 )

    다각형의 안티얼라이어싱 : 켜기 ( glEnable( GL_POLYGON_SMOOTH );  ),   끄기 ( glDisable( GL_POLYGON_SMOOTH );  )

  4. 삼각형 그리기 

    glBegin( GL_TRIANGLES );
       glVertex3f( 0.0, 0.0, 1.0 );
       glVertex3f( 0.0, 0.0, 1.0 );
       glVertex3f( 0.0, 0.0, 1.0 );
    glEnd();

 

* void glFlush( void );

앞에서 요청한 OpenGL 커멘트들을 강제로 실행시켜서 요청된 커맨트들이 일정한 시간내에 완전히 종료한다는것을 보장
이것으로 부족할 경우 glFinish() 사용

[출처] [OPENGL]기본도형그리기|작성자 가츠

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://blog.naver.com/crazygats/60017606725

 

 

Texture

 

void glTexImage2D( GLenum target, GLint level, GLint internalFormat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* texels )

 

void glBindTexture( GLenum target, GLuint textureName );

 

vodi glDeleteTexture( )

void glTexCoord2f( float s, float t );
void glTexCoord2fv( float *coords );

void glTexEnvi( GLenum target, GLenum pname, GLint param );     // 텍스쳐와 광원 합체 
param : GL_BLEND, GL_DECAL, GL_MODULATE

 

밉맵

void glTexParameteri( GLenum target, GLenum pname, GLint param );

param( 필터값 ) : GL_NEAREST_MIPMAP_NEAREST, GL_NEREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_LINEAR

 glTexImage2D( GL_TEXTURE_2D, 1

 

고급텍스쳐 맵핑

#include "glext.h"  OpenGL확장을 위한 헤더

어떠한 특정 확장의 지원 여부를 알아보고자 할때 :  glGetString( GL_EXTENTION )

문자열에  "GL_ARB_multitexture" 가 존재하면 다중 텍스쳐링 지원

 

확장함수에 대한 접근 :

어떠한 OpenGL 확장함수를 사용하려면 그 확장함수에 대한 주소를 얻어야한다.
윈도우즈 경우 wglGetProcAddress() 함수를 사용한다. 이 함수에 확장 함수 이름을 
넣어서 호출하면 그 함수에 대한 함수  포인터를 돌려준다.

* glMultiTexCoordifARB( 여기서 i = 1.4 ) 이 함수들은 다중 텍스쳐링을 위한 텍스쳐 좌표들을 지정
*  glActiveTextureARB.     현재 텍스쳐 단위를 설정한다.

PFNGLACTIVETEXTUREARBPROC   glActiveTextureARB =

PFNGLCLINETACTIVETEXTURARBPROC  glClientActiveTextureARB =

 

 A TEXTURE

B TEXTURE

A  --> glTexEnvi(   ,    , GL_REPLACE )
B  --> glTexEnvi(   ,    , GL_MODULATE )
glActiveTextureARB( GL_TEXTURE0_ARB );
glEnable ( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, A );

렌더 부분

glMultiTexCoord2fARB

[출처] [OPENGL]Texturing|작성자 가츠

 

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://nehe.gamedev.net/tutorial/fullscreen_antialiasing/16008/

 

Fullscreen AntiAliasing

Howdy all, the Friendly Neighborhood Roach here with an interesting tutorial that will help you get your apps top notch. In the realm of getting your OpenGL programs to look better, a big problem we all run into is aliasing. That is, the square edged "jaggies" that exist on diagonal lines in relation to the square pixels that exist on your screen. Ie, Bad Mojo. The answer, termed Anti-Aliasing, is used to smudge those "jaggies" in order to create a smoother edge for objects. One process used to achieve anti-aliasing is called "Multisampling." The idea is that for each pixel, we sample the pixels around it to determine if this edge needs to be anti-aliased, basically, we discard the jaggies by "smudging" the pixel itself.

Fullscreen AntiAliasing is something that non-realtime rendering programs have always had an advantage in. However, with current hardware, we're able to pull off the same effect real time. The ARB_MULTISAMPLE extension allows us to do this. Essentially, each pixel is sampled by it's neighbors to find out the optimal antialias to perform. This comes at a cost however, and can slow down performance.

Vid_mem = sizeof(Front_buffer) + sizeof(Back_buffer) + num_samples

    * (sizeof(Front_buffer) +sizeof(ZS_buffer))

 

For more specific information on anti-aliasing, as well as the information I'm about to present, please check out the following links:

GDC2002 -- OpenGL Multisample
OpenGL Pixel Formats and Multisample Antialiasing

With that being said, a brief overview of how our process is going to work. Unlike other extensions, which relate to OpenGL rendering, the ARB_MULTISAMPLE extension must be dealt with during the creation of your rendering window. Our process shall go as follows:

  1. Create our Window as normal
  2. Query the possible Multisample pixel values (InitMultisample)
  3. If Multisampling is available, destroy this Window and recreate it with our NEW pixelFormat
  4. For parts we want to antialias, simply call glEnable(GL_ARB_MULTISAMPLE);

Let's start off from the top, and talk about our source file arbMultiSample.cpp. We start off with the standard includes for gl.h & glu.h, as well as windows.h, we'll talk about arb_multisample.h later.

#include <windows.h>

#include <gl/gl.h>

#include <gl/glu.h>

#include "arb_multisample.h"

The two lines below define our access points into the WGL string listing. We'll use these in accessing the pixel format attributes to test for our sample format. The other two variables are used other places in the program to access our data.

// Declarations We'll Use

#define WGL_SAMPLE_BUFFERS_ARB  0x2041

#define WGL_SAMPLES_ARB     0x2042

bool    arbMultisampleSupported = false;

int arbMultisampleFormat    = 0;

The next function we're going to talk about is WGLisExtensionSupported, which will be used to query the WGL extensions listing to verify if a given format is supported on the system. We'll provide the description of the code as we walk through it, because it's easier to do that then all the html back and forth jumping....

NOTE: The code below was rewritten by Henry Goffin. His update adds: Better parsing of the supported GL Extensions and fixes a problem with the fallback code if the first check was to fail.

bool WGLisExtensionSupported(const char * extension) {
    const size_t extlen = strlen(extension);
    const char * supported = NULL;
    // Try To Use wglGetExtensionStringARB On Current DC, If Possible
    PROC wglGetExtString = wglGetProcAddress("wglGetExtensionsStringARB");
    if (wglGetExtString) 
        supported = ((char * (__stdcall *)(HDC))wglGetExtString)(wglGetCurrentDC());
    
    // If That Failed, Try Standard Opengl Extensions String
    if (supported == NULL) 
        supported = (char *)glGetString(GL_EXTENSIONS);
    
    // If That Failed Too, Must Be No Extensions Supported
    if (supported == NULL) 
        return false;
    
    // Begin Examination At Start Of String, Increment By 1 On False Match
    for (const char * p = supported;; p ++) { // Advance p Up To The Next Possible Match
        p = strstr(p, extension);
        if (p == NULL) 
            return false;
        
        // No Match
        // Make Sure That Match Is At The Start Of The String Or That
        // The Previous Char Is A Space, Or Else We Could Accidentally
        // Match "wglFunkywglExtension" With "wglExtension"
        // Also, Make Sure That The Following Character Is Space Or NULL
        // Or Else "wglExtensionTwo" Might Match "wglExtension"
        if ((p == supported || p[-1] == ' ') && (p[extlen] == '\0' || p[extlen] == ' ')) 
            return true;
         // Match
    }
}

The next function is the gist of the program itself. Basically, we're going to query if our arb extention is supported on the system. From there, we'll move on to querying our device context to find out the scope of our multisample. Again... let's just jump into the code.

bool InitMultisample(HINSTANCE hInstance, HWND hWnd, PIXELFORMATDESCRIPTOR pfd) { // See If The String Exists In WGL!
    if (!WGLisExtensionSupported("WGL_ARB_multisample")) {
        arbMultisampleSupported = false;
        return false;
    }
    // Get Our Pixel Format
    PFNWGLCHOOSEPIXELFORMATARBPROC wglChoosePixelFormatARB = (PFNWGLCHOOSEPIXELFORMATARBPROC)wglGetProcAddress("wglChoosePixelFormatARB");
    if (!wglChoosePixelFormatARB) { // We Didn't Find Support For Multisampling, Set Our Flag And Exit Out.
        arbMultisampleSupported = false;
        return false;
    }
    // Get Our Current Device Context. We Need This In Order To Ask The OpenGL Window What Attributes We Have
    HDC hDC = GetDC(hWnd);
    int pixelFormat;
    bool valid;
    UINT numFormats;
    float fAttributes[] = {
        0,
        0
    };
    // These Attributes Are The Bits We Want To Test For In Our Sample
    // Everything Is Pretty Standard, The Only One We Want To
    // Really Focus On Is The SAMPLE BUFFERS ARB And WGL SAMPLES
    // These Two Are Going To Do The Main Testing For Whether Or Not
    // We Support Multisampling On This Hardware
    int iAttributes[] = {
        WGL_DRAW_TO_WINDOW_ARB,
        GL_TRUE,
        WGL_SUPPORT_OPENGL_ARB,
        GL_TRUE,
        WGL_ACCELERATION_ARB,
        WGL_FULL_ACCELERATION_ARB,
        WGL_COLOR_BITS_ARB,
        24,
        WGL_ALPHA_BITS_ARB,
        8,
        WGL_DEPTH_BITS_ARB,
        16,
        WGL_STENCIL_BITS_ARB,
        0,
        WGL_DOUBLE_BUFFER_ARB,
        GL_TRUE,
        WGL_SAMPLE_BUFFERS_ARB,
        GL_TRUE,
        WGL_SAMPLES_ARB,
        4, // Check For 4x Multisampling
        0,
        0
    };
    // First We Check To See If We Can Get A Pixel Format For 4 Samples
    valid = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, & pixelFormat, & numFormats);
    // if We Returned True, And Our Format Count Is Greater Than 1
    if (valid && numFormats >= 1) {
        arbMultisampleSupported = true;
        arbMultisampleFormat = pixelFormat;
        return arbMultisampleSupported;
    }
    // Our Pixel Format With 4 Samples Failed, Test For 2 Samples
    iAttributes[19] = 2;
    valid = wglChoosePixelFormatARB(hDC, iAttributes, fAttributes, 1, & pixelFormat, & numFormats);
    if (valid && numFormats >= 1) {
        arbMultisampleSupported = true;
        arbMultisampleFormat = pixelFormat;
        return arbMultisampleSupported;
    }
    // Return The Valid Format
    return arbMultisampleSupported;
}

Now that we've gotten our query code done, we have to modify how our window creation works. First, we have to include our arb_multisample.h file, along with moving the destroywindow and createwindow prototypes up to the top of the file.

#include <windows.h>// Header File For The Windows Library
#include <gl/gl.h>// Header File For The OpenGL32 Library
#include <gl/glu.h>// Header File For The GLu32 Library
#include "NeHeGL.h"// Header File For The NeHeGL Basecode
// ROACH
#include "ARB_MULTISAMPLE.h" BOOL DestroyWindowGL (GL_Window* window);
BOOL CreateWindowGL(GL_Window* window);
// ENDROACH

 

This next bit needs to be added inside the CreateWindowGL function, the original code is left, with where you need to make modifications. Basically, we're doing a bit of overkill to get the job done. We can't request the pixel format (to query the multisampling) until we've created a window. But we can't create a FSAA screen, unless we know the pixel format will support it. Kinda the chicken and the egg thing. So, what I've done, is a little 2 pass system; We create the window, query the pixelFormat, then destroy/remake the window if multisampling is supported. Kinda nifty...

 

window -> hDC = GetDC(window -> hWnd); // Grab A Device Context For This Window
if (window -> hDC == 0) {
    // Did We Get A Device Context?
    // Failed
    DestroyWindow(window -> hWnd); // Destroy The Window
    window -> hWnd = 0; // Zero The Window Handle
    return FALSE; // Return False
}
// ROACH
// Our First Pass, Multisampling Hasn't Been Created Yet, So We Create A Window Normally
// If It Is Supported, Then We're On Our Second Pass
// That Means We Want To Use Our Pixel Format For Sampling
// So Set PixelFormat To arbMultiSampleformat Instead
if (!arbMultisampleSupported) {
    PixelFormat = ChoosePixelFormat(window -> hDC, & pfd); // Find A Compatible Pixel Format
    if (PixelFormat == 0) {
        // Did We Find A Compatible Format?
        // Failed
        ReleaseDC(window -> hWnd, window -> hDC); // Release Our Device Context
        window -> hDC = 0; // Zero The Device Context
        DestroyWindow(window -> hWnd); // Destroy The Window
        window -> hWnd = 0; // Zero The Window Handle
        return FALSE; // Return False
    }
} else {
    PixelFormat = arbMultisampleFormat;
}
// ENDROACH
if (SetPixelFormat(window -> hDC, PixelFormat, & pfd) == FALSE) {
    // Try To Set The Pixel Format
    // Failed
    ReleaseDC(window -> hWnd, window -> hDC); // Release Our Device Context
    window -> hDC = 0; // Zero The Device Context
    DestroyWindow(window -> hWnd); // Destroy The Window
    window -> hWnd = 0; // Zero The Window Handle
    return FALSE; // Return False
}

 

 

Now, the window has been created, so we have a valid handle to query for Multisample support. If we find it's supported, we destroy this window, and call CreateWindowGL again, but with the new pixel format.

// Make The Rendering Context Our Current Rendering Context
if (wglMakeCurrent(window -> hDC, window -> hRC) == FALSE) { // Failed
    wglDeleteContext(window -> hRC); // Delete The Rendering Context
    window -> hRC = 0; // Zero The Rendering Context
    ReleaseDC(window -> hWnd, window -> hDC); // Release Our Device Context
    window -> hDC = 0; // Zero The Device Context
    DestroyWindow(window -> hWnd); // Destroy The Window
    window -> hWnd = 0; // Zero The Window Handle
    return FALSE; // Return False
}
// ROACH
// Now That Our Window Is Created, We Want To Query What Samples Are Available
// We Call Our InitMultiSample Window
// if We Return A Valid Context, We Want To Destroy Our Current Window
// And Create A New One Using The Multisample Interface.
if (!arbMultisampleSupported && CHECK_FOR_MULTISAMPLE) {
    if (InitMultisample(window -> init.application -> hInstance, window -> hWnd, pfd)) {
        DestroyWindowGL(window);
        return CreateWindowGL(window);
    }
}
// ENDROACH
ShowWindow(window -> hWnd, SW_NORMAL); // Make The Window Visible
window -> isVisible = TRUE;

OK, so setup is now complete! We've setup our screen to allow proper depth for multi sampling. Now comes the fun part, actually doing it! Luckily, ARB decided to make multisampling dynamic, so that allows us to turn it on and off with a glEnable / glDisable call.

glEnable(GL_MULTISAMPLE_ARB);
// Render The Scene
glDisable(GL_MULTISAMPLE_ARB);

 

And that's it! As far as the other code for the demo goes, it's a simple rotating effect to show off how nifty this method works. ENJOY!

Colt "MainRoach" McAnlis

* DOWNLOAD Visual C++ Code For This Lesson.

* DOWNLOAD Borland C++ Builder 6 Code For This Lesson. ( Conversion by Le Thanh Cong )
* DOWNLOAD Code Warrior 5.3 Code For This Lesson. ( Conversion by Scott Lupton )
* DOWNLOAD Delphi Code For This Lesson. ( Conversion by Michal Tucek )
* DOWNLOAD Dev C++ Code For This Lesson. ( Conversion by Neil Roy )
* DOWNLOAD Visual Studio .NET Code For This Lesson. ( Conversion by Joachim Rohde )

< Lesson 45Lesson 47 >

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

그외 링크:

컴퓨터그래픽스[2014] 04장 - 그래픽스 기본요소의 속성 v1.5.pdf
4.63MB
컴퓨터그래픽스특록_6장_김명식.pptx
1.28MB

 

http://gcland.tistory.com/140 //텍스쳐 폴리곤에 대해 매우잘설명

 

http://blog.daum.net/_blog/BlogTypeView.do?blogid=0G6E3&articleno=7243527&categoryId=282835®dt=20080929133415

 

http://nfish.tistory.com/category/%EA%B3%B5%EB%B6%80?page=3

 

http://www.slideserve.com/dara/3813168

 

GL_POLYGON_SMOOTH 텍스쳐 깨짐

 

https://gamedev.stackexchange.com/questions/110286/how-to-fix-texture-edge-artefacts

 

번역: https://translate.googleusercontent.com/translate_c?depth=1&hl=ko&ie=UTF8&prev=_t&rurl=translate.google.co.kr&sl=auto&sp=nmt4&tl=ko&u=https://gamedev.stackexchange.com/questions/110286/how-to-fix-texture-edge-artefacts&usg=ALkJrhg369qAtub8K8B7mrjLM18OoadJrg

 

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

출처: http://www.galexandria.com/main/nehe-opengl-tutorials/07-texture-filter-lighting-keyboard-controls

본문


이 강좌에서 저는 3가지 다른 텍스처 필터를 사용하는 방법을 설명할 것입니다. 키보드의 키를 이용하여 물체를 움직이는 법과 간단한 조명을 OpenGL 장면에 적용시키는 법도 배워봅시다. 이 강좌는 많은 내용들을 다루니 이전 강좌들을 이해하는데 어려움을 겪으신 분들이 계시다면 다시 한번 복습하고 오시길 바랍니다. 앞으로 보게될 코드로 가기전에 기초를 잘 이해하는 것이 정말 중요합니다.

다시 한번 첫번째 강좌에 나온 코드를 수정하는것부터 시작할것입니다. 평소와 마찬가지로 큰 변화가 있는 곳이 있다면 저는 수정된 코드 부분 전체를 다 올리겠습니다. 우선 프로그램에 몇개의 변수를 추가하는것부터 시작합니다.

 
#include    <windows.h>                            // 윈도우즈용 헤더파일
#include    <stdio.h>                              // 표준 입출력용 헤더파일 (새코드)
#include    <gl\gl.h>                              // OpenGL32 라이브러리용 헤더파일
#include    <gl\glu.h>                             // GLu32 라이브러리용 헤더파일
#include    <gl\glaux.h>                           // GLaux 라이브러리용 헤더파일

HDC         hDC=NULL;                              // GDI 장치 컨텍스트
HGLRC       hRC=NULL;                              // 렌더링 컨텍스트
HWND        hWnd=NULL;                             // 윈도우 핸들을 기억
HINSTANCE   hInstance;                             // 응용프로그램의 인스턴스를 기억

bool        keys[256];                             // 키보드 루틴에 사용하는 배열
bool        active=TRUE;                           // 윈도우 활성화 플래그
bool        fullscreen=TRUE;                       // 전체화면 플래그


다음의 코드는 새로운 코드들입니다. 3개의 불리언(boolean) 변수들을 추가합니다. BOOL은 그 변수의 값이 TRUE나 FALSE만 될 수 있음을 뜻합니다. light변수는 조명이 켜져 있는지 꺼져있는지를 판단하기 위한 변수입니다. lp와 fp는 'L'키나 'F'키가 눌려있는지를 확인하기 위한 변수입니다. 나중에 이 변수들이 필요한 이유를 설명드리겠습니다. 지금은 그저 저놈들이 중요하다고만 알아둡시다.


BOOL    light;                                    // 조명 ON / OFF
BOOL    lp;                                       // L이 눌렸는가?
BOOL    fp;                                       // F가 눌렸는가?


이제 5개의 변수를 선언할 차례입니다. xrot은 x축 회전각, yrot은 y축 회전각, xspeed는 x축 주위로 회전하는 속도, yspeed는 y축 주위로 회전하는 속도를 나타냅니다. z라는 이름의 변수도 있는데 이 변수는 화면바깥쪽으로 얼마나 나오는지를(z축을 따라)를 나타내는 깊이값입니다.

GLfloat    xrot;                                   // X 회전
GLfloat    yrot;                                   // Y 회전
GLfloat xspeed;                                    // X 회전속도
GLfloat yspeed;                                    // Y 회전속도
GLfloat    z=-5.0f;                                // 화면 안쪽으로 깊이

이제 조명을 생성하는 데 사용할 배열을 설정합니다. 우리는 두가지 다른 종류의 조명을 사용할 것입니다. 첫번째  조명은 주변광(ambient light)입니다. 주변광은 어떤 특정방향으로부터 오는 빛이 아닙니다. 장면 안에 있는 모든 물체들은 주변광에 의해 영향을 받습니다. 두번쨰 조명은 산란광(diffuse light)입니다. 산란광은 광원으로 부터 나온 빛이 장면안에 있는 물체의 표면에 반사됩니다. 이 조명이 직접 비추는 물체의 면은 매우 밝을 것이며 빛이 거이 닿지 않는 부분은 이보다 어두울 것입니다. 이것은 상자의 한쪽 측면에 그럴듯한 쉐이딩 효과를 만듭니다.

조명을 만드는 법은 색을 만드는 법과 동일합니다. 첫번째 수가 1.0f이고 다음 두 수가 0.0f라면 이는 밝은 빨강색 조명이 될 것입니다. 만약 세번째 값이 1.0f이고 처음 두 값이 0.0f이면 이는 밝은 파랑색 입니다. 마지막 숫자는 알파 값입니다. 일단 이 값을 1.0f로 나둡시다.

아래 라인에서 볼 수 있듯이 절반의 강도(0.5f)를 가진 흰색 주변광을 위한 값을 저장합니다. 모든 값이 0.5f이므로 완전히 꺼진것(검정)과 완전히 밝은(흰색)의 중간정도의 빛을 만들게 될 것입니다. 동일한 값의 빨강, 파랑, 녹색을 혼합하면 검정(0.0f)에서 흰색(1.0f) 사이의 무채색을 만들 수 있습니다. 주변광을 사용하지 않으면 산란광이이 닿지 않는 부분이 매우 어둡게 보일 것입니다.

 
GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };                // 주변광 값(새 코드)


다음 라인에서는 매우 밝고 최고의 강도를 가진 산란광 값을 설정합니다. 모든 값은 1.0f입니다. 이 값은 우리가 가질 수 있는 최대의 밝은 빛을 의미하며 이 산란광 값은 궤짝의 앞면을 근사하게 비쳐줄것입니다.

  
GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };                 // 산란광 값(새 코드)


드디어 빛의 위치를 저장할 차례입니다. 처음 3개의 수는 glTranslate에 들어가는 수와 동일합니다. 첫번째 수는 x평면상에서 좌우로 움직이는 것이고, 두번째 수는 y평면상에서 위아래로 움직이는 것입니다. 그리고 세번째 수는 z평면상에서 화면 안팎으로 이동하는 것입니다. 저희가 원하는 것은 빛이 궤짝의 앞면을 정면으로 비추는 것이므로 좌우로는 움직이지는 않겠습니다. 따라서 첫번째 값은 0.0f이며(x축 상에서 움직이지 않음) 위아래로도 움직이고 싶지 않으므로 두번째 값도 역시 0.0f입니다. 세번째 값은 빛이 언제나 궤짝의 앞면을 비춰야 하므로 관찰자 쪽으로 움직입니다. 여러분의 모니터에 있는 유리가 z평면상에 0.0f라고 하면 저희는 빛을 z평면상에 2.0f에 위치시킬 것입니다. 실제로 빛을 볼 수 있다면 이것은 모니터에 있는 유리 앞부분에 떠있을 것입니다. 모니터 유리 앞부분에 빛을 놓이게 하면 빛이 궤짝 뒤에 있을 수 있는 경우는 궤짝이 모니터 유리 앞쪽에 놓여 있는 경우일 뿐입니다. 물론 궤짝이 모니터에 있는 유리 뒤쪽에 있지 않다면 여러분은 궤짝을 더 이상 볼 수 없습니다. 그래서 빛이 어디에 있는지는 중요치 않습니다. 왜냐하면 빛이 어디에 있던지간에 모니터 앞에 괘짝이 놓여져 있으면 볼수가 없기 떄문이져! 이해가 되시나요?


세번째 매개변수를 간단히 설명할 수 있는 방법은 없습니다. -5.0f보다 -2.0f이 여러분에게 더 가깝다는 정도는 아시고 계실것이고, -100.0f는 화면 안쪽으로 멀리 있습니다. 0.0f를 사용하면 이미지가 전체 화면을 다 덮을정도로 너무 클 것입니다. 양수의 값을 사용하면 이미지가 이미 화면을 지나쳐 사라졌기에 더이상 화면에 보이지 않습니다. 이것이 제가 화면밖에 있다고 말하는 경우입니다. 그 물체는 여전히 화면밖에 있지만 여러분은 더 이상 볼 수 없습니다.

마지막 숫자는 1.0f으로 놓습니다. 이것은 앞의 세수에 의해 지정된 좌표가 광원의 위치라는 것을 나타내는 것으로 점광원을 의미합니다. 만약 0.0으로 한다면 앞의 세수로 이루어진 벡터 방향으로 무한대로 떨어진 곳에 광원이 위치한다는 것으로 평행광원을 의미합니다. 이에 대해 자세한 내용은 다른 튜토리얼에서 자세히 설명하겠습니다.

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };                 // 광원의 위치(새코드)


아래의 filter 변수는 출력할 텍스처를 기억합니다. 첫번째 텍스처(0번 텍스처)는 gl_nearest(smoothing 없음)을 사용하여 만듭니다. 두번째 텍스처(1번 텍스처)는 이미지를 어느정도 부드럽게 만드는 gl_linear필터링을 사용합니다. 세번째 텍스처(2번 텍스처)는 비트맵 텍스처를 사용하여 매우 그럴싸해 보이는 텍스처를 만들어냅니다. filter 변수는 저희가 사용하고자 하는 텍스처에 따라 0, 1, 2의 값을 가질 것입니다. 우선 첫번째 텍스처부터 시작해봅시다.

GLuint texture[3]는 3개의 상이한 텍스처를 담을 수 있는 저장공간을 만듭니다. 텍스처들은 texture[0], texture[1], texture[2]에 저장될 것입니다.

GLuint    filter;                                    // 사용할 필터
GLuint    texture[3];                                // 텍스처 3개용 저장공간

LRESULT    CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);                // WndProc 선언

이제 비트맵 하나를 불러와서 비트맵으로부터 3개의 다른 텍스처를 생성합니다. 이 튜토리얼은 glaux 라이브러리를 사용하여 비트맵을 불러오니 코드를 컴파일하시기전에 glaux 라이브러리를 포함시키는 것을 잊지 마십시요. 델파이와 Visual C++은 모두 glaux 라이브러리를 가지고 있는 것이 확실하지만 다른 프로그래밍 언어에 대해서는 아는 바 없습니다. 아래의 코드 중 새로운 코드라인들에 대해서만 설명하겠습니다. 주석이 없는 코드 부분에 대한 설명은 제6강을 참조해주십시요. 6강은 비트맵 이미지를 불러와서 텍스처 맵을 만드는 방법에 대해 상세히 설명하고 있습니다.

위의 코드에 바로 이어서 다음의 코드를 더합니다. 물론 이 코드는 ReSizeGLScene()보다는 위쪽에 위치합니다. 이는 강좌 6에서 비트맵 파일을 불러오기 위해 사용했던 코드입니다. 아무것도 바뀐 것이 없지요. 다음 라인 중에 이해가 안되는 부분이 있다면 튜토리얼 6강을 읽어주십시요. 거기에 자세한 설명이 있습니다.

AUX_RGBImageRec *LoadBMP(char *Filename)                    // 비트맵 이미지를 읽어들임
{
   FILE *File=NULL;                            // 파일 핸들

   if (!Filename)                                // 파일명이 매개변수로 전달되었는지 검사
   {
       return NULL;                            // 파일명이 전달되지 않았다면 NULL을 반환함
   }

   File=fopen(Filename,"r");                        // 파일이 존재하는지 검사

   if (File)                                // 파일이 존재하는가?
   {
       fclose(File);                            // 핸들을 닫음
       return auxDIBImageLoad(Filename);                // 비트맵을 읽어와 그 포인터를 반환
   }
   return NULL;                                // 읽기에 실패하였다면 NULL을 반환
}
  
이것이 바로위의 코드를 호출하여 비트맵을 로딩한 뒤 3개의 텍스처로 바꾸는 부분입니다. Status 변수를 사용하여 텍스처가 로딩되거나 생성되었는지 아닌지를 기억합니다.

 
int LoadGLTextures()                                // 비트맵 로딩 및 텍스처로 변환
{
   int Status=FALSE;                            // 상태를 나타냄

   AUX_RGBImageRec *TextureImage[1];                    // 텍스처용 저장공간을 만듬

   memset(TextureImage,0,sizeof(void *)*1);                // 포인터를 NULL로 만듬


이제 비트맵을 불러와서 비트맵을 텍스처로 변환합니다. TextureImage[0]=LoadBMP("Data/Crate.bmp")은 LoadBMP() 코드로 점프합니다. data 디렉터리에 위치한 Crate.bmp 파일이 읽혀질 것입니다. 모든 일이 잘되면 이미지 데이터가 TextureImage[0]에 저장될 것이며, Status는 TRUE가 되며 이제 텍스쳐를 만들 차례입니다.

  
   // 비트맵을 로딩. 오류 검사. 비트맵을 찾을 수 없다면 종료
   if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))
   {
       Status=TRUE;                            // 상태를 TRUE로 설정


이제 TextureImage[0]에 이미지 데이터를 읽어왔으니 이 데이터를 이용하여 3개의 텍스처를 만듭니다. 아래의 코드는 OpenGL에게 이제부터 3개의 텍스처를 만들어서 그 텍스처들을 차례대로 texture[0], texture[1], texture[2]에 저장하라고 명령합니다.

       glGenTextures(3, &texture[0]);                    // 3개의 텍스처를 만든다

제6강에서 저희는 선형필터 텍스쳐 맵을 사용했었습니다. 선형필터는 상당한 양의 CPU 시간을 필요로 하긴 하지만 정말 멋지게 보입니다. 여기서 만들어 낼 첫번째 텍스처는 GL_NEAREST를 사용합니다. 간단히 말해 이 텍스처 종류는 필터링을 전혀 가지지 않습니다. 이는 매우 약간의 CPU시간만을 사용하지만 정말 형편없어 보입니다. 텍스처가 각이져 보이는 게임을 플레이해본적이 있으시다면 아마 그 게임은 이런 타입의 텍스처를 사용하고 있을 것입니다. 이 텍스처 타입의 유일한 장점은 느린 컴퓨터에서도 빠르게 실행된다는 것입니다.


MIN과 MAG에 모두 GL_NEAREST를 사용하는 것 보이시죠? GL_NEAREST를 GL_LINEAR와 혼합할 수도 있으며, 그러면 텍스처가 조금은 더 나아 보일 것입니다. 하지만 저희는 속도에 관심이 있으므로 양쪽에 모두 저품질을 사용합니다. MIN_FILTER는 이미지가 실제 텍스처 사이즈보다 더 작게 그려질때 사용되는 필터이며, MAG_FILTER는 이미지가 원래 텍스처 크기보다 더 클 때 사용됩니다.

       // NEAREST 필터링을 사용한 텍스처를 만든다
       glBindTexture(GL_TEXTURE_2D, texture[0]);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); // ( 새코드 )
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); // ( 새코드 )
       glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);


다음으로 만들 텍스처는 제6강에서 사용했던 것과 동일한 선형 필터링을 적용한 텍스처입니다. 여기서 바뀐 점은 texture[0]이 아닌 texture[1]에 텍스처를 저장한다는 것입니다. 이것이 두번째 텍스처이기 때문입니다. 위처럼 이 텍스처도 texture[0]에 저장해버리면 GL_NEAREST 텍스처(첫번째 텍스처)를 덮어써 버릴 것입니다.
 
       // 선형필터를 사용하는 텍스처를 만든다
       glBindTexture(GL_TEXTURE_2D, texture[1]);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
       glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);


이제 텍스처를 만드는 새로운 방법을 살펴봅시다. 바로 밉매핑(Mipmapping)입니다! 화면상에서 이미지를 매우 작게 만들어보면 세세한 디테일들이 사라지는 현상을 보신적이 있으실 것입니다. 처음에는 멋지게 보이던 패턴들이 점차 이상하게 보이기 시작하지요. OpenGL에게 밉맵을 이용한 텍스처를 구축하라고 말해주면, OpenGL은 다른 크기의 고품질 텍스처를 만듭니다. 밉맵을 사용한 텍스처를 화면상에 그리면 OpenGL은 자신이 만들었던 텍스쳐중 가장 그럴듯해 보이는 녀석(최고의 디테일을 가진 텍스처)을 선택하여 화면에 그릴 것입니다. 즉, 원래 이미지의 크기를 변경하는 일을 하지 않습니다(이러면 디테일을 잃어버리기 때문이지요).

제6강에서 64, 128, 256 등 OpenGL 텍스처의 폭 및 높이제한을 피할 수 있는 방법이 있다고 말씀드린 바 있습니다. gluBuild2DMipmaps가 바로 그 해결방법입니다. 제가 알아낸 바로는 밉맵 텍스처를 구축할 때는 어떤 높이와 너비의 비트맵 이미지라도 사용할 수 있습니다. OpenGL이 그 이미지를 알맞은 너비 및 높이로 자동적으로 변경해줄 것입니다.

이 텍스처는 세번째이므로 texture[2]에 저장합니다. 이제 texture[0]에는 필터링을 사용하지 않는 텍스처가, texture[1]에는 선형 필터링을 사용하는 텍스처가, texture[2]에는 밉맵을 이용하는 텍스처가 저장되어 있습니다. 이쯤되면 본 튜토리얼에 필요한 텍스처를 모두 다 구축한 것이 되는군요.

       // 밉맵 텍스처를 만든다
       glBindTexture(GL_TEXTURE_2D, texture[2]);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); // ( 새코드 )

 
다음 라인은 밉맵 텍스처를 만듭니다. 저희는 3가지 색(빨강, 녹색, 파랑)을 이용해서 2D 텍스처를 만들 것입니다. TextureImage[0]->sizeX는 비트맵의 너비이고 TextureImage[0]->sizeY는 비트맵의 높이입니다. GL_RGB는 저희가 빨강, 녹색, 파랑의 순서로 색을 사용한다는 의미이고, GL_UNSIGNED_BYTE는  텍스쳐가 바이트로 구성되어 만들어졌다는 의미입니다. 마지막으로 TextureImage[0]->Data는 저희가 텍스쳐를 만드는데 사용했던 비트맵 데이터를 가르킵니다.

 
       gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); // ( 새코드 )
   }


이제 비트맵 데이터를 저장하기 위해 사용했었던 메모리 공간을 해제합니다. TextureImage[0]에 텍스처가 저장되어 있는지를 검사합니다. 만약 그렇다면 텍스처 이미지가 저장되어 있는지 검사합니다. 만약 그렇다면 이를 지웁니다. 그리고 이미지 구조체를 해제하여 사용중인 메모리를 모두 해제시킵니다.
  
   if (TextureImage[0])                            // 텍스처가 존재하는지 확인
   {
       if (TextureImage[0]->data)                    // 텍스처 이미지가 존재하는지 확인
       {
           free(TextureImage[0]->data);                // 텍스처 이미지 메모리를 해제함
       }

       free(TextureImage[0]);                        // 이미지 구조체를 해제함
   }


마지막으로 할 일은 상태를 반환하는 것입니다. 만약 모든 일이 제대로 되엇다면 Status 변수가 TRUE가 될 것입니다. 만약 뭔가 잘못되었다면 Status는 FALSE가 될 것입니다.

   return Status;                                // 상태를 반환한다
}


이제 텍스처를 로딩했으니 OpenGL 세팅을 초기화 해봅시다. InitGL의 첫번째 줄은 위의 코드를 이용하여 텍스쳐들을 읽어옵니다. 텍스처를 생성한 다음에는 glEnable(GL_TEXTURE_2D)를 이용하여 2D 텍스처 매핑을 활성화시킵니다. 쉐이드 모드는 부드러운 세이딩(smooth shading)으로 설정하며, 배경색은 검정으로 설정합니다. 깊이 테스팅도 활성화 시키고 멋드러진 원근 계산도 활성화 시킵니다.


int InitGL(GLvoid)                                // 모든 OpenGL 설정을 여기서 함
{
   if (!LoadGLTextures())                            // 텍스처 로딩 루틴으로 점프
   {
       return FALSE;                            // 텍스처를 로딩하지 못했다면 FALSE를 반환
   }

   glEnable(GL_TEXTURE_2D);                        // 텍스처 매핑을 활성화
   glShadeModel(GL_SMOOTH);                        // 부드러운 셰이딩을 활성화
   glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                    // 검정 배경색
   glClearDepth(1.0f);                            // 깊이버퍼 설정
   glEnable(GL_DEPTH_TEST);                        // 깊이테스트 설정
   glDepthFunc(GL_LEQUAL);                            // 깊이테스트의 종류
   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 매우 멋드러진 원근계산


이제 조명을 설치합니다. 아래의 코드는 light1이 주는 주변광의 양을 설정합니다. 이 강좌의 시작부분에서 LightAmbient에 주변광의 양을 저장해놨던거 기억하시나요? 이 배열에 저장해놨던 값을 사용할 것입니다(중간 정도 세기의 주변광이 되겠습니다).
  
  
   glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);                // 분산광을 설정


다음은 조명 번호 1번이 내뿜는 산란광의 양을 설정할 차례입니다. LightDiffuse에 미리 산란광의 양을 저장해놨었습니다. 이 배열에 저장해놓은 값을 사용할 것입니다(최고로 강한 흰색 빛 입니다).

 

   glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);                // 산란광을 설정


이제 빛의 위치를 설정합니다. LightPosition에 이 위치를 저장해놓았습니다. 이 배열에 저장해놓은 값을 사용할 것입니다(앞면의 중앙으로부터 오른쪽에 위치합니다. 좌표로 말하면 x = 0.0f, y = 0.0f, z = 관찰자 쪽으로 2 유닛입니다. 즉 z 평면상에서 화면 앞쪽으로 튀어나온 것이지요).


   glLightfv(GL_LIGHT1, GL_POSITION,LightPosition);            // 조명을 위치시킨다


이제 마지막으로 조명 번호 1번을 활성화 시킵니다. 우리는 GL_LIGHTING을 아직 활성화를 하지않았기 떄문에 아직 어떤 조명도 보이지 않을 것입니다. 조명의 설정 및 배치를 끝냈으며 이것을 활성화 시키기도 했지만 GL_LIGHTING을 활성화시키기 전까진 그 조명이 동작하지 않을 것입니다.


   glEnable(GL_LIGHT1);                            // 조명 1번을 활성화 시킨다
   return TRUE;                                // 초기화가 잘 끝났음
}


다음에 나오는 코드에서는 텍스처 매핑을 입힌 육면체를 그릴 것입니다. 새롭게 추가된 라인들에만 주석을 달겠습니다. 주석이 달리지 않은 코드들이 모를 의미하는건지 잘 모르겠다면 제6강을 살펴봐주세요.

int DrawGLScene(GLvoid)                                // 여기가 모든 드로잉을 하는 곳이다
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 장면과 깊이 버펄르 지움
   glLoadIdentity();                            // 뷰를 리셋함


다음의 세 라인의 코드는 텍스처 매핑을 입힌 육면체를 위치 및 회전시킵니다. glTranslatef(0.0f, 0.0f, z)는 육면체를 z평면상에서 z의 값만큼 이동시킵니다(관찰자쪽 또는 화면안쪽으로). glRotatef(xrot, 1.0f, 0.0f, 0.0f)는 xrot변수를 사용하여 육면체를 x축 상에서 회전시킵니다. glRotatef(yrot, 0.0f, 1.0f, 0.0f)는 yrot변수를 이용하여 y축 상에서 육면체를 회전시킵니다.


   glTranslatef(0.0f,0.0f,z);                        // 화면 안쪽 또는 바깥쪽으로 Z만큼 평행이동

   glRotatef(xrot,1.0f,0.0f,0.0f);                        // X축상에서 xrot만큼 회전
   glRotatef(yrot,0.0f,1.0f,0.0f);                        // Y축상에서 yrot만큼 회전


다음 라인은 제6강에서 사용했던 라인과 비슷하지만 텍스처[0]을 바인딩 하는대신 texture[filter]를 바인딩 합니다. 저희가 'F'키를 누를때마다 filter에 저장된 값이 증가할 것입니다. 만약 이 값이 2보다 크면 filter변수가 다시 0으로 재설정됩니다. 프로그램이 시작될 때도 filter는 0일 것입니다. 즉, 처음에는 glBindTexture(GL_TEXTURE_2D, texture[0])과 같을 것이고, 'F'를 한번 누르면 filter변수가 1이되므로 glBindTexture(GL_TEXTURE_2D, texture[1])과 같을 것입니다. filter변수를 사용함으로 해서 저희가 만들었던 세개의 텍스처중 아무거나 임의로 골라서 사용할 수 있습니다.


   glBindTexture(GL_TEXTURE_2D, texture[filter]);                // filter값에 따라 텍스처를 선택

   glBegin(GL_QUADS);                            // 쿼드를 그리기 시작함


glNormal3f가 등장하는 것은 처음이죠? 법선(normal)이란 한 폴리곤의 중앙에서 90도 각도로 위를 가르키는 직선입니다. 조명을 사용할 때는 법선을 지정해줘야 합니다. 법선은 OpenGL에게 폴리곤이 어떤 방향을 향하고 있는지, 즉 어느 쪽이 위인지를 알려줍니다. 법선을 지정해주지 않으면 별 기괴한 일들이 일어납니다. 조명을 받지 말아야 하는 면이 조명을 받고, 폴리곤의 잘못된 면이 빛을 받는 등 이상한 일이 일어납니다. 법선은 폴리곤에서 바깥쪽을 가르켜야 합니다.


앞면을 보면 법선이 z축 상의 양수라는 것을 알아채실 수 있으실 것입니다. 이것은 법선이 관찰자를 가르킨다는 말입니다. 이것이 바로 우리가 가르키려고 하는 방향입니다. 뒷면을 보면 법선이 관찰자로부터 반대방향으로, 즉 화면속으로 향합니다. 다시한번 이것은 저희가 바라는 그대로입니다. 만약 이 육면체를 x나 y축 상에서 180도 회전시키면 앞면이 화면안쪽을 향할 것이고 뒷면이 관찰자를 향할 것입니다. 어떤 면이 관찰자를 향하던 간에 그 면의 법선은 언제나 관찰자를 향하게 됩니다. 조명이 관찰자의 근처에 있기 때문에 법선이 관찰자를 향할 때에는 언제나 이것이 조명을 향하게 됩니다. 이런 일이 일어나면 그 면은 빛을 받을 것입니다. 법선이 조명에 가까이 갈 수록 그 면이 더욱 밝아집니다. 이 육면체의 중심쪽으로 들어가보시면 그 속이 어둡다는 사실을 밝견하실 겁니다. 법선이 밝을 향하지 않고 안으로 들어가기 때문입니다. 따라서 상자속에는 조명이 없습니다. 자, 저희가 바라는 대로죠?

 
       // 앞면
       glNormal3f( 0.0f, 0.0f, 1.0f);                    // 관찰자 쪽으로 향하는 법선
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 점 1 (앞면)
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 점 2 (앞면)
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 점 3 (앞면)
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 점 4 (앞면)
       // 뒷면
       glNormal3f( 0.0f, 0.0f,-1.0f);                    // 관찰자 반대쪽으로 향하는 법선
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 점 1 (뒷면)
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 점 2 (뒷면)
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 점 3 (뒷면)
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 점 4 (뒷면)
       // 윗면
       glNormal3f( 0.0f, 1.0f, 0.0f);                    // 위를 향하는 법선
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 점 1 (윗면)
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 점 2 (윗면)
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 점 3 (윗면)
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 점 4 (윗면)
       // 아랫면
       glNormal3f( 0.0f,-1.0f, 0.0f);                    // 아래를 향하는 법선
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 점 1 (아랫면)
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 점 2 (아랫면)
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 점 3 (아랫면)
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 점 4 (아랫면)
       // 오른면
       glNormal3f( 1.0f, 0.0f, 0.0f);                    // 오른쪽을 향하는 법선
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 점 1 (오른면)
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 점 2 (오른면)
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 점 3 (오른면)
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 점 4 (오른면)
       // 왼쪽면
       glNormal3f(-1.0f, 0.0f, 0.0f);                    // 왼쪽을 향하는 법선
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 점 1 (왼쪽면)
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 점 2 (왼쪽면)
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 점 3 (왼쪽면)
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 점 4 (왼쪽면)
   glEnd();                                // 사각형들을 모두 다 그렸음


다음의 두 라인은 xrot과 yrot을 xspeed와 yspeed에 저장된 값 만큼 증가시킵니다. xspeed나 yspeed안에 있는 값이 높다면 xrot과 yrot이 빠르게 증가할 것입니다. xrot이나 yrot이 빠르게 증가할수록 육면체가 해당 축상에서 빠르게 움직입니다.

 
   xrot+=xspeed;                                // xspeed를 xrot에 더함
   yrot+=yspeed;                                // yspeed를 yrot에 더함
   return TRUE;                                // 계속 진행할 것
}


이제 WinMain()을 살펴봅시다. 여기에 조명을 켜고 끄고, 상자를 회전 및 이동하며, 필터를 변경하는 코드를 추가할 것입니다. WinMain()의 마지막 부분에 SwapBuffer(hDC) 명령어가 있을 것입니다. 여기 바로 아래에 다음의 코드를 추가합니다.

이 코드는 키보드의 'L'키가 눌렸는지를 검사합니다. 처음 라인은 'L'키가 눌렸는지를 검사합니다. 'L'키가 눌렸으나 lp가 false가 아니라면 'L'이 이미 한번 눌렸거나 계속해 눌려져 있는 경우이므로 아무일도 일어나지 않습니다.

  
               SwapBuffers(hDC);                // 버퍼를 스왑한다(이중 버퍼링)
               if (keys['L'] && !lp)                // L키가 처음 눌렸는가?
               {


만약 lp가 false라면 'L'키가 이번에 처음 눌린 것입니다. 만약 이 키를 더이상 누르지 않으면 lp가 true가 됩니다. 이는 'L'키를 한번 누를 때 이 코드가 여러번 실행되는 것을 막기 위한 것입니다. 키가 계속 눌려있는지를 검사하지 않는다면 프로그램은 이 코드를 실행할 때마다 사용자가 계속 'L'키를 누른다고 생각할 것이므로 조명이 계속적으로 깜박이게 될 것입니다. 

일단 lp를 true로 만들어 컴퓨터가 'L'키가 계속 눌러져 있다는 사실을 알게 만들었다면 이제 조명을 끄고 켭니다. light 변수는 오직 true나 false가 될 수 있습니다. 따라서 light=!light라고 하면 "light는 light가 아닌 것과 같다"라는 뜻입니다. 따라서 light가 true라면 light를 true가 아니게(즉 false로) 만들고, light가 false라면 light를 false가 아니게(즉 true로) 만듭니다. 다시 간단히 말해 light가 true였다면 false가 되고 false였다면 true가 됩니다.

                   lp=TRUE;                // lp가 TRUE가 됨
                   light=!light;                // 빛을 TRUE/FALSE로 토글한다


이제 조명이 존재하는지 검사합니다. 첫번째 줄을 한국말로 번역하면 "만약(if) light가 거짓(false)과 같다면" 입니다. 따라서 이 모두를 다 설명하면 "만약 light가 거짓과 같다면 조명을 끈다"가 됩니다. 이것은 모든 조명을 다 끕니다. 'else' 명령어는 "만약 이것이 거짓(false)가 아니었다면"으로 설명할 수 있습니다. 따라서 light가 false가 아니었다면 true임이 분명하므로 조명을 켭니다.

                   if (!light)                // 조명을 꺼야 한다면
                   {
                       glDisable(GL_LIGHTING);        // 조명을 비활성화 시키고
                   }
                   else                    // 그렇지 않다면
                   {
                       glEnable(GL_LIGHTING);        // 조명을 활성화 시킨다.
                   }
               }


다음 라인은 'L'키 누르기를 멈췄는지를 검사합니다. 만약 그랬다면 lp 변수를 false로 만듭니다('L'키가 더이상 안눌린다는 뜻). 이 키를 더이상 누르고 있지 않은지를 검사하지 않는다면 조명을 한 번 밖에 켤 수 없을 것이며 컴퓨터는 언제나 'L'키가 눌린다고 생각하므로 다시 조명을 끌 수가 없을 것입니다.

               if (!keys['L'])                    // L키가 더이상 눌리지 않는다면
               {
                   lp=FALSE;                // lp가 FALSE로 된다
               }


이제 'F'키에 비슷한 일을 합니다. 만약 이 키가 눌렸고 그전에 눌리고 있던 자국이 없다면 이번에 처음 눌리는 것입니다. 따라서 fp변수가 true가 되어 이 키가 이제 눌리고 있다는 사실을 나타냅니다. 그리고 이제 filter라 불리는 변수의 값을 증가시킵니다. 만약 filter의 값이 2보다 크다면(texture[3]가 되겠고, 이 텍스처는 존재하지 않습니다) filter변수를 0으로 다시 리셋합니다.

               if (keys['F'] && !fp)                // F키가 눌렸다면
               {
                   fp=TRUE;                // fp가 TRUE로 되고
                   filter+=1;                // filter값을 하나 증가시킨다.
                   if (filter>2)                // 만약 이 값이 2 보다 크다면
                   {
                       filter=0;            // fitler를 0으로 만든다
                   }
               }
               if (!keys['F'])                    // F키가 더이상 눌리지 않는다면
               {
                   fp=FALSE;                // fp가 FALSE로 된다
               }


다음의 네 줄은 'Page Up'키를 누르고 있는지를 검사하는 라인입니다. 만약 그렇다면 z 변수의 값을 감소시킵니다. 이 변수값이 감소하면 화면에 있는 육면체가 멀리 이동할 것입니다. DrawGLScene 프로시저 안에서 사용한 glTranslatef(0.0f, 0.0f, z) 명령 때문이지요.

               if (keys[VK_PRIOR])                // PageUp이 눌렸다면
               {
                   z-=0.02f;                // 화면 안쪽으로 이동한다
               }


이 네 줄은 'Page Down'키룰 누르고 있는지를 검사합니다. 만약 그렇다면 z 변수값을 증가시켜 육면체를 관찰자 쪽으로 이동시킵니다. 역시 DrawGLScene 프로시저안에서 사용한 glTranslatef(0.0f, 0.0f, z) 명령어 때문입니다.

               if (keys[VK_NEXT])                // PageDown이 눌렸다면
               {
                   z+=0.02f;                // 관찰자를 향해 이동한다
               }


이제 저희가 검사해야 할 것은 방향키 밖에 없습니다. 왼쪽이나 오른쪽을 누르면 xspeed가 증가되거나 감소됩니다. 위쪽이나 아래쪽은 yspeed를 증가 및 감소시킵니다. 이 강좌의 저 위편에서 xspeed의 값이나 yspeed의 값이 크면 육면체가 빨리 회전할 것이라고 말씀드렸던거 기억하시나요? 화살표키를 오래 누르고 있으면 있을수록 해당 방향으로 육면체거 더 빨리 회전합니다.

               if (keys[VK_UP])                // 위쪽 화살표키가 눌렸다면
               {
                   xspeed-=0.01f;                // xspeed를 감소시킨다
               }
               if (keys[VK_DOWN])                // 아래쪽 화살표키가 눌렸다면
               {
                   xspeed+=0.01f;                // xspeed를 증가시킨다
               }
               if (keys[VK_RIGHT])                // 오른쪽 화살표키가 눌렸다면
               {
                   yspeed+=0.01f;                // yspeed를 증가시킨다
               }
               if (keys[VK_LEFT])                // 왼쪽 화살표키가 눌렸다면
               {
                   yspeed-=0.01f;                // yspeed를 감소시킨다
               }

  
이전에 다뤘던 모든 강좌와 마찬가지로 창위 위쪽에 있는 타이틀을 제대로 나오게 합니다.

 
               if (keys[VK_F1])                // F1키가 눌린다면
               {
                   keys[VK_F1]=FALSE;            // 이 키를 FALSE로 만들고
                   KillGLWindow();                // 현재 창을 죽인다
                   fullscreen=!fullscreen;            // 전체화면/창 모드를 토글한다
                   // OpenGL 창을 다시 만든다
                   if (!CreateGLWindow("NeHe의 텍스처, 조명, 키보드 튜토리얼",640,480,16,fullscreen))
                   {
                       return 0;            // 창을 만드는데 실패했으면 종료한다
                   }
               }
           }
       }
   }

   // 종료부분
   KillGLWindow();                                // 창을 죽인다
   return (msg.wParam);                            // 프로그램에서 탈출
}

이 강좌를 여기까지 정독하신 독자분들이시라면 쿼드로 구성된 고품질, 사실적인 텍스처 매핑된 물체들을 만들고 움직일 수 있을 것입니다. 키보드 상에 있는 특정 키를 누름으로써 화면상에 있는 물체들과 상호작용할 수 있으며, 마지막으로 장면에 간단한 조명을 적용하여 보다 사실적인 장면을 표현하는 법을 아시고 계실 것입니다.

소스코드 다운로드

이 강좌의 소스코드를 다운받으실 수 있습니다. 자신의 환경에 맞는 파일을 받아 사용하세요.

 

원문 정보

  • 저자: Jeff Molofee (NeHe)
  • 원문보기: Lesson 07

 

번역문 정보

  • 초벌번역: 포프
  • 재벌번역: 이스
  • 감수: 이스

 

현재 상태

  • 초벌번역시작 (2005년 8월 29일)
  • 초벌번역종료 (2006년 1월 2일)
  • 재벌및 감수  (2006년 1월 8일)

 

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

 

출처: http://www.galexandria.com/main/nehe-opengl-tutorials/06-texture-mapping

 

 

06. 텍스처 매핑

목차

  1. 소개
  2. 본문
  3. 소스코드 다운로드
  4. 원문 정보
  5. 번역문 정보
  6. 현재 상태

 

소개

 여러분의 열화와 같은 성원에 힘입어 이번에는 텍스처 매핑입니다! 이 튜토리얼에서는 비트맵 이미지를 육면체의 여섯면에 매핑하는 법을 다룰 것입니다. 저희는 첫번째 강좌의 GL 코드를 사용하여 이 프로젝트를 만들 것입니다. 전 강좌의 코드를 수정하는것보다 빈 GL창에서 시작하는게 더 쉽기 때문입니다.

신속하게 프로젝트를 개발하려고 할 때 강좌 1의 코드가 매우 유용할 것입니다. 강좌1의 코드는 여러분에게 필요한 모든 것들을 설정하고 있으며, 여러분이 해야할 일은 특수효과 프로그래밍에 집중하는 것 뿐입니다.

 

본문

텍스처 매핑은 용도가 다양합니다. 미사일이 화면을 가로질러 날라가게 하고 싶다고 생각해봅시다. 저희가 현재 알고 있는 방법은 전체 미사일을 폴리곤들로 만들고 그럴듯한 색상을 입히는 것 입니다. 텍스처 매핑을 사용하면 실제 미사일 사진을 가져와서 그 그림이 화면을 가로질러 날라가게 만들수 있습니다. 실제 사진과 삼각형/사각형으로 구성된 물체중에 어떤 방법이 더 그럴듯 해 보일지는 분명한 일이죠? 텍스처 매핑을 사용하면 보기에도 나을뿐만 아니라 프로그램도 더 빠르게 실행될 것입니다. 텍스처를 입힌 마사일은 화면을 가로지르는 사각형 하나가 될테니까요. 폴리곤으로 만든 미사일은 수백 또는 수천개의 폴리곤들을 사용할 것입니다. 텍스처 맵을 입힌 사각형 하나는 이보다 훨씬 적은 처리능력을 사용합니다.

자, 그럼 시작해볼까요? 1강의 코드 윗부분에 새 코드를 다섯줄 추가합니다. 첫번째 새로운 라인은 #include <stdio.h>입니다. 이 헤더파일을 추가하면 파일을 조작할 수 있습니다. 이 코드의 뒷부분에서 fopen()함수를 사용할 것인데 stdio.h를 포함해야만 fopen()함수를 사용할 수 있습니다. 이제 3개의 부동소수점 변수로 xrot, yrot, zrot을 추가합니다. 이 변수들은 x축, y축, z축 상에서 육면체를 회전시키기 위해 사용합니다. 마지막 라인은 GLuint texture[1]로 텍스처 한개의 저장공간을 준비합니다. 하나 이상의 텍스처를 로딩하려면 [1] 대신에 로딩하려는 텍스처의 수[?]를 적으시면 됩니다.
  
#include    <windows.h>                          // 윈도우즈용 헤더파일
#include    <stdio.h>                            // 표준 입출력용 헤더파일( 새코드 )
#include    <gl\gl.h>                            // OpenGL32 라이브러리용 헤더파일
#include    <gl\glu.h>                           // GLu32 라이브러리용 헤더파일
#include    <gl\glaux.h>                         // GLaux 라이브러리용 헤더파일

HDC         hDC=NULL;                            // GDI 컨텍스트
HGLRC       hRC=NULL;                            // 렌더링 컨텍스트
HWND        hWnd=NULL;                           // 우리의 윈도우 핸들을 기억
HINSTANCE   hInstance;                           // 응용프로그램의 인스턴스를 기억

bool        keys[256];                           // 키보드 루틴에 사용하는 배열
bool        active=TRUE;                         // 윈도우 활성여부 플래그
bool        fullscreen=TRUE;                     // 전체화면 플래그

GLfloat     xrot;                                // X 회전 ( 새 코드 )
GLfloat     yrot;                                // Y 회전 ( 새 코드 )
GLfloat     zrot;                                // Z 회전 ( 새 코드 )

GLuint      texture[1];                          // 텍스처 하나용 저장공간 ( 새코드 )

LRESULT     CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);                // WndProc 선언



이제 위의 코드블럭과 ReSizeGLScene() 사이에 다음의 코드블럭을 추가합니다. 이 코드가 하는일은 비트맵 파일을 로딩하는 것입니다. 만약 그 파일이 존재하지 않으면 NULL을 반환하여 텍스처를 읽을 수 없다고 알립니다. 코드를 설명드리기 전에 여러분이 '''반드시''' 알아두셔야할 것들이 있습니다. 이미지의 높이와 폭이 '''반드시''' 2의 승수여야 한다는 것입니다. 호환성 때문에 폭과 높이는 최소한 64픽셀은 되어야 하며 256픽셀이 넘어서는 안됩니다. 여러분이 사용하려고 하는 이미지의 폭과 높이가 64, 128 또는 256 픽셀이 아니라면 포토샵 같은 이미지 편집 프로그램에서 크기를 바꾸시기 바랍니다. 이 제한을 극복할 수 있는 꽁수가 있긴 하지만 일단은 표준 텍스처 크기만을 사용하도록 합시다.

첫번째로 하는 일은 파일 핸들을 만드는 것입니다. 핸들은 리소스를 식별하기 위한 값입니다. 이 값은 저희 프로그램이 리소스를 액세스할 때 필요로 합니다. 일단 핸들을 NULL로 놓고 시작합니다.
  

AUX_RGBImageRec *LoadBMP(char *Filename)                    // 비트맵 이미지를 읽어옴
{
   FILE *File=NULL;                            // 파일 핸들



다음은 파일이름이 실제로 주어졌는지를 확인합니다. 사용자가 로딩할 파일을 지정하지 않은채 LoadBMP()함수를 호출할 수도 있으므로 이를 반드시 검사해야합니다. 존재하지 않는 것을 로딩하고 싶진 않으시죠? :)
  
 
  if (!Filename)                                // 파일이름이 전달되었는지 확인
   {
       return NULL;                            // 그렇지 않다면 NULL을 반환
   }



파일이름이 주어졌다면 파일이 존재하는지를 검사합니다. 아래의 라인은 파일을 열려고 시도하는 코드입니다.
  
   File=fopen(Filename,"r");                        // 파일이 존재하는지 확인

파일을 열수 있었다면 그 파일은 분명히 존재하는 것입니다. fclose(File)로 파일을 닫은 뒤 이미지 데이터를 반환합니다. auxDIBImageLoad(Filename)은 이미지 데이터를 읽어들이는 함수입니다.
  
   if (File)                                // 파일이 존재하는가?
   {
       fclose(File);                            // 핸들을 닫음
       return auxDIBImageLoad(Filename);                // 비트맵을 읽어들이고 포인터를 반환
   }


파일을 열 수 없었다면 NULL을 반환합니다. NULL은 파일을 로딩할 수 없었다는 의미입니다. 이 프로그램의 뒷부분에서 여기서 반환한 값을 확인할 것입니다. 만약 NULL이 반환되면 -- 즉, 파일이 로딩되지 않았다면 -- 오류메시지와 함께 프로그램을 종료시킬 것입니다.

  
   return NULL;                                // 로딩이 실패하면 NULL을 반환
}


다음은 위의 코드를 호출하여 비트맵을 로딩하고 비트맵을 텍스처로 바꾸는 코드입니다
 
int LoadGLTextures()                                // 비트맵을 로드하고 텍스처로 변환
{



저희는 Status란 변수를 선언할 것입니다. 이 변수는 비트맵을 읽어들이고 텍스처를 만드는 작업이 성공했는지 실패했는지를 저장하는데 사용합니다. 디폴트 값을 FALSE로 설정하여 아직 아무것도 읽거나 만들지 않았다고 표시합니다.   

   int Status=FALSE;                            // 상태 표시기

이제 비트맵을 저장할 수 있는 이미지 레코드를 만듭니다. 이 레코드는 비트맵의 폭과 높이 및 데이터를 저장합니다.
  
   AUX_RGBImageRec *TextureImage[1];                    // 텍스처용 저장공간을 만듬

이미지 레코드를 지워 그것이 확실히 비워있게 만듭니다.

   memset(TextureImage,0,sizeof(void *)*1);                // 포인터를 NULL로 설정

이제 비트맵을 읽어들여 비트맵을 텍스처로 변환합니다. TextureImage[0] = LoadBMP("Data/NeHe.bmp")로 하면 저희의 LoadBMP() 코드로 이동합니다. Data 디렉터리에 있는 NeHe.bmp란 이름을 가진 파일이 로딩될 것입니다. 만약 모든것이 다 잘 진행되면 TextureImage[0]에 이미지 데이터가 저장되며, Status가 TRUE가 될것입니다. 이제 텍스처를 만들기 시작합니다.
  
   // 비트맵을 로딩하고 에러를 검사한다. 비트맵이 발견되지 않았다면 종료한다.
   if (TextureImage[0]=LoadBMP("Data/NeHe.bmp"))
   {
       Status=TRUE;                            // Status를 TRUE로 설정


이제 이미지 데이터를 TextureImage[0]에 읽어들였으니 이 데이터를 이용하여 텍스처를 만들 차례입니다. 첫번째 라인은 glGenTextures(1, &texture[0])으로 OpenGL에게 저희가 텍스처 이름 하나를 얻고 싶다고 말해줍니다(한 개 이상의 텍스처를 로딩할 떄는 이 숫자를 증가시켜주세요). 이 튜토리얼의 젤 앞부분에서 GLuint texture[1] 라인을 사용하여 텍스처 하나를 위한 공간을 만들었다는 사실을 기억하십시요. 첫번째 텍스처가 &texture[0]이 아니라 &texture[1]안에 저장될것이라고 생각하는 독자분들이 계실지도 모르겠지만 그건 안될 말이죠. C/C++의 배열 인덱스는 0부터 시작합니다. 따라서 첫번째 저장공간은 0이랍니다. 만약 저희가 2개의 텍스처를 원한다면 GLuint texture[2]를 사용하고 두번째 텍스처는 texture[1]안에 저장될 것입니다.

두번째 라인은 glBindTexture(GL_TEXTURE_2D, texture[0])로 OpenGL에게 texture[0]를 텍스처 타겟에 연결시키라고 말해줍니다. 2D 텍스처는 높이(Y축)와 폭(X축)을 가지고 있습니다. glBindTexture의 주요 기능은 텍스처 이름을 텍스처 데이터에 할당하는 것입니다. 이 경우 저희는 &texture[0]에 사용가능한 메모리가 있다고 OpenGL에게 알려줍니다. 따라서 텍스처를 만들면 &texture[0]가 참조하는 메모리에 그 텍스처가 저장될 것입니다.
  
       glGenTextures(1, &texture[0]);                    // 텍스처를 만듬

       // 비트맵으로부터 가져온 데이터를 사용한 일반적인 텍스처 생성
       glBindTexture(GL_TEXTURE_2D, texture[0]);


이제 실제 텍스처를 만듭니다. 다음 라인은 OpenGL에게 텍스처가 2D 텍스처(GL_TEXTURE_2D)가 될것이라고 알려줍니다. 0은 이미지의 디테일정도를 나타내며 0로 주는게 보통입니다. 3은 데이터 컴포넌트의 수입니다. 이 이미지가 빨강 데이터, 녹색 데이터, 파랑 데이터로 구성되어 있으므로 3개의 컴포넌트가 있는 것입니다. TextureImage[0]->sizeX는 텍스처의 폭입니다. 이 폭을 이미 알고 있다면 직접 숫자를 쳐 넣으셔도 되지만 컴퓨터가 알아서 찾아내게 만드는 것이 좀더 쉬운 방법입니다. TextureImage[0]->sizeY는 텍스처의 높이입니다. 0이 경계선입니다. 이것은 보통 0으로 남겨둡니다. GL_RGB는 저희가 사용할 이미지 데이터가 빨강,녹색,파랑 데이터 순서로 구성되어 있음을 OpenGL에게 알려줍니다. GL_UNSIGNED_BYTE는 이미지를 구성하는 데이터가 unsigned byte라는 것입니다. 그리고 마지막으로 TextureImage[0]->data는 OpenGL에게 텍스처 데이터를 얻을 수 있는 위치를 알려줍니다. 저희의 경우 이것은 TextureImage[0] 레코드 안에 저장된 데이터를 가르키고 있습니다.
  
       // 텍스처를 만든다
       glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);


다음의 두 라인은 이미지가 원래 텍스처의 크기보다 크거나(GL_TEXTURE_MAG_FILTER) 작게(GL_TEXTURE_MIN_FILTER) 뿌려질 때 OpenGL이 어떤 종류의 필터링을 사용할 것인지를 지정합니다. 저는 양쪽에 모두 GL_LINEAR를 사용하는 것이 보통입니다. 이는 멀리 떨어지거나 화면에 가까이 있는 텍스처들을 부드럽게 보이게 해줍니다. GL_LINEAR는 프로세서/비디오 카드의 처리시간을 많이 요구합니다. 따라서 여러분의 시스템이 느리다면 GL_NEAREST를 사용하시는것이 좋을 수도 있습니다. GL_NEAREST로 필터링된 텍스처를 잡아 늘리면 각이 져 보입니다. 두개를 조합해서 사용해 보실 수도 있습니다. 멀리 있는것보단 가까이에 있는 것들을 필터링 하는것이 더 보기 좋을 것입니다.

 
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);    // 선형 필터링
       glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);    // 선형 필터링
   }



이제 비트맵 데이터를 저장하기 위해 사용했던 램을 풀어줍니다. TextureImage[0]에 비트맵 데이터가 저장되었었는지를 검사합니다. 만약 그랬다면 데이터가 저장되었는지를 검사합니다. 데이터가 저장되었다면 데이터를 지웁니다. 그리고 이미지 구조체를 해제하여 사용했던 메모리들을 모두 풀어줍니다.
  
   if (TextureImage[0])                            // 텍스처가 존재하는지 확인
   {
       if (TextureImage[0]->data)                    // 텍스처 이미지가 존재하는지 확인
       {
           free(TextureImage[0]->data);                // 텍스처 이미지 메모리를 해제
       }

       free(TextureImage[0]);                        // 이미지 구조체를 해제
   }



마지막으로 상태를 반환합니다. 모든것이 잘 진행되었다면 Status는 TRUE가 될것입니다. 만약 하나라도 잘못된 것이 있다면 Status가 FALSE가 됩니다.

   return Status;                                // Status를 반환
}

저는 InitGL에 몇줄의 코드를 추가했습니다. 따라서 이 코드전체를 여기에 다시 보여드리겠습니다. 이러는 것이 제가 추가한 코드의 위치를 알아보기가 편하겠죠? 첫번째 라인인 if (!LoadGLTextures())는 비트맵을 로딩하고 그로부터 텍스처를 만드는 위의 루틴으로 점프합니다. 만약 LoadGLTextures()가 실패한다면 다음 코드라인이 FALSE를 반환할 것입니다. 모든것이 제대로 진행되었다면 텍스처가 만들어진 것이고, 저희는 2D 텍스처 매핑을 활성화시킵니다. 깜박잊고 텍스처 매핑을 활성화시키지 않으면 여러분의 개체가 완전한 흰색으로 보일 것입니다.

int InitGL(GLvoid)                                // 모든 OpenGL 설정을 여기서 함
{
   if (!LoadGLTextures())                            // 텍스처 로딩 루틴으로 점프함( 새코드 )
   {
       return FALSE;                            // 텍스처가 로딩되지 않았다면 FALSE를 반환 ( 새코드 )
   }

   glEnable(GL_TEXTURE_2D);                        // 텍스처 매핑을 활성화시킴 ( 새코드 )
   glShadeModel(GL_SMOOTH);                        // 부드러운 쉐이딩을 활성화시킴
   glClearColor(0.0f, 0.0f, 0.0f, 0.5f);                    // 검은색 배경
   glClearDepth(1.0f);                            // 깊이버퍼 설정
   glEnable(GL_DEPTH_TEST);                        // 깊이테스트를 켬
   glDepthFunc(GL_LEQUAL);                            // 깊이테스트 종류
   glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);            // 정말로 멋진 원근 계산
   return TRUE;                                // 초기화 성공
}


이제 텍스처를 입힌 육면체를 그릴 차례입니다. DrawGLScene을 아래의 코드로 교체하시던가 새로 추가된 코드만 찾아서 1강의 코드에 추가하십시요. 이 부분에 주석을 자세히 달아놓았으니 이해하시기 쉬울 것입니다. 첫번째 두 라인의 코드는 glClear()와 glLoadIdentity()로 강좌 1의 코드에도 있는 부분입니다. glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)은 InitGL()에서 선택했던 색상으로 화면을 지웁니다. 저희의 경우에 화면은 검정색으로 지워질 것입니다. 깊이 버퍼도 또한 지워집니다. 그리고 glLoadIdentity()가 뷰를 리셋합니다.
  
int DrawGLScene(GLvoid)                                // 모든 드로잉을 처리하는 부분
{
   glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);            // 화면과 깊이버퍼를 지움
   glLoadIdentity();                            // 현재 행렬ㅇ르 리셋
   glTranslatef(0.0f,0.0f,-5.0f);                        // 화면안쪽으로 5단위만큼 이동


다음의 세 라인의 코드는 육면체를 x축->y축->z축 순으로 회전시킵니다. 각 축 주위로 회전시키는 양은 xrot, yrot, zrot 변수안에 저장된 값에 따라 다릅니다.


   glRotatef(xrot,1.0f,0.0f,0.0f);                        // X축 주변으로 회전
   glRotatef(yrot,0.0f,1.0f,0.0f);                        // Y축 주변으로 회전
   glRotatef(zrot,0.0f,0.0f,1.0f);                        // Z축 주변으로 회전


다음 코드라인은 어떤 텍스처를 사용할 것인지를 선택합니다. 둘 이상의 텍스처를 장면에 사용하려면 glBindTexture(GL_TEXTURE_2D, texture[사용하고자 하는 텍스처의 개수])를 사용하여 텍스처를 선택해야할 것입니다. 텍스쳐를 변경하고자 한다면 새로운 텍스처에 바인딩해야 합니다. 참고로 한가지 알아두셔야 할 점은 glBegin()과 glEnd()사이에서는 텍스처를 바인딩 할 수 '''없다'''는 것입니다. glBegin() 전이나 glEnd()이후에 그것을 하셔야 합니다. 저희가 glBindTextures를 사용하여 어떤 텍스처를 만들고 특정 텍스처를 선택하는 방법을 잘 살펴봐주십시오.
 
   glBindTexture(GL_TEXTURE_2D, texture[0]);                // 우리의 텍스처를 선택함

텍스처를 쿼드 위에 적절히 입히려면 텍스처의 오른쪽 윗부분을 쿼드의 오른쪽 윗부분에 매핑해야 합니다. 텍스처의 왼쪽 위 역시 사각형의 왼쪽위에, 오른쪽 아래는 역시 오른쪽 아래에, 왼쪽 아래도 역시 왼쪽아래에 매핑합니다. 텍스쳐의 코너가 쿼드의 동일한 코너에 대응되지 않으면 이미지의 위아래가 뒤집히거나 좌우가 바뀌거나... 뭐 그런식이 될 것입니다.

glTexCoord2f의 첫번째 값은 X좌표입니다. 0.0f는 텍스처의 왼쪽입니다. 0.5f는 텍스처의 중앙입니다. 그리고 1.0f는 텍스처의 오른쪽입니다. glTexCoord2f의 두번째 값은 Y 좌표입니다. 0.0f는 텍스처의 아랫쪽입니다. 0.5f는 텍스처의 중앙입니다. 1.0f는 텍스처의 꼭대기입니다.

자, 이제 텍스처의 왼쪽 꼭대기가 X = 0.0f, Y = 0.0f이고, 쿼드의 왼쪽 꼭대기 정점이 X = -1.0f, Y = 1.0f인것을 알고 있습니다. 이제 여러분이 해야하는 일은 다른 세 텍스처 정점들을 쿼드의 나머지 세 코너에 매치시키는 것 뿐입니다.

glTexCoord2f의 x와 y값을 가지고 놀아보십시요. 1.0f를 0,5f로 바꾸면 텍스처의 왼쪽 반쪽만이 그려질 것입니다. 즉 0.0f(텍스처의 왼쪽) 부터 0.5f(텍스처의 중앙)까지이죠. 0.0f를 0.5f로 바꾸면 텍스처의 오른쪽 반쪽만이 그려질 것입니다. 0.5f(텍스처의 중앙)부터 1.0(텍스처의 오른쪽)까지입니다.

   glBegin(GL_QUADS);
       // 앞면
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽아래
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽아래
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽위
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽위
       // 뒷면
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽아래
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽위
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽위
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽아래
       // 윗면
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽위
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽아래
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽아래
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽위
       // 아랫면
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽위
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽위
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽아래
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽아래
       // 오른면
       glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽아래
       glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 오른쪽위
       glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽위
       glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 왼쪽아래
       // 왼면
       glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽아래
       glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽아래
       glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f,  1.0f,  1.0f);    // 텍스처와 쿼드의 오른쪽위
       glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, -1.0f);    // 텍스처와 쿼드의 왼쪽위
   glEnd();

이제 xrot, yrot, zrot의 값들을 증가시켜봅시다. 각 변수가 증가되는 양을 변경하여 육면체가 보다 빠르거나 보다 느리게 회전되도록 만들어 봅시다. 아니면 +를 -로 변경하여 다른 방향으로 회전시켜보는 것도 재밌겠네요.
  
   xrot+=0.3f;                                // X 축 회전
   yrot+=0.2f;                                // Y 축 회전
   zrot+=0.4f;                                // Z 축 회전
   return true;                               // 계속 진행
}



이제 여러분은 텍스처 매핑을 예전보다 잘 이해하고 계실 것입니다. 자신이 원하는 이미지를 쿼드의 표면에 텍스처 매핑하실 수 있으시죠? 2D 텍스처 매핑에 대해 자신감을 가지셨다면 육면체에 6개의 다른 텍스처를 추가해보세여.

일단 텍스처 좌표를 이해하면 텍스처 매핑은 더이상 이해하기 어려운 것이 아닙니다. 이 튜토리얼 중에 이해가 어려운 부분이 있다면 제게 알려주십시요. 그 부분을 다시 작성하거나 email로 답변을 드리겠습니다. 그럼, 텍스처 매핑으로 멋드러진 장면을 만들어보세요~ ^^

소스코드 다운로드

이 강좌의 소스코드를 다운받으실 수 있습니다. 자신의 환경에 맞는 파일을 받아 사용하세요.

 

  •  

 

 

 

원문 정보

  • 저자: Jeff Molofee (NeHe)
  • 원문보기: Lesson 06

 

번역문 정보

 

현재 상태

  • 초벌번역완료 (2005년 8월 22일)
  • 재벌 및 감수 완료 (2005년 8월 23일)

 

 

 

반응형


관련글 더보기

댓글 영역