简体   繁体   English

WebGL iOS渲染到浮点纹理

[英]WebGL iOS render to floating point texture

I'm trying to get rendering to a floating point texture working in WebGL on iOS Safari (not in a native app). 我正在尝试渲染到iOS Safari上的WebGL中的浮点纹理(不是在本机应用程序中)。 I have managed to get iOS to read a manually (eg from JavaScript) created floating point texture, however when I create a framebuffer of floating point type and use the GPU to render into it, it does not work. 我已经设法让iOS手动(例如从JavaScript)创建浮点纹理,但是当我创建一个浮点类型的帧缓冲区并使用GPU渲染它时,它不起作用。

I've isolated the issue to code that renders to a floating point texture, which is then passed to another shader to be displayed. 我已经将问题隔离到渲染到浮点纹理的代码,然后将其传递给另一个要显示的着色器。 Here is what the result looks like applied to a cube: 以下是应用于多维数据集的结果:

The render to texture draws a green square, half the size of the texture, which is then applied to each side of the cube. 纹理渲染绘制绿色正方形,纹理的一半大小,然后应用于立方体的每一侧。

This all works perfectly fine on both desktop and iOS WebGL as long as the type of the texture that the green square is rendered to is the standard unsigned byte type. 只要绿色方块呈现的纹理类型是标准的无符号字节类型,这在桌面和iOS WebGL上都可以正常工作。 However, changing the type to floating point causes the render to texture to fail on iOS devices (while continuing to work on desktop browsers). 但是,将类型更改为浮点会导致iOS设备上的渲染纹理失败(同时继续在桌面浏览器上工作)。 The texture is empty, as if nothing had been rendered to it. 纹理是空的,就好像没有渲染任何东西一样。

I have created an example project here to demonstrate the issue: https://github.com/felixpalmer/render-2-texture 我在这里创建了一个示例项目来演示这个问题: https//github.com/felixpalmer/render-2-texture

Changing the precision of the shaders using the THREE.Renderer.precision setting does not make a difference 使用THREE.Renderer.precision设置更改着色器的精度没有任何区别

As far as I know no iOS device supports rendering to a floating point texture (nor do most mobile devices at this point in time 3/2015) 据我所知,iOS设备不支持渲染浮点纹理(此时此时大多数移动设备也不支持)

My understanding of the WebGL spec is 我对WebGL规范的理解是

OES_texture_float : Allows you to create and read from 32bit float textures but rendering to a floating point is device dependent. OES_texture_float :允许您创建和读取32位浮点纹理,但渲染到浮点是依赖于设备的。

OES_texture_float_linear : Allows linear filter floating point textures. OES_texture_float_linear :允许线性滤镜浮点纹理。 If this doesn't exist and OES_texture_float does then you can only use gl.NEAREST for floating point textures. 如果这不存在且OES_texture_float那么你只能使用gl.NEAREST来表示浮点纹理。

OES_texture_half_float and OES_texture_half_float_linear are the same as above except for half float textures. 除半浮点纹理外, OES_texture_half_floatOES_texture_half_float_linear与上面相同。

The traditional way to see if you can render to a floating point texture in WebGL, assuming OES_texture_float exists, is to create a framebuffer, attach a floating point texture to it, then call gl.checkFramebufferStatus . 假设OES_texture_float存在,看看你是否可以渲染到WebGL中的浮点纹理的传统方法是创建一个帧缓冲区,为它附加一个浮点纹理,然后调用gl.checkFramebufferStatus If it returns gl.FRAMEBUFFER_COMPLETE then you can, if not then you can't. 如果它返回gl.FRAMEBUFFER_COMPLETE那么你可以,如果不是那么你就不能。 Note: This method should work regardless of the next paragraph. 注意:无论下一段如何,此方法都应该有效。

The spec was updated so you could also check WebGL extensions to find out if it's possible to render to a floating point texture. 规范已更新,因此您还可以检查WebGL扩展以查明是否可以渲染到浮点纹理。 The extension WEBGL_color_buffer_float is supposed to tell you you can render to floating point textures. 扩展名WEBGL_color_buffer_float应该告诉你可以渲染到浮点纹理。 The extension EXT_color_buffer_half_float is the same for half float textures. 半浮点纹理的扩展名EXT_color_buffer_half_float是相同的。 I know of no browser that actually shows these extensions though yet they support floating point rendering if the hardware supports it. 我知道没有浏览器实际显示这些扩展,但如果硬件支持它们,它们支持浮点渲染。

For example my 2012 Retina MBP on Chrome 41 reports 例如我在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 reports 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"

The browser vendors are busy implementing WebGL 2.0 and given the gl.checkFramebufferStatus method works there's no pressure to spend time making the other extension strings appear. 浏览器供应商正在忙于实现WebGL 2.0,并且在gl.checkFramebufferStatus方法工作的情况下,没有时间花费时间使其他扩展字符串出现。

Apparently some iOS devices support EXT_color_buffer_half_float so you could try creating a half float texture, attach it to a framebuffer and check its status then see if that works. 显然有些iOS设备支持EXT_color_buffer_half_float因此您可以尝试创建半浮动纹理,将其附加到帧缓冲区并检查其状态,然后查看是否有效。

Here's a sample to check support. 这是检查支持的示例。 Running it on my iPadAir2 and my iPhone5s I get 我在我的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

which is exactly what we expected. 这正是我们的预期。

 "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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM