2017-03-07 5 views
0

J'ai un problème au sujet d'un point de lumière mobile dans mon GLES 2.0 application Android:oeil Confondre et dans l'espace monde tout en positionnant la lumière dans GLES 2.0

J'ai une instance de personne marchant autour sur une grande surface. Cette personne doit avoir une lumière au-dessus de sa tête afin d'éclairer correctement une petite zone autour de lui. Puisque l'instance de la lumière a pour parent l'instance de la personne, sa position dans l'espace du monde se déplace exactement comme la personne se déplace (y-Offset +4). Mais à chaque fois que je lance l'application, la lumière n'est pas positionnée sur le dessus et ne bouge pas exactement comme la personne bouge (ou du moins il semble qu'elle ne l'est pas). Il semble que c'est à gauche et devant la personne même si la lumière et la personne partagent les mêmes valeurs x et z. La personne est un cube (pas encore de modèle complexe).

Voici mon code de la méthode de tirage au sort du cube:

public void draw(float[] pPMatrix, float[] pVMatrix) 
{ 
    float[] MVPMatrix = new float[16]; 
    Matrix.setIdentityM(getParent().getModelMatrix(),0); 
    Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL); 


    Matrix.multiplyMM(MVPMatrix, 0, pVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.multiplyMM(MVPMatrix, 0, pPMatrix, 0, MVPMatrix, 0); 

    // Add program to OpenGL ES environment 
    GLES20.glUseProgram(mProgram); 

    // ..... 

    GLES20.glUniformMatrix4fv(LightingProgram.getMVPMatrixHandle(), 1, false, MVPMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVMatrixHandle(), 1, false, pVMatrix, 0); 

    LightObject lo = mParent.getWorld().getLightObjects().get(0); 
    Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0); 
    GLES20.glUniform3f(LightingProgram.getLightPosHandle(), lo.getLightPosInEyeSpace()[0], lo.getLightPosInEyeSpace()[1], lo.getLightPosInEyeSpace()[2]); 

    // Draw the triangle 
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount); 

} 

Vertex Code Shader est:

Fragment Code Shader est:

precision mediump float; 
uniform vec3 u_LightPos; 
uniform sampler2D u_Texture; 

varying vec3 v_Position; 
varying vec4 v_Color; 
varying vec3 v_Normal 
varying vec2 v_TexCoordinate; 

void main() 
{ 

    float distance = length(u_LightPos - v_Position); 

    // Get a lighting direction vector from the light to the vertex. 
    vec3 lightVector = normalize(u_LightPos - v_Position); 

    float diffuse = max(dot(v_Normal, lightVector), 0.0); 
    diffuse = diffuse * (1.0/(1.0 + (0.25 * distance))); 

    // Add ambient lighting 
    diffuse = diffuse + 0.25; 

    gl_FragColor = (diffuse * v_Color * texture2D(u_Texture, v_TexCoordinate)); 
} 

Je pense qu'il a quelque chose à voir avec la façon dont je passe dans la position de l'objet lumineux ... mais je n'arrive pas à comprendre quelle est la bonne façon.

Merci à l'avance ... :-)

================================= =========

!! ÉDITER !! J'ai téléchargé une vidéo du problème: https://dl.dropboxusercontent.com/u/17038392/opengl_lighting_test.mp4 (2MB)

Chaque forme de cette scène est un cube. Lorsque la personne se tient au milieu de la pièce, la lumière n'a aucun effet sur le sol. Si la personne se déplace dans le coin supérieur, la lumière se déplace vers le milieu de la pièce. Maintenant, voici la chose très étrange: puisque la lumière est positionnée au-dessus de la personne, elle illumine les caisses jaunes quand la personne est au milieu de la pièce. COMMENT LA TERRE PEUT-ELLE SE PRODUIRE? ;-)

=====================================

EDIT 2: Ok, donc j'ai essayé de faire ce que vous avez dit. Mais, étant une recrue, je ne parviens pas à le faire correctement:

Mon tirage Méthode pour toute instance de cube:

public void draw(float[] pPMatrix, float[] pVMatrix) 
{ 
    float[] MVPMatrix = new float[16]; 
    float[] normalVMatrix = new float[16]; 
    float[] normalTransposed = new float[16]; 

    // Move object 
    Matrix.setIdentityM(getParent().getModelMatrix(),0); 
    Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL); 
    Matrix.multiplyMM(MVPMatrix, 0, pVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.multiplyMM(MVPMatrix, 0, pPMatrix, 0, MVPMatrix, 0); 

    // create normal matrix by inverting and transposing the modelmatrix 
    Matrix.invertM(normalVMatrix, 0, getParent().getModelMatrix(), 0); 
    Matrix.transposeM(normalTransposed, 0, normalVMatrix, 0); 

    // Add program to OpenGL ES environment 
    GLES20.glUseProgram(mProgram); 

    // ============================ 
    // POSITION 
    // ============================ 
    getVertexBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getPositionHandle(), COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, getVertexBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getPositionHandle()); 

    // ============================ 
    // COLOR 
    // ============================ 
    getColorBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getColorHandle(), COLOR_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getColorBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getColorHandle()); 

    // ============================ 
    // NORMALS 
    // ============================ 
    // Pass in the normal information 
    if(LightingProgram.getNormalHandle() != -1) 
    { 
     getNormalBuffer().position(0); 
     GLES20.glVertexAttribPointer(LightingProgram.getNormalHandle(), NORMAL_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getNormalBuffer()); 
     GLES20.glEnableVertexAttribArray(LightingProgram.getNormalHandle()); 
     checkGLError("normals"); 
    } 

    // ============================ 
    // TEXTURE 
    // ============================ 
    // Set the active texture unit to texture unit 0. 
    GLES20.glActiveTexture(GLES20.GL_TEXTURE0); 

    // Bind the texture to this unit. 
    GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, getTextureHandle()); 

    // Tell the texture uniform sampler to use this texture in the shader by binding to texture unit 0. 
    //GLES20.glUniform1i(mTextureUniformHandle, 0); 
    GLES20.glUniform1i(LightingProgram.getTextureUniformHandle(), 0); 


    getTextureBuffer().position(0); 
    GLES20.glVertexAttribPointer(LightingProgram.getTextureCoordinateHandle(), TEXTURE_DATA_SIZE, GLES20.GL_FLOAT, false, 0, getTextureBuffer()); 
    GLES20.glEnableVertexAttribArray(LightingProgram.getTextureCoordinateHandle()); 

    // Pass the projection and view transformation to the shader 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVPMatrixHandle(), 1, false, MVPMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getMVMatrixHandle(), 1, false, pVMatrix, 0); 
    GLES20.glUniformMatrix4fv(LightingProgram.getNormalHandle(), 1, false, normalTransposed, 0); 

    LightObject lo = mParent.getWorld().getLightObjects().get(0); 
    Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0); 
    GLES20.glUniform3f(LightingProgram.getLightPosHandle(), lo.getLightPosInEyeSpace()[0], lo.getLightPosInEyeSpace()[1], lo.getLightPosInEyeSpace()[2]); 

    // Draw the triangle 
    GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, mVertexCount); 

    // Disable vertex array 
    GLES20.glDisableVertexAttribArray(LightingProgram.getPositionHandle()); 
    GLES20.glDisableVertexAttribArray(LightingProgram.getTextureCoordinateHandle()); 
    if(LightingProgram.getNormalHandle() != -1) 
     GLES20.glDisableVertexAttribArray(LightingProgram.getNormalHandle()); 
    GLES20.glDisableVertexAttribArray(LightingProgram.getColorHandle()); 
    checkGLError("end"); 
} 

Alors, mon code de vertex shaders mis à jour est maintenant:

uniform mat4 u_MVPMatrix;  // A constant representing the combined model/view/projection matrix. 
uniform mat4 u_MVMatrix;  // A constant representing the combined model/view matrix. 
uniform mat4 u_NMatrix;   // combined normal/view matrix ??? 

attribute vec4 a_Position;  // Per-vertex position information we will pass in. 
attribute vec4 a_Color;   // Per-vertex color information we will pass in. 
attribute vec3 a_Normal;  // Per-vertex normal information we will pass in. 
attribute vec2 a_TexCoordinate; // Per-vertex texture coordinate information we will pass in. 

varying vec3 v_Position;  // This will be passed into the fragment shader. 
varying vec4 v_Color;   // This will be passed into the fragment shader. 
varying vec3 v_Normal;   // This will be passed into the fragment shader. 
varying vec2 v_TexCoordinate; // This will be passed into the fragment shader. 

// The entry point for our vertex shader. 
void main() 
{ 
    // Transform the vertex into eye space. 
    v_Position = vec3(u_MVMatrix * a_Position); 

    // Pass through the color. 
    v_Color = a_Color; 

    // Pass through the texture coordinate. 
    v_TexCoordinate = a_TexCoordinate; 

    // Transform the normal's orientation into eye space. 
    v_Normal = vec3(u_NMatrix * vec4(a_Normal, 0.0)); // THIS does not look right... 
    //v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0)); 

    // gl_Position is a special variable used to store the final position. 
    // Multiply the vertex by the matrix to get the final point in normalized screen coordinates. 
    gl_Position = u_MVPMatrix * a_Position; 
} 

Je pensais que peut-être si j'inversais et transposais la matrice du modèle et sauvegardais cela dans une matrice normale, cela pourrait peut-être régler le problème. Mais je pense que je l'ai eu complètement faux ..

Répondre

1

Il semble un peu brouillon. Du code est manquant et vous devez commencer à commenter votre code plus souvent. Même si seulement pour la question SO.

Je ne suis pas sûr de savoir quels sont les paramètres d'entrée de dessin, mais je peux supposer que pPMatrix est une matrice de projection et pVMatrix est une matrice de vue.Puis, dans le code, il y a cette étrange ligne: Matrix.translateM(getParent().getModelMatrix(),0,mXLL, mYLL, mZLL); qui, je suppose, déplace la personne vers sa position actuelle. Je m'attendrais alors à ce que cela fasse partie d'une matrice de vue si vous regardez du point de vue des personnes. Dans tous les cas, cette valeur n'est pas incluse dans le composant que vous utilisez pour la lumière. Puis encore une fois qu'est-ce que getLightPosInWorldSpace retournent?

Si nous essayons de le décomposer un peu, vous avez un character dont la position est définie par sa matrice de modèle. Ceci décrit son emplacement et son orientation dans votre scène. La matrice de projection est définie par la taille de votre vue et le champ de vision. Ensuite, la matrice de vue est calculée à partir de l'orientation des personnes ou de l'endroit où vous regardez la scène (la procédure lookAt est la plus courante).

Peu importe comment vous définissez tout cela, la position de la lumière dépend uniquement de la matrice du modèle de personne. Donc, vous devez multiplier la position de la lumière (0, 4, 0) par la matrice du modèle du personnage. Il se peut donc que ce soit ce que vous vouliez faire dans Matrix.multiplyMV(lo.getLightPosInEyeSpace(), 0, pVMatrix, 0, lo.getLightPosInWorldSpace(), 0);. En faisant cela, vous pouvez réellement tester sur le CPU que le résultat de la position de la lumière est correct en fonction de l'emplacement du caractère. Maintenant, ce que vous devez passer dans votre shader (voir ce que vous utilisez) est en fait la matrice MVP et la matrice du modèle à côté de la source de lumière calculée. La matrice MV ne doit pas être utilisée ici car la position des yeux n'affecte pas l'effet d'éclairage dans votre cas.

Maintenant, le v_Position doit être un fragment calculé dans les coordonnées de la scène, il doit donc être multiplié uniquement par la matrice du modèle. Cela vous donnera essentiellement la coordonnée du fragment (pixel) dans la scène, pas sur la vue. Utilisez maintenant cette position pour obtenir la distance de la lumière et continuer le calcul comme vous l'avez déjà fait.

Ensuite, il semble y avoir un problème avec vos normales. Le calcul des normales ne se fait pas en les multipliant avec une matrice modèle ou une matrice modèle-vue. Imaginez un scénario où vous avez un (0,1,0) normal et vous le multipliez avec une matrice qui a une traduction (10, 0, 0); la normale résultante est alors (10, 1, 0) qui, même normalisée, n'a pas de sens, le résultat doit toujours être (0,1,0) puisque aucune rotation n'a été appliquée. S'il vous plaît regardez dans la façon dont vous générez la matrice pour transformer vos normales qui inclut tous les cas de bord possibles. Mais notez que vous pouvez utiliser la partie supérieure gauche de la matrice (modèle) pour les transformer, y compris la normalisation pour la plupart des situations (ceci échoue dans les cas où la normalisation ne devrait pas être faite et dans les cas où la matrice chacune des axises).

EDIT:

pour examiner plus en théorie ce que vous faites affaire est que nous utilisons habituellement 3 matrices, modèle, vue et projection.

La matrice de projection définit la projection de formes sur votre écran. Dans votre cas, cela devrait dépendre de votre rapport d'affichage et du champ de vision que vous voulez montrer. Il ne devrait jamais affecter l'éclairage, où les formes sont positionnées ou quoi que ce soit au-delà de la façon dont tout cela est mappé sur votre écran.

La matrice de vue est généralement utilisée pour définir la façon dont vous regardez la scène. Dans votre cas, où regardez-vous votre scène depuis et vers quelle direction. Vous devriez probablement utiliser une procédure de recherche pour celui-là. Cette matrice n'affecte pas l'éclairage ou l'une des positions de l'objet, juste comment vous regardez sur les objets.

Ensuite, la matrice du modèle est celle qui est utilisée uniquement pour positionner un objet spécifique dans la scène. La raison pour laquelle nous utilisons est ce que vous pouvez avoir seulement 1 vertex buffer pour toutes les instances de votre objet dessiné. Donc, dans votre cas, vous avez 3 cubes qui devraient tous partager le même tampon de vertex mais sont dessinés à 3 endroits différents parce que leur matrice de modèle est différente. Maintenant, votre personnage n'est pas différent de tout autre objet dans votre scène. Il a un vertex buffer et il a une matrice de modèle. Si vous voulez passer d'une vue à la première personne à l'autre, vous devez seulement multiplier les vecteurs de base naturels avec la matrice du modèle de caractères, puis utiliser ces vecteurs dans une méthode de recherche pour construire une nouvelle matrice de vues. Les vecteurs de base étant probablement location(0,0,0), forward(0,0,1), up(0,1,0). Une fois que vous les avez transformés, vous pouvez alors construire le "centre" comme location+forward. Et ce faisant, vous n'avez toujours aucune différence dans la façon dont l'éclairage fonctionne ou comment les objets sont illuminés, votre vision sur la scène ne devrait avoir aucun effet sur cela.

Ainsi votre lumière est attachée au caractère décalé par un vecteur offset(0,4,0). Cela signifie que la position de la lumière dans la scène est ce même vecteur multiplié par la matrice de votre modèle de personnage puisque cette matrice est celle qui définit la position du personnage dans votre scène. On pourrait même interpréter que la position de la lumière est à (0,0,0) et déplacée vers un emplacement de caractère qui la multiplie avec la matrice du modèle puis traduite par offset donc de nouveau multipliée par la matrice de traduction créée avec ce vecteur. Ceci est important car vous pourriez par exemple construire cette matrice de traduction T et une matrice de rotation R qui tourne autour de l'axe X (par exemple) et ensuite en les multipliant comme modelMatrix*T*R ferait tourner la lumière autour de votre personnage.

Donc, en supposant que vous ayez toutes ces matrices pour tous les objets et que vous ayez une position d'éclairage, vous pouvez commencer à regarder dans les shaders. Vous devez construire la matrice MVP entière car c'est celle qui va mapper les objets sur votre écran. Donc, cette matrice est utilisée uniquement pour le gl_Position. En ce qui concerne le pixel réel dans la scène, vous devez le multiplier avec une matrice de modèle uniquement.

Ensuite, le premier problème est que vous devez également transformer les normales. Vous devez construire la matrice pour eux en inversant puis en transposant la matrice du modèle. The source. Multipliez donc votre normale avec cette matrice au lieu de la matrice du modèle.

Alors maintenant, les choses deviennent plutôt simples. Vous avez calculé la position de la lumière sur la CPU et l'avez envoyée en uniforme. Vous avez la position fragment sur scène et vous avez sa normale. Donc, avec ceux-ci, vous pouvez calculer l'éclairage que vous avez déjà dans votre fragment shader.

Maintenant j'ai un peu menti à propos de la matrice de vue n'ayant aucun effet sur l'éclairage. Cela l'affecte mais pas dans votre cas. Vous n'avez pas mis en œuvre de brillance de sorte que ce composant d'éclairage n'est pas ajouté. Mais quand (si) vous l'ajoutez alors il est plus facile de simplement passer votre position comme un autre uniforme puis d'utiliser une matrice de vue pour obtenir le même résultat.

Il est difficile de dire à partir du code affiché ce que tout est en conflit, mais au moins il semble que la position de la lumière se transforme de manière incorrecte et que les Normales se transforment de manière incorrecte.

EDIT: sur le débogage

Lorsque vous travaillez avec openGL, vous devez être inventive en matière de mise au point. En voyant vos résultats, il y a encore beaucoup de choses qui peuvent être fausses.Il est assez difficile de les vérifier sur le CPU ou d'avoir des logs et le meilleur moyen est généralement de modifier les shaders de la façon dont vous obtiendrez les résultats en vous donnant des informations supplémentaires.

Pour déboguer la position du fragment dans la scène:

Comme mentionné précédemment le fragment en position de scène est pas affectée par la perspective que vous êtes à la recherche de. Donc, cela ne devrait pas dépendre de la vue ou de la matrice de projection. Dans votre fragment shader, il s'agit de la valeur stockée dans v_Position.

Vous devez définir les limites dans lesquelles vous allez tester et celles-ci dépendent de la taille de votre scène (où vous placez vos murs et vos cubes ...).

Vous avez dit que vos murs sont décalés de 25, donc il est prudent de supposer que votre scène sera à portée [-30, 30]. vous voudrez déboguer chaque axe séparément, par exemple, prenons une valeur rouge à -25 et une valeur verte à 25. Pour tester la coordonnée Y (habituellement la hauteur), il suffit d'utiliser gl_FragColor = vec4(1.0-(v_Position.y+30)/60, (v_Position.y+30)/60), 0.0, 1.0). Cela devrait montrer un joli dégradé sur tous les objets où la valeur Y est plus rouge si la couleur est. Ensuite, vous faites la même chose pour les 2 autres composants et pour chacun d'eux, vous devriez obtenir ces jolis dégradés dans chaque direction. Le dégradé doit être visible sur toute la scène, pas seulement pour chacun des objets.

Pour déboguer les Normales de la scène:

Les Normales doivent être cohérentes entre les objets selon la façon dont ils sont confrontés. Puisque tous les objets dans votre cas sont parallèles aux axises et normalisés cela devrait être assez facile. Vous attendez seulement 6 valeurs possibles, donc 2 passes devraient faire le travail (positif et négatif).

Utilisez gl_FragColor = vec4(max(v_Normal.x, 0.0), max(v_Normal.y, 0.0), max(v_Normal.z, 0.0), 1.0) pour les normales positives. Cela montrera tous les visages qui font face à X en rouge, tous ceux qui sont confrontés Y vert positif et tous face positive Z bleu.

Le deuxième test est alors gl_FragColor = vec4(max(-v_Normal.x, 0.0), max(-v_Normal.y, 0.0), max(-v_Normal.z, 0.0), 1.0) qui fait exactement la même chose pour ceux qui sont confrontés à des coordonnées négatives.

Pour debug position de la lumière:

Lorsque le premier test passe la position de la lumière peut être testée en éclairant uniquement les objets à proximité. Donc, le length(u_LightPos - v_Position.xyz) vous montre la distance de la lumière. Vous devez le normaliser de sorte que dans votre scène, utilisez highp float scale = 1.0 - length(u_LightPos - v_Position.xyz)/50.0, puis utilisez-le dans la couleur comme gl_FragColor = vec4 (échelle, échelle, échelle, 1.0). Cela rendra tous les objets proches de la lumière blanche tandis que ceux très lointains seront noirs. Un joli dégradé devrait être montré.

Le point de ces tests:

Vous faites cela parce que votre résultat courant dépend de plusieurs valeurs où tous peuvent être mis sur écoute. En isolant les problèmes, vous trouverez facilement le problème.

Le premier test ignore les normales et la position de la lumière, donc si elle est incorrecte, cela signifie simplement que vous multipliez votre position avec une mauvaise matrice. Vous devez vous assurer que la matrice du modèle est utilisée pour multiplier la position lors du calcul v_Position.

Le deuxième test met à la fois la position dans la scène et la position de la lumière. Vous pourrez toujours voir votre scène mais les couleurs ne seront définies que par vos normales. Si le résultat est incorrect, vous avez d'abord des normales incorrectes ou elles sont incorrectement transformées en utilisant une matrice normale. Vous pouvez même désactiver la multiplication de la matrice normale juste pour voir lequel des deux est incorrect. Si la désactivation ne résout pas le problème, les normales sont incorrectes dans votre vertex buffer.

Le troisième test ignore vos normales et votre logique pour calculer l'effet d'éclairage. Le premier test doit réussir car nous avons toujours besoin de la position de vos modèles. Donc, si ce test échoue, vous ne positionnez probablement pas la lumière correctement. D'autres possibilités sont que les normales sont incorrectes (pour lesquelles vous avez le test) et la troisième est que vous calculez l'effet de lumière de façon incorrecte.

+0

Salut Matic, merci beaucoup d'avoir regardé mon code. Désolé de ne pas fournir suffisamment d'informations. Je n'ai pas pensé à cela ... Je vais répondre à vos questions/hypothèses plus tard. J'ai été occupé aujourd'hui. – AudioGuy

+0

Le jeu n'est pas la 1ère personne. C'est un peu comme Diablo, donc la caméra est au-dessus du personnage. Chaque cube a son espace modèle où sa partie inférieure gauche est à (0,0,0). Ensuite, le cube est traduit à sa position finale dans le monde avec translateM(); La scène se passe bien. Maintenant, si le cube est à (4,1,3) dans le monde (parce que son modèle a été traduit en (4,1,3)), je veux déplacer une lumière pour dire 4,4,3 (parce que toujours Donc, si le cube est à (4,1,3), je mets à jour manuellement la position des lumières à (4, 4, 3) .C'est ce que getLightPosInWorldSpace() retourne sur appel – AudioGuy

+0

I Mais avant de l'utiliser, je vois que vous multipliez la position par pVMatrix Pourquoi? Et puis, qu'est-ce que getLightPosInWorldSpace? –