我最近用頂點陣列對象(VAO)編寫了一些OpenGL 3.3代碼,稍後在英特爾圖形適配器上進行了測試,我發現我的失望是,元素數組緩衝區綁定顯然不是VAO狀態的一部分,作爲呼叫:VAO和元素陣列緩衝區狀態
glBindVertexArray(my_vao);
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);
沒有效果,而:
glBindVertexArray(my_vao);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, my_index_buffer); // ?
glDrawElements(GL_TRIANGLE_STRIP, count, GL_UNSIGNED_INTEGER, 0);
呈現幾何形狀。我認爲這只是英特爾實現OpenGL時的一個小問題(因爲它在GL_ARB_vertex_array_object(甚至在GL_OES_vertex_array_object)中明確聲明元素數組是保存狀態的一部分),但之後出現在移動NVIDIA Quadro 4200上。這就是不好玩。
它是我的代碼中的驅動程序錯誤,規格錯誤還是錯誤?代碼在GeForce 260和480上完美無缺地工作。
任何人都有類似的經驗?
還有一個奇怪的是,GL_EXT_direct_state_access沒有函數來將元素數組緩衝區綁定到VAO(但它確實有指定頂點attrib數組的函數,因此也有數組緩衝區)。 GPU製造商是否在破壞我們的規格和作弊行爲?或者是什麼?
編輯:
我本來不打算表現出任何的源代碼,因爲我認爲這是沒有必要在這裏。但是,隨着要求,這裏是再現問題的最少測試用例:
static GLuint n_vertex_buffer_object, p_index_buffer_object_list[3];
static GLuint p_vao[2];
bool InitGLObjects()
{
const float p_quad_verts_colors[] = {
1, 0, 0, -1, 1, 0,
1, 0, 0, 1, 1, 0,
1, 0, 0, 1, -1, 0,
1, 0, 0, -1, -1, 0, // red quad
0, 0, 1, -1, 1, 0,
0, 0, 1, 1, 1, 0,
0, 0, 1, 1, -1, 0,
0, 0, 1, -1, -1, 0, // blue quad
0, 0, 0, -1, 1, 0,
0, 0, 0, 1, 1, 0,
0, 0, 0, 1, -1, 0,
0, 0, 0, -1, -1, 0 // black quad
};
const unsigned int p_quad_indices[][6] = {
{0, 1, 2, 0, 2, 3},
{4, 5, 6, 4, 6, 7},
{8, 9, 10, 8, 10, 11}
};
glGenBuffers(1, &n_vertex_buffer_object);
glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
glBufferData(GL_ARRAY_BUFFER, sizeof(p_quad_verts_colors), p_quad_verts_colors, GL_STATIC_DRAW);
glGenBuffers(3, p_index_buffer_object_list);
for(int n = 0; n < 3; ++ n) {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(p_quad_indices[n]), p_quad_indices[n], GL_STATIC_DRAW);
}
glGenVertexArrays(2, p_vao);
glBindVertexArray(p_vao[0]);
{
glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[0]); // red
}
glBindVertexArray(0);
glBindVertexArray(p_vao[1]);
{
glBindBuffer(GL_ARRAY_BUFFER, n_vertex_buffer_object);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(0));
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), p_OffsetInVBO(3 * sizeof(float)));
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[1]); // blue
}
glBindVertexArray(0);
#ifdef BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[2]);
// bind the buffer with the black quad (not inside VAO, should NOT be seen)
#endif // BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER
// [compile shaders here]
return true; // success
}
上面的代碼創建包含三個四邊形,紅色,藍色和黑色的一個頂點緩衝區。然後它創建三個指向單個四邊形的索引緩衝區。然後創建和設置兩個VAO,一個應該包含紅色四邊形索引,另一個應該包含藍色四邊形索引。黑色方塊不應該渲染(假設BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER 爲定義)。
void onDraw()
{
glClearColor(.5f, .5f, .5f, 0);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
glUseProgram(n_program_object);
static int n_last_color = -1;
int n_color = (clock()/2000) % 2;
if(n_last_color != n_color) {
printf("now drawing %s quad\n", (n_color)? "blue" : "red");
n_last_color = n_color;
}
glBindVertexArray(p_vao[n_color]);
#ifdef VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, p_index_buffer_object_list[n_color]); // fixes the problem
#endif // VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
}
這清除視口灰色,且重複的方式呈現藍色或紅色四(它也打印哪一個)。雖然這可以在桌面GPU上工作,但它不能在筆記本GPU上工作(除非定義了VAO_DOESNT_STORE_ELEMENT_ARRAY_BUFFER宏,否則將呈現黑色四元組)。取消定義BIND_BLACK_QUAD_ELEMENT_ARRAY_BUFFER宏會使四元組藍色,因爲藍色索引緩衝區最後被綁定,呈現紅四不管是什麼。
所以我看到它的方式,這是無論是在我的應該怎麼維羅工作,在我的代碼中的錯誤,或者驅動程序錯誤理解一個致命的誤解。
Full source
Binaries (windows, 32 bit)
您的代碼更有可能不是最初將元素緩衝區放入VAO中。你爲什麼不向我們展示你的VAO初始化代碼。 –
哦,來吧,不是那麼愚蠢。另外我說它在GeForce 260/480上工作。在寫評論之前閱讀帖子。我完全有能力調試我的OpenGL代碼。這個問題是關於OpenGL實現和兼容性的區別。 –
只是因爲代碼的作品並不意味着它是*正確*。代碼可以通過偶然的情況或其他方式來工作。它既失敗又成功的NVIDIA驅動程序的事實表明用戶錯誤。如果它在NVIDIA上運行並且在ATI上失敗,反之亦然,那麼它很可能是驅動程序錯誤。但NVIDIA尤其是自相似。所以如果它有時在某些NVIDIA硬件上工作,有時不會,那聽起來像是用戶錯誤。 –