Cesium源码分析&3dtile数据着色器解码

首先我来看一下这样的一个gltf,一般来说3dtile数据是由gltf和头文件信息组建而成。而现在我们要对shaders中的着色器进行解码。shaders分别右两部分构成,一部分是顶点着色器,另外一部分是片元着色器。着色语言为webgl,即使用了glsl语言,类似于C语言进行编写。而放到shaders数组中的存放形式,可以是二进制形式,也可以是数据视图,存放着缓冲区,也可以是json格式的数据。而我们下面的数据是二进制的数据,有一个头说明"data:text/plain;base64",好了我们来看一下在Cesium中,是如何对这个数据进行解码的。

 

首先,3dtile数据,后缀为b3dm的格式,在数据加载的时候也是创建了一个模型,对应Model.js中的Model类,在update函数中进行转换,update函数的代码量还是比较大的,这里截取了几个转换函数,如下所示,大概在代码的4404的位置。

                    // We do this after to make sure that the ids don't change
                    addBuffersToLoadResources(this);
                    parseTechniques(this);
                    if (!this._loadRendererResourcesFromCache) {
                        parseBufferViews(this);
                        parseShaders(this);
                        parsePrograms(this);
                        parseTextures(this, context);
                    }
                    parseMaterials(this);
                    parseMeshes(this);
                    parseNodes(this);

当数据model(gltf)进入到parseShaders(this)中,看一下代码的逻辑实现。从下面的代码中,可以看出着色器,支持三种着色资源的定义,而我们这里选择的选择的第三种方式(具体的实现是根据编写的生成b3dm工具来确定)。

 function parseShaders(model) {
        var gltf = model.gltf;
        var buffers = gltf.buffers;
        var bufferViews = gltf.bufferViews;
        var sourceShaders = model._rendererResources.sourceShaders;
        ForEach.shader(gltf, function(shader, id) {
            // Shader references either uri (external or base64-encoded) or bufferView
            if (defined(shader.bufferView)) {
                var bufferViewId = shader.bufferView;
                var bufferView = bufferViews[bufferViewId];
                var bufferId = bufferView.buffer;
                var buffer = buffers[bufferId];
                var source = getStringFromTypedArray(buffer.extras._pipeline.source, bufferView.byteOffset, bufferView.byteLength);
                sourceShaders[id] = source;
            } else if (defined(shader.extras._pipeline.source)) {
                sourceShaders[id] = shader.extras._pipeline.source;
            } else {
                ++model._loadResources.pendingShaderLoads;

                var shaderResource = model._resource.getDerivedResource({
                    url: shader.uri
                });

                shaderResource.fetchText()
                    .then(shaderLoad(model, shader.type, id))
                    .otherwise(ModelUtility.getFailedLoadFunction(model, 'shader', shaderResource.url));
            }
        });
    }

将断点打入到该函数,我们将会进入到最后一个判断的fetchText函数。

现在我们来看一下这个函数的实现,fetchText()定义如下所示。

    Resource.prototype.fetchText = function() {
        return this.fetch({
            responseType : 'text'
        });
    };

fetchText函数非常简单,只不过说对Resource.js中的fetch函数传入返回类型为"text"的对象,然后调用而已。那么我们来看一下fetch函数是如何实现的。可是fetch也是非常简单的。

    Resource.prototype.fetch = function(options) {
        options = defaultClone(options, {});
        options.method = 'GET';

        return this._makeRequest(options);
    };

主要是来看一下_makeRequest函数,其中定义如下所示。

 Resource.prototype._makeRequest = function(options) {
        var resource = this;
        checkAndResetRequest(resource.request);

        var request = resource.request;
        request.url = resource.url;

        request.requestFunction = function() {
            var responseType = options.responseType;
            var headers = combine(options.headers, resource.headers);
            var overrideMimeType = options.overrideMimeType;
            var method = options.method;
            var data = options.data;
            var deferred = when.defer();
            var xhr = Resource._Implementations.loadWithXhr(resource.url, responseType, method, data, headers, deferred, overrideMimeType);
            if (defined(xhr) && defined(xhr.abort)) {
                request.cancelFunction = function() {
                    xhr.abort();
                };
            }
            return deferred.promise;
        };

        var promise = RequestScheduler.request(request);
        if (!defined(promise)) {
            return;
        }

        return promise
            .then(function(data) {
                return data;
            })
            .otherwise(function(e) {
                if (request.state !== RequestState.FAILED) {
                    return when.reject(e);
                }

                return resource.retryOnError(e)
                    .then(function(retry) {
                        if (retry) {
                            // Reset request so it can try again
                            request.state = RequestState.UNISSUED;
                            request.deferred = undefined;

                            return resource.fetch(options);
                        }

                        return when.reject(e);
                    });
            });
    };

requestFunction函数对uri数据进行了拆分,利用loadWithXhr函数对数据进行进一步的处理。我们来看一下这个函数。

Resource._Implementations.loadWithXhr = function(url, responseType, method, data, headers, deferred, overrideMimeType) {
        var dataUriRegexResult = dataUriRegex.exec(url);
        if (dataUriRegexResult !== null) {
            deferred.resolve(decodeDataUri(dataUriRegexResult, responseType));
            return;
        }

        if (noXMLHttpRequest) {
            loadWithHttpRequest(url, responseType, method, data, headers, deferred, overrideMimeType);
            return;
        }

        var xhr = new XMLHttpRequest();

        if (TrustedServers.contains(url)) {
            xhr.withCredentials = true;
        }

        xhr.open(method, url, true);

        if (defined(overrideMimeType) && defined(xhr.overrideMimeType)) {
            xhr.overrideMimeType(overrideMimeType);
        }

        if (defined(headers)) {
            for (var key in headers) {
                if (headers.hasOwnProperty(key)) {
                    xhr.setRequestHeader(key, headers[key]);
                }
            }
        }

        if (defined(responseType)) {
            xhr.responseType = responseType;
        }

        // While non-standard, file protocol always returns a status of 0 on success
        var localFile = false;
        if (typeof url === 'string') {
            localFile = (url.indexOf('file://') === 0) || (typeof window !== 'undefined' && window.location.origin === 'file://');
        }

        xhr.onload = function() {
            if ((xhr.status < 200 || xhr.status >= 300) && !(localFile && xhr.status === 0)) {
                deferred.reject(new RequestErrorEvent(xhr.status, xhr.response, xhr.getAllResponseHeaders()));
                return;
            }

            var response = xhr.response;
            var browserResponseType = xhr.responseType;

            if (method === 'HEAD' || method === 'OPTIONS') {
                var responseHeaderString = xhr.getAllResponseHeaders();
                var splitHeaders = responseHeaderString.trim().split(/[\r\n]+/);

                var responseHeaders = {};
                splitHeaders.forEach(function (line) {
                    var parts = line.split(': ');
                    var header = parts.shift();
                    responseHeaders[header] = parts.join(': ');
                });

                deferred.resolve(responseHeaders);
                return;
            }

            //All modern browsers will go into either the first or second if block or last else block.
            //Other code paths support older browsers that either do not support the supplied responseType
            //or do not support the xhr.response property.
            if (xhr.status === 204) {
                // accept no content
                deferred.resolve();
            } else if (defined(response) && (!defined(responseType) || (browserResponseType === responseType))) {
                deferred.resolve(response);
            } else if ((responseType === 'json') && typeof response === 'string') {
                try {
                    deferred.resolve(JSON.parse(response));
                } catch (e) {
                    deferred.reject(e);
                }
            } else if ((browserResponseType === '' || browserResponseType === 'document') && defined(xhr.responseXML) && xhr.responseXML.hasChildNodes()) {
                deferred.resolve(xhr.responseXML);
            } else if ((browserResponseType === '' || browserResponseType === 'text') && defined(xhr.responseText)) {
                deferred.resolve(xhr.responseText);
            } else {
                deferred.reject(new RuntimeError('Invalid XMLHttpRequest response type.'));
            }
        };

        xhr.onerror = function(e) {
            deferred.reject(new RequestErrorEvent());
        };

        xhr.send(data);

        return xhr;
    };

这里将会进入到第一个判断的函数,如下。

function decodeDataUri(dataUriRegexResult, responseType) {
        responseType = defaultValue(responseType, '');
        var mimeType = dataUriRegexResult[1];
        var isBase64 = !!dataUriRegexResult[2];
        var data = dataUriRegexResult[3];

        switch (responseType) {
            case '':
            case 'text':
                return decodeDataUriText(isBase64, data);
            case 'arraybuffer':
                return decodeDataUriArrayBuffer(isBase64, data);
            case 'blob':
                var buffer = decodeDataUriArrayBuffer(isBase64, data);
                return new Blob([buffer], {
                    type : mimeType
                });
            case 'document':
                var parser = new DOMParser();
                return parser.parseFromString(decodeDataUriText(isBase64, data), mimeType);
            case 'json':
                return JSON.parse(decodeDataUriText(isBase64, data));
            default:
                //>>includeStart('debug', pragmas.debug);
                throw new DeveloperError('Unhandled responseType: ' + responseType);
            //>>includeEnd('debug');
        }
    }

因为是text类型,我们进入到switch的第二个case.

    function decodeDataUriText(isBase64, data) {
        var result = decodeURIComponent(data);
        if (isBase64) {
            var res=atob(result);
            return atob(result);
        }
        return result;
    }

到了这里,我们可以看到使用了javascript的atob函数来为我们的数据进行解码。打一断点可以看到。数据如下所示。

"precision highp float;
uniform mat4 u_modelViewMatrix;
uniform mat4 u_projectionMatrix;
attribute vec3 a_position;
attribute vec2 a_texcoord0;
attribute float a_batchid;
varying vec2 v_texcoord0;
void main(void)
{	
	v_texcoord0 = a_texcoord0;
	gl_Position = u_projectionMatrix * u_modelViewMatrix * vec4(a_position, 1.0);
}

"

上面就是我们需要的返回数据,回到刚才转换着色器的第三个if中的函数,shaderLoader()函数,这个函数里面,由传入的参数model,将source资源赋给本身。这里算是将数据解析完了。

    function shaderLoad(model, type, id) {
        return function(source) {
            var loadResources = model._loadResources;
            loadResources.shaders[id] = {
                source : source,
                type : type,
                bufferView : undefined
            };
            --loadResources.pendingShaderLoads;
            model._rendererResources.sourceShaders[id] = source;
        };
    }

 

### 回答1: To add Cesium 3D Tiles with highlighing functionality to an Unreal Engine project, you can follow these general steps: 1. Install the Cesium for Unreal Engine plugin: You can download the plugin from the Epic Games Marketplace or build it from the source code available on GitHub. Follow the instructions provided with the plugin to install it in your Unreal Engine project. 2. Load the Cesium 3D Tileset: You can load the tileset using the &quot;CesiumTileset&quot; actor provided by the plugin. Add the actor to your level and set its properties to specify the URL of the 3D Tileset you want to load. 3. Set up highlighting functionality: To add highlighting to your 3D Tileset, you can use the &quot;CesiumHighlightActor&quot; provided by the plugin. Add the actor to your level and set its properties to specify the materials to use for highlighting and the events that trigger highlighting. 4. Implement the highlighting events: You can use Unreal Engine&#39;s blueprint system to implement the events that trigger highlighting. For example, you could use a trigger volume or a line trace to detect when the player&#39;s cursor or object intersects with the 3D Tileset, and then use the &quot;Highlight&quot; function of the CesiumHighlightActor to highlight the corresponding tile. 5. Customize the highlighting appearance: You can customize the appearance of the highlighted tiles by modifying the materials assigned to the CesiumHighlightActor. For example, you could change the color, opacity, or texture of the highlighted tiles. These are the general steps to add Cesium 3D Tiles with highlighting functionality to an Unreal Engine project using the Cesium for Unreal Engine plugin. However, the specific implementation details may vary depending on your project requirements and the version of the plugin you are using. ### 回答2: Cesium for UE是一种将Cesium的高亮功能与虚幻引擎(UE)集成的工具。用于在虚幻引擎中高亮3Dtile3Dtile是一种用于存储和传输大规模3D实体数据的文件格式。Cesium for UE可以通过加载3Dtile数据并提供高亮功能,使得在虚幻引擎中展示和编辑大规模3D模型变得更加方便。 通过Cesium for UE,可以在虚幻引擎中使用多种高亮效果,比如边框高亮、颜色高亮等,来突出显示3Dtile中的特定模型或部分。这样可以方便用户对模型进行修改、编辑或者查看。同时,Cesium for UE还提供了实时的高亮更新,使得用户在编辑模型时能够即时看到高亮效果的变化。 使用Cesium for UE高亮3Dtile的过程很简单。首先,需要将3Dtile数据导入到虚幻引擎中,然后使用Cesium for UE提供的蓝图节点或者代码函数来设置高亮效果。用户可以自定义高亮颜色、透明度以及其他相关参数,以满足不同的需求。 总之,Cesium for UE高亮3Dtile是一种集成了Cesium高亮功能的工具,让虚幻引擎用户能够更方便地展示、编辑和查看大规模3D模型数据。通过使用Cesium for UE的高亮功能,用户可以更直观地了解和操作3Dtile中的模型,提高工作效率。 ### 回答3Cesium for UE 是一种基于CesiumJS的虚幻引擎(Unreal Engine)插件,它可以实现对3D Tile数据进行高亮显示。 3D Tile是一种用于表示三维地理空间数据的开放式标准,它将地图数据分割成小的、可独立加载和渲染的块,使得对大规模地理数据的可视化处理变得更加高效和灵活。 Cesium for UE插件提供了高亮3D Tile的功能,使用户能够以可视化的方式突出显示感兴趣的地理要素。通过对特定3D Tile的修改,可以在场景中加入一些视觉效果,例如改变颜色、增加阴影、调整透明度等,突出显示这些地理要素。这种高亮显示有助于用户更加直观地了解数据的空间分布和特征。 使用Cesium for UE进行高亮3D Tile的操作相对简单。首先,用户需要加载3D Tile数据到虚幻引擎场景中,然后通过插件提供的接口找到要高亮显示的特定Tile。接下来,用户可以通过修改Tile的属性,例如改变材质、调整光照等,实现高亮效果。修改后的Tile会在场景中呈现出高亮的视觉效果,使用户能够快速准确地注意到这些特定地理要素。 Cesium for UE的高亮3D Tile功能可以用于许多应用场景,例如建筑设计中的场景演示、城市规划中的目标指示、地理信息系统中的数据展示等。通过将重要要素高亮显示,用户可以更好地理解和分析复杂的地理数据,提高工作效率和决策质量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yGIS

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值