计算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
- 相关文章
WebGL场景中多相机拍摄的原理和意义
一般而言,3D场景的渲染只需要一个相机,不过借助多相机可以获取一些单相机无法达到的特效。比如突显特定对象并模糊背景。
3D相机渲染的基本原理是依靠颜色...Monaco Editor 编辑器拷贝粘贴功能调用和获取选中文本
有时候需要在monaco editor外部调用编辑器的内置功能比如希望在页面主工具栏实现一些快捷操作。button
HTML5 And Canvas 2D Specs Are Now Feature Complete, First HTML 5.1 Working Draft Published
We’ve been writing about HTML5 for quite a while, but, until today, the actual HTML5 specs and standards were still moving targets. Now, however, the...
CentOS6 Apache2.2用域名配置多虚拟机
在CentOS下使用域名配置多虚拟机的步骤如下:
1. 使用踏得网精选2016年度10大最佳HTML5动画
踏得网精选2016年度最酷最新的HTML5动画集,评选标准为:创意新颖度+实现技术难度+趣味程度。使用一些在线H5生成工具的作品,因其主要使用图片和CSS3套路动画,...
创建非矩形网页页面元素的常用技术和实例代码
非矩形设计正在变成一种时尚,比如波浪形、菱形、三角形等:而随着技术发展,这种设计在技术实现上也变得更容易。本文以最简单的三角形为例,演示使用5种方法来...
常见面试题JavaScript闭包(ES5语法)
JavaScript闭包(Closure)是常见的JS面试题,是否理解闭包是一个简单的区分JS初级和高级程序员的判例。几乎每个JS程序员都在使用闭包,有意或无意间。比如编写一个jQuery鼠标点击处理函数:$(function()
Three.js入门教程1 - 基础知识和创建一个红色球体
[ TECHBROOD注:Three.js是一个主流的开源WebGL库,WebGL允许使用JavaScript直接操作GPU,在网页上实现3D效果。
Google的工程师Paul在网站aerotwist.com上...如何使用WebGL实现空气高温热变形动画特效
我们在炎炎夏日,或者在火堆旁,经常会观察到热源周围空气的不稳定波动现象。本文将讲解如何通过WebGL来实现这个特效。该效果可用于热变形、波浪、水面波光等场...
如何实现SVG clipPath自适应被裁剪对象
CSS3中引入的clip-path(裁剪路径)属性是一个很强大的特性。
clip-path的含义如下图所示,好比剪纸一样,你用剪刀沿着某条路径把目标对象(图像或元素)裁...jQuery Ribbles - 基于WebGL的水面涟漪动效插件
使用jQuery
使用requestAnimationFrame和Canvas给按钮添加绕边动画
要给按钮添加酷炫的绕边动画,可以使用Canvas来实现。基本的思路是创建一个和按钮大小相同的Canvas元素,内置在按钮元素中。然后在Canvas上实现边线环绕的动画。...
如何使用纯CSS3实现一个3D泡沫
要实现一个逼真的泡沫,涉及到比较复杂的光学/物理学知识。我们这里先简化下问题,实现一个相对简单而足够实用的泡沫元素。我们可以把基础的泡沫元素应用在很多场景中,比如水景、泡咖啡、啤酒甚至火焰特效中。泡沫首先是一个圆形元素.bubble
更多...