检测WebGL支持的正确方法是什么?

61

我试图在多个浏览器中检测WebGL支持,但遇到了以下情况。当前版本的Firefox似乎使用以下检查报告了正面的支持,即使访问者的显卡被列入黑名单和/或WebGL被禁用:

if (window.WebGLRenderingContext) {
    // This is true in Firefox under certain circumstances,
    // even when WebGL is disabled...
}

我曾经尝试过向用户说明如何启用WebGL,具体步骤如下。这在某些情况下起作用,但并不总是有效。显然,这不是我能要求所有公众做的事情:

  1. 在Firefox地址栏中输入about:config
  2. 要启用WebGL,请将webgl.force-enabled设置为true

这促使我创建了自己的检测支持方法,它使用jQuery注入画布元素以检测支持。这采用了我在各种WebGL库和插件中找到的技术。问题在于,它非常难以测试(如果您有任何关于以下链接是否有效的评论,我们将不胜感激!)。为了让这成为一个客观的问题,我想知道是否有一种普遍接受的方法来检测所有浏览器对WebGL的支持

测试URL:

http://jsfiddle.net/Jn49q/5/


1
哎呀,我不知道除了“experimental-webgl”之外还有其他获取webgl上下文的方法,感谢您指出。 - Matt Greer
你说你的方法很难测试,这是什么意思?你担心会产生假阳性/阴性吗?在我的看法中,如果你请求WebGL上下文但没有得到,那么你的应用程序就无法继续执行。除非我漏掉了什么? - Matt Greer
@MattGreer,很难测试是因为很难找到具有特定Firefox版本、黑名单视频卡和/或不受支持的图形的测试机。具体来说,我正在尝试查找在最新版本的Firefox下我的测试方法返回“false”的情况。 - Derek Hunziker
1
啊,好的。就我所知,我有一个基于webgl的小网站,使用Firefox会导致几台计算机崩溃。看起来大约是2009年左右的机器失败了(很抱歉我没有比这更好的数据)。我还可以通过在VirtualBox中运行Windows(以OSX为主机)来始终使Firefox失效,因为VirtualBox的3D加速支持相当弱。 - Matt Greer
仅仅检测WebGL支持并不意味着它一定会很好。WebGL可以通过“swiftshader”进行模拟,或者在Linux上的Firefox中,可以通过“mesa 3D”部分或完全模拟。由于Mesa能够很好地加速2D,因此即使WebGL似乎可用,手动选择画布也可能是有意义的。 - user185953
8个回答

62

优秀的Three库实际上有一种机制可以检测以下内容:

  1. WebGL支持
  2. File API支持
  3. Workers支持

特别是针对WebGL,以下是使用的代码:

function webgl_support () { 
   try {
    var canvas = document.createElement('canvas'); 
    return !!window.WebGLRenderingContext &&
      (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
   } catch(e) {
     return false;
   }
 };

这段代码片段是Detector类的一部分,该类还可以向用户显示相应的错误消息。


7
紧凑简洁,完全符合作者要求。我不明白为什么有些人在答案中加入额外的无关代码。 - user151496
双重非运算符 !!window.WebGLRenderingContext 的目的是什么? - workwise
在这里解释了 !!:https://dev59.com/jXRA5IYBdhLWcg3w6SRH - workwise

37

[2014年10月] 我已经更新了 Modernizr 的示例,以匹配其当前实现,该实现是从下面的http://get.webgl.org/进行简化的版本。

Modernizr会执行:

var canvas;
var ctx;
var exts;

try {
  canvas = createElement('canvas');
  ctx = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
  exts = ctx.getSupportedExtensions();
}
catch (e) {
  return;
}

if (ctx !== undefined) {
  Modernizr.webglextensions = new Boolean(true);
}

for (var i = -1, len = exts.length; ++i < len; ){
  Modernizr.webglextensions[exts[i]] = true;
}

canvas = undefined;

Chromium指向http://get.webgl.org/作为 WebGL 支持的官方实现。

try { gl = canvas.getContext("webgl"); }
catch (x) { gl = null; }

if (gl == null) {
    try { gl = canvas.getContext("experimental-webgl"); experimental = true; }
    catch (x) { gl = null; }
}

@Cupidvogel:所有支持canvas的浏览器,占88.94%。您可以通过将其包装在try-catch语句中来使其达到100%。 - rvighne
2
@Cupidvogel 在Safari上不起作用(截至版本7.0.3),它还不支持getContext(“webgl”),只能使用getContext(“experimental-webgl”)getContext(“webkit-3d”) - TachyonVortex

20

如在http://www.browserleaks.com/webgl#howto-detect-webgl中所示:

这是一个检测WebGL支持的正确javascript函数,包括各种实验性的WebGL上下文名称和检查特殊情况,比如通过NoScript或TorBrowser阻止WebGL功能。

它将报告三种WebGL能力状态之一:

  • WebGL已启用 - 返回TRUE或返回
  • WebGL对象(如果传递了第一个参数)
  • WebGL已禁用 - 返回FALSE,如果需要,可以更改它
  • WebGL未实现 - 返回FALSE
function webgl_detect(return_context)
{
    if (!!window.WebGLRenderingContext) {
        var canvas = document.createElement("canvas"),
             names = ["webgl2", "webgl", "experimental-webgl", "moz-webgl", "webkit-3d"],
           context = false;

        for(var i=0;i< names.length;i++) {
            try {
                context = canvas.getContext(names[i]);
                if (context && typeof context.getParameter == "function") {
                    // WebGL is enabled
                    if (return_context) {
                        // return WebGL object if the function's argument is present
                        return {name:names[i], gl:context};
                    }
                    // else, return just true
                    return true;
                }
            } catch(e) {}
        }

        // WebGL is supported, but disabled
        return false;
    }

    // WebGL not supported
    return false;
}

为什么在window.WebGLRenderingContext上要使用双重否定? - sijpkes
我真的不确定,我从上面的链接中复制了对我有用的代码,但这里有一些关于它的解释:https://dev59.com/jXRA5IYBdhLWcg3w6SRH - Juan Arias
2
@sijpkes的双重否定将真值/假值转换为它们的布尔表示形式:!!"" === false && !!1 === true - WickyNilliams
@WickyNilliams 在 PHP 中的类型转换总是令人困惑,但这真是难上加难。 - sijpkes
return_context 不应该只是 context 吗?只要做出这个更改,代码就可以在我的电脑上运行。 - ibsenleo
@ibsenleo 实际上,return_context 只是一个可选的布尔参数,你可以在需要函数返回的上下文时发送它。它与 webgl_detect 内部使用的 context 变量不同。 - Juan Arias

8
除了@Andrew的答案,还有实验模式可以支持。我已经编写了以下代码片段:
var canvasID = 'webgl',
    canvas = document.getElementById(canvasID),
    gl,
    glExperimental = false;

function hasWebGL() {

    try { gl = canvas.getContext("webgl"); }
    catch (x) { gl = null; }

    if (gl === null) {
        try { gl = canvas.getContext("experimental-webgl"); glExperimental = true; }
        catch (x) { gl = null; }
    }

    if(gl) { return true; }
    else if ("WebGLRenderingContext" in window) { return true; } // not a best way, as you're not 100% sure, so you can change it to false
    else { return false; }
}

根据您的ID更改canvasID变量。

已在Chrome、Safari、Firefox、Opera和IE(8至10)上进行测试。对于Safari,请记住它是可用的,但需要显式启用WebGL(启用开发人员菜单并启用Web GL选项)。


2
为了检测支持WebGL的浏览器,但又排除那些不支持它或支持不好的老旧浏览器(如在WebGL detected as supported when it is actually not中用于排除Android 4.4.2设备),我正在添加一个更严格但无关的检查:
function hasWebGL() {
    var supported;

    try {
        var canvas = document.createElement('canvas');
        supported = !! window.WebGLRenderingContext && (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'));
    } catch(e) { supported = false; }

    try {
        // let is by no means required, but will help us rule out some old browsers/devices with potentially buggy implementations: http://caniuse.com/#feat=let
        eval('let foo = 123;');
    } catch (e) { supported = false; }

    if (supported === false) {
        console.log("WebGL is not supported");
    }

    canvas = undefined;

    return supported;
},

1
// this code will detect WebGL version until WebGL Version maxVersionTest 
var
maxVersionTest = 5,
canvas = document.createElement('canvas'),
webglVersion = (canvas.getContext('webgl') || canvas.getContext('experimental-webgl')) ? 1 : null,
canvas = null; // free context

// range: if maxVersionTest = 5 makes [5, 4, 3, 2]
Array.apply(null, Array(maxVersionTest - 1))
.map(function (_, idx) {return idx + 2;})
.reverse()
.some(function(version){
    // cant reuse canvas, potential to exceed contexts or mem limit *
    if (document.createElement('canvas').getContext('webgl'+version))
        return !!(webglVersion = version);
});

console.log(webglVersion);

* 关于“可能超出上下文或内存限制”的问题,请参见https://bugs.chromium.org/p/chromium/issues/detail?id=226868


惊人的解决方案。您能再详细解释一下canvas = null;以及return吗? - Nikola Lukic
创建画布需要消耗资源,将canvas = null设置为null可以释放第一个版本测试中使用的资源。 此外,至少在Chrome中存在最大上下文限制。 循环中创建的画布是匿名的,并且一旦超出范围就会被允许释放。 !!value只是将值转换为布尔值,期望返回到Array.prototype.some,例如:!!null == false。 - ekerner

1
MDN中获取:

// Run everything inside window load event handler, to make sure
// DOM is fully loaded and styled before trying to manipulate it.
window.addEventListener("load", function() {
  var paragraph = document.querySelector("p"),
    button = document.querySelector("button");
  // Adding click event handler to button.
  button.addEventListener("click", detectWebGLContext, false);
  function detectWebGLContext () {
    // Create canvas element. The canvas is not added to the
    // document itself, so it is never displayed in the
    // browser window.
    var canvas = document.createElement("canvas");
    // Get WebGLRenderingContext from canvas element.
    var gl = canvas.getContext("webgl")
      || canvas.getContext("experimental-webgl");
    // Report the result.
    if (gl && gl instanceof WebGLRenderingContext) {
      paragraph.innerHTML =
        "Congratulations! Your browser supports WebGL.";
    } else {
      paragraph.innerHTML = "Failed to get WebGL context. "
        + "Your browser or device may not support WebGL.";
    }
  }
}, false);
body {
  text-align : center;
}
button {
  display : block;
  font-size : inherit;
  margin : auto;
  padding : 0.6em;
}
<p>[ Here would go the result of WebGL feature detection ]</p>
<button>Press here to detect WebGLRenderingContext</button>


0
// Check for WebGL support
function isWebGLSupported() {
    try {
        var canvas = document.createElement('canvas');
        return !!(
            window.WebGLRenderingContext &&
            (canvas.getContext('webgl') || canvas.getContext('experimental-webgl'))
        );
    } catch (e) {
        return false;
    }
}

// Example usage
if (isWebGLSupported()) {
    console.log('WebGL is supported in this browser!');
} else {
    console.log('WebGL is not supported in this browser.');
}

1
你的回答可以通过提供更多的支持性信息来改进。请编辑以添加进一步的细节,例如引用或文档,以便他人可以确认你的回答是否正确。你可以在帮助中心找到有关如何撰写好的回答的更多信息。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接