Cesium官方教程12--材质(Fabric)
2020-03-05 17:46:19
1128次阅读
0个评论
最后修改时间:2020-03-15 00:03:59

原文地址:https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric


介绍


Fabric 是Cesium中基于JSON格式来描述materials的机制。材质描述多边形、折线、椭球等对象的外观特征。
材质可以简单的是覆盖一张图片,或者是条纹或者棋盘图案。使用Fabric 和GLSL,可以从零开始写脚本新建材质,也可以从现有的材质中派生。比如潮湿碎裂的砖块可以使用程序生成的纹理、凹凸贴图和反射贴图来组合。
对象通过material属性来支持材质效果。当前这些对象是多边形、折线、椭球等(这篇文章写的较早,其实现在已经很多几何体都支持材质了)。

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

上面,Color是一个内置材质,它表示了包含透明度在内的一个颜色值。Material.fromType是简略写法,完整的Fabric的JSON应该是这样的:

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

每一个材质包含0或者更多个uniforms,uniform是一种输入参数变量,在创建材质时或者创建材质后修改。比如 ,Color有一个color uniform ,它包含red,green,blue, 和alpha四个部件。

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

// 把红色半透明修改为 白色不透明
polygon.material.uniforms.color = Cesium.Color.WHITE;


内置材质


Cesium有一些内置材质,应用最广泛的是这两个:

image.png

如同上面的Color一样,所有的内置材质都可以这么创建。比如:

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

或者

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


程序生成的纹理 (Procedural Textures)


程序生成的纹理,他们不依赖于外部图片文件,是通过GPU编程计算的图案,他们可以表示颜色和透明。

image.png

基本材质


Base materials represent fine-grain fundamental material characteristics, such as how much incoming light is reflected in a single direction, i.e., the specular intensity, or how much light is emitted, i.e., the emission. These materials can be used as is, but are more commonly combinedusing Fabric to create a more complex material.

image.png

折线材质


这只一种只能添加到折线几何体上的材质。

image.png

其他材质


还有一些不适合归到其他类的材质

image.png

了解更多材质,可以去看下这个 Cesium Materials Plugin.


通用的Uniforms


很多材质都有一个image uniform,它是一个图片访问地址,或者数据URI。

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要求图片有一个通道。我们可以指定渲染的时候从哪些通道(或者什么顺序)从原始图片中读取数据,通过channel这个字符串uniform来设置。比如,默认Specular材质是从r读取高光反射参数。不过我们可以如下修改它:

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

这就是说可以把多个材质的信息放到一个图片里,比如在同一个图片内,用rgb通道存储diffuse值,用a通道存储specular值。也就是说,我们的图片只需要载入一次。
通常材质里有一个repeat uniform,它控制了图片在水平和垂直方向重复了多少次。这个在表面重复贴图的时候很方便:

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


创建新的材质


使用Fabric,只需要一点点GLSL或者其他材质就可以了。
如果不打算复用材质,那么不要设置type参数。

var fabric = {
   // 没有类型
   //fabric的剩余JSON值
};
polygon.material = new Cesium.Material({
  fabric : fabric
});


当在new Cesium.Material时,传入一个不存在的type类型之后,这个材质将被缓存下来。下次调用new Cesium.Material或者Material.fromType就会引用缓存里的,就如同我们内置的材质一样,那时候就不需要提供整个Fabric的定义,而仅仅传递type以及想更改的uniforms值。

var fabric = {
   type : 'MyNewMaterial',
   //剩余JSON值
};
polygon.material = new Cesium.Material({
  fabric : fabric
});
//再次使用的时候,只需要这样
anotherPolygon.material = Material.fromType('MyNewMaterial');


Components


或许最简单有趣的材质就是纯白色散射光:

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


稍微复杂一点,增加一个高光元素,当视角正对反射光的时候更亮一些,当视角在边上的时候稍微亮一些。

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


components属性包含了 定义了材质外观的子属性。每个子属性是一个GLSL的代码段,比如上面的vec3(0.5),它实际创建了一个三维向量,每个分量都设置为0.5。这里可以访问所有的GLSL函数,包括mix,cos,texture2D`等等。现在有5种子属性:


image.png

综上所述,子属性或者components 定义了材质的特点。他们是材质的输出值,是光照系统的输入值。


代码


提供完整的GLSL代码是一种比前面components更灵活的方式。通过自定义czm_getMaterial函数,返回材质的各个分量。代码如下:

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 这么定义:

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


下面的示例代码,只设置了diffusespecular分量的值:

czm_material czm_getMaterial(czm_materialInput materialInput)
{
    czm_materialInput m = czm_getDefaultMaterial(materialInput);
    m.diffuse = vec3(0.5);
    m.specular = 0.5;
    return m;
}


source相对components更加繁琐,但是更灵活,比如定义一些公用的函数,共享每个分量的计算过程等等。有个原则就是优先使用components属性,除非明确需要实现czm_getMaterial函数。也就是说components的子属性实际也是实现czm_getMaterial函数。而两种方式下,我们都可以访问GLSL 的内置函数和Cesium提供的GLSL函数(functions), 变量(uniforms), and 常量(constants)(链接已失效)。


材质输入


materialInput变量在sourcecomponents属性中都可以配置。它具有下面的字段,用来计算材质分量:

image.png

把纹理坐标的st值显示出来的简单方法:

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

类似的,查看视点坐标下的法向量,只需要 把materialInput.normalEC设置到diffuse分量上。

除此之外,在materialInput里,可以访问uniforms变量,包括Cesium 提供的内置变量 uniforms 和 材质设置的uniforms变量。比如,我们可以设置自定义的Color材质,依据一个color 变量来设置diffusealpha

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

Fabric中,uniform属性的子属性是GLSL中的uniform变量名 ,也是new MaterialMaterial.fromType返回中JavaScript的对象属性名。子属性的值也是GLSL中uniform变量的值。(这块意思就是说uniform下的属性和值在GLSL的GPU环境和js的内存环境中一致的)。
可以通过一个自定义的image变量来实现材质的DiffuseMap

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

上面代码里,'czm_defaultImage'是一个1x1的图片。前面说过,这个值可以是一个图片URL地址或者 数据URI。比如用户可以使用我们自定义的OurDiffuseMap材质,这么来设置纹理:

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

也有一个内置的立体贴图:czm_defaultCubeMap。GLSL 标准的uniform变量类型float,vec3,mat4都是支持的。Uniform数组还不支持,但是已经在计划内 roadmap


材质的合并


至此,我们可以使用内置的材质,可以通过设置材质的components来自定义 ,或者实现完整的GLSL代码source来自定义。我们还可以通过继承已有的材质来新建材质。
Fabric 有个materials属性,它的每个子属性也是Fabric材质。他们的材质可以可以在components或者source中引用。比如一个塑料材质可以通过DiffuseMapSpecularMap两个材质的合并来模拟。

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

这个材质的diffusespecular都是从其他材质中提取的。子属性的名字叫diffuseMaterialspecularMaterial(根据类型DiffuseMapSpecularMap创建的材质。不要搞混 类型 和 实例对象的名称,在componentssource属性中,子材质通过名称访问,因为他们都是一个czm_material结构,所以可以访问.diffuse.specular分量。
基于这个Fabric材质,可以这么用我们的材质:

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

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


Fabric 格式


Fabric 是基于JSON 格式格式定义。这格式定义里详细描述了Fabric的属性和子属性,包括type,materials,uniforms,components, 和source等。那里面有一些JSON的格式示例,但是没有必要去看。
对于一些严格要求的Fabric文件,可以使用一些类似 JSV的工具去验证Fabric格式。


渲染流水线中的材质


Polygon, PolylineCollection, Ellipsoid, CustomSensorVolume等几何体 已经 和材质系统集成。大部分用户只需要简单的设置material就可以了。可是,用户还是想实现自己的材质渲染代码。直接了当的去做就行了。


在渲染阶段,材质就是一段GLSL函数czm_getMaterial和 一些uniform变量。片段着色器需要构造一个czm_MaterialInput结构,然后调用czm_getMaterial方法,把获得的czm_material结果传递给光照处理函数去计算图元颜色。
在JavaScript代码里,这些对象应该有一个material属性。当这个属性变换的时候,update函数应该把材质的GLSL代码转为对象的片段着色器代码,并且把对象和材质的uniform变量合并起来。

var fsSource =  this.material.shaderSource +  ourFragmentShaderSource;

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

 



收藏 0 0

登录 后评论。没有帐号? 注册 一个。

西部世界

VIP会员 工作人员
  • 0 回答
  • 0 粉丝
  • 0 关注