GLSurfaceView onDrawFrame中glBufferSubData修改顶点丢失问题
问题:GLSurfaceView Native层修改顶点缓冲区导致点丢失
问题描述
使用GLSurfaceView通过Native代码绘制三个点,原本运行正常,但在onDrawFrame中调用glBufferSubData修改点位置时,第一个点丢失,使用glMapBufferRange也存在同样问题。
原代码
Java层Renderer代码
private static class Renderer implements GLSurfaceView.Renderer { private final GLSurfaceView glSurfaceView; private Renderer(GLSurfaceView glSurfaceView) { this.glSurfaceView = glSurfaceView; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { NativeMethod.nSurfaceCreated(glSurfaceView.getContext().getAssets()); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { NativeMethod.nSurfaceChanged(width, height); } @Override public void onDrawFrame(GL10 gl) { NativeMethod.nDrawFrame(); } }
Native层初始代码
JNIEXPORT void JNICALL Java_xxx_NativeMethod_nSurfaceCreated(JNIEnv *env, jclass clazz, jobject asset_manager) { renderer = create_program_from_assets(env, asset_manager, "vertex_shader.glsl", "fragment_shader.glsl"); glUseProgram(renderer); glClearColor(1.0F, 1.0F, 1.0F, 1.0F); glGenVertexArrays(1, vao); glBindVertexArray(vao[0]); glGenBuffers(1, vbo); glBindBuffer(GL_ARRAY_BUFFER, vao[0]); // 错误行 float vertices[] = { -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f, 0.0f, 0.5f, 0.0f }; glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_DYNAMIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 3, (void *) 0); glEnableVertexAttribArray(0); } JNIEXPORT void JNICALL Java_xxx_NativeMethod_nSurfaceChanged(JNIEnv *env, jclass clazz, jint width, jint height) { glViewport(0, 0, width, height); } JNIEXPORT void JNICALL Java_xxx_NativeMethod_nDrawFrame(JNIEnv *env, jclass clazz) { glClear(GL_COLOR_BUFFER_BIT); glDrawArrays(GL_POINTS, 0, 3); }
修改后出现问题的nDrawFrame代码
JNIEXPORT void JNICALL Java_xxx_NativeMethod_nDrawFrame(JNIEnv *env, jclass clazz) { glClear(GL_COLOR_BUFFER_BIT); float vertex_fr[] = {0.0F, 0.0F, 10.0F}; glBufferSubData(GL_ARRAY_BUFFER, 0 , sizeof(vertex_fr), vertex_fr); glDrawArrays(GL_POINTS, 0, 3); }
问题原因及修复方案
1. 致命错误:绑定VBO时误用VAO的ID
在nSurfaceCreated中,glBindBuffer(GL_ARRAY_BUFFER, vao[0]);这一行错误地将VAO(顶点数组对象)的ID传给了glBindBuffer,正确的参数应该是生成的VBO(顶点缓冲区对象)ID vbo[0]。VAO不是缓冲区对象,绑定错误的ID会导致后续所有缓冲区操作都指向无效对象,修改的数据根本没写入正确的VBO。
修复该行:
glBindBuffer(GL_ARRAY_BUFFER, vbo[0]); // 替换为vbo[0]
2. 顶点Z值超出裁剪空间范围
修改后的顶点Z值为10.0f,而默认OpenGL裁剪空间的Z范围是[-1.0, 1.0],该点会被裁剪掉,导致看起来“丢失”。需要将Z值调整到有效范围内,比如0.0f:
float vertex_fr[] = {0.0F, 0.0F, 0.0F};
3. 确保绘制时绑定正确的VAO
虽然VAO会记录绑定的VBO状态,但在onDrawFrame中最好显式绑定目标VAO,避免上下文状态被意外修改:
JNIEXPORT void JNICALL Java_xxx_NativeMethod_nDrawFrame(JNIEnv *env, jclass clazz) { glClear(GL_COLOR_BUFFER_BIT); glBindVertexArray(vao[0]); // 显式绑定正确的VAO float vertex_fr[] = {0.0F, 0.0F, 0.0F}; glBufferSubData(GL_ARRAY_BUFFER, 0 , sizeof(vertex_fr), vertex_fr); glDrawArrays(GL_POINTS, 0, 3); }
总结
问题并非无法在onDrawFrame中修改缓冲区,而是Native代码存在两处关键错误:绑定VBO时用错了ID,以及顶点Z值超出裁剪范围。修正后即可正常修改顶点位置。
内容的提问来源于stack exchange,提问作者L. Swifter
相关产品推荐
相关产品推荐

