2013-03-18 42 views
-1

我正在用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方法,繪製的第一線,我決定把第二初始化在另一個線程())通過調用initializeLines(的代替initializeLinesSameThread(onSurfaceCreated)線,但這樣做,我只看到第一行,我從來沒有看到第二個。

我的猜測是,在線程的draw方法被調用時,渲染器線程無法訪問其他線程中完成的某些事情。

我的問題是爲什麼會發生這種情況?

(我會很感激,如果你能給我如何實現我想要做一個建議)

回答

1

一般來說,你應該做的所有OpenGL調用在同一個線程。否則你會得到意想不到的行爲。

+0

感謝您的評論,我覺得我需要解釋一些事情:1.我在另一個線程中調用第二行的原因是允許渲染器的onDrawFrame()繪製第一行(類似於加載屏幕),而我加載所有實際的遊戲資產。 2.我解釋了Thread.sleep只是爲了顯示我面臨的問題,否則即使按照我的預期工作,我也不會看到第一行。 3. vertexCount始終是相同的(2),它是COORDS_PER_VERTEX和lineCoors長度的函數。這可能會更清楚,但我選擇了離開它,因爲它在我從它得到的教程中。 – Smig 2013-03-19 12:11:12

+0

4.算法應該在渲染器的currentLineToRender變量中分配第一行的實例,然後將第二行分配給該變量。如果足夠的時間在兩者之間傳遞,並且如果可以在另一個線程中初始化第二行,則應在屏幕中繪製第一行。否則,我在初始化所有遊戲資產時看不到如何繪製加載屏幕。 – Smig 2013-03-19 12:13:57

+0

對不起,我用線程睡眠重讀了部分,你對vertexCount的東西是正確的。但無論如何,你不能使用另一個線程來初始化第二行。 OpenGL不能在另一個線程中使用。所以所有的gl調用都必須在渲染器線程中完成。 – Dirk 2013-03-19 12:35:48