2013-09-05 17 views
5

Używam OpenGL ES 2.0 na Androida, ale zakładam, że to pytanie jest ogólne pytanie OpenGL.Jaki jest zakres glVertexAttribPointer i glEnableVertexAttribArray?

Uczę się OpenGL i próbuję dokładnie zrozumieć, które interfejsy API muszą być wywoływane dla każdej klatki, a nie tylko raz. Początkowo wywoływałem każdą klatkę w postaci glVertexAttribPointer i glEnableVertexAttribArray, ale kiedy zmieniłem program testowy tak, aby wywoływał je tylko raz dla każdego programu Shader, otrzymywałem zachowanie, którego nie spodziewam się.

Wygląda na to, że glVertexAttribPointer i glEnableVertexAttribArray powinny być wywoływane tylko raz, a nie każda klatka, ponieważ mają one sens tylko w kontekście określonego programu Shader. Mówię to, ponieważ glVertexAttribPointer przyjmuje jako pierwszy parametr GLuint index, który został zwrócony z glGetAttribLocation, który określa ciąg, który odpowiada nazwie zmiennej w programie cieniowania. Wydaje się więc, że dane są powiązane z konkretną zmienną "atrybutową" w konkretnym programie cieniującym.

Mój program testowy renderuje dwa rozłączne kształty (trójkąty), stosując dwa zupełnie różne programy - różne źródła fragment shader, innego źródła vertex shader, oddzielne rozmowy, aby glLinkProgram itp początkowo prowadził następujący kod dla każdej klatki, i wszystko działa zgodnie z oczekiwaniami - widzę prawidłowo oba kształty.

m_glhook.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 

// Shape/Program 1   
m_glhook.glUseProgram(m_iGlProgramId); 
int iPositionLocation = m_glhook.glGetAttribLocation(m_iGlProgramId, "a_Position"); 
m_bfVertices.rewind(); 
m_glhook.glVertexAttribPointer(iPositionLocation, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices); 
m_glhook.glEnableVertexAttribArray(iPositionLocation);  
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); 

// Shape/Program 2 
m_glhook.glUseProgram(m_iGlProgramId2); 
int iPositionLocation2 = m_glhook.glGetAttribLocation(m_iGlProgramId2, "a_Position"); 
m_bfVertices2.rewind(); 
m_glhook.glVertexAttribPointer(iPositionLocation2, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices2); 
m_glhook.glEnableVertexAttribArray(iPositionLocation2); 
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); 

Następnie zmodyfikowałem wywołania wykonane dla każdej ramki, jak pokazano poniżej. m_bFirstDraw ma wartość true dla pierwszej klatki i wartość false dla kolejnych klatek.

m_glhook.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); 

// Shape/Program 1   
m_glhook.glUseProgram(m_iGlProgramId); 
if (m_bFirstDraw) 
{ 
    int iPositionLocation = m_glhook.glGetAttribLocation(m_iGlProgramId, "a_Position"); 
    m_bfVertices.rewind(); 
    m_glhook.glVertexAttribPointer(iPositionLocation, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices); 
    m_glhook.glEnableVertexAttribArray(iPositionLocation); 
} 
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); 

// Shape/Program 2 
m_glhook.glUseProgram(m_iGlProgramId2); 
if (m_bFirstDraw) 
{   
    int iPositionLocation2 = m_glhook.glGetAttribLocation(m_iGlProgramId2, "a_Position"); 
    m_bfVertices2.rewind(); 
    m_glhook.glVertexAttribPointer(iPositionLocation2, 4, GLES20.GL_FLOAT, false, STRIDE, m_bfVertices2); 
    m_glhook.glEnableVertexAttribArray(iPositionLocation2); 
} 
gl.glDrawArrays(GLES20.GL_TRIANGLES, 0, 3); 
m_bFirstDraw = false; 

Zachowanie widzę z powyższym kodzie jest, że dla pierwszej klatki wszystko jest jeszcze w porządku (co ma sens, ponieważ dla pierwszej klatki sekwencji połączeń są dokładnie takie same). Ale dla kolejnych klatek Kształt 1 jest rysowany za pomocą wierzchołków Shape2. Widzę więc tylko Kształt2, ponieważ rysuje się dokładnie na wierzchu Shape1.

Proszę mi pomóc zrozumieć ... dlaczego ustawienie danych (wierzchołków kształtu) dla jednego programu wpływa na dane używane przez inny program w kolejnych klatkach? Brakuje mi czegoś ...

Odpowiedz

6

Chociaż programy GLSL (w szczególności wierzchołki cieniowania) używają atrybutów wierzchołków opartych na ogólnych numerach szczelin, w rzeczywistości nie dostarczasz programu GLSL ze wskaźnikami wierzchołków.

Wskaźniki te są częścią stanu wierzchołków wierzchołków, który w nowszych wersjach OpenGL jest obsługiwany całkowicie przez Vertex Array Objects. Jeśli chcesz, aby każdy program miał własny zestaw wskaźników, sugerowałbym utworzenie VAO, które przechowuje stan i wiąże VAO po związaniu programu.

Jeśli VAO nie są dostępne, będziesz musiał ustawić swoje wskaźniki ręcznie po przełączeniu programów GLSL. Zauważ, że w tym przypadku istnieje globalny kontekst, w którym przechowywane są tablice wskaźników vertex, powiązane bufory vertex, tablice włączone/wyłączone itp. Musisz więc uważać, aby wyłączyć tablice atrybutów wierzchołków, których nie używasz, lub możesz skończyć z odczytaniem GL z nieprawidłowej pamięci.

VAO to znacznie bardziej eleganckie rozwiązanie i należy je faworyzować, gdy tylko będą dostępne.

+0

Wielkie dzięki za odpowiedź Andon. Patrząc na specyfikację, VAO nie są dostępne w OpenGL ES 2.0, więc nie będę mógł ich używać. Tak więc, aby potwierdzić, bez VAO, za każdym razem, gdy przełączam Programy, należy wywoływać 'glVertexAttribPointer' i' glEnableVertexAttribArray'.Jeśli mam wiele programów uruchamianych z każdą ramką (w tej samej kolejności), oznacza to, że muszę wywoływać te funkcje dla każdego programu, na zawsze ramkę. Czy to jest poprawne? –

+0

@DavidStone: Niekoniecznie, musisz to zrobić tylko, jeśli chcesz narysować za pomocą innego bufora wierzchołków lub jeśli potrzebujesz wskaźników atrybutu do mapowania do różnych lokalizacji, aby dostarczyć modułowi cieniującemu odpowiednie dane w odpowiednim miejscu wiązania . –

+0

OK Myślę, że teraz rozumiem. Moje dwa shadery muszą działać na różnych wierzchołkach i oba są uruchamiane w każdej klatce, w tej samej kolejności. W takim przypadku konieczne jest wywołanie 'glVertexAttribPointer' ** dwa razy ** dla każdej ramki. Z jakiegoś powodu, gdy nazywam 'glGetAttribLocation' i pozwalam OpenGL wybrać dla mnie indeks atrybutów wierzchołków, zawsze wybiera 0, więc używam tego samego indeksu dla obu atrybutów wierzchołków. Jednak odkryłem, że 'glBindAttribLocation' ** jest ** dostępna w OpenGL ES 2.0 i pozwala mi określić różne indeksy dla obu programów, unikając tego problemu. –