为什么在渲染器线程以外的线程上实例化后不绘制线条?

时间:2013-03-18 18:14:27

标签: android opengl-es-2.0

我正在Android上用OpenGL ES 2.0制作2D游戏,作为学习一堆东西的借口。我绘制了许多线来制作网格,但设置所有内容需要时间,我想在设置稍后将绘制的对象时渲染加载屏幕。

在尝试解决这个问题时,我遇到了一个我无法理解的问题所以我的问题是为什么我会看到这种行为。

代码:

这是其实例代表每一行的类:

这只是我在网上发现的一个稍微修改过的例子。调用Thread.sleep只是为了增加创建它所需的时间,这样我就可以显示我面临的问题。

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.opengl.GLES20;

public class LineExample {

    private final String vertexShaderCode =
                "attribute vec4 vPosition;" +
                "void main() {" +
                "   gl_Position = vPosition;" +
                "}";

    private final String fragmentShaderCode =
                "precision mediump float;" +
                "void main() {" +
                "  gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);" +
                "}";

    private int mProgram;
    private FloatBuffer vertexBuffer;
    private ByteBuffer bb;



    // number of coordinates per vertex in this array
    static final int COORDS_PER_VERTEX = 2;
    private final int vertexStride = COORDS_PER_VERTEX * 4; // bytes per vertex
    private float lineCoords[] = new float[2*COORDS_PER_VERTEX];
    private int vertexCount = lineCoords.length / COORDS_PER_VERTEX;

    public LineExample(float[] lineCoords) {

        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        this.lineCoords = lineCoords;

        // initialize vertex byte buffer for shape coordinates
        bb = ByteBuffer.allocateDirect(
        // (# of coordinate values * 4 bytes per float)
                lineCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(lineCoords);
        vertexBuffer.position(0);

        // prepare shaders and OpenGL program
        int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER,
                                                   vertexShaderCode);
        int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,
                                                     fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // create OpenGL program executables

    }

    public void draw() {

        // Add program to OpenGL environment
        GLES20.glUseProgram(mProgram);

        // get handle to vertex shader's vPosition member
        int mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the triangle coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX,
                                     GLES20.GL_FLOAT, false,
                                     vertexStride, vertexBuffer);

        // Draw
        GLES20.glDrawArrays(GLES20.GL_LINES, 0, vertexCount);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
    }

    private static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }

}

这是渲染器:

public class MyRenderer2 implements Renderer {

    LineExample currentLineToRender = null;

    @Override
    public void onDrawFrame(GL10 unused) {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        if (currentLineToRender!=null){
            currentLineToRender.draw();
        }
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        //initializeLines();
        initializeLinesSameThread();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {
        GLES20.glViewport(0, 0, width, height);
        GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    }

    private void initializeLines(){
        float[] waitingLineVerts = {-0.5f,0.0f , 0.5f,0.0f};
        currentLineToRender = new LineExample(waitingLineVerts);
        new Thread(){
            public void run() {
                float[] doneLineVerts = {0.0f,-0.5f , 0.0f,0.5f};
                currentLineToRender = new LineExample(doneLineVerts);
            }
        }.start();
    }

    private void initializeLinesSameThread(){
        float[] waitingLineVerts = {-0.5f,0.0f , 0.5f,0.0f};
        currentLineToRender = new LineExample(waitingLineVerts);
        float[] doneLineVerts = {0.0f,-0.5f , 0.0f,0.5f};
        currentLineToRender = new LineExample(doneLineVerts);
    }

}

目标是看到水平线5秒(类似于加载屏幕)然后看到一条垂直线(类似于实际的游戏网格)。但是,使用这段代码,我得到一个黑暗的屏幕约10秒,然后绘制第二行。

我认为线程正忙于初始化代码所以它没有绘制第一行,所以为了让它到达 onDrawFrame 方法并绘制第一行,我决定调用在另一个线程上初始化第二行(通过在 onSurfaceCreated 上调用 initializeLines()而不是 initializeLinesSameThread(),但通过这样做,我只看到第一行,我从来没有看到第二行。

我的猜测是,当调用行的draw方法时,渲染器线程无法访问其他线程中完成的某些操作。

我的问题是为什么会发生这种情况?

(如果你能给我一个如何实现我想做的建议,我将不胜感激)

1 个答案:

答案 0 :(得分:1)

通常,您应该在同一个线程中执行所有OpenGL调用。否则你会得到意想不到的行为。