Cesium官方教學——Fabric

2023-03-21 12:04:09

1、簡介

Fabric 是Cesium中定義的描述材質Material的JSON 結構體。Material代表了一個物體的外觀。

材質Material可以是比較簡單的,比如直接將一張圖片賦予表面,或者使用條紋狀、棋盤狀的圖案;也可以使用FabricGLSL,重新建立一個新的材質或者組合現有的材質。例如,我們可以通過程式生成的紋理(procedural brick)、凹凸貼圖(bump map)和高光貼圖(specular map)來生成一個潮溼碎裂的磚塊材質。

在Cesium中支援賦予材質的圖元都有一個material屬性。

polygon.material = Material.fromType('Color');

上邊這行程式碼中,Color是一個內建材質屬性,代表了單一顏色,包括透明度(alpha)。Material.fromType是一個快捷的方式,完整的Fabric JSON 應該是這樣:

polygon.material = new Cesium.Material({
  fabric : {
    type : 'Color'
  }
});

每一種材質(material)都有uniforms屬性,uniforms可以在建立材質(material)時或之後設定。

注:uniform是一個webgl概念,用於與頂點無關的資料。

polygon.material = new Cesium.Material({
  fabric : {
    type : 'Color',
    uniforms : {
      color : new Cesium.Color(1.0, 0.0, 0.0, 0.5)
    }
  }
});

// Change from translucent red to opaque white
polygon.material.uniforms.color = Cesium.Color.WHITE;

2、內建材質

Cesium有一些內建的材質,常用的兩種為:

名稱 縮圖 描述
Color 顏色,包括透明度
Image 圖片,可以沒有透明度(.jpg),也可以包含透明度(.png)格式;
漫反射、rgb、alpha通道組合。

所有內建的材質都可以像我們建立顏色那樣簡單的建立:

polygon.material = Material.fromType('Image');
polygon.material.uniforms.image = 'image.png';

也可以這樣建立:

polygon.material = new Cesium.Material({
  fabric : {
    type : 'Image',
    uniforms : {
      image : 'image.png'
    }
  }
});

2.1 程式化紋理

程式化紋理不直接依賴於圖片檔案,而是通過GPU計算而來,他們可以設定漫反射(diffuse)和透明度(alpha)

名稱 縮圖 描述
Checkerboard Checkerboard with alternating light and dark colors.
Stripe Alternating light and dark horizontal or vertical stripes
Dot A pattern of dots organized by row and column.
Grid A grid of lines, useful for displaying 3D volumes.

2.2 基礎材質

基礎材質較為底層,可以設定基礎的材質特性,比如某個方向有多少光線被反射(鏡面強度)、或者光線發射量等等。這些材質特性通常組合起來使用,從而建立一個複雜的材質。

Name 註釋 Screenshot Description
漫反射貼圖
DiffuseMap
可以表現出物體被光照射到而顯出的顏色和強度 一張具有一個三維向量(vec3)的圖片,定義了光在所有方向上的散射顏色。
高光貼圖
SpecularMap
高光貼圖是用來表現當光線照射到模型表面時,其表面屬性的 一張具有標量分量的圖片,定義了單一方向反射的入射光的強度。
通過用於使物體表面的一部分有光澤,比如:水和地面。
透明貼圖
AlphaMap

一張具有標量分量的圖片,定義了材質的透明度。通常用於使物體表面的一部分有透明度,比如:刪欄。


法線貼圖
NormalMap

一張具有三維向量的圖片,定義了物體表面發現的切線座標。通常用於增加物體表面的細節。
凹凸貼圖
BumpMap
也叫高度貼圖,豐富物體表面的細節。 一張具有標量分量的圖片,類似法線貼圖,凹凸貼圖用於增加物體表面的細節無需通過實際修改物體表面。
自發光貼圖
EmissionMap

一張具有三維向量(vec3)的圖片,定義了材質在所有方向上發射的光線。例如:沿著一條公路的燈

2.3 多段線(polyline)材質

多段線材質只可以用於線狀的物體。

Name 註釋 Screenshot
帶箭頭線
PolylineArrow
Places an arrow head at the end point of a line.
發光線
PolylineGlow
Makes glowing lines.
帶邊框的線
PolylineOutline
Line outline.

2.4 其他材質

名稱 註釋 簡介
Water Animating water with waves and ripples.
高亮輪廓
RimLighting
突出邊緣輪廓

更多的內建材質,請檢視 Cesium Materials Plugin.

3、uniform

很多的材質(material)都有image uniform,定義了圖片路徑或資料路徑:

polygon.material.uniforms.image = 'image.png';
polygon.material.uniforms.image = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAC/SURBVDhPrZPRDYQgEEQpjVKuFEvhw0IoxU6QgQwMK+vdx5FsooT3GHdjCM4qZnnnHvvkYoxFi/uvIhwiRCClXFC6v5UQ1uQAsbrkHCLsbaPjFgIzQQc1yUOwu33ePGE3BQUaee2BpjhbP5YUmkAlbNzsAURfBDqJnMIyyv4JjsCCgCnIR32uZUfcJuGBOwEk6bOKhoAADh31EIq3MgFg1mgkE1BA2AoUZoo2iZ3gyqGgmMDC/xWwkfb3/eUd7A1v3kxjNW9taQAAAABJRU5ErkJggg=='

一些屬性,例如漫反射(diffuse)或法線貼圖(NormalMap)的屬性,要求圖片的每個畫素具有RGB三個通道;其他材質屬性,例如高光(specular)和透明度(alpha),要求具有一個通道。我們可以指定從紋理圖片的那個通道中獲取資料,通過channels或者channel來設定。
例如,預設情況下高光specular屬性讀取r通道,我們可以改變讀取的通道:

polygon.material = new Cesium.Material({
  fabric : {
    type : 'SpecularMap',
    uniforms : {
      image : 'specular.png',
      channel : 'a'
    }
  }
});

允許多個材質從相同的圖片中讀取資料,例如一張相同的圖片即儲存了漫反射屬性(diffuse)所需的rgb通道,也儲存了高光屬性(specular)所需的a通道。這樣這張圖片只會載入一次,節省了資源。

材質material也可以使用images的repeat屬性,控制圖片在橫向和縱向的重複次數。經常被用於在物體表面的瓦片紋理。

polygon.material = new Cesium.Material({
  fabric : {
    type : 'DiffuseMap',
    uniforms : {
      image : 'diffuse.png',
      repeat : {
        x : 10,
        y : 2
      }
    }
  }
});

4、建立一個新的材質

建立一個新的材質,可以通過Fabric、一些GLSL程式碼,以及其他的材質組合起來。
如果新建立的材質只使用一次,可以不設定Type屬性:

var fabric = {
   // no type
   // ...rest of fabric JSON
};
polygon.material = new Cesium.Material({
  fabric : fabric
});

當使用一個新的材質型別type時,材質會在第一次繪製時快取,之後的繪製(通過new Cesium.Material或者Material.fromType)就可以直接使用快取的材質,就像使用內建材質一樣,不需要提供完整的Fabric描述,只需要通過type和定義需要使用的uniforms即可。

var fabric = {
   type : 'MyNewMaterial',
   // ...rest of fabric JSON
};
polygon.material = new Cesium.Material({
  fabric : fabric
});
// ... later calls just use the type.
anotherPolygon.material = Material.fromType('MyNewMaterial');

4.1 Components

可能最簡單的使用材質的方式就是直接反射白色:

var fabric = {
  components : {
    diffuse : 'vec3(1.0)'
  }
}

稍微複雜的例子是新增高光反射屬性specular component,這樣材料的反射光線在直視的時候最強烈,而在斜視時就變弱。

{
  components : {
    diffuse : 'vec3(0.5)',
    specular : '0.1'
  }
}

components屬性包含了定義材料外觀的子屬性。每個子屬性都是一個GLSL程式碼片段,比如上面範例程式碼中的vec(0.5)建立了一個3D向量,3D向量中每個分量都被設定為0.5,。
共有以下幾個子屬性:

屬性名 預設值 描述
漫反射
diffuse
'vec3(0.0)' 漫反射屬性是一個三維向量vec3,這個向量定義了光在所有方向上的散射值。
高光
specular
0.0 高光屬性,使用浮點數定義了單一方向上的反射強度。
光澤度
shininess
1.0 鏡面反射的光澤度。越高的值建立一個更小、更集中的鏡面高光。
法線
normal

材質的法線屬性是一個三維向量vec3,定義了視點座標系下的表面法向量。通常用於法向貼圖。預設值是物體表面預設的發現。
自發光
emission
'vec3(0.0)' 自發光屬性使用三維向量vec3定義,定義了在所有方向上燈光發出的顏色值。預設是vec3(0.0),也就是不發光。
alpha 1.0 阿法爾通道使用浮點數定義,0.0表示全透,1.0表示不透明。

總的來說,這些子屬性定義了材質的特點,他們是材質material的輸出值,同時也是光照系統的輸入值。

4.2 Source

source屬性通過為czm_getMaterial函數定義GLSL程式碼的方式,提供了一個更加靈活的定義材質的方式。

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);

使用source最簡單的方式是每一個屬性都直接返回預設值。

czm_material czm_getMaterial(czm_materialInput materialInput)
{
    return czm_getDefaultMaterial(materialInput);
}

Fabric定義:

{
  source : 'czm_material czm_getMaterial(czm_materialInput materialInput) { return czm_getDefaultMaterial(materialInput); }'
}

我們把之前的例子中通過source來實現的:

// 通過FABRIC 定義
{
	components : {
		diffuse : 'vec3(0.5)',
		specular : '0.1'
	}
}

// 通過source
{
	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;
	}`
}

使用source代替components的方式程式碼量更加多,但也會更加靈活,包括可以為不同的元件公用相同的計算邏輯和函數。一個經驗法則是在大多數情況下使用components,除非需要用到czm_getMaterial的靈活性。在底層實現上,components子屬性也是通過czm_getMaterial來實現的。在這兩種方式下,我們都可以用到GLSLCesium提供的內建的函數、變數、常數等。

4.3 Input

materialInput變數在sourcecomponents屬性中都是有效的,materialInput具有以下引數欄位:

名稱 型別 描述
**s** float 一維紋理座標
**st** vec2 二維紋理座標
**str** vec3 三維紋理座標。> 注意:一維、二維、三維紋理座標之間並不一定分量相同,不能保證str.st == st and st.s == s。例如,對於一個橢圓體,一維紋理座標s可能是從底部到頂部,二維紋理座標st是經緯度座標,三維紋理座標是沿著座標軸的包圍盒。

|
| **tangentToEyeMatrix** | mat3 | 片元切線空間到視點座標系的轉換矩陣,通常用於法線貼圖和凹凸貼圖中。 |
| **positionToEyeEC** | vec3 | 在視點座標系下從片元到視點的向量,用於反射、折射等等。值大小表示從片元到視點的距離。 |
| **normalEC** | vec3 | 片元在視點座標系下單位化後的發現了,用於凹凸貼圖、反射、折射等。 |

一個使用二維紋理座標st的簡單材質使用如下:

{
  components : {
    diffuse : 'vec3(materialInput.st, 0.0)'
  }
}

同樣的我們可以設定materialInput.normalECdiffuse,以在視點座標系下法線的視覺化。
除此之外,對於materialInput,材質還可以使用uniform變數,不管是Cesium內建的uniforms還是材質特定的uniforms
例如,我們可以實現我們自己的Color材質,基於color uniform設定diffuse以及alpha屬性。

{
  type : 'OurColor',
  uniforms : {
    color : new Color(1.0, 0.0, 0.0, 1.0)
  },
  components : {
    diffuse : 'color.rgb',
    alpha : 'color.a'
  }
}

Fabric中,uniform屬性的子屬性也是**GLSL**中的uniforms的名稱,以及使用new Material以及Material.fromType中的返回的**JavaScript**物件。子屬性的值也是uniform的值(對於標量及向量)。
我們可以通過image uniform實現我們自己的diffuseMap屬性

{
  type : 'OurDiffuseMap',
  uniforms : {
    image : 'czm_defaultImage'
  },
  components : {
    diffuse : 'texture2D(image, materialInput.st).rgb'
  }
}

在上面程式碼中,'czm_defaultImage'是一個1x1預留位置影象,也可以使用影象URL或資料URL來指定紋理影象。例如,使用者可以這樣來建立一個OurDiffuseMap

polygon.material = Material.fromType('OurDiffuseMap');
polygon.material.uniforms.image = 'diffuse.png';

還有一個立方圖預留位置 czm_defaultCubeMap。支援標準的GLSLuniform 型別,floatvec3mat4等等。Uniform陣列還不支援。

4.4組合材質

到目前為止,我們可以使用內建材質,或者建立我們自己的材質。我們也可以從現有的材質中建立材質(遞迴),形成材質的層次結構。

材質Fabric具有materials屬性,這個materials屬性的子屬性的值都是一個材質Fabric,也就是一種材質。這些materials可以在componentssource屬性中參照。例如,一個代表塑料的材質可以通過設定diffuseMapspecularMap來實現。

{
  type : 'OurMappedPlastic',
  materials : {
    diffuseMaterial : {
      type : 'DiffuseMap'
    },
    specularMaterial : {
      type : 'SpecularMap'
    }
  },
  components : {
      diffuse : 'diffuseMaterial.diffuse',
      specular : 'specularMaterial.specular'
  }
};

上邊這段程式碼中,components屬性中有漫反射diffuse以及鏡面反射specular屬性,他們從材質的materials屬性中提取數值。命名為diffuseMaterialspecularMaterial的子屬性(通過型別DiffuseMapSpecularMap建立,不要混淆了名稱——範例——型別(也可以叫類))。在componentssource屬性中,子屬性可以通過欄位名來存取,就像他們是czm_material結構體,因此在上面程式碼中通過.diffuse.specular欄位可以存取。

鑑於這一點,我們的材質material可以像其他材質material一樣被使用。

var m = Material.fromType('OurMappedPlastic');
polygon.material = m;

m.materials.diffuseMaterial.uniforms.image = 'diffuseMap.png';
m.materials.specularMaterial.uniforms.image = 'specularMap.png';

5、Fabric 的結構

FabricJSON結構描述在Cesium倉庫中。所有Fabric屬性、子屬性的細節,包括type, materials, uniforms, components, source。有一些JSON。
除了更嚴格的Fabric檔案外,該模式還可用於使用JSV等工具來驗證Fabric。

6、渲染管線中的材質(使用GLSL自定義材質)

幾何物件包括 Polygon, PolylineCollection, Ellipsoid, CustomSensorVolume,等,都與材質系統整合以支援材質。大多數使用者只需簡單設定他們所需的材質屬性即可,但對於想用書寫自定義渲染程式碼的使用者,可能也需要與材質系統結合。

從渲染的角度,材質是GLSL 函數,czm_getMaterial,以及uniforms的組合。片元著色器需要構造一個czm_MaterialInput,呼叫czm_getMaterial函數,之後將計算的czm_material結果傳遞給lighting函數,以計算片元著色器顏色。

在JavaScript中,物件應該有一個公共的material屬性。當這個屬性變化時,update函數應該將材質中的GLSL源預置到幾何物件的片元著色器中,並且組合幾何物件和材料的uniforms

var fsSource =
  this.material.shaderSource +
  ourFragmentShaderSource;

this._drawUniforms = combine([this._uniforms, this.material._uniforms]);