为什么我的 OpenGL 片段在更新时停止重绘?
Why has my OpenGL fragment stopped redrawing on an update tick?
我制作了一个初学者 OpenGL 应用程序,它显示了一个从 obj 文件加载的旋转金牛座。它工作得很好,我给我的朋友们看了。昨天我打开应用程序,但视图不再更新。如果我按下主页按钮,然后再次点击该应用程序,它将更新视图,因此我知道主循环处于活动状态。
我回到家并将其插入 android studio 以确认渲染线程正在正常启动并且 view.requestRender();
也被调用。
我不知道为什么它停止工作。
这是我的 android 加载视图和渲染器的片段
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
/**
* Inflate the layout for this fragment
*/
View root = inflater.inflate(R.layout.glfragment, container, false);
GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);
Log.i("Method", "OpenGLFragment::onCreateView()");
Context context = this.getActivity().getApplicationContext();
MyRenderer renderer = new MyRenderer(context);
glview.setEGLContextClientVersion(2);
glview.setRenderer(renderer);
glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
loadMeshes(context); // loads obj file into variable tourus
renderer.addToScene(tourus);
update = new GraphicsThread(glview);
update.start();
return root;
}
@Override
public void onDestroyView() {
if(update != null) {
update.quit();
}
super.onDestroyView();
}
这是图形线程:
public class GraphicsThread extends Thread {
private GLSurfaceView.Renderer renderer;
private GLSurfaceView view;
private boolean isRunning;
public GraphicsThread(GLSurfaceView view) {
this.view = view;
this.isRunning = true;
}
@Override
public void run() {
while (this.isRunning) {
view.requestRender(); // I verified this loop is executed just fine
}
}
public void quit() {
this.isRunning = false;
}
}
这是MyRenderer
代码
public class MyRenderer implements GLSurfaceView.Renderer {
private int program; // default shader program
private List<Mesh> drawables;
private Context context;
private long lastFrameTime;
private RenderInfo info; // MVP and Light matrices
private Bitmap bg;
public MyRenderer(Context context) {
this.drawables = new ArrayList<>();
this.context = context;
this.lastFrameTime = 0;
this.info = null;
}
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
info = new RenderInfo(context);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glEnable(GLES20.GL_CULL_FACE);
}
public void onDrawFrame(GL10 unused){
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
float elapsed = getElapsedTime();
float rot = 10.0f*elapsed;
for(Mesh m : drawables) {
m.rotateX(rot);
m.draw(info, elapsed);
}
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
if(width > 0 && height > 0) {
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
info.resizePerspective(left, right, 1, -1);
}
}
public void addToScene(Mesh mesh) {
drawables.add(mesh);
}
private float getElapsedTime() {
long currentTime = SystemClock.elapsedRealtime();
float elapsed = (float)(currentTime - lastFrameTime) / 1000.0f; //convert ms to seconds
lastFrameTime = currentTime;
return elapsed;
}
}
最后是我绘制网格的方法。 RenderInfo
具有世界信息,如相机 MVP 矩阵和灯光及其矩阵。与问题无关。
public void draw(RenderInfo info, float elapsed) {
if(!loaded) {
Log.d("Mesh", "failed to draw");
return;
};
final int program = info.getProgram();
int position = GLES20.glGetAttribLocation(program, "a_Position");
int normal = GLES20.glGetAttribLocation(program, "a_Normal");
int aColor = GLES20.glGetAttribLocation(program, "a_Color");
//int textcoord = GLES20.glGetAttribLocation(program, "a_TexCoordinate");
GLES20.glEnableVertexAttribArray(position);
GLES20.glVertexAttribPointer(position, 3, GLES20.GL_FLOAT, false, 3 * 4, verticesBuffer);
GLES20.glEnableVertexAttribArray(aColor);
GLES20.glVertexAttribPointer(aColor, 4, GLES20.GL_FLOAT, true, 4*4, colorBuffer);
//GLES20.glEnableVertexAttribArray(normal);
//GLES20.glVertexAttribPointer(normal, 3, GLES20.GL_FLOAT, false, 3 * 4, normalBuffer);
float[] modelMatrix = new float[16];
Matrix.setIdentityM(modelMatrix, 0);
Matrix.setRotateM(modelMatrix, 0, rotX , 1.0f, 0.0f, 0.0f);
//Matrix.setRotateM(modelMatrix, 0, rotY , 0.0f, 1.0f, 0.0f);
//Matrix.setRotateM(modelMatrix, 0, rotZ , 0.0f, 0.0f, 1.0f);
float[] mvpMatrix = info.getMVP(modelMatrix);
int MVP = GLES20.glGetUniformLocation(program, "u_MVP");
GLES20.glUniformMatrix4fv(MVP, 1, false, mvpMatrix, 0);
float[] mvMatrix = info.getMV();
int MV = GLES20.glGetUniformLocation(program, "u_MV");
GLES20.glUniformMatrix4fv(MV, 1, false, mvMatrix, 0);
int lightM = GLES20.glGetAttribLocation(program, "u_LightPos");
GLES20.glUniformMatrix4fv(lightM, 1, false, info.getLightMatrix(), 0);
int lightCol = GLES20.glGetAttribLocation(program, "u_LightCol");
GLES20.glUniform4fv(lightCol, 1, info.getLightColor(), 0);
Log.d("boogio", "u_LightCol is: " + Integer.toString(lightCol));
GLES20.glDrawElements(GLES20.GL_TRIANGLES, facesList.size() * 3, GLES20.GL_UNSIGNED_SHORT, facesBuffer);
GLES20.glDisableVertexAttribArray(position);
GLES20.glDisableVertexAttribArray(aColor);
//GLES20.glDisableVertexAttribArray(normal);
}
TL;DR: 应用程序呈现良好并用于更新。突然,该应用程序不会在视觉上更新(没有重绘)。它仅在应用程序失去焦点并重新获得 1 帧焦点时重绘。不知道为什么。
您没有 post 所有代码,因此不可能重现这种情况。我所能做的就是提供一些建议。
1 - 保留上下文并添加 OpenGL 检查错误
首先,最好告诉 GLView 使用 setPreservceEGLContext. In case this does not resolve the situation, it is better to enable DEBUG trace on OpenGL ES context, using setDebugFlas 方法保存设置的 OpenGL 上下文。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
/**
* Inflate the layout for this fragment
*/
View root = inflater.inflate(R.layout.glfragment, container, false);
GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);
Log.i("Method", "OpenGLFragment::onCreateView()");
Context context = this.getActivity().getApplicationContext();
MyRenderer renderer = new MyRenderer(context);
glview.setEGLContextClientVersion(2);
// added code
glview.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR); // enable log
glview.setPreserveEGLContextOnPause(true); // default is false
glview.setRenderer(renderer);
glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
loadMeshes(context); // loads obj file into variable tourus
renderer.addToScene(tourus);
update = new GraphicsThread(glview);
update.start();
return root;
}
这会在 logcat
中为您的应用程序添加更多调试信息(希望您已经知道它是什么)。我方面你会发现一些错误(带有 OPEN GL 错误的行......一些东西)。
我怀疑您重新打开 activity,OpenGL 上下文尝试使用不再有效的资源(因为 OpenGL 上下文已被破坏)。在上下文重建期间需要管理的典型资源示例是纹理和着色器程序。
2 - 检查图形线程
只需将一些日志调试信息添加到图形线程:
public class GraphicsThread extends Thread {
private GLSurfaceView.Renderer renderer;
private GLSurfaceView view;
private boolean isRunning;
public GraphicsThread(GLSurfaceView view) {
this.view = view;
this.isRunning = true;
Log.i("GraphicsThread", "GraphicsThread::constructor()");
}
@Override
public void run() {
while (this.isRunning) {
Log.i("GraphicsThread", "requestRender");
view.requestRender(); // I verified this loop is executed just fine
}
}
public void quit() {
this.isRunning = false;
Log.i("GraphicsThread", "GraphicsThread::quit()");
}
}
GraphicThread 上的日志信息将帮助您检查线程是否按方面工作。
一些解释
glview.setPreserveEGLContextOnPause(true);
在您破坏上下文(屏幕旋转或在后台 activity 时保留资源):另一种方法是重新创建并重新加载所有资源。我想这是因为,一开始,Android 当 GLView 被销毁时,设备没有内存来保存 GLContext。
RENDERMODE_CONTINUOUSLY
告诉 GLView 尽可能绘制场景(这是我所知道的最好的方法)。
希望我的建议可以帮到你
我制作了一个初学者 OpenGL 应用程序,它显示了一个从 obj 文件加载的旋转金牛座。它工作得很好,我给我的朋友们看了。昨天我打开应用程序,但视图不再更新。如果我按下主页按钮,然后再次点击该应用程序,它将更新视图,因此我知道主循环处于活动状态。
我回到家并将其插入 android studio 以确认渲染线程正在正常启动并且 view.requestRender();
也被调用。
我不知道为什么它停止工作。
这是我的 android 加载视图和渲染器的片段
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
/**
* Inflate the layout for this fragment
*/
View root = inflater.inflate(R.layout.glfragment, container, false);
GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);
Log.i("Method", "OpenGLFragment::onCreateView()");
Context context = this.getActivity().getApplicationContext();
MyRenderer renderer = new MyRenderer(context);
glview.setEGLContextClientVersion(2);
glview.setRenderer(renderer);
glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
loadMeshes(context); // loads obj file into variable tourus
renderer.addToScene(tourus);
update = new GraphicsThread(glview);
update.start();
return root;
}
@Override
public void onDestroyView() {
if(update != null) {
update.quit();
}
super.onDestroyView();
}
这是图形线程:
public class GraphicsThread extends Thread {
private GLSurfaceView.Renderer renderer;
private GLSurfaceView view;
private boolean isRunning;
public GraphicsThread(GLSurfaceView view) {
this.view = view;
this.isRunning = true;
}
@Override
public void run() {
while (this.isRunning) {
view.requestRender(); // I verified this loop is executed just fine
}
}
public void quit() {
this.isRunning = false;
}
}
这是MyRenderer
代码
public class MyRenderer implements GLSurfaceView.Renderer {
private int program; // default shader program
private List<Mesh> drawables;
private Context context;
private long lastFrameTime;
private RenderInfo info; // MVP and Light matrices
private Bitmap bg;
public MyRenderer(Context context) {
this.drawables = new ArrayList<>();
this.context = context;
this.lastFrameTime = 0;
this.info = null;
}
@Override
public void onSurfaceCreated(GL10 unused, EGLConfig config) {
info = new RenderInfo(context);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
GLES20.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
GLES20.glEnable(GLES20.GL_CULL_FACE);
}
public void onDrawFrame(GL10 unused){
// Redraw background color
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
float elapsed = getElapsedTime();
float rot = 10.0f*elapsed;
for(Mesh m : drawables) {
m.rotateX(rot);
m.draw(info, elapsed);
}
}
public void onSurfaceChanged(GL10 unused, int width, int height) {
GLES20.glViewport(0, 0, width, height);
if(width > 0 && height > 0) {
final float ratio = (float) width / height;
final float left = -ratio;
final float right = ratio;
info.resizePerspective(left, right, 1, -1);
}
}
public void addToScene(Mesh mesh) {
drawables.add(mesh);
}
private float getElapsedTime() {
long currentTime = SystemClock.elapsedRealtime();
float elapsed = (float)(currentTime - lastFrameTime) / 1000.0f; //convert ms to seconds
lastFrameTime = currentTime;
return elapsed;
}
}
最后是我绘制网格的方法。 RenderInfo
具有世界信息,如相机 MVP 矩阵和灯光及其矩阵。与问题无关。
public void draw(RenderInfo info, float elapsed) {
if(!loaded) {
Log.d("Mesh", "failed to draw");
return;
};
final int program = info.getProgram();
int position = GLES20.glGetAttribLocation(program, "a_Position");
int normal = GLES20.glGetAttribLocation(program, "a_Normal");
int aColor = GLES20.glGetAttribLocation(program, "a_Color");
//int textcoord = GLES20.glGetAttribLocation(program, "a_TexCoordinate");
GLES20.glEnableVertexAttribArray(position);
GLES20.glVertexAttribPointer(position, 3, GLES20.GL_FLOAT, false, 3 * 4, verticesBuffer);
GLES20.glEnableVertexAttribArray(aColor);
GLES20.glVertexAttribPointer(aColor, 4, GLES20.GL_FLOAT, true, 4*4, colorBuffer);
//GLES20.glEnableVertexAttribArray(normal);
//GLES20.glVertexAttribPointer(normal, 3, GLES20.GL_FLOAT, false, 3 * 4, normalBuffer);
float[] modelMatrix = new float[16];
Matrix.setIdentityM(modelMatrix, 0);
Matrix.setRotateM(modelMatrix, 0, rotX , 1.0f, 0.0f, 0.0f);
//Matrix.setRotateM(modelMatrix, 0, rotY , 0.0f, 1.0f, 0.0f);
//Matrix.setRotateM(modelMatrix, 0, rotZ , 0.0f, 0.0f, 1.0f);
float[] mvpMatrix = info.getMVP(modelMatrix);
int MVP = GLES20.glGetUniformLocation(program, "u_MVP");
GLES20.glUniformMatrix4fv(MVP, 1, false, mvpMatrix, 0);
float[] mvMatrix = info.getMV();
int MV = GLES20.glGetUniformLocation(program, "u_MV");
GLES20.glUniformMatrix4fv(MV, 1, false, mvMatrix, 0);
int lightM = GLES20.glGetAttribLocation(program, "u_LightPos");
GLES20.glUniformMatrix4fv(lightM, 1, false, info.getLightMatrix(), 0);
int lightCol = GLES20.glGetAttribLocation(program, "u_LightCol");
GLES20.glUniform4fv(lightCol, 1, info.getLightColor(), 0);
Log.d("boogio", "u_LightCol is: " + Integer.toString(lightCol));
GLES20.glDrawElements(GLES20.GL_TRIANGLES, facesList.size() * 3, GLES20.GL_UNSIGNED_SHORT, facesBuffer);
GLES20.glDisableVertexAttribArray(position);
GLES20.glDisableVertexAttribArray(aColor);
//GLES20.glDisableVertexAttribArray(normal);
}
TL;DR: 应用程序呈现良好并用于更新。突然,该应用程序不会在视觉上更新(没有重绘)。它仅在应用程序失去焦点并重新获得 1 帧焦点时重绘。不知道为什么。
您没有 post 所有代码,因此不可能重现这种情况。我所能做的就是提供一些建议。
1 - 保留上下文并添加 OpenGL 检查错误
首先,最好告诉 GLView 使用 setPreservceEGLContext. In case this does not resolve the situation, it is better to enable DEBUG trace on OpenGL ES context, using setDebugFlas 方法保存设置的 OpenGL 上下文。
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
/**
* Inflate the layout for this fragment
*/
View root = inflater.inflate(R.layout.glfragment, container, false);
GLSurfaceView glview = (GLSurfaceView)root.findViewById(R.id.surface_view);
Log.i("Method", "OpenGLFragment::onCreateView()");
Context context = this.getActivity().getApplicationContext();
MyRenderer renderer = new MyRenderer(context);
glview.setEGLContextClientVersion(2);
// added code
glview.setDebugFlags(GLSurfaceView.DEBUG_CHECK_GL_ERROR); // enable log
glview.setPreserveEGLContextOnPause(true); // default is false
glview.setRenderer(renderer);
glview.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
loadMeshes(context); // loads obj file into variable tourus
renderer.addToScene(tourus);
update = new GraphicsThread(glview);
update.start();
return root;
}
这会在 logcat
中为您的应用程序添加更多调试信息(希望您已经知道它是什么)。我方面你会发现一些错误(带有 OPEN GL 错误的行......一些东西)。
我怀疑您重新打开 activity,OpenGL 上下文尝试使用不再有效的资源(因为 OpenGL 上下文已被破坏)。在上下文重建期间需要管理的典型资源示例是纹理和着色器程序。
2 - 检查图形线程
只需将一些日志调试信息添加到图形线程:
public class GraphicsThread extends Thread {
private GLSurfaceView.Renderer renderer;
private GLSurfaceView view;
private boolean isRunning;
public GraphicsThread(GLSurfaceView view) {
this.view = view;
this.isRunning = true;
Log.i("GraphicsThread", "GraphicsThread::constructor()");
}
@Override
public void run() {
while (this.isRunning) {
Log.i("GraphicsThread", "requestRender");
view.requestRender(); // I verified this loop is executed just fine
}
}
public void quit() {
this.isRunning = false;
Log.i("GraphicsThread", "GraphicsThread::quit()");
}
}
GraphicThread 上的日志信息将帮助您检查线程是否按方面工作。
一些解释
glview.setPreserveEGLContextOnPause(true);
在您破坏上下文(屏幕旋转或在后台 activity 时保留资源):另一种方法是重新创建并重新加载所有资源。我想这是因为,一开始,Android 当 GLView 被销毁时,设备没有内存来保存 GLContext。RENDERMODE_CONTINUOUSLY
告诉 GLView 尽可能绘制场景(这是我所知道的最好的方法)。
希望我的建议可以帮到你