Fabric 是 Cesium 中用於描述材質的一種 JSON 規定。數組
材質表現了多邊形、折線、橢圓等形狀的外觀。緩存
使用 Fabric 和 GLSL,能夠徹底自定義材質。app
經過幾何對象的 material
屬性能夠建立材質,這個屬性是 Cesium.Material
對象。函數
能夠這麼用:this
// 假設 polygon 是一個 primitive polygon.appearance.material = Cesium.Material.fromType('color');
這就建立了一個只有顏色的材質,包括透明度的顏色。Cesium.Material.fromType()
方法是一個簡寫,完整的寫法是:編碼
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'Color' // 大寫 } })
每個 Material 均可以有 0 ~ N 個 uniform,這個參數在建立時指定,也能夠在渲染後修改。例如,color 類型的 Material 就有格式爲 rgba 的顏色 uniform:url
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'Color', uniforms: { color: new Cesium.Color(1.0, 0.0, 0.0, 0.5) } } }) // 修改顏色 polygon.appearance.material.uniforms.color = Cesium.Color.WHITE
Cesium 有幾個內置的材質。列舉兩個比較經常使用的:3d
材質類型 | 截圖 | 描述 |
---|---|---|
type: 'Color' |
一個簡單的顏色,包括透明通道 | |
type: 'Image' |
jpg 或 png 貼圖類型的材質 |
全部的內置材質能夠簡單地使用 Cesium.Material.fromType()
方法建立:code
polygon.appearance.material = Cesium.Material.fromType('Image') polygon.appearance.material.uniforms.image = 'image.png'
或者用全寫法:component
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'Image', uniforms: { image: 'image.png' } } })
從這兒開始,介紹因這個 fabric 對象中的 type
不一樣的十幾種內置紋理,2.1~2.5
只需指定幾個參數,就能夠生成一些有規律的紋理貼圖,不須要依賴外部貼圖文件。它們至關於漫反射+透明度的組合。
類型 | 截圖 | 描述 |
---|---|---|
type: 'Checkerboard' |
國際象棋格子 | |
type: 'Stripe' |
豎條紋旗幟 | |
type: 'Dot' |
行列點陣 | |
type: 'Grid' |
線狀網格,顯示一些網狀結構的圖形 |
基礎材料表達的是各個材質因子表示的材料特徵,例如鏡面反射強度、自發光。一般,組合在一個 fabric 對象中建立複雜的材質。
注:若是不懂這些東西,能夠請教技術美工。
類型 | 截圖 | 描述 |
---|---|---|
type: 'DiffuseMap' |
漫反射貼圖,即最多見的貼圖,一般是 rgb 三個顏色 | |
type: 'SpecularMap' |
單通道貼圖,表示的是入射光強度貼圖 | |
type: 'AlphaMap' |
單通道的不透明度貼圖 | |
type: 'NormalMap' |
三通道貼圖,表示的是法線貼圖 | |
type: 'BumpMap' |
單通道的凹凸貼圖 | |
type: 'EmissionMap' |
三通道的自發光貼圖 |
折線材質只做用於折線圖形。
類型 | 截圖 | 描述 |
---|---|---|
type: 'PolylineArrow' |
箭頭線,終點在折線末端 | |
type: 'PolylineGlow' |
發光線 | |
type: 'PolylineOutline' |
描邊線 |
還有一些材質不屬於上面的分類,例如:
類型 | 截圖 | 描述 |
---|---|---|
type: 'Water' |
水面貼圖,看起來有水波動效 | |
type: 'RimLighting' |
邊緣會比較亮 |
許多材質是有 image
的,多是一個 base64 編碼的字符串或文件路徑:
polygon.appearance.material.uniforms.image = 'image.png'; polygon.appearance.material.uniforms.image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAC/SURBVDhPrZPRDYQgEEQpjVKuFEvhw0IoxU6QgQwMK+vdx5FsooT3GHdjCM4qZnnnHvvkYoxFi/uvIhwiRCClXFC6v5UQ1uQAsbrkHCLsbaPjFgIzQQc1yUOwu33ePGE3BQUaee2BpjhbP5YUmkAlbNzsAURfBDqJnMIyyv4JjsCCgCnIR32uZUfcJuGBOwEk6bOKhoAADh31EIq3MgFg1mgkE1BA2AoUZoo2iZ3gyqGgmMDC/xWwkfb3/eUd7A1v3kxjNW9taQAAAABJRU5ErkJggg==';
有的材質要求貼圖有三個顏色份量,而其餘材料(如高光和透明貼圖)的貼圖只須要一個顏色份量。
能夠指定貼圖的通道。例如,鏡面反射的材質中,鏡面反射默認取貼圖的自 r 顏色通道,可是能夠修改它爲 a 通道:
polygon.appearance.material = new Cesium.Material({ fabric : { type : 'SpecularMap', uniforms : { image : 'specular.png', channel : 'a' } } });
這意味着,容許把各類貼圖集中到一個貼圖文件種,而後使用不一樣的通道便可,減小加載請求次數。
有些材質的貼圖紋理能夠重複屢次繪製,例如水平或垂直上的重複:
polygon.appearance.material = new Cesium.Material({ fabric: { type: 'DiffuseMap', uniforms: { image: 'diffuse.png', repeat: { x: 10, y: 2 } } } })
使用 fabric 對象 + GLSL 代碼和其餘素材,就能夠建立材質。
若是一個材質不想被複用,那麼就不要指定它的 type
屬性。
let fabric = { // ... } polygon.appearance.material = new Cesium.Material({ fabric: fabric })
當 fabric 對象中的 type
屬性以前是沒有指定過的,那麼在第一次調用 new Cesium.Material()
時,這個新的 fabric 材質將被緩存,隨後再次 new Material 或 Material.fromType() 時將從緩存中取用。
let fabric = { type : 'MyNewMaterial', // ...其餘 fabric JSON 的屬性 } polygon.appearance.material = new Cesium.Material({ fabric : fabric }); // ... 而後在另外一處須要這個 fabric anotherPolygon..appearance.material = Material.fromType('MyNewMaterial');
白色的漫反射材質或許是最經常使用的:
let fabric = { components: { diffuse: 'vec3(1.0)' } }
稍微複雜一些,加一點鏡面反射,使得正射視角看反光最強,側面變弱:
let fabric = { components : { diffuse : 'vec3(0.5)', specular : '0.1' } }
components
屬性包含了 fabric 所定義的材質的各類子因素。每一個子因素均使用簡短的 glsl 代碼字符串表示,所以上面寫的 vec(0.5)
就表示 rgb 均爲 0.5 的一種顏色。這個簡單的 glsl 代碼可使用全部 glsl 內置的函數,例如 mix、cos、texture2D 等。components
能夠定義 6 個屬性:
名稱 | 默認值 | 描述 |
---|---|---|
diffuse |
'vec3(0.0)' |
漫反射顏色,即物體的基本顏色 |
specular |
'0.0' |
鏡面反射,定義的是單方向反射光強度 |
shininess |
'1.0' |
鏡面反射的清晰度,這個值越大會出現更小的高光光斑 |
normal |
法線,默認沒法線 | |
emission |
'vec3(0.0)' |
自發光,默認不發光 |
alpha |
'1.0' |
不透明度,0.0 是徹底透明,1.0 是不透明。 |
components
還有一個更強大而靈活的選擇是 glsl 源代碼,經過 glsl 的方式修改材質。這個途徑將設置的 glsl 代碼傳遞到 czm_getMaterial
函數,這個函數執行後返回材質的 components:
struct czm_materialInput { float s; vec2 st; vec3 str; mat3 tangentToEyeMatrix; vec3 positionToEyeEC; vec3 normalEC; }; struct czm_material { vec3 diffuse; float specular; float shininess; vec3 normal; vec3 emission; float alpha; }; czm_material czm_getMaterial(czm_materialInput materialInput);
默認狀況下,材質的默認值會被返回:
czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }
這個時候的 fabric 對象是:
let fabric = { components: { source: `czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }` } }
上面修改了漫反射和鏡面反射的例子能夠經過 glsl 改寫爲:
let fabric = { source: `czm_material czm_getMaterial(czm_materialInput materialInput) { czm_material m = czm_getDefaultMaterial(materialInput); m.diffuse = vec3(0.5); m.specular = 0.5; return m; }` }
使用 glsl 代替 components 雖然看起來代碼比較冗長,可是提供了靈活性。
若是不是有特別的需求,使用 components 屬性指定材質的各類因子就能夠了。可是,不論是哪種,在這些 glsl 代碼中,都是能夠直接使用 glsl 的內置函數和 Cesium 預約義的 函數、結構體、常量的。
materialInput
變量在 source 和 components 中都可以使用,在 glsl 代碼的定義中,這個變量是 czm_materialInput
結構體有以下字段:
名稱 | 類型 | 描述 |
---|---|---|
s |
float |
一維紋理座標 |
st |
vec2 |
二維紋理座標 |
str |
vec3 |
三維紋理座標,三維紋理的二維部分不必定就是二維紋理座標,切記。例如,在一個橢球幾何中,s可能就是從下到上,st多是經緯度,str三維紋理就是包圍盒的三軸方向。 |
tangentToEyeMatrix |
mat3 |
用於法線貼圖、凹凸貼圖的轉換矩陣,轉換切線空間座標到視圖座標 |
positionToEyeEC |
vec3 |
從 fragment 到 視圖空間座標的向量(不知道這個 fragment 說的是什麼),用於反射和折射等。向量的長度是 fragment 到視圖(相機)的距離。 |
normalEC |
vec3 |
fragment 在視圖座標中的法線(已歸一化),做用於凹凸貼圖、反射、折射等 |
例如能夠這麼設置來可視化紋理座標:
let fabric = { components: { diffuse: 'vec3(materialInput.st, 0.0)' } }
同樣的,能夠把 diffuse 組件設置爲 materialInput.normalEC
來可視化法線。
除了 materialInput
這個傳入的參數,還能夠訪問 Cesium 提供的 uniform 變量。
例如,能夠經過一個 color uniform 去設置 diffuse 組件和 alpha 組件,來建立本身的 Color 材質:
let fabric = { type: 'MyColor', uniforms: { color: new Color(1.0, 0.0, 0.0, 1.0) }, components: { diffuse: 'color.rgb', alpha: 'color.a' } }
在 fabric 中,glsl 中的 uniform 變量、new Cesium.Material()
和 Cesium.Material.fromType()
返回的 js 對象中的 uniform 變量與 uniforms
屬性的子屬性(例如這裏的 uniforms.color)具備相同的名稱。
子屬性的值(對於標量來講)或子屬性(對於向量來講)即 uniform 的值。
官方這說的什麼東西...
下例,經過 image uniform 來實現自定義的 DiffuseMap 材質:
let fabric = { type: 'OurDiffuseMap', uniforms: { image: 'czm_defaultImage' }, components: { diffuse: 'texture2D(image, materialInput.st).rgb' } }
czm_defaultImage
是 1x1 分辨率的圖片,根據上面的說法,這能夠從 dataurl 或圖片文件中獲取,例如:
polygon.appearance.material = Material.fromType('OurDiffuseMap'); polygon.appearance.material.uniforms.image = 'diffuse.png';
還有一個多維數據集的變量:czm_defaultCubeMap
。
支持 glsl 的 uniform 類型,例如 float、vec三、mat4 等。
對於 uniform 數組還不支持,不過在規劃中了。
譯者注
這段是真夠燒腦子的,不知所云。
梳理一下,在 fabric 這個對象中,uniforms 下的全部屬性均被 glsl 認做是 uniform 變量,能夠直接當變量使用,例如上例中的
texture2D(image, materialInput.st)
中的 image 參數。在這裏,規定了 image 這個 uniform 的類型是 Cesium 內置的
czm_defaultImage
結構體類型。
到如今爲止,可使用內置的材質或者經過指定 fabric 中的 components 屬性(或直接使用 glsl 源代碼)來建立材質對象。
還能夠經過現有材質來建立複合類型的材質。
fabric 對象有一個 materials
屬性,它的每個子對象都可以是 fabric 對象,最終即一個材質對象。在 materials
中設置的子一級 fabric,能夠在最頂級的 fabric 的 components、source 中引用。例如,如今將 DiffuseMap 材質 和 SpecularMap 材質建立一個複合材質:
let rootFabric = { type: 'OurMappedPlastic', materials: { diffuseMaterial: { type: 'DiffuseMap' // 基本類型中的一種 }, specularMaterial: { type: 'SpecularMap' } }, components: { diffuse: 'diffuseMaterial.diffuse', specular: 'specularMaterial.specular' } }
這個 rootFabric 材質擁有兩個組件:diffuse(漫反射)和 specular(鏡面反射強度),而這兩個組件的值是來自 materials 中的兩個子 fabric。顯然,在 materials
定義的兩個子屬性的名稱,將會在 components 的 glsl 代碼中被做爲變量使用,直接點出 diffuse 和 specular 字段。
而後就能夠像其餘材質同樣使用它了:
let m = Cesium.Material.fromType('OurMappedPlastic') polygon.appearance.material = m; m.materials.diffuseMaterial.uniforms.image = 'diffuseMap.png'; m.materials.specularMaterial.uniforms.image = 'specularMap.png';
用了這麼多 fabric 對象,其實這個 fabric 對象是有一些規定的。
在 Cesium 的官方打包包中,找到 Documentation/Schemas/Fabric
,就是 fabric 對象的規定。諸如 type
、materials
、uniforms
、components
、source
這幾個屬性均能找到詳細的定義。
從渲染的角度看,一種材質實際上是一個 glsl 函數:czm_getMaterial
。片元着色器須要構造一個 czm_MaterialInput
結構體變量,調用 czm_getMaterial
函數,而後生成 czm_material
結構變量,傳遞給照明函數來計算片元的顏色。
在 js 中,fabric 對象應該有一個 material
屬性。當此屬性發生變更時,圖元的 update 函數觸發,而後將 fabric 材質最終的 glsl 代碼與默認的片元着色器代碼合併在一塊兒,而後再將 uniform 合併:
const fsSource = this.material.shaderSource + ourFragmentShaderSource; this._drawUniforms = combine([this._uniforms, this.material._uniforms]);
這篇文章較爲詳細地介紹了 Appearance 對象中的 fabric 屬性構成。
fabric 是一個有官方規定如何寫的 js 對象,它擁有 5 個屬性:
其中,type 用於聲明 fabric 對象最終會生成什麼材質,若是是官方內置的,直接用官方內置的(2.1~2.4),不然則建立自定義的材質並緩存。
materials 容許再塞進去子一級的 fabric,構成複合材質。
uniforms 是一些全局變量,例如你能夠在這裏寫一個 myUniformVariable
,而後你就能夠在 components 或者 source 的 glsl 代碼中用到這個 uniform 變量了。
source 是 glsl 源代碼,它主要是對 czm_getMaterial
這個 Cesium 內置的 glsl 函數的實現,返回值是 czm_material
components 是幾個基本材質因子的 glsl 代碼快捷入口,是 source 的一種簡略實現。
建立好 fabric 材質對象後,隨之就能夠建立 Appearance 對象,與幾何實例一塊兒建立 Primitive 了。