2013-09-05 54 views
5

Tôi đang sử dụng OpenGL ES 2.0 trên Android, nhưng tôi cho rằng câu hỏi này là một câu hỏi chung về OpenGL.Phạm vi của glVertexAttribPointer và glEnableVertexAttribArray là gì?

Tôi đang tìm hiểu OpenGL và đang cố gắng hiểu chính xác API nào phải được gọi cho mỗi khung hình, so với chỉ một lần. Ban đầu tôi gọi glVertexAttribPointerglEnableVertexAttribArray mỗi khung hình, nhưng khi tôi thay đổi chương trình thử nghiệm của mình để chỉ gọi chúng một lần cho mỗi chương trình Shader, tôi nhận được hành vi không phải là những gì tôi mong đợi.

Có vẻ như glVertexAttribPointerglEnableVertexAttribArray chỉ nên được gọi một lần, thay vì mọi khung hình, vì chúng chỉ có ý nghĩa trong ngữ cảnh của một chương trình Shader cụ thể. Tôi nói điều này vì glVertexAttribPointer lấy làm tham số đầu tiên là GLuint index được trả về từ glGetAttribLocation chỉ định một chuỗi tương ứng với tên biến trong chương trình đổ bóng. Vì vậy, có vẻ như dữ liệu được gắn với một biến "thuộc tính" cụ thể trong một chương trình đổ bóng cụ thể.

Chương trình thử nghiệm của tôi hiển thị hai hình dạng rời nhau (hình tam giác), sử dụng hai chương trình hoàn toàn khác nhau - nguồn đổ bóng phân đoạn khác nhau, nguồn đổ bóng khác nhau, gọi riêng đến glLinkProgram. như mong đợi - tôi thấy cả hai hình dạng được hiển thị chính xác.

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); 

Sau đó, tôi đã sửa đổi các cuộc gọi được thực hiện cho mỗi khung như được hiển thị bên dưới. m_bFirstDraw là đúng cho khung đầu tiên và giả cho khung hình liên tiếp.

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; 

Hành vi tôi thấy với mã trên là cho khung đầu tiên mọi thứ vẫn ổn (điều này có ý nghĩa vì khung đầu tiên chuỗi cuộc gọi giống hệt nhau). Nhưng đối với các khung hình liên tiếp, Shape1 được vẽ bằng các đỉnh của Shape2. Vì vậy, tôi chỉ thấy Shape2 vì nó được vẽ chính xác trên đỉnh của Shape1.

Hãy giúp tôi hiểu điều này ... tại sao việc đặt dữ liệu (hình dạng đỉnh) cho một chương trình ảnh hưởng đến dữ liệu được chương trình khác sử dụng trong khung liên tiếp? Tôi đang thiếu một cái gì đó ...

Trả lời

6

Mặc dù các chương trình GLSL (các trình đổ bóng đỉnh cụ thể) sử dụng các thuộc tính đỉnh dựa trên các số thứ tự chung, bạn không thực sự cung cấp chương trình GLSL với các con trỏ đỉnh.

Những con trỏ này là một phần của trạng thái Vertex Array, trong đó các phiên bản OpenGL mới hơn được xử lý hoàn toàn theo Vertex Array Objects. Nếu bạn muốn mỗi chương trình có bộ con trỏ riêng của nó, những gì tôi sẽ đề nghị là bạn tạo một VAO mà lưu trữ nhà nước và ràng buộc VAO khi bạn ràng buộc chương trình của bạn.

Nếu VAO không có sẵn, bạn sẽ phải thiết lập con trỏ của bạn bằng tay bất cứ khi nào bạn chuyển chương trình GLSL. Lưu ý rằng trong trường hợp này, có một bối cảnh toàn cục trong đó các con trỏ mảng đỉnh, các bộ đệm đỉnh liên kết, các mảng được bật/vô hiệu hóa, vv được lưu trữ. Vì vậy, bạn phải cẩn thận để vô hiệu hóa mảng attrib đỉnh bạn không sử dụng hoặc bạn có thể gió lên làm GL đọc từ bộ nhớ không hợp lệ.

VAO là giải pháp thanh lịch hơn nhiều và bạn nên ưu tiên chúng bất cứ khi nào có sẵn.

+0

Cảm ơn rất nhiều vì câu trả lời Andon. Nhìn vào spec, VAOs không có sẵn trong OpenGL ES 2.0 vì vậy tôi sẽ không thể sử dụng chúng. Vì vậy, chỉ để xác nhận, mà không có VAOs nó là cần thiết để gọi 'glVertexAttribPointer' và' glEnableVertexAttribArray' mỗi khi tôi chuyển đổi chương trình.Nếu tôi có nhiều chương trình chạy với mọi khung hình (theo thứ tự như nhau), điều này có nghĩa là tôi phải gọi những hàm đó cho mọi Chương trình, cho từng khung hình. Đúng không? –

+0

@DavidStone: Không nhất thiết, bạn chỉ phải làm điều này nếu bạn muốn vẽ với một bộ đệm đỉnh khác hoặc nếu bạn cần các con trỏ thuộc tính ánh xạ tới các vị trí khác nhau để cung cấp cho trình đổ bóng với đúng dữ liệu ở vị trí gắn kết phù hợp . –

+0

OK Tôi nghĩ bây giờ tôi đã hiểu. Hai shaders của tôi cần phải hoạt động trên các đỉnh khác nhau và cả hai đều chạy trong mọi frame, theo thứ tự như nhau. Vì vậy, trong trường hợp đó nó là cần thiết để gọi 'glVertexAttribPointer' ** hai lần ** cho mỗi khung hình. Vì lý do gì, khi tôi gọi 'glGetAttribLocation' và để OpenGL chọn chỉ mục thuộc tính đỉnh cho tôi, nó luôn chọn 0 vì vậy tôi đang sử dụng cùng một chỉ mục cho cả hai thuộc tính đỉnh. Tuy nhiên tôi thấy rằng 'glBindAttribLocation' ** là ** có sẵn trong OpenGL ES 2.0 và nó cho phép tôi chỉ định các chỉ số khác nhau cho hai chương trình, tránh vấn đề này. –