Cesium原理篇:Property

本文深入探讨Cesium中实体(Entity)属性的封装方法,包括defineProperties、createPropertyDescriptor及多种Property类型的使用技巧,如ConstantProperty与CallbackProperty。通过具体示例阐述了属性封装背后的原理与应用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       之前主要是Entity的一个大概流程,本文主要介绍Cesium的属性,比如defineProperties,Property(ConstantProperty,CallbackProperty,ConstantPositionProperty)以及createPropertyDescriptor的相关内容,研究一下Cesium对Object的属性设计和使用方式。

       我们以Entity为例,看看它是如何封装自己的属性:

       如上,我截取了五个属性:id,name,description,position,rectangle。每个属性的创建方式可以说大同小异。下面看看用户是如何使用Entity的这些属性:


       如上说明亮点,Cesium中的成员变量和属性方法之间就是一个下划线_,比如成员变量为_id,对应的属性方法为id,这是Cesium中的一个规范。另外这些属性也是提供了set&get方法来进行赋值和取值,所以可以直接通过=运算符。下面我们由易到难,分别介绍。

defineProperties

       Cesium.defineProperties本质上就是Object.defineProperties。我们不妨看一下它的定义:

       结合之前Entity.id代码,我们大概可以掌握通过Object.defineProperties封装多个属性的方式。通过下面这个范例,我们来强化一下理解:


       如上的代码中,我们创建了obj这个function,然后对obj.prototype这个对象创建了两个属性property1&property2,两个属性创建的方式略有不同,我是想通过这种不同来强调需要注意的地方,大家在编码的时候还是要遵守一种风格,保持一致性。首先,props参数,value,set&get,writable,confugurable以及enumerable。value和set&get都是用来赋值的,两种方式是有冲突的,二选一,两中方式略有不同,个人理解如果采用set&et,property1就是_id的引用。其次,writable,confugurable以及enumerable三个属性的默认值为false,具体的作用可以参考API,当然通过字面意思也不难理解。最后,defineProddrties的第一个参数为obj,所以可以使用原型或new一个obj出来,Cesium使用的是前者,用不用原型对应的原型链还是有一些差别的。下面是对比图,前者是obj+set&get的方式,set和get也是obj实例的,后者是prototype+set&get,property1为引用,而set&get是prototype的实例。想一下,假如是prototype+value呢?这个其实是prototype的内容,我们就不过于跑题了。

compare

       这样,通过defineProperties的属性封装,我们保证了每一个Entity中,其原型中,对每一个属性都对应一个set和get方法的能力(需要与否则看实际情况),同时属性值对应function中的实例(而不是原型中提供该属性)。

Property

       在进入createProperty这类方法前,我们先了解一下CesiumProperty,然后在一步步的展开。首先,个人认为这些Property在本质上和defineProperties并无本质的区别,defineProperties直接封装一些基本类型,如果是Object,这种accessor是引用的形式。因此,通过Property的封装,将引用或复制的权利交给Object的设计者,同时提供一些特殊功能,满足特殊的需求,比如SampledProperty提供插值功能,MaterialProperty则专门针对材质。这里我们主要通过两个有代表性的,Entity中涉及到的Property,了解一下Cesium中Property的实现。

  • ConstantProperty
  • CallbackProperty

ConstantProperty

       如下是ConstantProperty的一个精简的代码片段,结合个人的注释,应该能对其有一个大概的认识:




      可见,相比基本类型,ConstantProperty是一个高级版的属性,首先在构造函数的参数中,value可以是一个基本类型,如此,该ConstantProperty会蜕化成defineProperties下直接封装的属性,当然,getValue,setValue以及equals方法本质上也是基本类型之间的赋值和对比,唯一不同的是提供了属性值变化的事件。如果value是一个obj,则用户可以提供clone以及equals方法,满足自己的特殊需要,当然你也可以不提供,那该obj就是默认的赋值和对比操作符。

CallbackProperty

       下面是该属性的一个精简版,主要列出和ConstantProperity的不同处,方便对比。

       首先,ConstantProperity就是传进来一个value,返回该value,就是一个直来直去的思路。但有些时候事情并不是一成不变,比如在不同的状态下,希望返回不同的属性值。这种情况就需要提供回调方法,用户自己提供一个callback函数,CallbackProperty则通过setValue来绑定该回调方法,通过getValue来调用该方法,至于返回的属性值是什么,用户通过该callback方法自己负责。这就是CallbackProperty的应用场景。

Property

       当然,除了ConstantProperity和CallbackProperty之外,Cesium还提供了CompositeProperty、SampledProperty、TimeIntervalCollectionProperty、MaterialProperty、PositionProperty、ReferenceProperty等。各个属性内部实现不一,但都会提供setValue&getValue等方法。当然,为了统一标准,Cesium提供了Property类,提供了一套接口来供用户调用:

       这样,用户可以不必过于纠结具体的property,通过Property提供的方法获取结果,比如上述代码中给出的Property.getValueOrUndefined,内部调用getValue获取属性值。

createPropertyDescriptor

       当然,上述的这些Property还是处于Object类型,和obj._id还是同一个级别,和还需要经过封装,提供property1这样的形式,使其符合defineProperties规范,这样才能提供accessor的能力。这就是createProperty函数的作用:

       如上就是createProperty方法的一个精简版,粗看一下,创建了一个obj并返回,而且该对象有三个成员变量:configurable,set&get(function),和之前介绍的defineProperties.props参数如出一辙,现在可以肯定这个方法的作用就是把各种Property加工成符合defineProperties规范的属性。当然这样的判断并不严谨,感觉的成分更大一些,我们结合Entity.description属性的创建过程具体了解一下。

       如上是Entity.description属性,同时我还保留了id属性,用来参考。如果把createPropertyDescriptor函数替换为createProperty返回的obj,大概如下:

       这样就确定一定以及肯定了这个结论:createProperty就是返回一个符合defineProperties规范的属性。刨根问底的话,还是有两处疑问,首先,Entity.description调用的createPropertyDescriptor,又是如何传递到createProperty方法。其次,我们只是大概了了解了createProperty,内部具体做了什么还没有关注。逐个击破。

        可见,description是该属性的accessor,对应Entity内部的属性变量是'_' + name.toString(),也就是_description,这个是Cesium默认的属性变量和属性accessor接口之间的规范。configurable默认为false,createPropertyCallback如果为null,则采用ConstantProperty类型,对应的是createConstantProperty回调函数,暴露了我C++程序员的身份。这样,createPropertyDescriptor就很自然的过渡到createProperty方法。接着,再看看createProperty内部的实现:

       此时privatename就是_description,相比id属性,description属性本质上没有差别,只是多了一些繁枝缛节,多了一些异常判断,多了一些Event事件响应,当然还有对value的封装,比如description时,将value封装成了ConstantProperty类型,可以算的上是一个加强版的属性accessor方式。

       有了这样一套机制,不用写过多的代码,通过createPropertyDescriptor就可以很好的实现属性accessor的封装,代码间接,同时考虑了很多异常,并会有事件来满足用户的各种情况,提高稳定性。同时createPropertyCallback参数也提供了更多的选择,来满足我们的各类需求,或者没有需求,比如在Entity中,对属性accessor用如下三种形式:

  • createRawPropertyDescriptor
  • createPropertyTypeDescriptor
  • createPositionPropertyDescriptor

       有了上面的理解,我们大概做一下说明,应该不难理解他们的差别和不同的作用,我们在自己的使用场景中可以因地制宜。

createRawPropertyDescriptor&createPositionPropertyDescriptor

       这个没啥可说的吧,RawProperty比ConstantProperty还简单,人如其名,不加任何粉饰。同理createPositionPropertyDescriptor和createPropertyDescriptor也差不多,只是Property不是默认的ConstantProperty,而是ConstantPositionProperty。

createPropertyTypeDescriptors

       这个稍有不同,通过闭包的方式,这时对应的createPropertyCallback是用户自定义的,他甚至不是一个Property而是一个function类。我们可以这样理解,createPropertyDescriptor只是负责封装实现属性accessor,createPropertyCallback原意是让用户创建各类Property对象,但在Entity里面扩展了一下,并没用严格规定必须是Property类,而是可以用其他的function类,让createPropertyDescriptor的使用场景更广泛了。下面这种写法是否看起来就亲切很多,只是因为这类Graphics太多,所以Cesium用模版的思想抽象为Type参数,简化编码提高稳定,这也是值得我们学习的一点,Cesium在代码质量上的标准还是很严格的。

CallbackProperty

       刚才我们已经比较全面的介绍了Property相关的各类情况,尽管我们提到了CallbackProperty,但并没有涉及如何使用,有点遗忘的再回去看看CallbackProperty的实现,重点是getValue方法,我们下面通过一个例子,看看如何通过CallbackProperty实现自定义效果,范例链接

       当然,得益于之前Entity.description通过createPropertyDescriptor提供了accessor接口,这里,等号运算符会调用set方法,只是将oldValue(ConstantProperty)更改为value(CallbackProperty)。此时,内部通过Property.getValueOrUndefined->CallbackProperty.getValue->this._callback,实现回调函数的调用。

总结

       如上是Cesium在Property模块的一些实现原理和使用方式,我们简单总结一下知识点如下,不知道大家有什么收获,是否能够从Cesium优雅的设计和封装中学到什么奇技淫巧。

  • Property
  • ConstantProperty
  • CallbackProperty
  • defineProperties
  • createPropertyDescriptor
  • createRawPropertyDescriptor
  • createPositionPropertyDescriptor
  • createPropertyTypeDescriptor

<think>根据错误信息,问题是在渲染过程中片段着色器编译失败,错误日志为null。这通常与显卡驱动或WebGL环境有关,但也可能是由于Cesium的某些特定问题。以下是一些可能的解决方案: 1. **更新显卡驱动**:确保你的显卡驱动是最新的。 2. **检查WebGL支持**:访问[WebGL Report](https://webglreport.com/)检查浏览器是否支持WebGL 2.0(Cesium推荐使用WebGL 2.0)。 3. **尝试其他浏览器**:比如Chrome或Firefox的最新版本。 4. **检查Cesium版本**:确保使用的是最新版本的Cesium。如果使用的是本地服务器,尝试更新Cesium库。 5. **检查代码中是否有自定义着色器**:如果有自定义着色器代码,请检查其正确性。 6. **简化场景**:如果场景复杂,尝试逐步移除实体,以确定是哪个实体导致的问题。 7. **硬件加速**:确保浏览器启用了硬件加速。 8. **清除缓存**:清除浏览器缓存或尝试无痕模式。 由于错误信息中提到了片段着色器编译失败,并且日志为null,这可能是由于着色器代码中的错误,但Cesium未能捕获具体错误。可以尝试以下步骤: - 在Cesium初始化时开启调试: ```javascript viewer = new Cesium.Viewer('cesiumContainer', { contextOptions: { webgl: { preserveDrawingBuffer: true // 有助于调试 } } }); ``` - 使用Cesium的调试工具:在浏览器控制台中输入`viewer.scene.debugShowWebGLErrors = true;`,这可能会提供更多错误信息。 如果以上方法都不能解决问题,可能是由于特定硬件兼容性问题。可以尝试在Cesium初始化时设置`requestWebGL1: true`来降级到WebGL 1.0: ```javascript viewer = new Cesium.Viewer('cesiumContainer', { contextOptions: { webgl: { requestWebGL1: true } } }); ``` 另外,检查控制台是否有其他错误,比如资源加载失败(如地形、影像等),这些有时也会间接导致渲染问题。 由于错误发生在渲染过程中,且具体错误信息不足,需要逐步排查。如果问题仍然存在,可以考虑在Cesium的GitHub仓库中提交issue,提供详细的错误信息和环境描述。 §§相关问题§§ 1. 如何获取更详细的WebGL错误信息以帮助诊断片段着色器编译失败的问题? 2.Cesium中,如何降级到WebGL 1.0以解决兼容性问题? 3. 除了更新显卡驱动,还有哪些方法可以解决WebGL渲染问题? 4. 如何检查浏览器是否支持WebGL 2.0? 5.Cesium中,如何开启调试模式以获取更多的渲染错误信息?</think>这个错误表明 Cesium 在渲染过程中遇到了片段着色器编译失败的问题。着色器是 WebGL 的核心组件,用于控制图形渲染效果。以下是对此问题的分析和解决方案: ### 错误原因分析 1. **着色器编译失败**: - 片段着色器负责计算每个像素的颜色 - 错误日志为 `null` 表示浏览器未提供具体错误信息 - 常见于 WebGL 兼容性问题或着色器代码错误 2. **可能原因**: - 显卡驱动过时或不兼容 - 浏览器 WebGL 实现存在缺陷 - Cesium 版本与当前环境不兼容 - 自定义着色器代码错误 - 硬件加速被禁用 ### 解决方案 #### 1. 基础检查 ```javascript // 检查WebGL支持 if (!Cesium.FeatureDetection.supportsWebgl()) { console.error("浏览器不支持WebGL"); } // 检查着色器编译支持 const context = scene.context; console.log("最大纹理尺寸:", context.maximumTextureSize); console.log("着色器精度:", context.fragmentShaderPrecision); ``` #### 2. 浏览器端修复 ```javascript // 在Cesium初始化时添加错误处理 const viewer = new Cesium.Viewer('cesiumContainer', { contextOptions: { webgl: { preserveDrawingBuffer: true // 有助于调试 } } }); // 启用详细错误日志 viewer.scene.debugShowWebGLErrors = true; ``` #### 3. 代码级修复 ```javascript // 1. 降级到WebGL 1.0 const viewer = new Cesium.Viewer('cesiumContainer', { contextOptions: { webgl: { requestWebGL1: true // 使用WebGL 1.0 } } }); // 2. 禁用高级渲染功能 viewer.scene.highDynamicRange = false; viewer.scene.fxaa = false; ``` #### 4. 系统/环境修复 1. **更新显卡驱动**:确保使用最新版本 2. **启用硬件加速**: - Chrome:`设置 > 系统 > 使用硬件加速模式` - Firefox:`选项 > 常规 > 性能 > 使用推荐性能设置` 3. **浏览器更新**:使用最新版 Chrome/Firefox/Edge 4. **清除缓存**:清除浏览器缓存和 WebGL 状态 ### 替代方案 如果问题持续存在,考虑回退到稳定版本: ```bash npm install cesium@1.107 # 使用长期支持版本 ``` ### 调试建议 在浏览器控制台运行: ```javascript // 检查WebGL上下文 const canvas = document.querySelector('canvas'); const gl = canvas.getContext('webgl'); console.log(gl.getShaderInfoLog(gl.createShader(gl.FRAGMENT_SHADER))); // 检查Cesium状态 console.log(Cesium.Context.getNumberOfTextureUnits()); console.log(Cesium.Context.maximumTextureSize); ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值