WebGL iOS渲染到浮点纹理

时间:2015-03-03 08:50:21

标签: javascript ios opengl-es webgl

我正在尝试渲染到iOS Safari上的WebGL中的浮点纹理(不在本机应用程序中)。我设法让iOS手动(例如从JavaScript)创建浮点纹理,但是当我创建一个浮点类型的帧缓冲区并使用GPU渲染它时,它不起作用。

我已将问题隔离到呈现浮点纹理的代码,然后将其传递给另一个要显示的着色器。以下是应用于多维数据集的结果:

纹理渲染绘制一个绿色正方形,纹理的一半大小,然后应用于立方体的每一面。

只要绿色方块呈现的纹理类型是标准的无符号字节类型,这在桌面和iOS WebGL上都可以正常工作。但是,将类型更改为浮点会导致iOS设备上的渲染纹理失败(同时继续在桌面浏览器上工作)。纹理是空的,就像没有渲染任何东西一样。

我在这里创建了一个示例项目来演示该问题:https://github.com/felixpalmer/render-2-texture

使用THREE.Renderer.precision设置更改着色器的精度并没有什么区别

1 个答案:

答案 0 :(得分:15)

据我所知,没有任何iOS设备支持渲染到浮点纹理(此时此刻,大多数移动设备也不支持)

我对WebGL规范的理解是

OES_texture_float:允许您创建和读取32位浮点纹理,但渲染到浮点取决于设备。

OES_texture_float_linear:允许线性滤镜浮点纹理。如果这不存在并且OES_texture_float那么你只能使用gl.NEAREST来表示浮点纹理。

除了半浮动纹理外,

OES_texture_half_floatOES_texture_half_float_linear与上面相同。

传统的方法是在WebGL中渲染到浮点纹理,假设存在OES_texture_float,就是创建一个帧缓冲区,为它附加一个浮点纹理,然后调用gl.checkFramebufferStatus。如果它返回gl.FRAMEBUFFER_COMPLETE那么你可以,如果不是那么你就不能。注意:无论下一段如何,此方法都应该有效。

规范已更新,因此您还可以检查WebGL扩展,以确定是否可以渲染到浮点纹理。扩展WEBGL_color_buffer_float应该告诉您可以渲染到浮点纹理。对于半浮点纹理,扩展EXT_color_buffer_half_float是相同的。我知道没有实际显示这些扩展的浏览器,但如果硬件支持它们,它们还支持浮点渲染。

例如,我在Chrome 41上报道的2012年Retina MBP

gl = document.createElement("canvas").getContext("webgl").getSupportedExtensions()
["ANGLE_instanced_arrays", 
 "EXT_blend_minmax", 
 "EXT_frag_depth",  
 "EXT_shader_texture_lod",  
 "EXT_sRGB",  
 "EXT_texture_filter_anisotropic",  
 "WEBKIT_EXT_texture_filter_anisotropic",  
 "OES_element_index_uint",  
 "OES_standard_derivatives",  
 "OES_texture_float",  
 "OES_texture_float_linear",  
 "OES_texture_half_float",  
 "OES_texture_half_float_linear",  
 "OES_vertex_array_object",  
 "WEBGL_compressed_texture_s3tc",  
 "WEBKIT_WEBGL_compressed_texture_s3tc",  
 "WEBGL_debug_renderer_info",  
 "WEBGL_debug_shaders",  
 "WEBGL_depth_texture",  
 "WEBKIT_WEBGL_depth_texture",  
 "WEBGL_lose_context",  
 "WEBKIT_WEBGL_lose_context"]

Firefox 36报告

gl = document.createElement("canvas").getContext("webgl").getSupportedExtensions().join("\n")
"ANGLE_instanced_arrays
EXT_blend_minmax
EXT_frag_depth
EXT_sRGB
EXT_texture_filter_anisotropic
OES_element_index_uint
OES_standard_derivatives
OES_texture_float
OES_texture_float_linear
OES_texture_half_float
OES_texture_half_float_linear
OES_vertex_array_object
WEBGL_compressed_texture_s3tc
WEBGL_depth_texture
WEBGL_draw_buffers
WEBGL_lose_context
MOZ_WEBGL_lose_context
MOZ_WEBGL_compressed_texture_s3tc
MOZ_WEBGL_depth_texture"

浏览器供应商正在忙于实施WebGL 2.0,并且假设gl.checkFramebufferStatus方法正常工作,没有时间花费时间使其他扩展字符串出现。

显然有些iOS设备支持EXT_color_buffer_half_float,因此您可以尝试创建半浮动纹理,将其附加到帧缓冲区并检查其状态,然后查看是否有效。

以下是检查支持的示例。在我的iPadAir2和我的iPhone5s上运行它我

can make floating point textures
can linear filter floating point textures
can make half floating point textures
can linear filter floating point textures
can **NOT** render to FLOAT texture
successfully rendered to HALF_FLOAT_OES texture

这正是我们的预期。

"use strict";

function log(msg) {
  var div = document.createElement("div");
  div.appendChild(document.createTextNode(msg));
  document.body.appendChild(div);
}                  

function glEnum(gl, v) {
  for (var key in gl) {
    if (gl[key] === v) {
      return key;
    }
  }
  return "0x" + v.toString(16);
}

window.onload = function() {
  // Get A WebGL context
  var canvas = document.getElementById("c");
  var gl = canvas.getContext("webgl");
  if (!gl) {
    return;
  }
  
  
  function getExt(name, msg) {
    var ext = gl.getExtension(name);
    log((ext ? "can " : "can **NOT** ") + msg);
    return ext;
  }
  
  var testFloat = getExt("OES_texture_float", "make floating point textures");
  getExt("OES_texture_float_linear", "linear filter floating point textures");
  var testHalfFloat = getExt("OES_texture_half_float", "make half floating point textures");
  getExt("OES_texture_half_float_linear", "linear filter half floating point textures");
  
  gl.HALF_FLOAT_OES = 0x8D61;
  
  // setup GLSL program
  var program = webglUtils.createProgramFromScripts(gl, ["2d-vertex-shader", "2d-fragment-shader"]);
  gl.useProgram(program);

  // look up where the vertex data needs to go.
  var positionLocation = gl.getAttribLocation(program, "a_position"); 
  var colorLoc = gl.getUniformLocation(program, "u_color");

  // provide texture coordinates for the rectangle.
  var positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
     -1.0, -1.0,
      1.0, -1.0,
     -1.0,  1.0,
     -1.0,  1.0,
      1.0, -1.0,
      1.0,  1.0]), gl.STATIC_DRAW);
  gl.enableVertexAttribArray(positionLocation);
  gl.vertexAttribPointer(positionLocation, 2, gl.FLOAT, false, 0, 0);
  
  var whiteTex = gl.createTexture();
  gl.bindTexture(gl.TEXTURE_2D, whiteTex);
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE, 
                new Uint8Array([255, 255, 255, 255]));
  
  function test(format) {
    var tex = gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, format, null);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    
    var fb = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
    var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
    if (status !== gl.FRAMEBUFFER_COMPLETE) {
      log("can **NOT** render to " + glEnum(gl, format) + " texture");
      return;
    }
    
    // Draw the rectangle.
    gl.bindTexture(gl.TEXTURE_2D, whiteTex);
    gl.uniform4fv(colorLoc, [0, 10, 20, 1]);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
    
    gl.bindTexture(gl.TEXTURE_2D, tex);
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);
    
    
    gl.clearColor(1, 0, 0, 1);
    gl.clear(gl.COLOR_BUFFER_BIT);
    
    gl.uniform4fv(colorLoc, [0, 1/10, 1/20, 1]);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
    
    var pixel = new Uint8Array(4);
    gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
    
    if (pixel[0] !== 0 ||
        pixel[1] < 248 ||
        pixel[2] < 248 ||
        pixel[3] < 254) {
      log("FAIL!!!: Was not able to actually render to " + glEnum(gl, format) + " texture");
    } else {
      log("succesfully rendered to " + glEnum(gl, format) + " texture");
    }    
  }
  if (testFloat) {
    test(gl.FLOAT);
  }
  if (testHalfFloat) {
    test(gl.HALF_FLOAT_OES);
  }
}
canvas {
  border: 1px solid black;
}
<script src="//webglfundamentals.org/webgl/resources/webgl-utils.js"></script>
<canvas id="c" width="16" height="16"></canvas>  
<!-- vertex shader -->
<script id="2d-vertex-shader" type="x-shader/x-vertex">
attribute vec4 a_position;

void main() {
   gl_Position = a_position;
}
</script>
<!-- fragment shader -->
<script id="2d-fragment-shader" type="x-shader/x-fragment">
precision mediump float;
uniform vec4 u_color;
uniform sampler2D u_texture;

void main() {
   gl_FragColor = texture2D(u_texture, vec2(0.5, 0.5)) * u_color;
}
</script>