解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題

問題說明
Cesium 1.50(2018/10/01)版本打開3dtiles可能會出現加載不上導致渲染停止的錯誤。

錯誤說明爲:RuntimeError: Unsupported glTF Extension: KHR_technique_webgl

錯誤截圖如下:
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
解決方案
我發現原因是KHR_technique_webgl擴展新版Cesium已經不支持的緣故,需要升級一下gltf數據,使用KHR_techniques_webgl擴展即可(注意多了一個s)。
當然如果直接修改3dtiles數據,比較勞心費神。這裏提供一個簡單的方法,只需要在Cesium.js加載以後,運行以下代碼即可正常顯示3dtiles數據了。
var fixGltf = function(gltf) {
if (!gltf.extensionsUsed) {
return;
}

var v = gltf.extensionsUsed.indexOf('KHR_technique_webgl');
var t = gltf.extensionsRequired.indexOf('KHR_technique_webgl');
// 中招了。。
if (v !== -1) {
    gltf.extensionsRequired.splice(t, 1, 'KHR_techniques_webgl');
    gltf.extensionsUsed.splice(v, 1, 'KHR_techniques_webgl');
    gltf.extensions = gltf.extensions || {};
    gltf.extensions['KHR_techniques_webgl'] = {};
    gltf.extensions['KHR_techniques_webgl'].programs = gltf.programs;
    gltf.extensions['KHR_techniques_webgl'].shaders = gltf.shaders;
    gltf.extensions['KHR_techniques_webgl'].techniques = gltf.techniques;
    var techniques = gltf.extensions['KHR_techniques_webgl'].techniques;

    gltf.materials.forEach(function (mat, index) {
        gltf.materials[index].extensions['KHR_technique_webgl'].values = gltf.materials[index].values;
        gltf.materials[index].extensions['KHR_techniques_webgl'] = gltf.materials[index].extensions['KHR_technique_webgl'];

        var vtxfMaterialExtension = gltf.materials[index].extensions['KHR_techniques_webgl'];

        for (var value in vtxfMaterialExtension.values) {
            var us = techniques[vtxfMaterialExtension.technique].uniforms;
            for (var key in us) {
                if (us[key] === value) {
                    vtxfMaterialExtension.values[key] = vtxfMaterialExtension.values[value];
                    delete vtxfMaterialExtension.values[value];
                    break;
                }
            }
        };
    });

    techniques.forEach(function (t) {
        for (var attribute in t.attributes) {
            var name = t.attributes[attribute];
            t.attributes[attribute] = t.parameters[name];
        };

        for (var uniform in t.uniforms) {
            var name = t.uniforms[uniform];
            t.uniforms[uniform] = t.parameters[name];
        };
    });
}

}

Object.defineProperties(Cesium.Model.prototype, {
_cachedGltf: {
set: function (value) {
this._vtxf_cachedGltf = value;
if (this._vtxf_cachedGltf && this._vtxf_cachedGltf._gltf) {
fixGltf(this._vtxf_cachedGltf._gltf);
}
},
get: function () {
return this._vtxf_cachedGltf;
}
}
});
尋根探底
KHR_technique_webgl的來源
這裏不得不說下gltf2.0版本曾經存在的奇葩問題:只支持PBR材質,而不支持自定義shader。
2015年的gltf1.0標準時,本來是天然的支持也僅支持自定義shader的。大家都知道webgl是不支持固定管線的,gltf1.0支持自定義shader,和webgl標準是相當地匹配,但凡按照gltf1.0中的shader來渲染自然是不成問題。
但沒想到gltf升級到2.0以後,居然默認只能使用PBR材質,而且是不再支持自定義shader。這個就很要命了:
首先各家渲染引擎(three.js/babylon/Cesium)對PBR材質的理解貌似都不一樣,結果導致同一個gltf模型在不同的渲染引擎中渲染出來的效果還都有差別。。所以到目前爲止也沒有統一。從渲染效果上來說,three.js的渲染效果很棒,babylon也還可以,Cesium貌似理解得不太到位,效果差了一大截。。
其次,另外一個問題對Cesium更嚴重,Cesium早期本身按照gltf1.0的標準來繪製。到了gltf2.0時代,Cesium的Model類爲了能同時支持gltf1.0和gltf2.0,不得不做出調整,將所有gltf1.0的模型都在內部自動升級成gltf2.0,然後再渲染。問題是gltf2.0不支持自定義shader啊,gltf1.0的自定義shader怎麼轉化成gltf2.0?自定義shader的自由度太大,是不太可能轉成PBR材質的。。

那麼Cesium怎麼解決gltf1.0升級gltf2.0的問題呢?這裏就用到了gltf2.0當時還處於草案階段的擴展KHR_technique_webgl。通過這個擴展來在gltf2.0中實現自定義shader。
然而隱患恰恰來自於這個KHR_technique_webgl擴展。。
因爲這個擴展尚未推出,於是出現了一個奇怪的現象。對於某些gltf2.0的模型(使用了KHR_technique_webgl擴展),只有Cesium能打開,連three.js、babylon都愛莫能助。Cesium變得異常強大。。
然而,好景不長,Cesium在使用這個擴展時,gltf2.0也在逐步進化,居然連KHR_technique_webgl這個擴展的名字都改了,technique後面加了一個s。。所以現在的擴展名叫KHR_techniques_webgl。。
這下對廣大使用Cesium做開發的網友來說,就不太好了。因爲之前發佈模型時,但凡最終轉化成帶有KHR_technique_webgl擴展的數據就都會變得不能用。Cesium這次做得也很絕,連兼容性都不做一下。。這就意味着從Ceisum 1.50版本以後,之前使用了該擴展的3dtiles數據就都打不開了。(如果Cesium官方重視的話,或許會推出一個Cesium 1.50.1的補丁版本來解決這個問題吧,呵呵,畢竟也還算好改,就是上文提到的代碼就能解決大部分問題了。)
從KHR_technique_webgl升級到KHR_techniques_webgl
注意前面是technique,後面是techniques。
Khronos把KHR_technique_webgl升級到KHR_techniques_webgl,也並非只是改了下名字這樣簡單。內部也做了一些調整。主要變化有:
1 把techniques、programs、shaders這三種類型的節點中都放到了擴展中去定義,不像之前直接放在主節點下;
2 techniques下的technique屬性原先會有attributes、parameters、uniforms等幾個節點,其中attributes、uniforms節點中定義的shader變量,是要去parameters節點下找到最終的類型和語義(semantic)的。之前看gltf1.0時也是這個地方不太理解爲啥要這麼折騰下。。現在看來貌似是沒有必要了。KHR_techniques_webgl的擴展變得更加精簡。直接去掉了parameters節點,並將其中的定義挪至attributes、uniforms中去。
3 KHR_technique_webgl擴展時,materials節點下的material也需要定義成擴展形式
4 還有其他的,我還沒用到。。
令人欣喜的Cesium和隱憂
這一次Cesium貌似又走在了各大引擎的前列,首先支持上了KHR_techniques_webgl擴展。而其他引擎,比如three.js、babylon都還沒支持上呢。以下是測試結果:

three.js視景器
https://gltf-viewer.donmccurdy.com/

three.js讀取貌似正常,實際上會給出警告,沒有使用這個擴展。
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
babylon視景器
babylonjs比較直白,直接表示不認得KHR_techniques_webgl。

http://sandbox.babylonjs.com/
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
國產引擎clay
clay也是著名的ECharts使用的底層WebGL引擎。

https://pissang.github.io/clay-viewer/editor/

居然沒有報錯。。而且渲染效果也不錯!不得不讚嘆一下國產引擎的強大。
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
隱憂
不太好的問題是KHR_techniques_webgl擴展仍然處於草案階段,也就是說以後還是有可能會修改的。。這就意味着即使現在的3dtiles能渲染,或許以後Cesium版本升級,還會有渲染不出來的問題。。
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題
歡迎關注 Cesium實驗室 ,QQ羣號:595512567。
解決Cesium1.50對gltf2.0/3dtiles數據讀取的問題

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章