Cesium开发高级篇 | 05场景后期处理

为实现三维模型的更炫、更酷、更美观,Cesium在1.46的版本中新增了场景的后期处理(Post Processing)功能,包括模型描边、黑白图、明亮度调整、夜市效果、环境光遮蔽,也包括雷达扫描、原型扩散等一些特效。今天我们来学习一下场景后期处理的基础知识和实现流程。

场景后期处理流程

场景的后期处理这个词比较陌生,但说起照片的PS大家都很熟悉,这两个过程非常类似。日常生活中我们拍摄完照片之后,发现太亮或太暗,又或者是皮肤不够白、脸上痘痘明显,我们可以调整亮度、修复一下嫩白的脸蛋,经过几波操作之后,得到了一张我们非常满意的照片。

image210.png

我们可以把照片的修复过程简单理解成场景的后期处理过程,修图的过程就比喻成对三维场景中初始渲染的效果进行再处理,比如添加物体描边、明暗度调整、夜市效果等,最终把综合之后的效果在场景中渲染出来。Cesium中的场景后期处理的大概流程如下图所示:

image209.png

下面结合Cesium本身的PostProcess类,详细的说明一下处理流程:
第一步:通过PostProcessStageLibrary创建一个或者多个后处理效果对象,得到多个PostProcessStage或PostProcessStageComposite;
第二步:将他们加入到PostProcessStageCollection对象中,并设置PostProcessStage或PostProcessStageComposite一些参数,如uniforms;
第三步:PostProcessStageCollection对象就会按照加入的顺序进行屏幕后期处理,在所有的效果都处理完毕后,最后绘制到屏幕上。
当然也可以省略第一步,直接利用PostProcessStageCollection实例化对象中已有的处理效果去实现,如ambientOcclusion、bloom、fxaa。

场景后期处理相关类

上述提到了PostProcess类,基本上涉及到4个类文件,具体每个类的作用又是什么呢?我们来说明一下。
(1)PostProcessStage
对应于某个具体的后期处理效果,它的输入为场景渲染图或者上一个后期处理的结果图,输出结果是一张处理后的图片。

// Simple stage to change the colorvar 
fs =
    'uniform sampler2D colorTexture;\n' +
    'varying vec2 v_textureCoordinates;\n' +
    'uniform float scale;\n' +
    'uniform vec3 offset;\n' +
    'void main() {\n' +
    '    vec4 color = texture2D(colorTexture, v_textureCoordinates);\n' +
    '    gl_FragColor = vec4(color.rgb * scale + offset, 1.0);\n' +
    '}\n';
scene.postProcessStages.add(new Cesium.PostProcessStage({
    fragmentShader : fs,
    uniforms : {
        scale : 1.1,
        offset : function() {
            return new Cesium.Cartesian3(0.1, 0.2, 0.3);
        }
    }}));

fragmentShader:片源着色器代码字符串,它是GLSL代码语言,需要成对配置顶点着色器和片元着色器。
uniforms:片源着色器代码字符串中需要在前端传入的变量。
(2)PostProcessStageComposite
一个集合对象,按顺序存储了不同的场景处理对象,存储类型为PostProcessStage或者PostProcessStageComposite的元素,并存储在stages属性中。

// Example 1: separable blur filter
// The input to blurXDirection is the texture rendered to by the scene or the output of the previous stage.
// The input to blurYDirection is the texture rendered to by blurXDirection.
scene.postProcessStages.add(new Cesium.PostProcessStageComposite({
    stages : [blurXDirection, blurYDirection]}));

(3)PostProcessStageLibrary
负责创建具体的后期处理效果,提供了一些创建常用场景特效的方法,包括createBlackAndWhiteStage-黑色和白色渐变渲染、createBlurStage-高斯模、createBrightnessStage-纹理饱和、createDepthOfFieldStage-景深效果等,创建返回的结果是PostProcessStageComposite或者PostProcessStage类型。相对来说比较简单,直接调用即可。

var stages = viewer.scene.postProcessStages;
  var silhouette = stages.add(
    Cesium.PostProcessStageLibrary.createSilhouetteStage()
  );

(4)PostProcessStageCollection
是一个集合类型的类,负责管理和维护放到集合中的PostProcessStage或PostProcessStageComposite类型对象,实例化对象可通过viewer.scene.postProcessStages直接获取,提供了一些常用的方法,如add、contains、destroy、remove等。
但需要注意的是,该集合中也设定了三个ambientOcclusion、bloom、fxaa效果,如果此类中的环境光遮挡-ambientOcclusion或发光效果-bloom被启用,它们将在所有其他阶段之前执行,优先级最高;如果近似抗锯齿-fxaa被启用,它将在所有其他阶段之后执行,优先级最低。

场景后期处理效果

Cesium为我们提供了一些默认的示例效果,但基本上可分为如下三类:
(1)利用PostProcessStageCollection集合类提供的三个效果
包括ambientOcclusion环境光遮挡、bloom发光效果、fxaa近似抗锯齿,我们挑选前两个为例进行说明。

  • ambientOcclusion环境光遮挡
function updatePostProcess() {
  const ambientOcclusion =
    viewer.scene.postProcessStages.ambientOcclusion;
  ambientOcclusion.enabled =
    Boolean(viewModel.show) || Boolean(viewModel.ambientOcclusionOnly);
  ambientOcclusion.uniforms.ambientOcclusionOnly = Boolean(
    viewModel.ambientOcclusionOnly
  );
  ambientOcclusion.uniforms.intensity = Number(viewModel.intensity);
  ambientOcclusion.uniforms.bias = Number(viewModel.bias);
  ambientOcclusion.uniforms.lengthCap = Number(viewModel.lengthCap);
  ambientOcclusion.uniforms.stepSize = Number(viewModel.stepSize);
  ambientOcclusion.uniforms.blurStepSize = Number(
    viewModel.blurStepSize
  );
}

image212.png

没有开启AO效果如上图一,开启AO效果如上图二,单纯的AO图如上图三

  • bloom发光效果
function updatePostProcess() {
  const bloom = viewer.scene.postProcessStages.bloom;
  bloom.enabled = Boolean(viewModel.show);
  bloom.uniforms.glowOnly = Boolean(viewModel.glowOnly);
  bloom.uniforms.contrast = Number(viewModel.contrast);
  bloom.uniforms.brightness = Number(viewModel.brightness);
  bloom.uniforms.delta = Number(viewModel.delta);
  bloom.uniforms.sigma = Number(viewModel.sigma);
  bloom.uniforms.stepSize = Number(viewModel.stepSize);
}

image214.png

(2)直接调用PostProcessStageLibrary中提供的方法去渲染场景特效
Cesium在场景处理库中默认为我们提供了如下8个效果,其实也是非常简单的,直接调用即可。

下面是一个切换动画小人效果的简单示例:

const stages = viewer.scene.postProcessStages;
const silhouette = stages.add(
  Cesium.PostProcessStageLibrary.createSilhouetteStage()
);
const blackAndWhite = stages.add(
  Cesium.PostProcessStageLibrary.createBlackAndWhiteStage()
);
const brightness = stages.add(
  Cesium.PostProcessStageLibrary.createBrightnessStage()
);
const nightVision = stages.add(
  Cesium.PostProcessStageLibrary.createNightVisionStage()
);

function updatePostProcess() {
  silhouette.enabled = Boolean(viewModel.silhouette);
  silhouette.uniforms.color = Cesium.Color.YELLOW;
  blackAndWhite.enabled = Boolean(viewModel.blackAndWhiteShow);
  blackAndWhite.uniforms.gradations = Number(
    viewModel.blackAndWhiteGradations
  );
  brightness.enabled = Boolean(viewModel.brightnessShow);
  brightness.uniforms.brightness = Number(viewModel.brightnessValue);
  nightVision.enabled = Boolean(viewModel.nightVisionShow);
}

物体描边.gif

(3)编写自定义Shader实现场景特效
要想实现自定义Shader,不仅需要开发者了解顶点着色器和片元着色器,openGL,还需要会编写GLSL(GL Shading Language)语言,通过自定义Shader可以表达更多的场景特效。关于GLSL编程语法,这里就不多赘述了,感兴趣的可以查看其官网。下面是一个简单的给动画小人打马赛克的示例:

const fragmentShaderSource = `
  uniform sampler2D colorTexture; 
  varying vec2 v_textureCoordinates; 
  const int KERNEL_WIDTH = 16; 
  void main(void) 
  { 
      vec2 step = czm_pixelRatio / czm_viewport.zw; 
      vec2 integralPos = v_textureCoordinates - mod(v_textureCoordinates, 8.0 * step); 
      vec3 averageValue = vec3(0.0); 
      for (int i = 0; i < KERNEL_WIDTH; i++) 
      { 
          for (int j = 0; j < KERNEL_WIDTH; j++) 
          { 
              averageValue += texture2D(colorTexture, integralPos + step * vec2(i, j)).rgb; 
          } 
      } 
      averageValue /= float(KERNEL_WIDTH * KERNEL_WIDTH); 
      gl_FragColor = vec4(averageValue, 1.0); 
  }
  `;
viewer.scene.postProcessStages.add(
  new Cesium.PostProcessStage({
    fragmentShader: fragmentShaderSource,
  })

图片1.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容