计算WebGL中的uniforms变量使用数
在使用Three.js为人体模型加载皮肤材料时,启用了skinning:true的参数。
有时候会导致GL编译错误,提示“too many uniforms”。下面的文章有助于理解错误原因和检测uniforms的使用情况。
For a recent consulting project I was attempting to render some fairly complex skeletal animations in WebGL on Firefox and Chrome. I quickly ran into a situation where the animation was rendering on Linux and Mac computers, but not on Windows. All the test machines had up-to-date graphics drivers, but the Windows machines threw a “too many uniforms” error when attempting to link the shader programs. So what does this error mean, and how do we fix it? In this post I’ll share the results of my research that allowed me to get everything working.
Uniforms
So what is a uniform? According to the OpenGL wiki, a uniform is “a global GLSL variable declared with the ‘uniform’ storage qualifier.” To be a little more specific: your shader executes on the GPU, which is physically distinct from the rest of the computer, separated by a bus. This means there’s a very limited amount of data that can be shared quickly between the GPU and your web browser. A uniform variable is what lets you declare “this is data that is exposed externally for transport over the bus”, and there’s a specific API on the JavaScript/WebGL side that lets you get and set this data. For example, you might declare the following variables at the start of a GLSL vertex shader:
uniform mat4 uMVMatrix; uniform mat4 uPMatrix; uniform vec3 someVector; varying vec4 vColor;
That shader has, you guessed it, three uniforms. Later, in your JavaScript program code, you might access, transform, and return one of the uniforms like this:
// get a copy of our vec3 uniform var vec3Uni = gl.getUniformLocation(shaderProgram, "someVector"); // increment the x component of the vector by 10 vec3Uni[0] += 10; // write it back to the GPU gl.uniform3fv(shaderProgram, vec3Uni);
If you’ve ever used web workers, this data flow should be familiar to you. This is for good reason: we’re simply doing parallel computing here, but instead of the shader code running on a spawned worker thread, it’s running on the GPU.
How Many is Too Many?
So, we know what a uniform is: it’s data that’s transferred back and forth between the GPU. Because the GPU is physically distinct from the rest of the computer, we are severely limited in the amount of data that we can quickly push back and forth over the bus between the CPU and the GPU. So we need to watch our uniform usage very closely, and we need to understand that different combinations of graphics cards and graphics drivers allow for different uniform caps.
When it seems like I’m hitting a performance limit, the first thing I do is benchmark my system. Fortunately, webgl-bench is a great website that can benchmark your WebGL performance in seconds, giving you a lot of statistics that can be valuable for debugging. For this problem in particular, we’re interested in the value returned forMAX_VERTEX_UNIFORM_VECTORS. As you might imagine, having more active uniforms than the maximum is what causes a “too many uniforms” error.
Running the benchmark on Mac, Linux, and Windows machines highlighted something odd: while most of the Mac and Linux machines capped out at 1024 uniforms, the Windows computers supported only 256 vertex uniforms, even if they had amazing graphics cards and the latest drivers.
This is due to a peculiarity in how Chrome and Firefox render WebGL. In Mac and Linux, the browsers render WebGL directly, using native OpenGL drivers. The problem on Windows is that the native OpenGL drivers are notoriously unstable, causing sometimes massive performance hits. To avoid these problems, the Windows versions of both browsers default to using ANGLE, which translates OpenGL ES 2.0 API calls to DirectX 9 or DirectX 11 API calls.
(Frank Olivier of the IE GPU team informs me that IE11 WebGL supports more than 256 vertex uniforms because it’s already using the DX11 runtime.)
The problem is that the ANGLE configuration currently used by both browsers uses DirectX 9. The DX9 configuration imposes an artificial cap of 256 vertex uniforms in use at any given time, no matter how good the graphics card is. Users can manually disable ANGLE (more info here) but it’s an onerous process only meant for developers; you can’t expect an end user to do it.
Fortunately, an upcoming Chrome release (version 34 or possibly earlier) is going to default to using DirectX 11, which should increase the cap to 1024 maximum vertex uniforms. You can follow the tracking issue for this feature here. Firefox is actively looking into doing the same, but they don’t have a timetable yet.
Counting the Uniforms
So now I needed to figure out how many uniforms I was using with my complex model. Obviously I was using more than 256, but exactly how many more?
For simple shaders you can manually count the number of uniforms by visually inspecting your script. But for most combinations of shaders and dynamic scenes, it’s likely that the number of uniforms in use will scale with the complexity and the number of the models being rendered. We need to dynamically count the active uniforms.
After a bunch of research, I wrote a function does this for you. You pass it a shader program and it returns an object containing useful data.
Here’s the code for the function:
function getProgramInfo(gl, program) { var result = { attributes: [], uniforms: [], attributeCount: 0, uniformCount: 0 }, activeUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS), activeAttributes = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES); // Taken from the WebGl spec: // http://www.khronos.org/registry/webgl/specs/latest/1.0/#5.14 var enums = { 0x8B50: 'FLOAT_VEC2', 0x8B51: 'FLOAT_VEC3', 0x8B52: 'FLOAT_VEC4', 0x8B53: 'INT_VEC2', 0x8B54: 'INT_VEC3', 0x8B55: 'INT_VEC4', 0x8B56: 'BOOL', 0x8B57: 'BOOL_VEC2', 0x8B58: 'BOOL_VEC3', 0x8B59: 'BOOL_VEC4', 0x8B5A: 'FLOAT_MAT2', 0x8B5B: 'FLOAT_MAT3', 0x8B5C: 'FLOAT_MAT4', 0x8B5E: 'SAMPLER_2D', 0x8B60: 'SAMPLER_CUBE', 0x1400: 'BYTE', 0x1401: 'UNSIGNED_BYTE', 0x1402: 'SHORT', 0x1403: 'UNSIGNED_SHORT', 0x1404: 'INT', 0x1405: 'UNSIGNED_INT', 0x1406: 'FLOAT' }; // Loop through active uniforms for (var i=0; i < activeUniforms; i++) { var uniform = gl.getActiveUniform(program, i); uniform.typeName = enums[uniform.type]; result.uniforms.push(uniform); result.uniformCount += uniform.size; } // Loop through active attributes for (var i=0; i < activeAttributes; i++) { var attribute = gl.getActiveAttrib(program, i); attribute.typeName = enums[attribute.type]; result.attributes.push(attribute); result.attributeCount += attribute.size; } return result; }
It returns an object containing the following data:
uniformCount – the total number of currently active uniforms in this program
uniforms – an array containing the name, size, type, and typeName of the each active uniform. In particular, typeName can be useful since otherwise you’d have to look up the integer type value and cross reference it to the enum declarations in the webGL spec
attributeCount, attributes – same as above, but for attributes (attributes are out of scope for this article, but are similarly capped and nice to keep count of)
Here’s some sample output using console.table (more info on that function here) to show the information from the returned object’s uniforms array. This is data from my original, complex model:
Looking at this I was able to immediately discern that we had 288 active vertex uniforms, and most of them were in the joint matrix (aka the complexity of our model’s skeleton). This helped me tell our artist that we needed to reduce the number of joints in the model by about 20% for it to work on Windows machines.
原文链接:
https://bocoup.com/weblog/counting-uniforms-in-webgl


- 相关文章
CentOS6 Apache2.2多站点HTTPS配置
可以使用letsencrypt(certbot)免费证书服务。支持多系统、多站点和多目录,支持wildcard(通配符域名),90天生效,可用定时任务自动更新。需要注意一点的是apache2.4以下版本需要在默认的ssl配置中添加如下的指令:NameVirtualHost
html5跨平台实战-第一周-水平测验-新闻列表页面
这是一个DIV+CSS布局页面的一个实例,主要介绍POSITION定位、导航UL LI的制作、利用浮动原理对页面分栏、分列的页面布局。新闻页面的效果图
WebGL、Asm.js和WebAssembly概念简介
随着HTML技术的发展,网页要解决的问题已经远不止是简单的文本信息,而包括了更多的高性能图像处理和3D渲染方面。这正是要引入WebGL、Asm.js和WebAssembly这些技...
ES6小知识:动态对象键(Dynamic Object Keys)语法简介
在ES5,对象的键(key)总是被解释为字符串。ES6允许我们使用计算的值作为对象的键,使用方括号:[myKey]const
CSS3弹性布局弹性流(flex-flow)属性详解和实例
弹性布局是CSS3引入的强大的布局方式,用来替代以前Web开发人员使用的一些复杂而易错hacks方法(如使用float进行类似流式布局)。其中flex-flow是flex-direction...
深入理解CSS3滤镜(filter)功能和实例详解
CSS3滤镜功能源自SVG滤镜规范,SVG滤镜最早用来给矢量图添加类似PS中像素图的一些特效。
把这个滤镜功能引入到普通HTML元素中可以带来很有趣的效果(模糊、...WebGL 纹理映射模式以及WRAP_S | WRAP_T参数详解
我们在纹理滤镜一文中已经说明了2个重要的纹理参数,用来定义对象缩放时纹理的处理方式:GL_TEXTURE_MIN_FILTERGL_TEXTURE_MAG_FILTER本文讲解其余几个纹理参数...
Three.js入门教程6 - 创建全景图和纹理
全景图非常酷。使用Three.js做一个属于自己的全景图并不是那么困难。要做一个全景图,你需要一个软件用来做一张全景图片。我使用了iPhone上的Microsoft Photosyn...
Three.js入门教程4 - 创建粒子系统动画
嗨,又见面了。这么说我们已经开始学习Three.js了,如果你还没有看过之前三篇教程,建议你先读完。如果你已经读完前面的教程了,你可能会想做一些关于粒子的东西。让我们直面这个话题吧,每个人都爱粒子效果。不管你是否知道,你可以很轻易地创建它们。
Three.js入门教程1 - 基础知识和创建一个红色球体
[ TECHBROOD注:Three.js是一个主流的开源WebGL库,WebGL允许使用JavaScript直接操作GPU,在网页上实现3D效果。
Google的工程师Paul在网站aerotwist.com上...浏览器控制台报JS脚本执行错误:Module is not defined
现在JS分成了两个分支,一部分在服务器端发展如NodeJS,一部分是传统的浏览器运行环境。
有些插件在编写JS代码时,是针对Node编写的,所以直接在浏览器中使...如何基于Canvas来模拟真实雨景Part2:重力掉落和雨滴融合
如何基于Canvas来模拟真实雨景Part1:预备知识和创建基本对象
D3.js读取外部json数据
D3.js是一个很好的数据可视化工具,支持从web服务读取json数据,或者从外部文件如.json, .csv文件中直接读取。由于部分服务比如flickrs上的图文数据服务需要VPN...
如何使用CSS3实现一个3D商品标签
使用3D缎带形状的标签是常见的一个设计模式,用在商品折扣、文章标题或网站推荐信息上,来突出显示重点内容,吸引用户视觉焦点。实现的方法有2种,一种是使用背...
更多...