[ Java JOGL 프로그래밍 ] VAO, VBO 적용하여 쉐이더에 텍스처 좌표 넘기기 - Whitmem
[ Java JOGL 프로그래밍 ] VAO, VBO 적용하여 쉐이더에 텍스처 좌표 넘기기
그래픽 개발
2024-08-21 21:21 게시 bcf31e35c29950eb8469

0
0
71
이 페이지는 외부 공간에 무단 복제할 수 없으며 오직 있는 그대로 게시되며 부정확한 내용을 포함할 수 있습니다. 법률이 허용하는 한 가이드 라인에 맞춰 게시 내용을 인용하거나 출처로 표기할 수 있습니다.
This page is not to be distributed to external services; it is provided as is and may contain inaccuracies.
기존에는 GL2.Vertex등을 활용해서 직접 좌표를 찍고 매 호출시마다 CPU 호출로 정점을 출력해주었는데 이런 경우 나중에 성능 문제가 발생할 수 있다. 지금은 정점이 한 두개 밖에 안돼서 딱히 큰 체감을 못 느끼지만 실제 게임이나 3D 모델링에서 버텍스 개수는 수 만개는 기본이다. 캐릭터 한 개 당 수 만 개의 버텍스를 출력하는데, 대량의 캐릭터, 배경, 건물 등을 렌더링하다보면 수십~수백만은 기본적으로 넘어가게 된다. 이러한 작업을 모두 for 반복을 통해 출력을 하게 되면 당연히 성능 저하가 올 수 밖에 없으며, 그것이 CPU의 한계이다. 따라서 이 모든 데이터를 버텍스 버퍼 공간에 담아 GPU로 업로드한 뒤 GPU에서 한 번에 쏴주도록 구현을 해야한다. 또한 그 과정에서 Layout을 지정해서 각 정점 좌표들이 어디에 해당하는지, 어떤 좌표인지 쉐이더에 알려줄 수도 있다.
VBO
VBO 는 기본적으로 버텍스 데이터를 담는 버퍼 공간이라고 생각하면 되는 것 같다. 실제 정점 정보가 있다면 이 정점 정보는 모두 VBO GenBuffers 하여 이 공간에 담는다.
IntBuffer VBO = IntBuffer.allocate(4); gl.glGenBuffers(1, VBO); ... gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBOHandle); gl.glBufferData(GL.GL_ARRAY_BUFFER, fb.capacity() * Buffers.SIZEOF_FLOAT, fb,GL.GL_STATIC_DRAW);
위 코드는 4바이트 Int Buffer 공간을 만들고 (!개의 정수) 이 공간에 GenBuffers의 핸들 값을 할당받는다. 버퍼는 우선 한 개만 삽입할 것이므로, 개수는 1로 지정하였다. 그리고 GL.GL_ARRAY_BUFFER 옵션을 통해 VBO 핸들에 대해서 작업한다고 명시하고 (VBOHandle은 VBO IntBuffer의 get 한 값이다.) 이 명시된 공간에 실제 fb라는 데이터를 복사한다. 그 과정에서 실제 바이트 크기를 넘겨야하는데 정점 정보는 모두 실수이므로, 정점 개수 * float 데이터 형 크기로 넘겨야한다. 즉 전체 소스로 보면 다음과 같다.
특히 vertices 에는 메모리 공간에 탑재할 실제 정점 정보를 입력해야한다. 여기서 오해하면 안되는 것은, Vertex 좌표, Texcoord 좌표, Normal 좌표등 넘길 값을 모조리 다 한번에 넘기고 있다. 구분 방법은 존재해야하는데 나 같은 경우 "버텍스 좌표 2개" "텍스처 좌표 2개" 형식으로 반복하면서 넣고 있다. 이는 추후 VAO를 구성할 때 규칙을 지정하여 어느 부분이 Vertex 이고 어느 부분이 TexCoord 인지 명시해야한다.
VAO
위 버텍스 공간에 버텍스들을 할당했다면 이제 이 정보들과 규칙들을 명시할 차례이다. VAO 공간을 할당하기 위해서는 glGenVertexArrays를 사용한다.
IntBuffer VAO = IntBuffer.allocate(4); gl.glGenVertexArrays(1, VAO); int VAOHandle = VAO.get();
이렇게 만들어진 VAO 핸들 공간에 Binding 해주고, 어떤 VBO Handle 인지 명시해준다.
gl.glBindVertexArray(VAOHandle); gl.glBindBuffer(GL.GL_ARRAY_BUFFER, VBOHandle);
그리고 이제 실제 어떤 규칙의 데이터인지 명시를해야하는데, 이는 glVertexAttribPointer을 사용해서 명시할 수 있다.
gl.glVertexAttribPointer(0, 2, GL.GL_FLOAT, false, 4 * Buffers.SIZEOF_FLOAT, 0); gl.glEnableVertexAttribArray(0);
먼저 첫 번째 인자는 Layout 넘버(인덱스)이므로 0번으로 할당한다. 이는 추후 쉐이더에 넘길 때 사용된다. 다음으로는 개수(size)인데, x,y 2개가 한 개의 정점 좌표를 의미하므로 2를 입력한다. 다섯 번째 인자에는 Stride 간격을 넣는데 주의할 점은 bytes 단위로 넣어야한다. 즉 시작 지점에서부터 4 float 뒤에 다시 다음 2개의 정점 좌표가 오기 때문에, 4 * float_data_size 크기로 넣어야 한다. 따라서 나는 4* Buffers.SIZEOF_FLOAT 로 기입하였다. 결과적으로는 16바이트마다 반복되는 것 이다. 마지막 인자는 시작 바이트 위치인데, 정점 좌표는 제일 앞에서부터 기입했으므로 0바이트를 넣는다.
gl.glVertexAttribPointer(1, 2, GL.GL_FLOAT, false, 4 * Buffers.SIZEOF_FLOAT, 2* Buffers.SIZEOF_FLOAT); gl.glEnableVertexAttribArray(1);
한편 TexCoord 의 경우는 버텍스 x,y 2개(정점 한개)가 나온 뒤에서야 TexCoord가 반복된다. 사이즈는 그대로 2이고, Stride 간격도 역시 16바이트(TexCoord 시작으로부터 4개 정점 이후)이나, 시작 위치는 비로소 2번째 인덱스부터 시작되기 때문에 2 * Buffers.SIZEOF_FLOAT 로 8바이트 오프셋 부터로 정의한다. TexCoord는 1번 레이아웃으로 지정하였다.
렌더
렌더링할 때는 이제 VAO 정보만 있으면 된다. VAO 핸들만 있으면 이미 초기화단계에서 모두 명시하고 참조해두었기 때문에, 그리는 방법과 총 최종 정점의 개수만 있으면 된다.
gl.glBindVertexArray(vaoA); gl.glDrawArrays(GL2.GL_QUADS, 0, 4);
이 상태에서 정상적으로 QUADS 가 출력됨을 확인할 수 있었다.
쉐이딩에서의 텍스처 처리
이 상황에서 Vertex의 레이아웃 넘버는 0, TexCoord 의 레이아웃 넘버는 1로 지정했기 때문에 쉐이더에서도 1로 받아 텍스처 좌표를 처리할 수 있게 되었다.
Vertex Shader에서 layout (location = 1) 은 Vec2 텍스처 좌표 u,v 를 의미하기에 vec2 로 지정하였다. (위 pos는 vec3로 하였는데 잘 되는거 봐서는 안에서 알아서 데이터 형을 맞춰주나보다.) 이 값을 그대로 FragmentShader 측으로 넘긴다.
이렇게 넘겨진 oTex Coord는 Fragment Shader 에서 다시 받아 texcoord 와 sampler2D 로 합성해주고 색상을 표시한다.
나 같은 경우 시간 Uniform에 Sin 함수를 적용해서 이미지가 채워졌다 안채워졌다 반복하게끔 작업하였다.
* 참고로 uniform sampler2D tex 에 Texture을 넘기기 위해서는 TextureHandle 을 Uniform에 바인딩해주면 된다.
int uniformLoc = gl.glGetUniformLocation(shader, "tex"); gl.glUniform1f(uniformLoc, textureID);
textureID 는 텍스처를 로드하고 나오는 핸들 값을 의미한다. 이 부분은 텍스처 로드에 관련하여 작성한 일지를 참고하길 바란다.
시간이 지남에 따라서 글자가 서서히 나타나는 것을 확인할 수 있다.
사실 왜 자바에서 GL을 공부하냐면 개인적으로 사용할 가사 재생 프로그램을 만들고 싶어서였다.
댓글 0개
댓글을 작성하는 경우 댓글 처리 방침에 동의하는 것으로 간주됩니다. 댓글을 작성하면 일회용 인증키가 발급되며, 해당 키를 분실하는 경우 댓글을 제거할 수 없습니다. 댓글을 작성하면 사용자 IP가 영구적으로 기록 및 부분 공개됩니다.
확인
Whitmemit 개인 일지 블로그는 개인이 운영하는 정보 공유 공간으로 사용자의 민감한 개인 정보를 직접 요구하거나 요청하지 않습니다. 기본적인 사이트 방문시 처리되는 처리 정보에 대해서는 '사이트 처리 방침'을 참고하십시오. 추가적인 기능의 제공을 위하여 쿠키 정보를 사용하고 있습니다. Whitmemit 에서 처리하는 정보는 식별 용도로 사용되며 기타 글꼴 및 폰트 라이브러리에서 쿠키 정보를 사용할 수 있습니다.
이 자료는 모두 필수 자료로 간주되며, 사이트 이용을 하거나, 탐색하는 경우 동의로 간주합니다.