為實現(xiàn)三維模型的更炫最欠、更酷、更美觀惩猫,Cesium在1.46的版本中新增了場景的后期處理(Post Processing)功能芝硬,包括模型描邊、黑白圖轧房、明亮度調(diào)整拌阴、夜市效果、環(huán)境光遮蔽锯厢,也包括雷達掃描皮官、原型擴散等一些特效。今天我們來學習一下場景后期處理的基礎(chǔ)知識和實現(xiàn)流程实辑。
場景后期處理流程
場景的后期處理這個詞比較陌生捺氢,但說起照片的PS大家都很熟悉,這兩個過程非常類似剪撬。日常生活中我們拍攝完照片之后摄乒,發(fā)現(xiàn)太亮或太暗,又或者是皮膚不夠白、臉上痘痘明顯馍佑,我們可以調(diào)整亮度斋否、修復一下嫩白的臉蛋,經(jīng)過幾波操作之后拭荤,得到了一張我們非常滿意的照片茵臭。
我們可以把照片的修復過程簡單理解成場景的后期處理過程,修圖的過程就比喻成對三維場景中初始渲染的效果進行再處理舅世,比如添加物體描邊旦委、明暗度調(diào)整、夜市效果等雏亚,最終把綜合之后的效果在場景中渲染出來缨硝。Cesium中的場景后期處理的大概流程如下圖所示:
下面結(jié)合Cesium本身的PostProcess類,詳細的說明一下處理流程:
第一步:通過PostProcessStageLibrary創(chuàng)建一個或者多個后處理效果對象罢低,得到多個PostProcessStage或PostProcessStageComposite查辩;
第二步:將他們加入到PostProcessStageCollection對象中,并設(shè)置PostProcessStage或PostProcessStageComposite一些參數(shù)网持,如uniforms宜岛;
第三步:PostProcessStageCollection對象就會按照加入的順序進行屏幕后期處理,在所有的效果都處理完畢后翎碑,最后繪制到屏幕上谬返。
當然也可以省略第一步,直接利用PostProcessStageCollection實例化對象中已有的處理效果去實現(xiàn)日杈,如ambientOcclusion、bloom佑刷、fxaa莉擒。
場景后期處理相關(guān)類
上述提到了PostProcess類,基本上涉及到4個類文件瘫絮,具體每個類的作用又是什么呢涨冀?我們來說明一下。
(1)PostProcessStage
對應(yīng)于某個具體的后期處理效果麦萤,它的輸入為場景渲染圖或者上一個后期處理的結(jié)果圖鹿鳖,輸出結(jié)果是一張?zhí)幚砗蟮膱D片。
// 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
負責創(chuàng)建具體的后期處理效果杂抽,提供了一些創(chuàng)建常用場景特效的方法,包括createBlackAndWhiteStage-黑色和白色漸變渲染韩脏、createBlurStage-高斯模缩麸、createBrightnessStage-紋理飽和、createDepthOfFieldStage-景深效果等赡矢,創(chuàng)建返回的結(jié)果是PostProcessStageComposite或者PostProcessStage類型匙睹。相對來說比較簡單,直接調(diào)用即可济竹。
var stages = viewer.scene.postProcessStages;
var silhouette = stages.add(
Cesium.PostProcessStageLibrary.createSilhouetteStage()
);
(4)PostProcessStageCollection
是一個集合類型的類痕檬,負責管理和維護放到集合中的PostProcessStage或PostProcessStageComposite類型對象,實例化對象可通過viewer.scene.postProcessStages直接獲取送浊,提供了一些常用的方法梦谜,如add、contains袭景、destroy唁桩、remove等。
但需要注意的是耸棒,該集合中也設(shè)定了三個ambientOcclusion荒澡、bloom、fxaa效果与殃,如果此類中的環(huán)境光遮擋-ambientOcclusion或發(fā)光效果-bloom被啟用单山,它們將在所有其他階段之前執(zhí)行,優(yōu)先級最高幅疼;如果近似抗鋸齒-fxaa被啟用米奸,它將在所有其他階段之后執(zhí)行,優(yōu)先級最低爽篷。
場景后期處理效果
Cesium為我們提供了一些默認的示例效果悴晰,但基本上可分為如下三類:
(1)利用PostProcessStageCollection集合類提供的三個效果
包括ambientOcclusion環(huán)境光遮擋、bloom發(fā)光效果逐工、fxaa近似抗鋸齒铡溪,我們挑選前兩個為例進行說明。
- ambientOcclusion環(huán)境光遮擋
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
);
}
沒有開啟AO效果如上圖一泪喊,開啟AO效果如上圖二棕硫,單純的AO圖如上圖三
- bloom發(fā)光效果
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);
}
(2)直接調(diào)用PostProcessStageLibrary中提供的方法去渲染場景特效
Cesium在場景處理庫中默認為我們提供了如下8個效果,其實也是非常簡單的窘俺,直接調(diào)用即可饲帅。
下面是一個切換動畫小人效果的簡單示例:
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);
}
(3)編寫自定義Shader實現(xiàn)場景特效
要想實現(xiàn)自定義Shader复凳,不僅需要開發(fā)者了解頂點著色器和片元著色器,openGL灶泵,還需要會編寫GLSL(GL Shading Language)語言育八,通過自定義Shader可以表達更多的場景特效。關(guān)于GLSL編程語法赦邻,這里就不多贅述了髓棋,感興趣的可以查看其官網(wǎng)。下面是一個簡單的給動畫小人打馬賽克的示例:
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,
})