fabric 材质定义:着色器实践

1. 示例代码

贴到沙盒里就可以运行:

  1. var viewer = new Cesium.Viewer("cesiumContainer");
  2. viewer.scene.globe.depthTestAgainstTerrain = true;
  3. viewer.camera.setView({
  4. destination : new Cesium.Cartesian3(-2644963.9889313546, 5763731.142118295, 2199400.7089496767), //世界坐标系下的一个坐标点
  5. orientation : {//旋转角度
  6. heading :6.075,
  7. pitch :-0.727,
  8. roll : 6.283
  9. }
  10. });
  11. const extrudedPolygon = new Cesium.PolygonGeometry({
  12. polygonHierarchy : new Cesium.PolygonHierarchy(
  13. Cesium.Cartesian3.fromDegreesArray([
  14. 112.41726298378288, 23.290411251106182,
  15. 113.67072522399741, 23.560312361463682,
  16. 114.09370956893551, 22.590768298743153,
  17. 112.83803246418894, 22.285610818885644
  18. ])
  19. ),
  20. extrudedHeight: 3000
  21. });
  22. const instance = new Cesium.GeometryInstance({
  23. geometry: extrudedPolygon,
  24. id: 'box with height'
  25. });
  26. const m = new Cesium.Material({
  27. fabric: {
  28. type: 'Color',
  29. uniforms: {
  30. color: new Cesium.Color(216 / 255.0, 170 / 255.0, 208 / 255.0).withAlpha(0.618),
  31. },
  32. }
  33. });
  34. const aper = new Cesium.MaterialAppearance({
  35. material : m,
  36. });
  37. var p = viewer.scene.primitives.add(new Cesium.Primitive({
  38. geometryInstances: instance,
  39. appearance: aper,
  40. releaseGeometryInstances: false,
  41. compressVertices: false,
  42. }));
  43. // p.readyPromise.then(v => console.log(v));
  44. const vs = aper.vertexShaderSource;
  45. const fs = aper.fragmentShaderSource;
  46. const fs2 = aper.getFragmentShaderSource();
  47. console.log(`// 顶点着色器:
  48. ${vs}`);
  49. console.log(`// 片元着色器:
  50. ${fs}`);
  51. console.log(`// 片元着色器2:
  52. ${fs2}`);

2. 修改 fabric 对象

  1. const m = new Cesium.Material({
  2. fabric: {
  3. source: `float a = 12.0;`,
  4. }
  5. });

随便定义一个浮点数,发现报错:

加上 uniform 限定字,报错稍微改了一点:

所以,这个 source 是有规则的。

2.1. 必须拥有函数 czm_getMaterial(czm_materialInput materialInput)

我改成这样:

  1. source:
  2. `czm_material czm_getMaterial(czm_materialInput materialInput)
  3. {
  4. }`,

报错变化了:

大意是指,czm_getMaterial 这个函数没有返回值。这很正常,强类型的 GLSL 规定了这个函数的返回值类型是结构体 czm_material,那么再次修改它。

2.2. 必须有返回值:不妨返回个默认值

  1. source:
  2. `czm_material czm_getMaterial(czm_materialInput materialInput)
  3. {
  4. czm_material material = czm_getDefaultMaterial(materialInput);
  5. return material;
  6. }`,

这时,形状有颜色了:

material 这个变量是一个结构体,通过修改其材质因子即可实现材质修改。

修改其漫反射因子:

注意,glsl 中创建结构体 vec3 的默认值是 (0, 0, 0),现在我想要个粉色,rgb色值是:(216 / 255.0, 170 / 255.0, 208 / 255.0),即 (0.8470588235294118, 0.66666666, 0.8156862745098039)

  1. source:
  2. `czm_material czm_getMaterial(czm_materialInput materialInput)
  3. {
  4. czm_material material = czm_getDefaultMaterial(materialInput);
  5. material.diffuse = vec3(0.8470588235294118, 0.66666666, 0.8156862745098039);
  6. return material;
  7. }`,

没毛病,颜色出来了:

2.3. 顶点着色器与片元着色器

你可以在很多个地方获取材质、外观的着色器源代码:

  • Material.prototype.shaderSource:可读可写:当前 material 对象的 source 属性,支持实时修改
  • Appearance.prototype.vertexShaderSource:只读:当前外观对象的顶点着色器,仅支持构造时传入
  • Appearance.prototype.fragmentShaderSource:只读:当前外观对象的片元着色器,仅支持构造时传入
  • Appearance.prototype.getFragmentShaderSource():返回最终完全版片元着色器源代码。

上面在 fabric 对象中的 source 属性指定的 glsl 源代码,与 console.log(m.shaderSource) 出来的是完全一样的,所以此处忽略。

当通过 2.2 节中对漫反射属性的设置后,外观对象的 vertexShaderSourcefragmentShaderSource 输出结果如下:

  1. // 顶点着色器:
  2. attribute vec3 position3DHigh;
  3. attribute vec3 position3DLow;
  4. attribute vec3 normal;
  5. attribute vec2 st;
  6. attribute float batchId;
  7. varying vec3 v_positionEC;
  8. varying vec3 v_normalEC;
  9. varying vec2 v_st;
  10. void main()
  11. {
  12. vec4 p = czm_computePosition();
  13. v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
  14. v_normalEC = czm_normal * normal; // normal in eye coordinates
  15. v_st = st;
  16. gl_Position = czm_modelViewProjectionRelativeToEye * p;
  17. }
  1. // 片元着色器:
  2. varying vec3 v_positionEC;
  3. varying vec3 v_normalEC;
  4. varying vec2 v_st;
  5. void main()
  6. {
  7. vec3 positionToEyeEC = -v_positionEC;
  8. vec3 normalEC = normalize(v_normalEC);
  9. #ifdef FACE_FORWARD
  10. normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
  11. #endif
  12. czm_materialInput materialInput;
  13. materialInput.normalEC = normalEC;
  14. materialInput.positionToEyeEC = positionToEyeEC;
  15. materialInput.st = v_st;
  16. czm_material material = czm_getMaterial(materialInput);
  17. #ifdef FLAT
  18. gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
  19. #else
  20. gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
  21. #endif
  22. }

捕获重点:片元着色器

观察片元着色器代码中的主函数,其中有一句调用 czm_material material = czm_getMaterial(materialInput);

这一句便是对我们在 fabric 对象中写入的 glsl 代码的调用。

最终,进入 #ifdef FLAT 分支(才疏学浅,不知道这个 FLAT 宏是什么),对像素着色,使用 material 结构的漫反射因子 + 自发光因子 + 透明度因子进行叠加,生成最终的颜色值。所以,这个时候不妨回到 Material 的 source 中,继续动手脚。

是存在直接修改 Appearance 对象 fragmentShader、vertexShader 的大佬的,后面有机会展开说说。

2.4. 牛刀小试:发个光吧

  1. source:
  2. `czm_material czm_getMaterial(czm_materialInput materialInput)
  3. {
  4. czm_material material = czm_getDefaultMaterial(materialInput);
  5. material.diffuse = vec3(0.8470588235294118, 0.66666666, 0.8156862745098039);
  6. material.specular = 1.0;
  7. material.shininess = 0.8;
  8. return material;
  9. }`,

(我偷偷把高度设为了 30000,不然不太明显)

换个地图和参数:

稍微有那么一点感觉了。

  1. const m = new Cesium.Material({
  2. translucent: false,
  3. fabric: {
  4. source:
  5. `czm_material czm_getMaterial(czm_materialInput materialInput)
  6. {
  7. czm_material material = czm_getDefaultMaterial(materialInput);
  8. material.diffuse = vec3(0.24313725490196078, 0.7372549019607844, 0.9333333333333333);
  9. material.specular = 0.5;
  10. material.shininess = 0.8;
  11. material.emission = vec3(0.0, 0.66666666, 0.0);
  12. return material;
  13. }`,
  14. }
  15. });

3. *高级运用:直接修改 Appearance 的片元着色器

用的是 1. 中的代码,修改 aper 对象的构造参数,直接将 2.3 中的片元着色器代码贴入看看:

  1. const aper = new Cesium.MaterialAppearance({
  2. fragmentShaderSource:
  3. ` varying vec3 v_positionEC;
  4. varying vec3 v_normalEC;
  5. varying vec2 v_st;
  6. void main()
  7. {
  8. vec3 positionToEyeEC = -v_positionEC;
  9. vec3 normalEC = normalize(v_normalEC);
  10. #ifdef FACE_FORWARD
  11. normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
  12. #endif
  13. czm_materialInput materialInput;
  14. materialInput.normalEC = normalEC;
  15. materialInput.positionToEyeEC = positionToEyeEC;
  16. materialInput.st = v_st;
  17. czm_material material = czm_getMaterial(materialInput);
  18. #ifdef FLAT
  19. gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
  20. #else
  21. gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
  22. #endif
  23. }
  24. `
  25. });

没有问题:

所以,基于此模板,只要胆大心细(划掉)只要对 Cesium 内置的着色器足够了解,完全可以自己改顶点和片元着色器。

试一试:把 2.2 和 2.4 中的尝试加入

  1. const aper = new Cesium.MaterialAppearance({
  2. fragmentShaderSource:
  3. ` varying vec3 v_positionEC;
  4. varying vec3 v_normalEC;
  5. varying vec2 v_st;
  6. void main()
  7. {
  8. vec3 positionToEyeEC = -v_positionEC;
  9. vec3 normalEC = normalize(v_normalEC);
  10. #ifdef FACE_FORWARD
  11. normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
  12. #endif
  13. czm_materialInput materialInput;
  14. materialInput.normalEC = normalEC;
  15. materialInput.positionToEyeEC = positionToEyeEC;
  16. materialInput.st = v_st;
  17. czm_material material = czm_getMaterial(materialInput);
  18. material.diffuse = vec3(0.24313725490196078, 0.7372549019607844, 0.9333333333333333);
  19. material.emission = vec3(0.0, 0.66666666, 0.0);
  20. material.specular = 0.5;
  21. material.shininess = 0.8;
  22. #ifdef FLAT
  23. gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
  24. #else
  25. gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
  26. #endif
  27. }
  28. `
  29. });

4. **实验性记录:修改 Appearance 的顶点着色器

  1. const apr = new Cesium.MaterialAppearance({
  2. vertexShaderSource:
  3. `
  4. attribute vec3 position3DHigh;
  5. attribute vec3 position3DLow;
  6. attribute vec3 normal;
  7. attribute vec2 st;
  8. attribute float batchId;
  9. varying vec3 v_positionEC;
  10. varying vec3 v_normalEC;
  11. varying vec2 v_st;
  12. void main()
  13. {
  14. float zh = position3DHigh.z * 0.97;
  15. float zl = position3DLow.z * 0.97;
  16. vec3 th = vec3(position3DHigh.xy, zh);
  17. vec3 tl = vec3(position3DLow.xy, zl);
  18. vec4 p = czm_translateRelativeToEye(th, tl);
  19. v_positionEC = (czm_modelViewRelativeToEye * p).xyz; // position in eye coordinates
  20. v_normalEC = czm_normal * normal; // normal in eye coordinates
  21. v_st = st;
  22. gl_Position = czm_modelViewProjectionRelativeToEye * p;
  23. }
  24. `,
  25. })

依旧是上方 1. 的例子,只不过在顶点着色器稍微动动手脚,可达到变形的效果:

很可惜这个 position3DHigh 和 position3DLow 并不是这个 Primitive 的局部相对坐标,所以直接修改 z = 0 是压不平的,但是从图中可略见端倪,猜测这个 z 值是世界坐标,后续使用半透明地形看看。

5. 给材质的着色器代码传入动态值:uniforms 的运用

uniform 在 WebGL 中就是恒定值的意思。一般 WebGL 用 attribute 关键字指定顶点属性或外来值,用 uniform 关键字指定常量,用 varying 关键字指定顶点着色器、片元着色器共享的变量。

在 Cesium fabric 规则中,fabric.uniforms 的所有变量,在 fabric.source 中可以直接使用。

例如,我需要传入一个透明度:

  1. const m = new Cesium.Material({
  2. fabric: {
  3. uniforms: {
  4. my_var: 0.5,
  5. },
  6. source:
  7. `
  8. czm_material czm_getMaterial(czm_materialInput materialInput)
  9. {
  10. czm_material material = czm_getDefaultMaterial(materialInput);
  11. material.diffuse = vec3(0.5, 0.9, 0.3);
  12. material.alpha = my_var;
  13. return material;
  14. }
  15. `
  16. }
  17. });

是可以的:

打印 这个 m变量,可以轻松看到 glsl 代码:

并且支持直接对 js 的变量进行修改以重新着色:

  1. m.uniforms.my_var = 0.9;

注:了解 uniforms

uniforms 是 fabric 对象的一个属性,按理说,你可以给这个对象传递任何与 glsl 内置结构、数据类型有对应关系的数据,例如上例的 my_var,是数字类型,在着色器内部自动解析为 uniform float my_var_0;

参考官方给出的二十多种预置 Material,如果你有兴趣,可以直接把它们的 source 打印出来观察。

例如,在镜面反射材质中,它的 uniforms 就有这两个:

  1. uniforms : {
  2. image : 'specular.png',
  3. channel : 'a'
  4. }

一个是图片路径,一个是图片用于镜面反射强度的通道(此处是 alpha 通道)。

如果你传递的是对象,例如最常见的纹理材质中:

  1. uniforms: {
  2. image: 'diffuse.png',
  3. my_struct: {
  4. x: 10,
  5. y: 2
  6. }
  7. }

这个 my_struct,最终会传入一个结构体 uniform vec2 my_struct_0;

当然有的时候不要作死,比如这个情况是转译不了的:

  1. uniforms: {
  2. my_var: 0.5,
  3. my_struct: {
  4. x: 12,
  5. y: 5,
  6. name: {
  7. value: 'aaa'
  8. },
  9. obj: false,
  10. time: 5
  11. }
  12. }

会报错,因为 my_struct 已经超出了 glsl 能理解的类型。

事实上,你在 uniforms 对象中写的任何数据,在 fabric.components 中一样能用,并且 Cesium 的内置结构体常量、函数都是可以直接使用的。

从着色器的角度看,一种材质无非就是 czm_getMaterial() 函数的返回值罢了。

这里仅仅改的是材质,属于片元着色器阶段发生的事情,在第四节中已经看到了 Material 中写的着色器代码是如何被 Appearance 对象的片元着色器代码调用的。如果你想修改 Primitive 的形状,那就要去修改 Appearance 的顶点着色器。

Primitive 是 Scene 对象下的,在渲染循环中它是最终被宰杀的羔羊(划掉),只要是 Primitive,只要你有能力去修改它的着色器代码,就可以自定义很多东西出来。

玩一玩:可视化纹理坐标

将纹理坐标作为漫反射颜色写入,就能看到纹理坐标的样子了:

  1. const m = new Cesium.Material({
  2. fabric: {
  3. uniforms: {
  4. my_var: 0.5,
  5. },
  6. source:
  7. `
  8. czm_material czm_getMaterial(czm_materialInput materialInput)
  9. {
  10. czm_material material = czm_getDefaultMaterial(materialInput);
  11. material.diffuse = vec3(materialInput.st, 0.0);
  12. material.alpha = my_var;
  13. return material;
  14. }
  15. `
  16. }
  17. });

6. 后续想做的

  • 研究自带材质的着色器以及最终生成的顶点着色器、片元着色器
  • 继续结合源代码,研究各路 Primitive
  • 研究自带 glsl 结构体、函数、常量,灵活运用
  • 理解顶点着色器的坐标含义

【Cesium 颜狗初步】fabric 材质定义与自定义着色器实践的更多相关文章

  1. cesium编程入门(八)设置材质

    cesium编程入门(八)设置材质 Cesium中为几何形状设置材质有多种方法 第一种方法 Material 直接构建Cesium.Material对象,通过设置Material的属性来进行控制,官方 ...

  2. ThreeJS 物理材质shader源码分析(像素着色器)

    再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ 像素着色器(meshphysical_frag.glsl) #define PHYSICAL uniform ve ...

  3. ThreeJS 物理材质shader源码分析(顶点着色器)

    再此之前推荐一款GLTF物理材质在线编辑器https://tinygltf.xyz/ ThreeJS 物理材质shader源码分析(顶点着色器) Threejs将shader代码分为ShaderLib ...

  4. three.js 着色器材质之glsl内置函数

    郭先生发现在开始学习three.js着色器材质时,我们经常会无从下手,辛苦写下的着色器,也会因莫名的报错而手足无措.原因是着色器材质它涉及到另一种语言–GLSL,只有懂了这个语言,我们才能更好的写出着 ...

  5. lucene定义自己的分词器将其分成单个字符

    问题描写叙述:将一句话拆分成单个字符.而且去掉空格. package com.mylucene; import java.io.IOException; import java.io.Reader; ...

  6. unity 模型 材质 贴图 关系;着色器属性

    模型包含 材质(Material),包括 [核心]着色器(Shader) 贴图和其他参数,贴图也算是一种参数 其他,如网格渲染器(Mesh Renderer).动画.坐标 一个材质可以看做为一个Sha ...

  7. Flume-自定义 Interceptor(拦截器)

    使用 Flume 采集服务器本地日志,需要按照日志类型的不同,将不同种类的日志发往不同的分析系统. 在实际的开发中,一台服务器产生的日志类型可能有很多种,不同类型的日志可能需要发送到不同的分析系统. ...

  8. Cesium - Fabric 材质【转官网】

    https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric Fabric Hannah edited this page on 24 Dec ...

  9. 夺命雷公狗---微信开发17----自定义菜单的事件推送,响应菜单的CLICK

    废话不多说,index.php 代码如下所示: <?php /** * wechat php test */ //define your token require_once "com ...

随机推荐

  1. pikachs 渗透测试1-环境及暴力破解

    一.安装 PhpStudy20180211,默认安装 1.mysql默认密码是root,因为在虚拟机,保留不动 2.解压pikachs 到 C:\phpStudy\PHPTutorial\WWW\pi ...

  2. __METHOD__

  3. 常见的名片尺寸如何在CorelDRAW预设

    说到名片想必大家肯定不陌生,是我们生活中随处可见的物品,也是商家宣传必不可少的印刷物料.那么名片的尺寸是多少?我们做名片的时候该如何把握好名片的尺寸呢?在CDR中有专门的名片尺寸,下面小编就为大家简单 ...

  4. 追踪聚光特效怎么实现,有Vegas就够了

    舞台聚光灯大家一定都不陌生,在电视上某些颁奖活动里,主持人的进场一定伴随着舞台灯光的聚光效果.随着主持人的移动,灯光也随之移动.这里的舞台灯光就起到了一个追踪聚光的效果. Vegas Pro 16 增 ...

  5. 【VUE】7.Vuex基本使用

    1. 安装Vuex npm install vuex --save 2. 导入Vuex包 import Vuex from 'vuex' Vue.use(Vuex) 3. 创建store对象 cons ...

  6. ⭐NES.css推荐⭐

    今天发现一个有意思的CSS框架,叫NES.css 官网地址:https://nostalgic-css.github.io/NES.css/ gitHub地址:https://github.com/n ...

  7. 洛谷 P1284 三角形牧场 题解(背包+海伦公式)

    题目链接 题目大意 给你 n块木板(n<=40),每块木板长度为\(l[i]<=40\) 每块木板都要用,求最大的三角形面积×100,答案直接舍去小数 题目思路 首先如果已知三条边的长度可 ...

  8. 3D显微镜笔记

    1. 三视图:能够正确反映物体长.宽.高尺寸的正投影工程图(主视图,俯视图,左视图三个基本视图)为三视图,这是工程界一种对物体几何形状约定俗成的抽象表达方式. 附上自己大二时候设计的减速器--设计了两 ...

  9. 网络基础:ip地址

    原文链接:http://blog.51cto.com/xiexiaojun/1882088 很棒的总结,概念+例题很清晰

  10. 最新小样本学习综述 A Survey on Few-Shot Learning | 四大模型Multitask Learning、Embedding Learning、External Memory…

    目录 原文链接: 小样本学习与智能前沿 01 Multitask Learning 01.1 Parameter Sharing 01.2 Parameter Tying. 02 Embedding ...