WebGL 响应上下文丢失解决方案

目录

响应上下文丢失

如何响应上下文丢失

上下文事件

示例程序(RotatingTriangle_contextLost.js)


响应上下文丢失

WebGL使用了计算机的图形硬件,而这部分资源是被操作系统管理,由包括浏览器在内的多个应用程序共享。在某些特殊情况下,如另一个程序接管了图形硬件,或者操作系统进入休眠,浏览器就会失去使用这些资源的权利,并导致存储在硬件中的数据丢失。在这种情况下,WebGL绘图上下文就会丢失。比如,如果你正在一台笔记本电脑或智能手机上运行WebGL程序,如下图(左)所示,然后使其进入休眠状态,通常此时浏览器的控制台会显示一条错误新消息。当你将电脑或手机重新唤醒后,操作系统确实回到了休眠前的状态,但是浏览器中运行的WebGL程序却不见了,如下图(右)所示。网页的背景色是白色,所以浏览器上一片空白。

比如,当你运行RotatingTriangle程序并使计算机进入休眠,控制台上可能会显示:

WebGL error CONTEXT_LOST_WEBGL in uniformMatrix4fv([object WebGLUniformLocation,false,[object Float32Array]]

这条信息表示,系统进入休眠状态前或被唤醒后,浏览器正在调用gl.uniform-Matrix4fv()函数并出错了。这条消息的具体内容依赖于进入上下文丢失时程序正在做什么。这一节就来解释如何处理上下文丢失的问题。

如何响应上下文丢失

如前所述,在某些情况下,上下文可能会丢失。实际上,WebGL提供了两个事件来表示这种情况,上下文丢失事件 (webglcontextlost)和上下文恢复事件(webglcontextrestored)。如表10.4所示。

上下文事件

当上下文事件丢失的时候,由getWebGLContext()函数获得的渲染上下文对象gl就失效了,而之前在gl上的所有操作,如创建缓冲区对象和纹理对象、初始化着色器、设置背景色等等,也都失效了。浏览器重置WebGL系统后,就触发了上下文恢复事件,这时我们需要重新完成上述步骤。在JavaScript中保存的变量不会受到影响,可以照常使用。

研究示例代码前,我们需要使用<canvas>的addEventListener()函数注册上下文丢失事件和上下文恢复事件的响应函数。你应该还记得,之前我们直接通过<canvas>元素的onmousedown属性来注册鼠标事件响应函数,但是<canvas>并不支持某个特殊的属性来注册关于上下文事件的响应函数,所以必须使用addEventListener()函数。

示例程序(RotatingTriangle_contextLost.js)

我们建立了示例程序RotatingTriangle_contextLost ,该示例程序修改了RotatingTriangle,使其能够处理上下文丢失事件,如上所示。如下显示了程序的代码。

javascript 复制代码
var VSHADER_SOURCE =
  'attribute vec4 a_Position;\n' +
  'uniform mat4 u_ModelMatrix;\n' +
  'void main() {\n' +
  '  gl_Position = u_ModelMatrix * a_Position;\n' +
  '}\n';
var FSHADER_SOURCE =
  'void main() {\n' +
  '  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
  '}\n';

function main() {
  var canvas = document.getElementById('webgl'); // 获取canvas元素
  // 注册事件响应函数以处理上下文丢失和恢复事件
  canvas.addEventListener('webglcontextlost', contextLost, false); 
  canvas.addEventListener('webglcontextrestored', function(ev) { start(canvas); }, false);
  start(canvas);   // 开始与WebGL相关的过程
}

var ANGLE_STEP = 45.0;
var g_currentAngle = 0.0; // 从局部变量改为全局变量
var g_requestID; // requestAnimationFrame() 函数的返回值
function start(canvas) {
  // 获取WebGL渲染上下文
  var gl = getWebGLContext(canvas);
  if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return
  var n = initVertexBuffers(gl);   // Write the positions of vertices to a vertex shader
  gl.clearColor(0.0, 0.0, 0.0, 1.0);   // Specify the color for clearing <canvas>
  var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
  var modelMatrix = new Matrix4();   // Create a model matrix
  var tick = function() {    // Start drawing
    g_currentAngle = animate(g_currentAngle);                // Update current rotation angle
    draw(gl, n, g_currentAngle, modelMatrix, u_ModelMatrix); // Draw the triangle
    g_requestID = requestAnimationFrame(tick, canvas);       // Reregister this Function again
  };
  tick();
}

function contextLost(ev) { // 上下文丢失事件响应函数
  cancelAnimationFrame(g_requestID); //  停止动画
  ev.preventDefault();  // 阻止默认行为
}

function initVertexBuffers(gl) {
  var vertices = new Float32Array ([
    0.0, 0.5,   -0.5, -0.5,   0.5, -0.5
  ]);
  var n = 3;   // The number of vertices
  var vertexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_Position);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  return n;
}

function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {
  modelMatrix.setRotate(currentAngle, 0, 0, 1);
  gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, n);
}

var g_last = Date.now();
function animate(angle) {
  var now = Date.now();
  var elapsed = now - g_last;
  g_last = now;
  var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
  return newAngle %= 360;
}

处理上下文丢失的过程与着色器没有关系,而发生在main()函数中。本例的main()函数非常简单:首先分别注册上下文丢失和上下文恢复事件响应函数(第15、16行),然后调用start()方法(第17行),就结束了。

start()函数执行了RotatingTriangle.js中main()函数的大部分逻辑(第23行),当上下文丢失又恢复后,应当再次调用该函数。为了处理上下文恢复时重新初始化WebGL程序,start()函数有两处重要的改变。

首先,程序将三角形的当前角度存储在全局变量g_currentAngle而不是局部变量中(第21行),这样当上下文恢复之后,就能从中获取角度以绘制三角形。其次,为了在上下文丢失后停止动画(即停止反复调用tick()函数),程序还将requestAnimationFrame()函数的返回值保存在全局变量g_requestID中(第22行)。

下面来看上下文事件响应函数。上下文丢失事件响应函数contextLost()只有两行,停止调用产生动画的函数以保证在上下文恢复之前不再尝试重绘(第40行),以及阻止浏览器对该事件的默认处理行为(第41行)。浏览器对上下文丢失事件的默认处理行为是,不再触发上下文恢复事件,而本例需要触发该事件,所以我们要阻止浏览器的默认行为。

上下文恢复事件响应函数很简单,直接调用start()函数以重置WebGL系统,所以我们将其定义为匿名函数(第16行)。

注意,当触发上下文丢失事件时,浏览器总会在控制台显示下面这样一行警告:

WARNING: WebGL content on the page might have caused the graphics card to reset

通过响应上下文丢失事件,WebGL程序就能够在上下文丢失的情况下也能正常运行。

相关推荐
苏州邦恩精密1 小时前
蔡司3D扫描仪厂家如何应用于新能源行业检测
人工智能·机器学习·3d·自动化·制造
云飞云共享云桌面1 小时前
河北钣金工厂10个solidworks设计共享一台高性能服务器的云桌面方案
运维·服务器·3d·自动化·负载均衡·制造
蓝速科技2 小时前
蓝速科技 3D 全息舱 AI 数字人校园智慧升级实效展示
人工智能·科技·3d
苏州邦恩精密2 小时前
蔡司3D扫描仪厂家如何应用于航空航天制造
大数据·数据库·人工智能·3d·自动化·制造
cd_9492172116 小时前
为什么AI3D创作中贴图材质尤为重要?因为它决定模型能否成为可用资产
人工智能·3d·贴图
虚焦像素_1 天前
MapLibre GL 实战:一套引擎搞定 2D 平面与 3D 球面
webgl
苏州邦恩精密1 天前
浙江蔡司3D扫描仪如何服务汽车及模具行业?
人工智能·科技·3d·自动化·汽车·制造
3D小将1 天前
3D格式转换之NWD 转 GLB 格式标准化转换技术
3d·solidworks模型·ug模型·rhino模型·catia模型
玖玥拾1 天前
Unity 3D 基础(一)Unity基础架构、物理系统、视觉渲染、空间变换与对象查询
笔记·游戏·3d·unity·游戏引擎
在水一缸1 天前
Box3D 深度解析:当物理引擎传奇迈向三维空间
3d·游戏开发·开源项目·物理引擎·box3d·3d仿真·box2d