我遇到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) {
}
}
答案 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个值确实很小。