往期内容:
基础理论
后期处理Post Processing
概念
在计算机图形学和图像处理领域,后期处理(Post Processing)是指对生成的图像或视频进行进一步处理、增强或修改的过程,以改善视觉效果或实现特定的艺术目标。
它通常发生在图像或视频生成后的阶段。

包含哪些操作
- 视觉增强 (调整颜色、对比度、饱和度)
- 错误修正(P 图,去除噪点、减少图像模糊或消除色彩偏差来纠正图像中的问题)
- 特效添加 (模糊、景深、光晕、色调映射)
- 艺术创作(滤镜、调整色彩和形态)
- 细节增强(锐化、增加局部对比度)
- 格式转换 (将图像或视频转换为不同的格式或尺寸,以适应不同的应用和平台要求)
cesium中使用后处理
cesium中封装了几个核心类,专门用于后处理的效果,其中我们着重需要了解PostProcessStage和
PostProcessStageLibary这两个类
PostProcessStage
根据当前场景渲染输出的纹理(或者紧随上一个 Post Processing 后期处理阶段),来进行一个(新)的 Post Processing 后期处理。
可以将整个cesium场景想象为一块大的画布,后处理允许我们在这个大画布上使用shader去自己完成一些效果
比如,我们可以让整个场景变得更亮一些
我们使用PostProcessStage这个api,可以自定义后处理效果
简单介绍一下cesium中对于glsl的编译环境
在后处理中,cesium会为我们提供colorTexture,当前视图的图片资源
v_textureCoordinates,当前渲染的像素坐标
iTime,一个随帧数不断增大的值
// Simple stage to change the color
const fs =`
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
uniform float scale;
uniform vec3 offset;
void main() {
vec4 color = texture2D(colorTexture, v_textureCoordinates);
gl_FragColor = vec4(color.rgb * scale + offset, 1.0);
}`;
- colorTexture
颜色纹理图,即场景渲染的结果,或者上一个后处理的结果(如果使用postProcessStageComposite的话)。
- depthTexture
深度纹理图,即场景深度图,存储了每个像素的深度(可以还原为像素到相机的距离)
- v_textureCoordinates
纹理坐标,纹理坐标的范围为(0,0)-(1,1)
一般会使用颜色采样器,从颜色纹理中获取到当前场景的颜色
texture2D(colorTexture, v_textureCoordinates);
还可以通过深度纹理,获取到像素到相机的距离
float getDistance(sampler2D depthTexture, vec2 texCoords)
{
float depth = czm_unpackDepth(texture(depthTexture, texCoords));
vec4 eyeCoordinate = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
return -eyeCoordinate.z / eyeCoordinate.w;
}
现在分析一下Cesium中获取片元到相机距离的思路
- 使用czm_unpackDepth将打包的深度值转为原始的浮点深度值
- czm_windowToEyeCoordinates能够将屏幕像素坐标结合深度值转为相机坐标系下的坐标
- 由于相机坐标系是左手坐标系,最终结果需要取反,其中w是齐次坐标,在透视投影中,w分量通常表示深度信息,w分量越大,看起来越小,所以这里为了得到准确的坐标值,需要使用齐次坐标到笛卡尔坐标的转换公式,这样得到的坐标信息能够正确反映深度和位置
eyeCoordinate.xyz / eyeCoordinate.w
上一节中我们知道如何通过深度图获取深度值,并且将其转为了眼睛坐标,Cesium还提供了czm_inverseView这个内置函数,将眼睛坐标转到世界坐标
vec3 getWorldCoordinate(sampler2D depthTexture, vec2 texCoords)
{
float depth =czm_unpackDepth(texture(depthTexture, v_textureCoordinates));
vec4 eyeCoordinate4 = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
vec3 eyeCoordinate3 = eyeCoordinate4.xyz/eyeCoordinate4.w;
vec4 worldCoordinate4 = czm_inverseView * vec4(eyeCoordinate3,1.) ;
vec3 worldCoordinate = worldCoordinate4.xyz / worldCoordinate4.w;
return worldCoordinate;
}
我们可以用代码验证一下

限制后处理范围为地球
深度值如果>1的话,说明片元不在地球上
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
varying vec2 v_textureCoordinates;
void main(void) {
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
float depth = czm_unpackDepth(texture2D(depthTexture, v_textureCoordinates));
vec4 eyeCoordinate4 = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
vec3 eyeCoordinate3 = eyeCoordinate4.xyz / eyeCoordinate4.w;
vec4 worldCoordinate4 = czm_inverseView * vec4(eyeCoordinate3, 1.);
vec3 worldCoordinate = worldCoordinate4.xyz / worldCoordinate4.w;
if(depth>=1.0){
return;
}
if(worldCoordinate.z < 0.) {
gl_FragColor.r = 1.;
} else {
gl_FragColor.g = 1.;
}
}

我们可以通过shader中的一个概念,有向距离场SDF来规定后处理的范围
SDF函数,中文译作“符号距离函数”,它用于描述这么一个函数:它将空间里的一个位置作为输入,并返回该位置到给定形状的距离,它的前面还有个“符号”,是因为在形状外的距离为正数(“+”号),在形状内的距离为负数(“-”号),边界处的值恰好为 0。
图形学大咖 Inigo Quilez(后文简称 iq)的博客上有篇文章把常用的2D图形的SDF函数都列了出来,如果有需要可以随时查阅,点我直达文章
也就是说,想要在shader中绘制图形,我们只需要得到一个sdf函数,然后通过片元到中心点的距离,就可以判断哪些片元是在图形内的,哪些片元是不在的
可以看一下这个shaderToy案例,理解sdf函数的意义:https://www.shadertoy.com/view/XsjGDt
在本例中,和shaderToy中的区别就在于坐标,shaderToy中是平面的,所以通过uv坐标结合center来判断图形
本例中,我们可以通过uniform传入一个center(笛卡尔坐标),然后结合sdf函数,确定绘制范围
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
uniform vec3 center;
uniform float radius;
varying vec2 v_textureCoordinates;
float sdCircle(vec3 center, vec3 worldCoordinate, float r) {
// distance可以判断两个点之间的距离
return distance(center, worldCoordinate) - r;
}
void main(void) {
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
float depth = czm_unpackDepth(texture2D(depthTexture, v_textureCoordinates));
vec4 eyeCoordinate4 = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
vec3 eyeCoordinate3 = eyeCoordinate4.xyz / eyeCoordinate4.w;
vec4 worldCoordinate4 = czm_inverseView * vec4(eyeCoordinate3, 1.);
vec3 worldCoordinate = worldCoordinate4.xyz / worldCoordinate4.w;
float d = sdCircle(center, worldCoordinate, radius);
if(d < 0.0) {
gl_FragColor.r = 1.;
}
}

限制后处理范围为矩形
同理,我们可以从iq大神的博客中找到矩形的sdf函数
float sdBox(in vec2 p,in vec2 b)
{
vec2 d=abs(p)-b;
return length(max(d,0.))+min(max(d.x,d.y),0.);
}
这里注意,sdf函数传入的p是当前片元的二维坐标,但是我们这里只有世界坐标,所以我们需要基于中心点,将三维的笛卡尔坐标转到模型坐标系下,再将模型坐标进行俯视,转为二维平面坐标
// ------------------------画矩形------------------------
let m = Cesium.Transforms.eastNorthUpToFixedFrame(destination);
let inverse = Cesium.Matrix4.inverse(m, new Cesium.Matrix4());
将逆矩阵*世界坐标=模型坐标,取模型坐标的xy,得到平面坐标,调用sdf函数,判断距离,显示图形
uniform sampler2D colorTexture;
uniform sampler2D depthTexture;
uniform vec3 center;
uniform float radius;
uniform mat4 inverse;
varying vec2 v_textureCoordinates;
float sdBox(in vec2 p,in vec2 b)
{
vec2 d=abs(p)-b;
return length(max(d,0.))+min(max(d.x,d.y),0.);
}
void main(void) {
gl_FragColor = texture2D(colorTexture, v_textureCoordinates);
float depth = czm_unpackDepth(texture2D(depthTexture, v_textureCoordinates));
vec4 eyeCoordinate4 = czm_windowToEyeCoordinates(gl_FragCoord.xy, depth);
vec3 eyeCoordinate3 = eyeCoordinate4.xyz / eyeCoordinate4.w;
vec4 worldCoordinate4 = czm_inverseView * vec4(eyeCoordinate3, 1.);
vec3 worldCoordinate = worldCoordinate4.xyz / worldCoordinate4.w;
// 由于shaderToy中的sdf都是二维基于uv坐标的,所以我们需要将世界坐标转到模型坐标,再转二维
vec3 modelCoordinate = (inverse * vec4(worldCoordinate, 1.)).xyz;
float d=sdBox(modelCoordinate.xy,vec2(1000.,1000.));
if(d<0.){
gl_FragColor.r=1.;
}
}


看不明白没关系,需要上述视频教程+源码
+下方↓↓小助手【cesium进阶】无偿获取
Cesium后处理绘制图形技术解析
1962

被折叠的 条评论
为什么被折叠?



