I have a problem with an Open GL ES 2 fragment shader and was wondering if anybody could help me.
I'm using the shader to draw metaballs on the screen. It's relatively simple and the code is as follows:
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";
The shader compiles fine and works well when NUMBER_OF_BALLS is less than 15. Unfortunately when number of balls is greater than 15, it renders as if the locations of all the balls are at position (0,0) (see here). I've checked the input to the shader and it is definitely right so there must be a problem with the shader itself. Furthermore if I change the precision from mediump to highp then I can up the number of balls to 20 before the rendering becomes a problem.
Can anyone tell me what I am doing wrong?
Edit: Here is the complete code just in case the problem is not wirth the fragment shader itself.
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");开发者_如何学C
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) {
}
}
This code:
for (int i = 0; i < NUMBER_OF_BALLS; ++i) {
GLES20.glUniform2fv(i, 1, balls[i], 0);
checkGlError("glUniform2fv");
}
Is not right. You are passing i in [0, NUMBER_OF_BALLS-1] as uniform locations, but uniform locations MUST be get from OpenGL using glGetUniformLocation.
The hardware on the phones is fairly limited and the shader's have a fixed amount of cycles they can execute. I know when I was programming shaders in XNA, if I tried to loop too many times I would get an error stating that the shader ran out of registers. Could this possibly be affecting your shader as well?
15 values does seem quite small though.
精彩评论