2016-06-22 2 views
5

OpenGL 4.3과 OpenGL ES 3.1에는 버텍스 배열 지정을위한 몇 가지 대체 함수가 추가되었습니다. glVertexAttribFormat, glBindVertexBuffers 등입니다.하지만 이미 버텍스 배열을 지정하는 함수가있었습니다. 즉 glVertexAttribPointer입니다.glVertexAttribPointer 및 glVertexAttribFormat : 다른 점은 무엇입니까?

  1. 이전 API와 동일한 기능을하는 새로운 API를 추가해야하는 이유는 무엇입니까?

  2. 새 API는 어떻게 작동합니까?

+1

'glVertexAttribFormat()'보다'glBindVertexBuffer()'에 관한 것들이 더 많습니다. http://stackoverflow.com/questions/26767939/do-opengl-vertex-array-objects-store-vertex- buffer-names-and-indices-or-only-in, http://stackoverflow.com/questions/29220416/can-opengl-vertex-buffer-binding-points-be-reused-across-different-vaos. –

답변

20

glVertexAttribPointer에는 2 개의 결함이 있으며, 그 중 하나는 반 - 주관적이며, 다른 하나는 객관적입니다.

첫 번째 결함은 GL_ARRAY_BUFFER에 대한 종속성입니다. 이는 glVertexAttribPointer의 동작이 호출되었을 때 GL_ARRAY_BUFFER에 바인딩 된 모든 것을 조건으로한다는 것을 의미합니다. 그러나 일단 호출되면 GL_ARRAY_BUFFER에 바인딩 된 것은 더 이상 중요하지 않습니다. 버퍼 오브젝트의 참조가 VAO로 복사됩니다. 이 모든 것은 매우 숙련되지 않은 사용자에게조차도 매우 직관력이없고 혼란 스럽습니다.

또한 정수 바이트 오프셋이 아닌 "포인터"로 버퍼 객체에 오프셋을 제공해야합니다. 이는 정수에서 포인터로의 어색한 캐스트를 수행함을 의미합니다 (드라이버에서 똑같이 어색한 캐스트와 일치해야 함).

두 번째 결함은 논리적으로 상당히 분리 된 두 가지 작업을 결합한다는 것입니다. OpenGL에서 읽을 수있는 버텍스 배열을 정의하려면 다음 두 가지를 제공해야합니다.

  • 메모리에서 데이터를 가져 오는 방법.
  • 해당 데이터의 모양.

glVertexAttribPointer은 이들을 동시에 제공한다. GL_ARRAY_BUFFER 버퍼 객체와 오프셋 "포인터"및 스트라이드는 데이터가 저장되는 위치와 가져 오는 방법을 정의합니다. 다른 매개 변수는 단일 데이터 단위가 어떻게 생겼는지 설명합니다. 배열의 vertex format이라고합시다.

실용적인 문제로, 사용자는 정점 데이터보다 정점 데이터가 나오는 위치를 훨씬 더 쉽게 변경할 수 있습니다. 결국 장면의 많은 객체는 동일한 방식으로 정점을 저장합니다. 그 방법이 무엇이든간에 : 위치에 대해 3 개의 플로트, 색상에 대해 4 개의 부호없는 바이트, 텍스 코드에 대한 부호없는 2 개의 짧은 바이트 등이 있습니다. 일반적으로 몇 가지 정점 포맷 만 있습니다.

반면에 데이터를 가져 오는 위치가 훨씬 더 넓습니다. 개체가 모두 같은 버퍼에서 가져 오더라도 오프셋 범위 내에서 업데이트하여 개체에서 개체로 전환 할 수 있습니다.

glVertexAttribPointer을 사용하면 오프셋을 업데이트 할 수 없습니다. 한 번에 전체 포맷 + 버퍼 정보를 지정해야합니다. 매번

VAO는 개체 당 모든 호출을 수행하지 않아도되지만 실제로 문제를 해결하지는 못합니다. 오, 물론 glVertexAttribPointer으로 전화하지 않아도됩니다. 그렇다고해서 꼭지점 형식 변경이 비싸면이라는 사실이 바뀌지는 않습니다.

As discussed here, 꼭지점 형식 변경은 꽤 비쌉니다. 새 VAO를 바인드 할 때 (또는 새 VAO를 바인딩 한 후 렌더링 할 때), 구현은 관계없이 정점 형식을 변경하거나 정의 된 정점 형식이 다른지 확인하기 위해 두 VAO를 비교해야합니다. 어느 쪽이든, 그것은 할 필요가없는 일을하고 있습니다.

glVertexAttribFormatglBindVertexBuffer은이 두 가지 문제를 모두 해결합니다. glBindVertexBuffer은 버퍼 객체를 직접 지정하고 바이트 오프셋을 실제 (64 비트) 정수로 사용합니다. 따라서 GL_ARRAY_BUFFER 바인딩을 어색하게 사용하지 마십시오. 그 바인딩은 버퍼 객체를 조작하기 위해서만 사용됩니다.

두 개의 별도 개념이 이제 별도의 함수이기 때문에 형식을 저장하고 바인드 한 다음 렌더링 할 각 객체 또는 객체 그룹에 대한 정점 버퍼를 바인드하는 VAO를 가질 수 있습니다. 버텍스 버퍼 바인딩 상태를 변경하는 것은 버텍스 포맷 상태보다 저렴합니다.


별도의 속성 바인딩 기능은 다음과 같이 작동합니다. glVertexAttrib*Format 함수는 속성에 대한 모든 정점 형식화 매개 변수를 제공합니다. 각 매개 변수는 glVertexAttrib*Pointer에 해당하는 호출의 매개 변수와 완전히 같은 의미입니다.

다소 혼란스러운 점은 glBindVertexBuffer입니다.

첫 번째 매개 변수는 색인입니다. 그러나 이것은 이 아니며 속성 위치입니다. 그것은 단순히 버퍼 바인딩 포인트 일뿐입니다. 이것은 자신의 최대 한도가있는 속성 위치의 별도 배열입니다. 따라서 인덱스 0에 버퍼를 바인드한다는 것은 이 없음을 의미합니다.은 속성 위치 0이 데이터를 가져 오는 위치를 의미합니다.

버퍼 바인딩과 속성 위치 간의 연결은 glVertexAttribBinding으로 정의됩니다. 첫 번째 매개 변수는 특성 위치이고 두 번째 매개 변수는 해당 특성의 위치를 ​​가져 오는 버퍼 바인딩 인덱스입니다. 함수의 이름이 "VertexAttrib"로 시작하기 때문에 이것을 정점 형식 상태의 일부로 간주해야하므로 변경하는 데 비용이 많이 듭니다.

오프셋의 특성은 처음에는 조금 혼란 스러울 수 있습니다. glVertexAttribFormat에는 오프셋 매개 변수가 있습니다. 하지만 역시 glBindVertexBuffer입니다. 그러나 이러한 상쇄는 다른 것을 의미합니다. 차이를 이해하는 가장 쉬운 방법은 인터리빙 된 데이터 구조의 예를 사용하는 것이다 : 옵셋 결합

struct Vertex 
{ 
    GLfloat pos[3]; 
    GLubyte color[4]; 
    GLushort texCoord[2]; 
}; 

정점 버퍼는 제 정점 색인 버퍼 객체의 시작으로부터의 바이트 오프셋을 지정한다. 즉, 인덱스 0을 렌더링하면 GPU는 버퍼 객체의 주소 + 바인딩 오프셋에서 메모리를 가져옵니다. 오프셋

정점 포맷은 그 특정 속성 데이터의 각 정점 의 시작으로부터 오프셋을 지정. 버퍼의 데이터가 Vertex에 의해 정의 된 경우, 다음이 될 것입니다 각 속성에 대한 오프셋 :

glVertexAttribFormat(0, ..., offsetof(Vertex, pos)); //AKA: 0 
glVertexAttribFormat(1, ..., offsetof(Vertex, color)); //Probably 12 
glVertexAttribFormat(2, ..., offsetof(Vertex, texCoord)); //Probably 16 

그래서 각 속성의 데이터가 오는 곳 형식 오프셋을 정의하면서 정점 0, 메모리의 어디에 정의 오프셋 바인딩 에서 내의 정점.

마지막으로 이해해야 할 점은 보폭이 인 버퍼 바인딩이 있다는 것입니다. 이는 이상하게 보일 수 있지만 하드웨어 측면에서 생각하면 좋습니다.

버퍼 바인딩에는 하드웨어가 정점 인덱스 또는 인스턴스 인덱스를 메모리 위치로 변환하는 데 필요한 모든 정보가 포함되어야합니다. 끝나면 정점 형식은 해당 메모리 위치의 바이트를 해석하는 방법을 설명합니다.

이는 인스턴스 제수가 glVertexBindingDivisor을 통해 버퍼 바인딩 상태의 일부인 이유이기도합니다. 하드웨어는 인스턴스 인덱스를 메모리 주소로 변환하기 위해 제수를 알아야합니다.

물론 이것은 더 이상 OpenGL을 사용하여 보폭을 계산할 수 없음을 의미합니다. 위의 캐스팅에서는 단순히 sizeof(Vertex)을 사용합니다. 이 동등한 기능 속성 위치에 대해 동일한 인덱스 값을 사용

void glVertexAttrib*Pointer(GLuint index​, GLint size​, GLenum type​, {GLboolean normalized​,} GLsizei stride​, const GLvoid * pointer​) 
{ 
    glVertexAttrib*Format(index, size, type, {normalized,} 0); 
    glVertexAttribBinding(index, index); 

    GLuint buffer; 
    glGetIntegerv(GL_ARRAY_BUFFER_BINDING, buffer); 
    if(buffer == 0) 
    glErrorOut(GL_INVALID_OPERATION); //Give an error. 

    if(stride == 0) 
    stride = CalcStride(size, type); 

    GLintptr offset = reinterpret_cast<GLintptr>(pointer); 
    glBindVertexBuffer(index, buffer, offset, stride); 
} 

참고

개별 속성 포맷 완전히 이전 기능이 이제 새로운 관점에서 완전히 정의됨을 잘 오래 glVertexAttribPointer 모델 커버 및 버퍼 바인딩 인덱스 인터리브 된 속성을 수행하는 경우 가능하면이 작업을 피해야합니다. 대신 동일한 버퍼에서 인터리브 된 모든 속성에 단일 버퍼 바인딩을 사용하십시오.

+0

IIUC 당신의 설명,'glVertexAttrib * Pointer'의 재 구현은 교체 된 스왑을 가지고 있습니다. 캐스트 된 포인터는'glVertexAttrib * Format'과 함께 사용되어야하며 0은'glBindVertexBuffer'와 함께 사용되어야합니다. 또는 어쩌면 내가 한 번 더 대답을 다시 읽어야합니다. – dvd

+0

@dvd : ARB_vertex_attrib_binding 사양에서 복사했습니다. 형식 오프셋은 특정 속성에 대한 버퍼 바인딩의 오프셋으로부터의 오프셋이며 고정 된 상한을 갖습니다. 버퍼 바인딩의 오프셋은 버퍼 객체의 시작에서 해당 바인딩에 대한 0 위치까지의 오프셋입니다. 인터리빙과'Format' 명령에 대해서는 위의 부분을 참조하십시오. –

+0

감사합니다! 나는 (n 번 째) 대답을 다시 읽었으며 이제는 더 명확 해졌습니다! – dvd

관련 문제