OpenGLES Programming Guide for iOS 번역 - 9장. 정점 데이터를 처리하기 위한 모범적 방법
OpenGL ES/iOS 2011. 9. 22. 14:15본 번역은 얼마든지 정확하지 않을 수 있습니다. 참고용으로만 보시길... ^^;;;
OpenGL ES를 사용하여 프레임을 렌더링하려면 응용프로그램에서 그래픽 파이프라인을 설정하고 렌더링하는 그래픽 primitives를 보냅니다. 모든 기본형의 파이프라인 설정을 사용하는 그리기 응용프로그램도 있고, 프레임을 구성하는 다른 요소를 서로 다른 기술을 사용하여 그리기 응용프로그램도 있습니다. 그러나 응용프로그램이 어떤 프리미티브를 사용하거나 파이프라인이 어떻게 설정되어 있는지에 관계없이 응용프로그램은 OpenGL ES에 정점을 제공합니다. 이 장에서는 정점 데이터에 관한 유용한 정보를 제공하고, 정점 데이터를 효율적으로 처리하는 방법에 대해 대상을 맞춘 조언을 소개합니다.
정점은 위치, 색, 법선, 텍스처 좌표 등 하나 또는 그 이상의 속성(attributes)으로 구성되어 있습니다. OpenGL ES 2.0 응용프로그램은 고유의 특성을 자유롭게 정의할 수 있습니다. 정점 데이터의 각 속성은 정점 셰이더의 입력 기능을하는 특성 변수와 대응하고 있습니다. OpenGL 1.1 응용프로그램은 고정 기능 파이프라인에 의해 정의되는 속성을 사용합니다.
응용프로그램은 1~4 개의 구성요소(components)로 구성된 벡터로 속성을 정의합니다. 속성의 모든 구성 요소가 공통의 데이터 형식을 공유합니다. 예를 들어, 색상은 4개의 GLbyte 구성 요소(알파, 빨강, 녹색, 파랑)로 정의되는 경우가 있습니다. 속성이 셰이더 변수에 로드할 때 응용프로그램 데이터에서 제공되지 않은 구성 요소는 OpenGL ES의 기본 값으로 채워집니다. 마지막 구성 요소는 1로 채워지고 지정되지 않은 다른 구성 요소는 0으로 채워집니다.
응용프로그램 속성을 상수로 설정할 가능성이있다는 것은 드로잉 명령의 일부로 전송되는 모든 정점에 대해 동일한 값을 사용되는 것을 의미하고 배열로 설정할 수 있다는 것은 각 정점이 속성의 값을 의미합니다. 응용프로그램이 OpenGL ES의 함수를 호출하여 일련의 정점을 그릴 때, 정점 데이터는 응용프로그램에서 그래픽 하드웨어로 복사됩니다. 그리고 그래픽 하드웨어 정점 데이터를 처리, 쉐이더에서 각 정점을 처리하고 기본형으로 조립 및 래스터화 하여 프레임 버퍼에 보냅니다. OpenGL ES의 장점 중 하나는, 정점 데이터를 OpenGL ES로 전송하는 함수의 단일 집합을 표준으로 채용하고 있고, OpenGL이 제공하고 있던 낡고 비효율적인 구조는 삭제된 것입니다.
프레임을 렌더링하기 위해 다수의 프리미티브를 전송해야하는 응용프로그램이 정점 데이터 및 정점 데이터의 OpenGL ES 에 제공 방법을 주의깊게 관리해야합니다.
이 장에서 설명하는 방법은 다음과 같은 몇 가지 기본 원칙을 요약 할 수 있습니다.
● 정점 데이터의 크기를 감소시킨다.
● OpenGL ES가 그래픽 하드웨어 정점 데이터를 전송하기 전에 발생하기 전에 처리를 감소시킨다.
● 그래픽 하드웨어 정점 데이터를 복사하는 데 걸리는 시간을 감소시킨다.
● 각 정점에 대해 실행되는 연산을 감소시킨다.
모델의 단순화
iOS 기반 장치의 그래픽 하드웨어는 매우 고성능이지만 장치의 표시 이미지는 매우 작은 경우가 많습니다. iOS에 매력적인 그래픽을 표시하는 데 매우 복잡한 모델은 필요 없습니다. 모델의 렌더링에 사용하는 정점의 수를 줄이는 것은 정점 데이터의 크기와 정점 데이터에 대해 실행되는 연산을 줄이기에 직접 연결됩니다.
모델의 복잡도는 다음 기술을 사용함으로써 줄일 수 있습니다.
● 다른 세부 수준의 모델을 여러 버전 준비하고 카메라에서 객체의 거리와 디스플레이의 크기에 따라 적절한 모델을 실행할 때 선택한다.
● 텍스처를 사용하여 일정한 정점 정보를 필요로한다. 예를 들어, 범프 맵은 정점 데이터를 추가하지 않고도 모델에 디테일을 추가하는 데 사용할 수 있습니다.
● 모델은 정점을 추가하여 조명 디테일 및 렌더링의 품질을 향상시킨다. 이것은 일반적으로 값을 각 정점에 대해 계산 래스터 화 단계에서 삼각형을 대상으로 값을 보간하는 경우에 행해집니다. 예를 들어, 스포트 라이트를 삼각형의 중심으로 향하는 경우, 스포트 라이트의 가장 밝은 부분은 정점을 향해되어있지 않기 때문에 스포트 라이트 효과는 미치지 않을 수 있습니다. 정점을 추가하여, 정점 데이터의 크기와 모델에 대해 실행되는 연산이 증가하는 희생을 치르는 추가 보간 포인트가 제공됩니다. 추가 정점을 추가 대신에 파이프라인 조각 단계로 연산을 이동하는 것을 고려합니다:
● 응용프로그램에서 OpenGL ES 2.0을 사용하는 경우 응용프로그램은 정점 셰이더의 연산을 실행하고 varying 변수에 결과를 할당합니다. varying 값은 그래픽 하드웨어 보간 입력으로 조각 쉐이더에 전달됩니다. 대신 연산의 입력을 varying 변수에 할당 조각 쉐이더 연산을 수행합니다. 이렇게하면 작업을 수행하는 비용이 정점 단위의 비용에서 조각 단위 비용으로 변경되어 파이프라인의 정점 단계 부하, 그것보다 큰 조각 단계의 부하가 감소됩니다. 이것은 정점 처리에 응용프로그램이 차단되는 경우, 계산 부하가 크지 않은 경우, 및 변화에 의해 정점의 수가 크게 감소할 가능성이있는 경우에 실행합니다.
● 응용프로그램에서 OpenGL ES 1.1을 사용하는 경우는 DOT3 라이팅을 사용하여 조각 단위의 라이팅을 수행할 수 있습니다. 이것은 구체적으로, 법선 정보를 유지하는 범프 맵 텍스처를 추가하고 GL_DOT3_RGB 모드에서 텍스처 합성 작업을 사용하여 범프 맵 적용해서 실행합니다.
속성 배열 상수를 저장하는 것을 피하기
응용프로그램 모델 전체에서 고정된 상태의 데이터를 사용하는 속성이 포함되어있는 경우, 각 정점에 대해 데이터를 복제하지 마십시오. OpenGL ES 2.0 응용프로그램은 고정적인 정점 속성을 설정하는것 대신 값을 보관 유지하는 일정한 쉐이더 값을 사용할 수도 있습니다. OpenGL ES 1.1 응용프로그램은 glColor4ub 및 glTexCoord2F 같은 정점 단위의 특성 함수를 사용합니다.
속성은 가능한 한 최소의 형식을 사용
속성의 각 구성 요소의 크기를 지정할 때 원하는 결과를 얻을 수 최소 데이터 형식을 선택합니다. 다음 몇 가지 가이드를 제공합니다.
● 정점의 색상은 4개의 부호없는 바이트 구성 요소( GL_UNSIGNED_BYTE )를 사용하여 지정한다.
● 2개 또는 4개의 부호없는 바이트 값( GL_UNSIGNED_BYTE ) 또는 부호없는 short 값( GL_UNSIGNED_SHORT )을 사용하여 텍스처 좌표를 지정한다. 텍스처 좌표의 여러 세트를 단일 속성에 속하지 않도록 합니다.
● OpenGL ES의 GL_FIXED 데이터 형식의 사용을 피한다. 이 데이터 형식은 GL_FLOAT 와 같은 양의 메모리를 필요로하지만, 값의 범위는 더 좁아집니다. 모든 iOS 장치가 하드웨어 부동 소수점 유닛을 지원하고 부동 소수점 값을보다 빠르게 처리할 수 있습니다.
비교적 작은 구성 요소를 지정하는 경우는, 정점 데이터의 차이를 피하기 위해 정점 포맷을 반드시 재구성하십시오. 자세한 내용은 "어긋난 정점 데이터를 피한다"를 참조하십시오.
Interleaved 정점 데이터를 사용
OpenGL ES를 사용하여 응용프로그램은 정점 데이터를 연속 배열 (also known as a struct of arrays)배열 구조 또는 각 요소가 여러 특성이 포함된 배열(an struct of arrays)로 지정할 수 있습니다. iOS에 적합한 형식은 single interleaved vertex 포맷을 사용하는 구조체의 배열입니다. interleaved data를 사용하면 각 정점에 대해 향상된 메모리 배치가 가능합니다.
그림 9-1. Interleaved 메모리 구조는 정점에 대한 모든 데이터를 정리하여 메모리에 배치
이 규칙의 예외는 일부의 정점 데이터를 다른 정점 데이터는 서로 다른 속도로 업데이트해야하는 경우, 또는 일부의 데이터를 두 개 이상의 모델에서 공유할 수있는 경우입니다.
두 경우 모두 속성 데이터를 두 개 이상의 구조로 나눌 수 있습니다.
그림 9-2. 일부 데이터의 사용 방법이 다른 경우 여러 정점 구조체를 사용
어긋난 정점 데이터를 피하기
정점 구조체를 설계할 때 각 속성의 선두를 오프셋(구성 요소의 크기의 배수 또는 4 바이트 중 더 큰)에 맞춥니다. 속성이 어긋나있는 경우 iOS는 그래픽 하드웨어에 데이터를 전달하기 전에 추가 작업을 실행해야 합니다.
그림 9-3 (74 페이지)에서는 위치와 법선 데이터가 각각 3개의 short 정수로 총 6바이트로 정의되어 있습니다. 법선 데이터는 오프셋 6에서 시작합니다. 이것은 기본 크기(2 바이트)의 배수, 4바이트의 배수가 아닙니다. 이 정점 데이터를 iOS에 보낸면 iOS는 그래픽 하드웨어에 데이터를 전달하기 전에 데이터를 복사하고 정렬하기위한 시간을 더 지출해야합니다. 이것을 해결하기 위해 각 특성 뒤에 2바이트의 패딩 바이트를 명시적으로 추가합니다.
그림 9-3. 추가 처리를 피하기 위한 정점 데이터 정렬
삼각형 스트립을 사용하여 정점 데이터를 일괄 처리
삼각형 스트립을 사용하여 OpenGL ES가 모델에 대해 실행하면 정점 계산의 수가 크게 감소합니다. 그림 9-4의 왼쪽에서 총 9개의 정점을 사용하여 3개의 삼각형이 지정되어 있습니다. C , E , G는 실제로 같은 정점을 지정합니다. 데이터를 삼각형 스트립으로 지정하여, 정점의 수를 9개에서 5개로 줄일 수 있습니다.
그림 9-4. 삼각형 스트립
경우에 따라서는 여러 삼각형 스트립을 더 큰 단일 삼각형 스트립으로 통합할 수 있습니다. 모든 삼각형 스트립은 렌더링 요구 사항이 동일해야 합니다. 이것은 다음을 의미합니다.
● OpenGL ES 2.0 응용프로그램은 동일한 쉐이더를 사용하여 모든 삼각형 스트립을 드로잉 해야합니다.
● OpenGL ES 1.1 응용프로그램은 OpenGL의 상태를 전혀 바꾸지 않고 모든 삼각형 스트립을 렌더링해야 합니다.
● 삼각형 스트립은 정점의 속성이 동일해야 합니다.
2개의 삼각형 스트립을 병합하려면 그림 9-5 과 같이, 첫 번째 스트립의 마지막 정점과 두 번째 스트립의 첫 번째 정점을 이중으로 합니다. 이 지구를 OpenGL ES로 전송하면 삼각형 DEE 삼각형 EEF 삼각형 EFF 삼각형 FFG이 저하된 것으로 간주되어 처리도 클러스터화도 되지 않습니다.
그림 9-5. 저하된 삼각형을 사용하여 삼각형 스트립을 병합
최고의 성능을 얻기 위해 모델은 glDrawArrays를 사용하여 정점의 중복을 최소화하여 인덱스없는 단일 삼각형 스트립으로 전송해야합니다. 모델에서(삼각형 스트립으로 연속적으로 나타나지 않는 삼각형으로 많은 정점이 공유되므로 또는 응용프로그램이 수많은 작은 삼각형 스트립을 병합했기 때문에) 많은 정점을 중복시킬 필요가 있는 경우 별도의 인덱스 버퍼 를 사용하여 glDrawElements를 호출하여 더 나은 성능을 얻을 수 있습니다. 인덱스의 유무는 트레이드 오프 관계에 있으며, 인덱스없이 삼각형 스트립이 전체의 정점을 정기적으로 이중해야한다 반면 인덱싱된 삼각형 목록은 인덱스에 추가 메모리가 필요 정점 검색에 오버헤드가 추가됩니다.
최상의 결과를 얻기 위해 인덱싱된 및 인덱스없는 두 삼각형 스트립을 사용하여 테스트하고 가장 빠른 모델을 사용합니다.
가능하면 같은 정점을 공유하는 삼각형이 삼각형 스트립에서 서로 비교적 가까운 위치에 그려지도록, 정점과 인덱스의 데이터를 정렬합니다. 그래픽 하드웨어는 최근 정점 계산을 캐시하는 수 많은 계산 결과를 로컬로 참조할 수 있음에서 그래픽 하드웨어가 정점을 여러 번 계산을 피할 수있는 가능성이 있습니다.
정점 버퍼 객체를 사용하여 정점 데이터의 복사본 관리
목록 9-1 에서는 간단한 응용프로그램이 정점 셰이더 위치 및 색상 데이터를 제공하는 데 사용할 수있는 함수를 보여줍니다. 이 함수는 2개의 속성이 활성화되어 각각의 속성이 intenleaved 정점 구조체를 참조하도록 설정되어 있습니다. 마지막으로 glDrawElements 함수를 호출하여 모델을 단일 삼각형 스트립으로 렌더링합니다.
목록 9-1. OpenGL ES 2.0 에 정점 데이터 전송
typedef struct _vertexStruct
{
GLfloat position [2]; GLubyte color [4];
} vertexStruct;
enum {
ATTRIB_POSITION,
ATTRIB_COLOR,
NUM_ATTRIBUTES
};
void DrawModel()
{
const vertexStruct vertices[] = {...};
const GLubyte indices[] = {...};
glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), &vertices[0].position);
glEnableVertexAttribArray(ATTRIB_POSITION);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), &vertices[0].color);
glEnableVertexAttribArray (ATTRIB_COLOR);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, indices);
}
이 코드는 작동은 하지만 비효율적입니다. DrawModel이 호출 될 때마다 인덱스 데이터와 정점 데이터가 OpenGL ES에 복사되고 그래픽 하드웨어에 전송됩니다.호출과 호출 사이에 정점 데이터가 변화하지 않으면 데이터의 불필요한 복사본이 성능에 영향을 미칠 우려가 있습니다. 불필요한 복사를 피하기 위해 응용프로그램이 정점 데이터를 정점 버퍼 오브젝트(Vertex Buffer Objects, VBO)에 저장해야합니다. OpenGL ES는 정점 버퍼 객체의 메모리를 보유하고 있기 때문에 그래픽 하드웨어가 빠르게 액세스할 수있는 메모리 버퍼를 저장하거나 데이터를 그래픽 하드웨어에 적합한 형식이되도록 사전 처리할 수 있습니다.
목록 9-2 에서는, 정점 버퍼 객체 쌍( 첫 번째 정점 데이터 유지를위한 두 번째 스트립의 인덱스 용)을 만듭니다. 두 객체의 경우도 코드는 새 객체를 생성하고 그 객체를 현재의 버퍼에 바인딩된 버퍼를 채웁니다. CreateVertexBuffers 함수는 응용프로그램의 초기화될 때 호출됩니다.
목록 9-2. 정점 버퍼 객체 만들기
GLuint vertexBuffer;
GLuint indexBuffer;
void CreateVertexBuffers ()
{
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glGenBuffers(1, &indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
목록 9-3 은 목록 9-1을 수정, 정점 버퍼 객체를 사용합니다. 목록 9-3의 주요 차이점은 glVertexPointer 함수 glColorPointer 함수의 매개 변수가 정점 배열을 가리키는 것이 없게 되었다는 점입니다. 대신 매개 변수는 버텍스 버퍼 객체에 대한 오프셋입니다.
목록 9-3. OpenGL ES 2.0 의 버텍스 버퍼 객체를 사용하여 그리기
void DrawModelUsingVertexBuffers ()
{
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), (void *)offsetof (vertexStruct, position));
glEnableVertexAttribArray(ATTRIB_POSITION);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), (void *)offsetof (vertexStruct, color);
glEnableVertexAttribArray(ATTRIB_COLOR);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void *)0);
}
버퍼 사용 팁
이전 예제는 정점 데이터가 초기화된 후 렌더링 동안은 변경할 필요가없는 것을 전제로 했습니다. 그러나 OpenGL ES는 런타임에 버퍼의 데이터를 변경할 수 있습니다. 정점 버퍼 객체 디자인의 중요한 부분은 버퍼에 저장되는 데이터의 사용 방법을 응용프로그램이 OpenGL ES에 알릴 수있는 것입니다. 용도의 매개 변수는 OpenGL ES 응용프로그램이 데이터를 사용 목적에 따라 버퍼에 저장하는 방법을 선택할 수 있습니다. 목록 9-2 glBufferData 함수의 각 호출의 마지막 매개 변수 용도의 힌트가 지정되어 있습니다. GL_STATIC_DRAW을 glBufferData에 전달하여 두 버퍼의 내용이 변경될 예정은 전혀없는 것이 OpenGL ES에 전달하고 이를 통해 OpenGL ES는 데이터를 저장하는 방법과 위치를 최적화 하기위한 기회가 더 주어집니다.
OpenGL ES는 다음의 용도에 대응하고 있습니다.
● GL_STATIC_DRAW은 한 번 지정하면 변경하지 정점 데이터를 사용합니다. 응용프로그램은 초기화하는 동안이 정점 버퍼 객체를 만들고 객체를 필요 없을 때까지 사용합니다.
● GL_DYNAMIC_DRAW는 버퍼에 저장된 데이터가 렌더링 루프에 변화 가능성이있는 경우에 사용합니다. 응용프로그램이 초기화될 때이 버퍼를 초기화하고 glBufferSubData 함수를 호출하여 필요한 데이터를 업데이트합니다.
● GL_STREAM_DRAW 는 렌더링 횟수가 적고 렌더링 후 삭제되는 임시적인 형상을 만들 필요가있는 경우에 사용합니다. 이것은 응용프로그램 프레임 마다 새로운 정점 데이터를 동적으로 계산해야 하는 경우, 쉐이더 내부에서 계산을 수행하는 경우에 가장 유용합니다. 일반적으로 응용프로그램 스트림 그리기를 사용하는 경우 다른 2개의 버퍼를 만들 생각합니다. 2개를 만드는 경우 OpenGL ES가 한 버퍼의 내용을 사용하여 그리는 한편, 응용프로그램은 다른 버퍼를 채웁니다. 자세한 내용은 "버퍼의 이중화를 사용하여 리소스 충돌 방지"를 참조하십시오. 스트림 그리기는 OpenGL ES 1.1 에서 사용할 수 없습니다. 대신 GL_DYNAMIC_DRAW 을 사용합니다.
정점 포맷의 다양한 속성이 다른 사용 패턴을 필요로하는 경우는, 정점 데이터를 여러 구조로 분할하고 사용하는 방법에 특징이 공통 특성의 컬렉션마다 독립적인 정점 버퍼 객체 를 할당합니다. 목록 9-4는 별도의 버퍼를 사용하여 색상 데이터를 유지하는 것 이전 예제를 수정한 것입니다. GL_DYNAMIC_DRAW 라는 힌트를 사용하여 색상 버퍼를 할당하여 OpenGL ES 응용프로그램이 적절한 성능을 유지하도록 버퍼를 할당할 수 있습니다.
목록 9-4. 다중 꼭지점 버퍼 객체를 사용하여 모델 그리기
typedef struct _vertexStatic
{
GLfloat position[2];
} vertexStatic;
typedef struct _vertexDynamic
{
GLubyte color[4];
} vertexDynamic;
// 정적 데이터에 동적 데이터를위한 버퍼를 분리
GLuint staticBuffer;
GLuint dynamicBuffer;
GLuint indexBuffer;
const vertexStatic staticVertexData[] = {...};
vertexDynamic dynamicVertexData[] = {...};
const GLubyte indices[] = {...};
void CreateBuffers()
{
// 정적 위치 데이터
glGenBuffers(1, & staticBuffer);
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(staticVertexData), staticVertexData, GL_STATIC_DRAW);
// 동적 색상 데이터
// 여기에 나와 있지 않지만,이 버퍼의 데이터 프레임 사이에 변경될 수 있음
glGenBuffers(1, & dynamicBuffer);
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(dynamicVertexData), dynamicVertexData, GL_DYNAMIC_DRAW);
// 정적 인덱스 데이터
glGenBuffers(1, & indexBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
void DrawModelUsingMultipleVertexBuffers()
{
glBindBuffer(GL_ARRAY_BUFFER, staticBuffer);
glVertexAttribPointer(ATTRIB_POSITION, 2, GL_FLOAT, GL_FALSE, sizeof(vertexStruct), (void *)offsetof (vertexStruct, position));
glEnableVertexAttribArray(ATTRIB_POSITION);
glBindBuffer(GL_ARRAY_BUFFER, dynamicBuffer);
glVertexAttribPointer(ATTRIB_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(vertexStruct), (void *)offsetof (vertexStruct, color);
glEnableVertexAttribArray(ATTRIB_COLOR);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);
glDrawElements(GL_TRIANGLE_STRIP, sizeof(indices)/sizeof(GLubyte), GL_UNSIGNED_BYTE, (void *)0);
}
정점 배열 객체를 사용하여 정점 배열 상태 변화의 조정 관리 통합
목록 9-4 DrawModelUsingMultipleVertexBuffers 함수를 자세히 보십시오. 이 함수는 많은 속성을 사용할 수 여러 정점 버퍼 오브젝트가 바인드되고 버퍼를 참조하도록 속성이 설정됩니다. 그 초기화 코드가 모두 기본적으로 정적이고 프레임과 프레임 사이에서 변화하는 매개 변수는 없습니다. 이 함수는 응용프로그램에서 프레임을 렌더링 때마다 호출되는 경우는 그래픽 파이프라인을 다시 설정하는 불필요한 오버헤드가 다수 존재하고 있습니다. 응용프로그램이 많이 다른 종류의 모델을 그릴 때, 파이프라인의 재구성은 진정한 병목이 될 수 있습니다. 이것을 방지하려면 정점 배열 객체를 사용하여 모든 속성 설정을 저장합니다. OES_vertex_array_object 확장 기능은 iOS 4.0 이후의 모든 iOS 장치에서 사용할 수 있습니다.
그림 9-6은 2 개의 정점 배열 객체를 사용하여 설정 예입니다. 각 설정은 다른 설정에서 독립하고 각각 참조하는 정점의 수를 참조하는 버텍스 버퍼 객체도 다릅니다.
그림 9-6. 정점 배열 객체 설정
목록 9-5 위의 첫 번째 정점 배열 객체의 설정에 사용하는 코드를 보여줍니다. 이 코드는 새로운 정점 배열 객체 식별자를 생성하고, 정점 배열 객체를 컨텍스트에 바인딩합니다. 그러면 코드가 정점 배열 객체를 사용하지 않은 경우와 같은 호출을 정점 속성을 설정합니다. 설정은 컨텍스트가 아니라 바인딩된 정점 배열 객체에 저장됩니다.
목록 9-5. 정점 배열 객체 설정
void ConfigureVertexArrayObject()
{
// 정점 배열 객체를 만들고 바인딩합니다.
glGenVertexArraysOES(1, &vao1);
glBindVertexArrayOES(vao1);
// Configure the attributes in the VAO.
glBindBuffer(GL_ARRAY_BUFFER, vbo1);
glVertexAttribPointer(ATT_POSITION, 3, GL_FLOAT, GL_FALSE, sizeof(staticFmt), (void*)offsetof(staticFmt,position));
glEnableVertexAttribArray(ATT_POSITION);
glVertexAttribPointer(ATT_TEXCOORD, 2, GL_UNSIGNED_SHORT, GL_TRUE, sizeof(staticFmt), (void*)offsetof(staticFmt,texcoord));
glEnableVertexAttribArray(ATT_TEXCOORD);
glVertexAttribPointer(ATT_NORMAL, 3, GL_FLOAT, GL_FALSE, sizeof(staticFmt), (void*)offsetof(staticFmt,normal));
glEnableVertexAttribArray(ATT_NORMAL);
glBindBuffer(GL_ARRAY_BUFFER, vbo2);
glVertexAttribPointer(ATT_COLOR, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(dynamicFmt), (void*)offsetof(dynamicFmt,color));
glEnableVertexAttribArray(ATT_COLOR);
// 기본 상태로 바인딩한다.
glBindBuffer(GL_ARRAY_BUFFER,0);
glBindVertexArrayOES(0);
}
그리려면 코드에서 정점 배열 객체를 바인딩하고 지금까지와 마찬가지로 드로잉 명령을 보냅니다.
최고의 성능을 위해 응용프로그램이 각 정점 배열 객체를 설정한 후 실행시 절대 변경하지 않도록합니다. 대신 정점 배열 객체를 프레임마다 변경해야하는 경우, 복수의 정점 배열 객체를 만듭니다. 예를 들어, 버퍼의 이중 암호화를 사용하는 응용프로그램은 정점 배열 객체의 첫 번째 세트의 홀수 프레임에, 그리고 두 번째 세트를 짝수 프레임에 설정할 수 있습니다. 정점 배열 객체의 각 세트는 세트의 프레임을 렌더링하는 데 사용하는 버텍스 버퍼 객체를 참조합니다. 정점 배열 객체의 설정이 변하지 않는 경우 OpenGL ES 는 정점 포맷에 대한 정보를 캐시하여 정점 속성을 처리하는 방법을 향상시킬 수 있습니다.