Comment linéariser correctement la profondeur dans OpenGL ES dans iOS?

J'essaye de rendre une scène forrest pour une application iOS avec OpenGL. Pour le rendre un peu plus sympa, j'aimerais implémenter un effet de profondeur dans la scène. Cependant, j'ai besoin d'une valeur de profondeur linéarisée à partir du tampon de profondeur OpenGL pour le faire. Actuellement j'utilise un calcul dans le fragment shader (que j'ai trouvé ici ).

Par conséquent, mon fragment de terrain shader ressemble à ceci:

#version 300 es precision mediump float; layout(location = 0) out lowp vec4 out_color; float linearizeDepth(float depth) { return 2.0 * nearz / (farz + nearz - depth * (farz - nearz)); } void main(void) { float depth = gl_FragCoord.z; float linearized = (linearizeDepth(depth)); out_color = vec4(linearized, linearized, linearized, 1.0); } 

Cependant, cela entraîne la sortie suivante:

résultat résultant Comme vous pouvez le voir, le "plus loin" vous évite, plus "rayé" la valeur de profondeur résultante obtient (surtout derrière le navire). Si la tuile de terrain est proche de la camera, la sortie est en quelque sorte d'accord.

J'ai même essayé un autre calcul:

 float linearizeDepth(float depth) { return 2.0 * nearz * farz / (farz + nearz - (2.0 * depth - 1.0) * (farz - nearz)); } 

ce qui a entraîné une valeur trop élevée, donc je l'ai réduit en divisant:

 float linearized = (linearizeDepth(depth) - 2.0) / 40.0; 

deuxième sortie résultante

Néanless, il a donné un résultat similaire.

Alors, comment réaliser une transition lisse et linéaire entre le plan proche et le plan lointain, sans bandes? Est-ce que quelqu'un a eu un problème similaire?

    le problème est que vous stockez des valeurs non linéaires qui sont tronquées alors quand vous jetez un coup d'oeil sur les valeurs de profondeur plus tard, vous obtenez un résultat saccadé parce que vous perdez de la précision plus vous êtes loin du plan znear. Peu importe ce que vous évaluez, vous n'obtiendrez pas de meilleurs résultats à less:

    1. Perte de précision inférieure

      Vous pouvez modifier les valeurs znear,zfar pour les rapprocher. agrandir znear autant que vous le pouvez afin que la zone plus précise couvre plus de votre scène.

      Une autre option est d'utiliser plus de bits par tampon de profondeur (16 bits est trop faible) pas sûr si vous pouvez le faire dans OpenGL ES mais en OpenGL standard, vous pouvez utiliser 24,32 bits sur la plupart des maps.

    2. utiliser un tampon de profondeur linéaire

      Donc stocker les valeurs linéaires dans le tampon de profondeur. Il y a deux façons. L'un est la profondeur de calcul donc après toutes les opérations sous-jacentes, vous obtiendrez une valeur linéaire.

      Une autre option consiste à utiliser une texture / FBO séparée et à stocker directement les profondeurs linéaires. Le problème est que vous ne pouvez pas utiliser son contenu dans le même passage de rendu.

    [Edit1] Tampon de profondeur linéaire

    Pour linéariser le tampon de profondeur lui-même (et pas seulement les valeurs qui en découlent), essayez ceci:

    Sommet:

     varying float depth; void main() { vec4 p=ftransform(); depth=pz; gl_Position=p; gl_FrontColor = gl_Color; } 

    Fragment:

     uniform float znear,zfar; varying float depth; // original z in camera space instead of gl_FragCoord.z because is already truncated void main(void) { float z=(depth-znear)/(zfar-znear); gl_FragDepth=z; gl_FragColor=gl_Color; } 

    Tampon de profondeur non linéaire linéarisé côté CPU (comme vous le faites): CPU

    Côté GPU du tampon de profondeur linéaire (comme vous devriez): GPU

    Les parameters de la scène sont:

     // 24 bits per Depth value const double zang = 60.0; const double znear= 0.01; const double zfar =20000.0; 

    et une simple plaque pivotante couvrant le champ de vision de la profondeur totale. Les images de glReadPixels(0,0,scr.xs,scr.ys,GL_DEPTH_COMPONENT,GL_FLOAT,zed); sont sockets par glReadPixels(0,0,scr.xs,scr.ys,GL_DEPTH_COMPONENT,GL_FLOAT,zed); et transformé en texture 2D RVB côté CPU . Puis rendu comme QUAD unique couvrant tout l'écran sur les masortingces d'unités …

    Maintenant, pour get la valeur de profondeur d'origine à partir du tampon de profondeur linéaire, procédez comme suit:

     z = znear + (zfar-znear)*depth_value; 

    J'ai utilisé les trucs anciens juste pour garder ça simple, alors portez le vers votre profil …

    Attention, je ne code pas en OpenGL ES ni en IOS donc j'espère ne pas avoir manqué quelque chose en rapport avec ça (je suis habitué à Win et PC).

    Pour montrer la différence, j'ai ajouté une autre plaque pivotée à la même scène (pour qu'elles se croisent) et utilise une sortie colorée (aucune profondeur n'obtenant plus):

    couper

    Comme vous pouvez le voir, le tampon de profondeur linéaire est beaucoup mieux (pour les scènes couvrant une grande partie du champ de vision).