2017-06-17 2 views
0

En utilisant Android Studio, mon code rend un tableau de flottants comme texture transmise à GLSL avec un flottant par texel dans la plage de 0 à 1, comme une texture en niveaux de gris. Pour cela j'utilise GL_LUMINANCE comme internalFormat et le format pour glTexImage2D et GL_FLOAT pour le type. Exécution de l'application sur un émulateur de périphérique Android fonctionne bien (qui utilise le GPU de mon PC), mais sur un appareil réel (Samsung Galaxy S7) appelant glTexImage2D donne erreur 1282, GL_INVALID_OPERATION. J'ai pensé que cela pourrait être un problème avec la non puissance de deux textures, mais la largeur et la hauteur sont certainement des puissances de deux.OpenGL ES 2 en utilisant GL_LUMINANCE avec GL_FLOAT sur les appareils Android

Le code utilise Jos Stam fluid simulation C code (compilé avec le NDK, non porté) qui fournit des valeurs de densité pour une grille. MSizeN est la largeur (identique à la hauteur) de la grille de simulation de fluide, bien que 2 lui soit ajouté par le fluide sim pour les conditions aux limites, de sorte que la largeur de la matrice renvoyée est mSizeN + 2; 128 dans ce cas.

Le système de coordonnées est configuré comme une projection orthographique avec 0,0,0,0 en haut à gauche de l'écran, 1,0,1,0 en bas à droite. Je dessine juste un quad complet et j'utilise la position interpolée à travers le quad dans GLSL comme coordonnées de texture pour le tableau contenant les valeurs de densité. Nice moyen facile de le rendre.

Ceci est la classe de rendu.

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); 
    } 
} 

Voici les fonctions d'aide de texture.

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)); 
} 

Fragment shader.

precision mediump float; 

uniform sampler2D u_aDensity; 
uniform int u_GridSize; 

varying vec4 v_Color; 
varying vec4 v_Position; 

void main() 
{ 
    gl_FragColor = texture2D(u_aDensity, vec2(v_Position.x, v_Position.y)); 
} 

La combinaison de GL_FLOAT et de GL_LUMINANCE n'est-elle pas prise en charge dans OpenGL ES 2?

android emulator pic. Pour ajouter, ai-je raison de dire que chaque valeur à virgule flottante sera réduite à un composant entier de 8 bits lors du transfert avec glTexImage2D (etc), donc la majorité de la précision à virgule flottante sera perdue? Dans ce cas, il peut être préférable de repenser l'implémentation du simulateur pour obtenir un point fixe. Cela peut être fait facilement, Stam le décrit même dans son journal.

Répondre

0

Le tableau 3.4 du spec indique le "format de pixel valide et les combinaisons de types" à utiliser avec glTexImage2D. Pour GL_LUMINANCE, la seule option est GL_UNSIGNED_BYTE.

OES_texture_float est l'extension appropriée à vérifier.

Une approche alternative qui fonctionnerait sur plus de périphériques est d'emballer vos données dans plusieurs canaux d'un RGBA. Here est une discussion sur l'emballage d'une valeur flottante dans un 8888. Notez, cependant, que tous les périphériques OpenGLES2 ne prennent pas en charge les 8888 cibles de rendu, vous devrez peut-être empaqueter dans un 4444.

Ou vous pouvez utiliser OpenGLES 3. Android est jusqu'à 61,3% de support d'OpenGLES3 selon this.

EDIT: En relisant plus attentivement, il n'y a probablement aucun avantage à utiliser une texture supérieure à 8 bits, car lorsque vous écrivez la texture dans gl_FragColor dans votre fragment shader, vous copiez dans un 565 ou 8888 framebuffer, donc toute précision supplémentaire est perdue de toute façon à ce moment-là.

+0

Il semble que le S7 ne supporte malheureusement pas OES_texture_float. Je ne trouve pas la page maintenant, mais j'ai eu l'idée d'utiliser GL_FLOAT avec GL_LUMINANCE à partir d'une liste de diffusion. J'aurais vraiment dû vérifier les documents. Merci cependant, semble avoir besoin d'un changement de format ici. Je vais trouver un moyen, j'espère que celui qui ne fonctionne pas à 2 fps aussi! –