ios objective-c opengl-es glsl

¿Cómo linealizar correctamente la profundidad en OpenGL ES en iOS?



objective-c opengl-es (1)

Estoy tratando de representar una escena más forrest para una aplicación iOS con OpenGL. Para hacerlo un poco más agradable, me gustaría implementar un efecto de profundidad en la escena. Sin embargo, necesito un valor de profundidad linealizado del búfer de profundidad OpenGL para hacerlo. Actualmente estoy usando un cálculo en el fragment shader (que encontré here ).

Por lo tanto, mi sombreador de fragmentos de terreno se ve así:

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

Sin embargo, esto da como resultado el siguiente resultado:

Como puede ver, cuanto más lejos se aleje, más "rayado" será el valor de profundidad resultante (especialmente detrás del barco). Si el mosaico del terreno está cerca de la cámara, la salida es algo aceptable.

Incluso probé otro cálculo:

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

lo que resultó en un valor demasiado alto, así que lo reduje dividiendo:

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

Sin embargo, dio un resultado similar.

Entonces, ¿cómo puedo lograr una transición suave y lineal entre el plano cercano y el lejano, sin rayas? ¿Alguien ha tenido un problema similar?


el problema es que almacena valores no lineales que se truncan, por lo que cuando mira los valores de profundidad más tarde obtiene un resultado entrecortado porque pierde precisión cuanto más lejos está del plano cercano. No importa lo que evalúe, no obtendrá mejores resultados a menos que:

  1. Baja pérdida de precisión

    Puede cambiar los valores znear,zfar para que estén más juntos. amplíe znear tanto como pueda para que el área más precisa cubra más de su escena.

    Otra opción es usar más bits por búfer de profundidad (16 bits es demasiado bajo), no estoy seguro si puede hacer esto en OpenGL ES, pero en OpenGL estándar puede usar 24,32 bits en la mayoría de las tarjetas.

  2. usar tampón de profundidad lineal

    Así que almacene valores lineales en el búfer de profundidad. Hay dos maneras. Una es la profundidad de cálculo, por lo que después de todas las operaciones subyacentes obtendrá un valor lineal.

    Otra opción es usar texturas / FBO separadas y almacenar las profundidades lineales directamente en ellas. El problema es que no puede usar su contenido en el mismo pase de representación.

[Editar1] Búfer de profundidad lineal

Para linealizar el búfer de profundidad en sí (no solo los valores tomados de él) intente esto:

Vértice:

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

Fragmento:

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

Búfer de profundidad no lineal linealizado en el lado de la CPU (como lo hace):

Lineal Depth buffer GPU lado (como debería):

Los parámetros de la escena son:

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

y una placa giratoria simple que cubre todo el campo de visión de profundidad. Las imágenes de la cabina son tomadas por glReadPixels(0,0,scr.xs,scr.ys,GL_DEPTH_COMPONENT,GL_FLOAT,zed); y transformado a textura 2D RGB en el lado de la CPU . Luego se muestra como QUAD único que cubre toda la pantalla en matrices de unidades ...

Ahora para obtener el valor de profundidad original del búfer de profundidad lineal, simplemente haga esto:

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

Usé las cosas antiguas solo para mantener esto simple, así que transfiérelo a tu perfil ...

Tenga cuidado, no codifico en OpenGL ES ni IOS, así que espero no perderme algo relacionado con eso (estoy acostumbrado a Win y PC).

Para mostrar la diferencia, agregué otra placa girada a la misma escena (para que se crucen) y use una salida de color (ya no se obtiene profundidad):

Como puede ver, el búfer de profundidad lineal es mucho mejor (para escenas que cubren gran parte del FOV de profundidad).