• 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,使其能够处理上下文丢失事件,如上所示。如下显示了程序的代码。 

    1. var VSHADER_SOURCE =
    2. 'attribute vec4 a_Position;\n' +
    3. 'uniform mat4 u_ModelMatrix;\n' +
    4. 'void main() {\n' +
    5. ' gl_Position = u_ModelMatrix * a_Position;\n' +
    6. '}\n';
    7. var FSHADER_SOURCE =
    8. 'void main() {\n' +
    9. ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' +
    10. '}\n';
    11. function main() {
    12. var canvas = document.getElementById('webgl'); // 获取canvas元素
    13. // 注册事件响应函数以处理上下文丢失和恢复事件
    14. canvas.addEventListener('webglcontextlost', contextLost, false);
    15. canvas.addEventListener('webglcontextrestored', function(ev) { start(canvas); }, false);
    16. start(canvas); // 开始与WebGL相关的过程
    17. }
    18. var ANGLE_STEP = 45.0;
    19. var g_currentAngle = 0.0; // 从局部变量改为全局变量
    20. var g_requestID; // requestAnimationFrame() 函数的返回值
    21. function start(canvas) {
    22. // 获取WebGL渲染上下文
    23. var gl = getWebGLContext(canvas);
    24. if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) return
    25. var n = initVertexBuffers(gl); // Write the positions of vertices to a vertex shader
    26. gl.clearColor(0.0, 0.0, 0.0, 1.0); // Specify the color for clearing
    27. var u_ModelMatrix = gl.getUniformLocation(gl.program, 'u_ModelMatrix');
    28. var modelMatrix = new Matrix4(); // Create a model matrix
    29. var tick = function() { // Start drawing
    30. g_currentAngle = animate(g_currentAngle); // Update current rotation angle
    31. draw(gl, n, g_currentAngle, modelMatrix, u_ModelMatrix); // Draw the triangle
    32. g_requestID = requestAnimationFrame(tick, canvas); // Reregister this Function again
    33. };
    34. tick();
    35. }
    36. function contextLost(ev) { // 上下文丢失事件响应函数
    37. cancelAnimationFrame(g_requestID); // 停止动画
    38. ev.preventDefault(); // 阻止默认行为
    39. }
    40. function initVertexBuffers(gl) {
    41. var vertices = new Float32Array ([
    42. 0.0, 0.5, -0.5, -0.5, 0.5, -0.5
    43. ]);
    44. var n = 3; // The number of vertices
    45. var vertexBuffer = gl.createBuffer();
    46. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    47. gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
    48. var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
    49. gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
    50. gl.enableVertexAttribArray(a_Position);
    51. gl.bindBuffer(gl.ARRAY_BUFFER, null);
    52. return n;
    53. }
    54. function draw(gl, n, currentAngle, modelMatrix, u_ModelMatrix) {
    55. modelMatrix.setRotate(currentAngle, 0, 0, 1);
    56. gl.uniformMatrix4fv(u_ModelMatrix, false, modelMatrix.elements);
    57. gl.clear(gl.COLOR_BUFFER_BIT);
    58. gl.drawArrays(gl.TRIANGLES, 0, n);
    59. }
    60. var g_last = Date.now();
    61. function animate(angle) {
    62. var now = Date.now();
    63. var elapsed = now - g_last;
    64. g_last = now;
    65. var newAngle = angle + (ANGLE_STEP * elapsed) / 1000.0;
    66. return newAngle %= 360;
    67. }

    处理上下文丢失的过程与着色器没有关系,而发生在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程序就能够在上下文丢失的情况下也能正常运行。 

  • 相关阅读:
    2023年中国铁路行车监测系统竞争格局、市场规模及行业发展趋势分析[图]
    富格林:揭露黑幕平台保障安全
    MATLAB环境下基于深层小波散射网络的纹理图像分类方法
    3D体验平台品牌应用——ENOVIA
    CentOS7.9安装
    【CesiumforUnreal插件】UE5 快速构建Cesium场景 快速入门!!!
    煤矿视频监控分析系统
    Docker 容器 jvm 内存参数调整优化
    Python之高阶函数
    java毕业设计冰球馆管理系统mybatis+源码+调试部署+系统+数据库+lw
  • 原文地址:https://blog.csdn.net/dabaooooq/article/details/133579649