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:
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;
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:
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.
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):
Côté GPU du tampon de profondeur linéaire (comme vous devriez):
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):
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).