Android OPEN GL ES 2片段着色器

时间:2011-01-08 12:41:31

标签: android opengl-es

我遇到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)(参见{{3 }})。我已经检查了着色器的输入,它肯定是正确的,所以着色器本身一定有问题。此外,如果我将精度从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) {

}

}

2 个答案:

答案 0 :(得分:0)

此代码:

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

不对。您将[0,NUMBER_OF_BALLS-1]中的i作为统一位置传递,但必须使用glGetUniformLocation从OpenGL获取统一位置。

答案 1 :(得分:0)

手机上的硬件相当有限,并且着色器具有固定的循环次数,可以执行。我知道当我在XNA中编写着色器时,如果我试图循环太多次,我会得到一个错误,说明着色器用完了寄存器。这可能也会影响你的着色器吗?

虽然15个值确实很小。