计算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
- 相关文章
常用光照类型基本概念工作原理及其计算公式
在三维场景中,原理上物体的渲染效果取决于光照与物体表面的相互作用,对于渲染程序而言,可以通过把一些数学公式应用于像素着色来实现,从而模拟出真实生活中的...
JavaScript语言多编程范式简介
和C++等语言类似,JS支持多范式(paradigms)编程。我们常常混合这些范式来完成一些大型Web项目。JS支持3种编程范式:命令式、面向对象和函数式。命令式(Imperative JavaScript)命令式就是简单的从上而下完成任务,流水账过程式编码风格:function
通过实例深入理解HTML5/CSS3/SVG/WebGL的技术本质
HTTP1.1协议现状、问题和解决方案
HTTP的现状最早的HTTP协议非常简单,只能用来传送文本,方法也只有GET,后来逐步发展到1.1,能够支持多种MIME格式数据(如文本、文件),支持GET,POST,HEAD,OPTI...
Three.js 对象局部坐标转换为世界坐标
在Three.js中进行顶点几何计算时,一个需要注意的地方是,需要统一坐标系。比如你通过Three.js提供的API创建了一个球体网孔对象,那么默认情况下,各网孔顶点的...
三维向量的简单运算和实用意义
在WebGL的实际应用中我们广泛使用向量的几何运算来计算角度、距离,判断点线、点面之间的关系,比如物体之间的碰撞检测。本文简要介绍三维计算机图形学中常用的...
深度贴图(depth map)概念简介和生成流程
Depth map 深度图是一张2D图片,每个像素都记录了从视点(viewpoint)到遮挡物表面(遮挡物就是阴影生成物体)的距离,这些像素对应的顶点对于观察者而言是“可...
Three.js入门教程6 - 创建全景图和纹理
全景图非常酷。使用Three.js做一个属于自己的全景图并不是那么困难。要做一个全景图,你需要一个软件用来做一张全景图片。我使用了iPhone上的Microsoft Photosyn...
Three.js入门教程2 - 着色器(上)
WebGL入门教程4 - 使用纹理贴图(Texture Map)
3D建模和纹理贴图的关系就好比人体和皮肤(或着装)的关系,3D建模用来处理空间属性,而贴图适合用来处理细腻的表面属性。如果不使用贴图,而想在表面达到足够的...
IE各版本CSS Hack(兼容性处理)语法速查表
为了兼容IE各个版本,需要在CSS中添加额外的代码,比如以前常用的_width。之所以工作,是因为浏览器会忽略不能解析的样式规则,因此举个例子来说,把_width写在w...
WebGL入门教程3 - Canvas、Context、API和绘制一个矩形
如何基于Canvas来模拟真实雨景Part2:重力掉落和雨滴融合
inline-block元素设置overflow:hidden属性导致相邻行内元素向下偏移
在表单修改界面中常会使用一个标签、一个内容加一个修改按钮来组成单行界面,如图1所示。那么在表单总长度受限的情况下,当中间的邮箱名称过长时,会遮盖到旁边...
更多...