使用Android Studio,我的代码将一个浮点数组渲染为传递给GLSL的纹理,每个纹素的浮点数为0到1,就像灰度纹理一样。为此,我使用GL_LUMINANCE作为glTexImage2D和GL_FLOAT类型的internalFormat和格式。在Android设备模拟器上运行应用程序工作正常(使用我的PC的GPU),但在真实设备(三星Galaxy S7)上调用glTexImage2D给出错误1282,GL_INVALID_OPERATION。我认为这可能是两个纹理无效的问题,但宽度和高度肯定是2的力量。
代码使用Jos Stam fluid simulation C code(使用NDK编译,未移植),输出网格的密度值。
mSizeN是流体模拟网格的宽度(与高度相同),虽然通过流体模拟为边界条件添加了2,因此返回的数组的宽度为mSizeN + 2;在这种情况下为128。
坐标系设置为正投影,屏幕左上角为0.0,0.0,右下角为1.0,1.0。我只绘制一个全屏四边形并使用GLSL中四边形的插值位置作为包含密度值的数组的纹理坐标。很容易渲染它。
这是渲染器类。
nameHeader
这是纹理辅助函数。
public class GLFluidsimRenderer implements GLWallpaperService.Renderer {
private final String TAG = "GLFluidsimRenderer";
private FluidSolver mFluidSolver = new FluidSolver();
private float[] mProjectionMatrix = new float[16];
private final FloatBuffer mFullScreenQuadVertices;
private Context mActivityContext;
private int mProgramHandle;
private int mProjectionMatrixHandle;
private int mDensityArrayHandle;
private int mPositionHandle;
private int mGridSizeHandle;
private final int mBytesPerFloat = 4;
private final int mStrideBytes = 3 * mBytesPerFloat;
private final int mPositionOffset = 0;
private final int mPositionDataSize = 3;
private int mDensityTexId;
public static int mSizeN = 126;
public GLFluidsimRenderer(final Context activityContext) {
mActivityContext = activityContext;
final float[] fullScreenQuadVerticesData = {
0.0f, 0.0f, 0.0f,
0.0f, 1.0f, 0.0f,
1.0f, 0.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
mFullScreenQuadVertices = ByteBuffer.allocateDirect(fullScreenQuadVerticesData.length * mBytesPerFloat)
.order(ByteOrder.nativeOrder()).asFloatBuffer();
mFullScreenQuadVertices.put(fullScreenQuadVerticesData).position(0);
}
public void onTouchEvent(MotionEvent event) {
}
@Override
public void onSurfaceCreated(GL10 glUnused, EGLConfig config) {
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
String vertShader = AssetReader.getStringAsset(mActivityContext, "fluidVertShader");
String fragShader = AssetReader.getStringAsset(mActivityContext, "fluidFragDensityShader");
final int vertexShaderHandle = ShaderHelper.compileShader(GLES20.GL_VERTEX_SHADER, vertShader);
final int fragmentShaderHandle = ShaderHelper.compileShader(GLES20.GL_FRAGMENT_SHADER, fragShader);
mProgramHandle = ShaderHelper.createAndLinkProgram(vertexShaderHandle, fragmentShaderHandle,
new String[] {"a_Position"});
mDensityTexId = TextureHelper.loadTextureLumF(mActivityContext, null, mSizeN + 2, mSizeN + 2);
}
@Override
public void onSurfaceChanged(GL10 glUnused, int width, int height) {
mFluidSolver.init(width, height, mSizeN);
GLES20.glViewport(0, 0, width, height);
Matrix.setIdentityM(mProjectionMatrix, 0);
Matrix.orthoM(mProjectionMatrix, 0, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f);
}
@Override
public void onDrawFrame(GL10 glUnused) {
GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glUseProgram(mProgramHandle);
mFluidSolver.step();
TextureHelper.updateTextureLumF(mFluidSolver.get_density(), mDensityTexId, mSizeN + 2, mSizeN + 2);
mProjectionMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_ProjectionMatrix");
mDensityArrayHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_aDensity");
mGridSizeHandle = GLES20.glGetUniformLocation(mProgramHandle, "u_GridSize");
mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle, "a_Position");
double start = System.nanoTime();
drawQuad(mFullScreenQuadVertices);
double end = System.nanoTime();
}
private void drawQuad(final FloatBuffer aQuadBuffer) {
// Pass in the position information
aQuadBuffer.position(mPositionOffset);
GLES20.glVertexAttribPointer(mPositionHandle, mPositionDataSize, GLES20.GL_FLOAT, false,
mStrideBytes, aQuadBuffer);
GLES20.glEnableVertexAttribArray(mPositionHandle);
// Attach density array to texture unit 0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mDensityTexId);
GLES20.glUniform1i(mDensityArrayHandle, 0);
// Pass in the actual size of the grid.
GLES20.glUniform1i(mGridSizeHandle, mSizeN + 2);
GLES20.glUniformMatrix4fv(mProjectionMatrixHandle, 1, false, mProjectionMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
}
}
片段着色器。
public static int loadTextureLumF(final Context context, final float[] data, final int width, final int height) {
final int[] textureHandle = new int[1];
GLES20.glGenTextures(1, textureHandle, 0);
if (textureHandle[0] != 0) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glPixelStorei(GLES20.GL_UNPACK_ALIGNMENT, 1);
GLES20.glPixelStorei(GLES20.GL_PACK_ALIGNMENT, 1);
GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_LUMINANCE,
(int) width, (int) height, 0, GLES20.GL_LUMINANCE, GLES20.GL_FLOAT,
(data != null ? FloatBuffer.wrap(data) : null));
}
if (textureHandle[0] == 0)
throw new RuntimeException("Error loading texture.");
return textureHandle[0];
}
public static void updateTextureLumF(final float[] data, final int texId, final int w, final int h) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texId);
GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, (int)w, (int)h, GLES20.GL_LUMINANCE, GLES20.GL_FLOAT, (data != null ? FloatBuffer.wrap(data) : null));
}
在OpenGL ES 2中是否不支持GL_FLOAT和GL_LUMINANCE的组合?
android emulator pic。
编辑: 要添加,我正确地说,当使用glTexImage2D(等)传输时,每个浮点值将减少为8位整数分量,因此大多数浮点精度将丢失?在这种情况下,最好重新考虑模拟器的实现以输出固定点。这可以很容易地完成,Stam甚至在他的论文中描述了它。
答案 0 :(得分:0)
spec的表3.4显示了与glTexImage2D一起使用的“有效像素格式和类型组合”。对于GL_LUMINANCE,唯一的选项是GL_UNSIGNED_BYTE。
OES_texture_float是您需要检查的相关扩展程序。
可以在更多设备上运行的替代方法是将数据打包在RGBA的多个通道中。 Here是关于将浮点值打包到8888中的一些讨论。但是,请注意,并非所有OpenGLES2设备都支持8888渲染目标,您可能需要打包到4444.
或者您可以使用OpenGLES 3.根据this,Android对OpenGLES3的支持率高达61.3%。
编辑:在仔细阅读时,使用任何高于8位纹理可能没有任何好处,因为当您在片段着色器中将纹理写入gl_FragColor时,您将复制到565或8888 framebuffer,所以在那时任何额外的精度都会丢失。