para - opengl es 3.1 android download
OpenGL ES escribe datos de profundidad para colorear (1)
Estoy tratando de implementar la funcionalidad similar a DepthBuffer usando OpenGL ES en Android.
En otras palabras, estoy tratando de obtener el punto 3D en la superficie que se representa en el punto [x, y] en el dispositivo del usuario. Para hacer eso necesito poder leer la distancia del fragmento en ese punto dado.
Responda en diferentes circunstancias:
Al usar OpenGL normal, puedes lograr esto creando FrameBuffer
y luego adjunta RenderBuffer
o Texture
con componente de profundidad. Ambos enfoques usan glReadPixels , con formato interno de GL_DEPTH_COMPONENT
para recuperar los datos del búfer / textura. Lamentablemente, OpenGL ES solo admite GL_ALPHA
, GL_RGB
y GL_RGBA
como formatos de GL_RGBA
, por lo que no hay forma de llegar directamente a los datos de profundidad del framebuffer.
El único enfoque viable que puedo pensar (y que he encontrado sugerido en Internet) es crear diferentes sombreadores solo para el almacenamiento en profundidad. El sombreador, que se usa solo para la representación en profundidad, debe escribir el valor de gl_FragCoord.z
(= el valor de la distancia que queremos leer) en el gl_FragColor
. Sin embargo:
La verdadera pregunta:
Cuando escribo el valor de gl_FragCoord.z
en gl_FragColor = new Vec4(vec3(gl_FragCoord.z), 1.0);
y luego use glReadPixels
para volver a leer los valores de rgb, esos valores de lectura no coinciden con la entrada.
Lo que he intentado:
Me doy cuenta de que solo hay 24 bits (r, g, b * 8 bits cada uno) que representan los datos de profundidad, así que intenté cambiar el valor devuelto por 8, para obtener 32 bits, pero no pareció funcionar. También traté de cambiar la distancia al aplicarlo a rojo, verde y azul, pero eso no pareció funcionar como se esperaba. He estado tratando de descubrir qué está mal al observar los bits, los resultados en la parte inferior.
fragmentShader.glsl (candidato # 3):
void main() {
highp float distance = 1.0; //currently just 1.0 to test the results with different values.
lowp float red = distance / exp2(16.0);
lowp float green = distance / exp2(8.0);
lowp float blue = distance / exp2(0.0);
gl_FragColor = vec4(red, green, blue, 1.0);
}
Método para leer los valores (= glReadPixels
)
private float getDepth(int x, int y){
FloatBuffer buffer = GeneralSettings.getFloatBuffer(1); //just creates FloatBuffer with capacity of 1 float value.
terrainDepthBuffer.bindFrameBuffer(); //bind the framebuffer before read back.
GLES20.glReadPixels(x, y, 1, 1, GLES20.GL_RGB, GLES20.GL_UNSIGNED_BYTE, buffer); //read the values from previously bind framebuffer.
GeneralSettings.checkGlError("glReadPixels"); //Make sure there is no gl related errors.
terrainDepthBuffer.unbindCurrentFrameBuffer(); //Remember to unbind the buffer after reading/writing.
System.out.println(buffer.get(0)); //Print the value.
}
Observaciones en bits utilizando el sombreador y el método anterior:
Value | Shader input | ReadPixels output
1.0f | 111111100000000000000000000000 | 111111110000000100000000
0.0f | 0 | 0
0.5f | 111111000000000000000000000000 | 100000000000000100000000
Un valor de coma flotante en rangos [0.0, 1.0] se puede empaquetar en un vec3
y desempaquetar desde un vec3
, de la siguiente manera (vea ¿Cómo empaqueta un int de 32 bits en 4, 8 bits en glsl / webgl? Y ¿Cómo convierto entre flotar y vec4, vec3, vec2? ):
vec3 PackDepth( in float depth )
{
float depthVal = depth * (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
vec4 encode = fract( depthVal * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
}
float UnpackDepth( in vec3 pack )
{
float depth = dot( pack, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}
Tenga en cuenta que, en general, la profundidad ( gl_FragCoord.z
/ gl_FragDepth
) se calcula de la siguiente manera (vea GLSL gl_FragCoord.z Cálculo y configuración gl_FragDepth ):
float ndc_depth = clip_space_pos.z / clip_space_pos.w;
float depth = (((farZ-nearZ) * ndc_depth) + nearZ + farZ) / 2.0;
Consulte el siguiente ejemplo de WebGL para PackDepth
:
var ShaderProgram = {};
ShaderProgram.Create = function( shaderList, uniformNames ) {
var shaderObjs = [];
for ( var i_sh = 0; i_sh < shaderList.length; ++ i_sh ) {
var shderObj = this.CompileShader( shaderList[i_sh].source, shaderList[i_sh].stage );
if ( shderObj == 0 )
return 0;
shaderObjs.push( shderObj );
}
var progObj = this.LinkProgram( shaderObjs )
if ( progObj != 0 ) {
progObj.unifomLocation = {};
for ( var i_n = 0; i_n < uniformNames.length; ++ i_n ) {
var name = uniformNames[i_n];
progObj.unifomLocation[name] = gl.getUniformLocation( progObj, name );
}
}
return progObj;
}
ShaderProgram.Use = function( progObj ) { gl.useProgram( progObj ); }
ShaderProgram.SetUniformInt = function( progObj, name, val ) { gl.uniform1i( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniformFloat = function( progObj, name, val ) { gl.uniform1f( progObj.unifomLocation[name], val ); }
ShaderProgram.SetUniform2f = function( progObj, name, arr ) { gl.uniform2fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniform3f = function( progObj, name, arr ) { gl.uniform3fv( progObj.unifomLocation[name], arr ); }
ShaderProgram.SetUniformMat44 = function( progObj, name, mat ) { gl.uniformMatrix4fv( progObj.unifomLocation[name], false, mat ); }
ShaderProgram.CompileShader = function( source, shaderStage ) {
var shaderScript = document.getElementById(source);
if (shaderScript) {
source = "";
var node = shaderScript.firstChild;
while (node) {
if (node.nodeType == 3) source += node.textContent;
node = node.nextSibling;
}
}
var shaderObj = gl.createShader( shaderStage );
gl.shaderSource( shaderObj, source );
gl.compileShader( shaderObj );
var status = gl.getShaderParameter( shaderObj, gl.COMPILE_STATUS );
if ( !status ) alert(gl.getShaderInfoLog(shaderObj));
return status ? shaderObj : 0;
}
ShaderProgram.LinkProgram = function( shaderObjs ) {
var prog = gl.createProgram();
for ( var i_sh = 0; i_sh < shaderObjs.length; ++ i_sh )
gl.attachShader( prog, shaderObjs[i_sh] );
gl.linkProgram( prog );
status = gl.getProgramParameter( prog, gl.LINK_STATUS );
if ( !status ) alert("Could not initialise shaders");
gl.useProgram( null );
return status ? prog : 0;
}
function drawScene(){
var canvas = document.getElementById( "ogl-canvas" );
var vp = [canvas.width, canvas.height];
var depthValue = document.getElementById( "depth" ).value / 1000.0;
document.getElementById( "depth_val" ).innerHTML = depthValue;
gl.viewport( 0, 0, canvas.width, canvas.height );
gl.enable( gl.DEPTH_TEST );
gl.clearColor( 0.0, 0.0, 0.0, 1.0 );
gl.clear( gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT );
ShaderProgram.Use( progDraw );
ShaderProgram.SetUniformFloat( progDraw, "u_depth", depthValue )
gl.enableVertexAttribArray( progDraw.inPos );
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
gl.vertexAttribPointer( progDraw.inPos, 2, gl.FLOAT, false, 0, 0 );
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.drawElements( gl.TRIANGLES, bufObj.inx.len, gl.UNSIGNED_SHORT, 0 );
gl.disableVertexAttribArray( progDraw.pos );
var w = vp[0];
var h = vp[1];
var readPixels = new Uint8Array( w * h * 4);
gl.readPixels( 0, 0, w, h, gl.RGBA, gl.UNSIGNED_BYTE, readPixels );
// flip Y
var pixels = new Uint8Array( w * h * 4);
for ( var i_x = 0; i_x < 4 * w; ++ i_x ) {
for ( var i_y = 0; i_y < h; ++ i_y ) {
var i_src = i_y * w * 4 + i_x;
var i_dest = (h - (i_y+1)) * w * 4 + i_x;
pixels[i_dest] = readPixels[i_src];
}
}
document.getElementById( "red_val" ).innerHTML = pixels[0];
document.getElementById( "green_val" ).innerHTML = pixels[1];
document.getElementById( "blue_val" ).innerHTML = pixels[2];
}
var gl;
var prog;
var bufObj = {};
function sceneStart() {
var canvas = document.getElementById( "ogl-canvas");
gl = canvas.getContext( "experimental-webgl" );
if ( !gl )
return;
progDraw = ShaderProgram.Create(
[ { source : "draw-shader-vs", stage : gl.VERTEX_SHADER },
{ source : "draw-shader-fs", stage : gl.FRAGMENT_SHADER }
],
[ "u_depth" ] );
progDraw.inPos = gl.getAttribLocation( progDraw, "inPos" );
if ( prog == 0 )
return;
var pos = [ -1, -1, 1, -1, 1, 1, -1, 1 ];
var inx = [ 0, 1, 2, 0, 2, 3 ];
bufObj.pos = gl.createBuffer();
gl.bindBuffer( gl.ARRAY_BUFFER, bufObj.pos );
gl.bufferData( gl.ARRAY_BUFFER, new Float32Array( pos ), gl.STATIC_DRAW );
bufObj.inx = gl.createBuffer();
bufObj.inx.len = inx.length;
gl.bindBuffer( gl.ELEMENT_ARRAY_BUFFER, bufObj.inx );
gl.bufferData( gl.ELEMENT_ARRAY_BUFFER, new Uint16Array( inx ), gl.STATIC_DRAW );
setInterval(drawScene, 50);
}
<script id="draw-shader-vs" type="x-shader/x-vertex">
precision mediump float;
attribute vec2 inPos;
varying vec2 vertPos;
void main()
{
vertPos = inPos;
gl_Position = vec4( inPos.xy, 0.0, 1.0 );
}
</script>
<script id="draw-shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vertPos;
uniform float u_depth;
vec3 PackDepth( in float depth )
{
float depthVal = depth * (256.0*256.0*256.0 - 1.0) / (256.0*256.0*256.0);
vec4 encode = fract( depthVal * vec4(1.0, 256.0, 256.0*256.0, 256.0*256.0*256.0) );
return encode.xyz - encode.yzw / 256.0 + 1.0/512.0;
}
float UnpackDepth( in vec3 pack )
{
float depth = dot( pack, 1.0 / vec3(1.0, 256.0, 256.0*256.0) );
return depth * (256.0*256.0*256.0) / (256.0*256.0*256.0 - 1.0);
}
void main()
{
vec3 color = PackDepth( u_depth );
float depth = UnpackDepth( color );
gl_FragColor = vec4( mix( color, vec3(depth), step(vertPos.y, 0.0) ), 1.0 );
}
</script>
<body onload="sceneStart();">
<div style="margin-left: 260px;">
<div style="float: right; width: 100%; background-color: #CCF;">
<form name="inputs">
<table>
<tr> <input type="range" style="width:100%" id="depth" min="0" max="1000" value="500"/> </tr>
<tr> <td> depth </td ><td><span id="depth_val">0</span> </td> </tr>
<tr> <td> red </td ><td><span id="red_val">0</span> </td> </tr>
<tr> <td> green </td ><td><span id="green_val">0</span> </td> </tr>
<tr> <td> blue </td ><td><span id="blue_val">0</span> </td> </tr>
</table>
</form>
</div>
<div style="float: right; width: 260px; margin-left: -260px;">
<canvas id="ogl-canvas" style="border: none;" width="256" height="256"></canvas>
</div>
<div style="clear: both;"></div>
</div>
</body>