2011-01-08 3 views
2

У меня возникла проблема с открытым шейдером Open ES ES 2 и было интересно, может ли кто-нибудь помочь мне.Android OPEN GL ES 2 Фрагментные шейдеры

Я использую шейдер для рисования метабаллов на экране. Это относительно простой и код выглядит следующим образом:

private static final String mFragmentShader = "precision mediump float;\n" 
               + "uniform vec2 balls[" 
               + NUMBER_OF_BALLS 
               + "];\n" 
               + "float sqr(highp float x) { return x*x; }\n" 
               + "void main() {\n" 
               + " vec4 pixelColor = vec4(0.0, 0.0, 0.0, 0.0);\n" 
               + " vec4 color = vec4(0.0, 0.5, 1.0, 1.0);\n" 
               + " for (int i=0; i<" 
               + NUMBER_OF_BALLS 
               + "; ++i) {\n" 
               + " vec2 dist = balls[i] - gl_FragCoord.xy;\n" 
               + " float val = 100.0/(sqr(dist.x) + sqr(dist.y));\n" 
               + " pixelColor += color * val;\n" 
               + " }\n" 
               + " highp float a = smoothstep(0.9, 1.0, pixelColor.a);\n" 
               + " gl_FragColor = vec4(pixelColor.rgb * a, 1.0);\n" 
               + "}\n"; 

Шейдер отлично компилируется и работает хорошо, когда NUMBER_OF_BALLS меньше 15. К сожалению, когда число шаров больше, чем 15, он делает так, как будто в местах расположения всех шары находятся в положении (0,0) (см. here). Я проверил вход в шейдер, и это определенно правильно, поэтому проблема с самим шейдером. Кроме того, если я изменяю точность от mediump до highp, тогда я могу увеличить количество шаров до 20 до того, как рендеринг станет проблемой.

Может ли кто-нибудь сказать мне, что я делаю неправильно?

Редактировать: Вот полный код на всякий случай, если проблема заключается не в том, а в том, что касается самого шейдера фрагмента.

 private static final String mFragmentShader = "precision mediump float;\n" 
               + "uniform vec2 balls[" 
               + NUMBER_OF_BALLS 
               + "];\n" 
               + "float sqr(highp float x) { return x*x; }\n" 
               + "void main() {\n" 
               + " vec4 pixelColor = vec4(0.0, 0.0, 0.0, 0.0);\n" 
               + " vec4 color = vec4(0.0, 0.5, 1.0, 1.0);\n" 
               + " for (int i=0; i<" 
               + NUMBER_OF_BALLS 
               + "; ++i) {\n" 
               + " vec2 dist = balls[i] - gl_FragCoord.xy;\n" 
               + " float val = 100.0/(sqr(dist.x) + sqr(dist.y));\n" 
               + " pixelColor += color * val;\n" 
               + " }\n" 
               + " highp float a = smoothstep(0.9, 1.0, pixelColor.a);\n" 
               + " gl_FragColor = vec4(pixelColor.rgb * a, 1.0);\n" 
               + "}\n"; 



private static final String mVertexShader = "attribute vec4 vPosition;\n" + "void main() {\n" 
              + " gl_Position = vPosition;\n" 
              + "}\n"; 

int gProgram; 
int gvPositionHandle; 

private float[][] balls = new float[NUMBER_OF_BALLS][2]; 

int[] lBalls = new int[NUMBER_OF_BALLS]; 

private static final int FLOAT_SIZE_BYTES = 4; 

private final float[] mQuadVerticesData = { 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, }; 

private FloatBuffer mQuadVertices; 

public MetaballRenderer() { 
    mQuadVertices = ByteBuffer.allocateDirect(mQuadVerticesData.length * FLOAT_SIZE_BYTES) 
           .order(ByteOrder.nativeOrder()) 
           .asFloatBuffer(); 
    mQuadVertices.put(mQuadVerticesData).position(0); 
} 

private int loadShader(int shaderType, String source) { 
    int shader = GLES20.glCreateShader(shaderType); 
    if (shader != 0) { 
     GLES20.glShaderSource(shader, source); 
     GLES20.glCompileShader(shader); 
     int[] compiled = new int[1]; 
     GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); 
     if (compiled[0] == 0) { 
      Log.e(TAG, "Could not compile shader " + shaderType + ":"); 
      Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); 
      GLES20.glDeleteShader(shader); 
      shader = 0; 
     } 
    } 
    return shader; 
} 

private int createProgram(String vertexSource, String fragmentSource) { 
    int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); 
    if (vertexShader == 0) { 
     return 0; 
    } 

    int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); 
    if (pixelShader == 0) { 
     return 0; 
    } 

    int program = GLES20.glCreateProgram(); 
    if (program != 0) { 
     GLES20.glAttachShader(program, vertexShader); 
     checkGlError("glAttachShader"); 
     GLES20.glAttachShader(program, pixelShader); 
     checkGlError("glAttachShader"); 
     GLES20.glLinkProgram(program); 
     int[] linkStatus = new int[1]; 
     GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); 
     if (linkStatus[0] != GLES20.GL_TRUE) { 
      Log.e(TAG, "Could not link program: "); 
      Log.e(TAG, GLES20.glGetProgramInfoLog(program)); 
      GLES20.glDeleteProgram(program); 
      program = 0; 
     } 
    } 
    return program; 
} 


private void init_balls() { 
    for (int i = 0; i < NUMBER_OF_BALLS; ++i) { 
     balls[i][0] = 200 + rand.nextInt(50); 
     balls[i][1] = 200 + rand.nextInt(50); 
    } 
} 

private void checkGlError(String op) { 
    int error; 
    while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { 
     Log.e(TAG, op + ": glError " + error); 
     throw new RuntimeException(op + ": glError " + error); 
    } 
} 

@Override 
public void onDrawFrame(GL10 arg0) { 

    GLES20.glUseProgram(gProgram); 
    checkGlError("glUseProgram"); 

    for (int i = 0; i < NUMBER_OF_BALLS; ++i) { 
     GLES20.glUniform2fv(i, 1, balls[i], 0); 
     checkGlError("glUniform2fv"); 
    } 

    GLES20.glVertexAttribPointer(gvPositionHandle, 2, GLES20.GL_FLOAT, false, 0, mQuadVertices); 
    checkGlError("glVertexAttribPointer"); 
    GLES20.glEnableVertexAttribArray(gvPositionHandle); 
    checkGlError("glEnableVertexAttribArray"); 

    GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 4); 
    checkGlError("glDrawArrays"); 

    GLES20.glUseProgram(0); 
    checkGlError("glUseProgram"); 

} 

@Override 
public void onSurfaceChanged(GL10 glUnused, int width, int height) { 

    // Ignore the passed-in GL10 interface, and use the GLES20 
    // class's static methods instead. 
    gProgram = createProgram(mVertexShader, mFragmentShader); 
    if (gProgram == 0) { 
     return; 
    } 

    gvPositionHandle = GLES20.glGetAttribLocation(gProgram, "vPosition"); 
    checkGlError("glGetAttribLocation"); 
    init_balls(); 
    for (int i = 0; i < NUMBER_OF_BALLS; ++i) { 
     lBalls[i] = GLES20.glGetUniformLocation(gProgram, "balls[" + i + "]"); 
     checkGlError("glGetUniformLocation"); 
    } 

    GLES20.glViewport(0, 0, width, height); 
    checkGlError("glViewport"); 

} 

@Override 
public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) { 

} 

}

+1

Видя, как шейдер фрагмента не влияет на выходное положение объекта, я не вижу, как проблема может существовать в шейдере фрагмента ... Конечно, это должен быть вершинный шейдер, с которым связана ошибка? – Goz

+0

Возможно, вы правы, но Vertex shader еще проще! Я добавил весь фрагмент кода в исходный вопрос - вы узнаете большую часть его из примера рендеринга треугольника Android GL2! –

+0

Не следует ли вызывать glEnableVertexAttribArray перед использованием glVertexAttribPointer? Пожалуйста, сообщите, если вы это поняли. Мне это очень интересно. – Utyi

ответ

0

Этот код:

for (int i = 0; i < NUMBER_OF_BALLS; ++i) { 
     GLES20.glUniform2fv(i, 1, balls[i], 0); 
     checkGlError("glUniform2fv"); 
} 

не прав. Вы передаете i в [0, NUMBER_OF_BALLS-1] в качестве однородных мест, но единообразные местоположения ДОЛЖНЫ быть получены из OpenGL с помощью glGetUniformLocation.

+0

Да, извините, это должен быть GLES20.glUniform2fv (lBalls [i], 1, balls [i], 0); Я установил lBalls [i] в ​​unifrom места в методе onSurfaceChanged(), который вызывается при запуске рендеринга. Исправление этого, к сожалению, приводит к тому же рендерингу proplems :( –

0

Аппаратное обеспечение на телефонах довольно ограничено, и шейдеры имеют фиксированное количество циклов, которые они могут выполнять. Я знаю, когда я программировал шейдеры в XNA, если бы я пытался зацикливаться слишком много раз, я бы получил ошибку, заявив, что в шейдере закончились регистры. Может ли это повлиять на ваш шейдер?

15 значений кажется довольно маленьким, хотя.

+0

это не невозможно, хотя было бы довольно сложно, если бы 15 значений были все, с чем он мог справиться. Я взгляну и посмотрю, вызвало ли это проблему, хотя я бы ожидал, что ошибка распространится через checkGLError, если это так? –

+0

Я не уверен на 100%, поскольку я еще не сделал GLSL. Я бы тоже ожидал ошибки. – SpencerElliott

Смежные вопросы