• Cesium深入浅出之阴影贴图【转】


    引子

    又偷懒了,说好的周更的,又拖了一个月咯。前面两篇写了可视域分析和视频投影,无一例外的都用到了ShadowMap也就是阴影贴图,因此觉得又必要单独写一篇阴影贴图的文章。当然了,还有另外一个原因,文章中视频投影是利用Cesium自带的Entity方式实现的,毫无技术性可言,在文章结尾我说了可以使用ShadowMap方式来做,原理类似于可视域分析,那么今天我就把实现方式给大家说一下。

    预期效果

    照例先看一下预期的效果,既然说了阴影贴图,当然不能满足于只贴视频纹理了,这里我放了三张图,代表着我用了三种纹理:图片、视频、颜色。小伙伴惊奇的发现,颜色贴图不就是可视域分析么?嘿嘿,是的,因为原理都是一样的嘛。

    实现原理

    上面说了实现原和可视域分析是一样的,涉及到的知识点ShadowMap、Frustum、Camera之类的请参考Cesium深入浅出之可视域分析,这里不在赘述。只简单讲一点,阴影贴图支持不同的纹理,那么我们要做的就是创建一个ShadowMap,然后把不同类型的Texture传给他就可以了。

    具体实现

    实现流程与可视域分析也大致相似,类→创建Camera→创建ShadowMap→创建PostProcessStage→创建Frustum,只多了一步设置Texture,当然最核心的内容是在shader里。

    因为代码高度重合,这里就不贴全部代码了,只贴核心代码,如果有疑问的可以留言、私信、群里询问,我看到了都会回答的。

    构造函数

    复制代码
     1 // 定义变量
     2 /** 纹理类型:VIDEO、IMAGE、COLOR */
     3 #textureType;
     4 /** 纹理 */
     5 #texture;
     6 /** 观测点位置 */
     7 #viewPosition;
     8 /** 最远观测点位置(如果设置了观测距离,这个属性可以不设置) */
     9 #viewPositionEnd;
    10 // ...
    11 
    12 // 构造函数
    13 constructor(viewer, options) {
    14     super(viewer);
    15 
    16     // 纹理类型
    17     this.#textureType = options.textureType;
    18     // 纹理地址(纹理为视频或图片的时候需要设置此项)
    19     this.#textureUrl = options.textureUrl;
    20     // 观测点位置
    21     this.#viewPosition = options.viewPosition;
    22     // 最远观测点位置(如果设置了观测距离,这个属性可以不设置)
    23     this.#viewPositionEnd = options.viewPositionEnd;
    24 
    25     // ...
    26 
    27     switch (this.#textureType) {
    28         default:
    29         case VideoShed.TEXTURE_TYPE.VIDEO:
    30             this.activeVideo();
    31             break;
    32         case VideoShed.TEXTURE_TYPE.IMAGE:
    33             this.activePicture();
    34             break;
    35     }
    36     this.#refresh()
    37     this.viewer.scene.primitives.add(this);
    38 }
    复制代码

    定义纹理类型,视频和图片投影需要引入纹理文件,初始化的时候设置文件路径,颜色投影不需要任何操作。

    视频纹理

    复制代码
     1 /**
     2  * 投放视频。
     3  *
     4  * @author Helsing
     5  * @date 2020/09/19
     6  * @param {String} textureUrl 视频地址。
     7  */
     8 activeVideo(textureUrl = undefined) {
     9     if (!textureUrl) {
    10         textureUrl = this.#textureUrl;
    11     } else {
    12         this.#textureUrl = textureUrl;
    13     }
    14     const video = this.#createVideoElement(textureUrl);
    15     const that = this;
    16     if (video /*&& !video.paused*/) {
    17         this.#activeVideoListener || (this.#activeVideoListener = function () {
    18             that.#texture && that.#texture.destroy();
    19             that.#texture = new Texture({
    20                 context: that.viewer.scene.context,
    21                 source: video,
    22                  1,
    23                 height: 1,
    24                 pixelFormat: PixelFormat.RGBA,
    25                 pixelDatatype: PixelDatatype.UNSIGNED_BYTE
    26             });
    27         });
    28         that.viewer.clock.onTick.addEventListener(this.#activeVideoListener);
    29     }
    30 }
    复制代码

    视频纹理是通过html5的video标签引入,需要动态创建标签,不过需要注意的是视频标签的释放是个问题,常规方式并不能彻底释放,最好不要每次都创建新的标签。

    图片纹理

    复制代码
     1 /**
     2  * 投放图片。
     3  *
     4  * @author Helsing
     5  * @date 2020/09/19
     6  * @param {String} textureUrl 图片地址。
     7  */
     8 activePicture(textureUrl = undefined) {
     9     this.deActiveVideo();
    10     if (!textureUrl) {
    11         textureUrl = this.#textureUrl;
    12     } else {
    13         this.#textureUrl = textureUrl;
    14     }
    15     const that = this;
    16     const img = new Image;
    17     img.onload = function () {
    18         that.#textureType = VideoShed.TEXTURE_TYPE.IMAGE;
    19         that.#texture = new Texture({
    20             context: that.viewer.scene.context,
    21             source: img
    22         });
    23     };
    24     img.onerror = function () {
    25         console.log('图片加载失败:' + textureUrl)
    26     };
    27     img.src = textureUrl;
    28 }
    复制代码

    图片纹理使用的是Image对象加载的,要注意的是在异步回调中设置纹理。

     

    PostProcessStage

    复制代码
     1 /**
     2  * 创建后期程序。
     3  *
     4  * @author Helsing
     5  * @date 2020/09/19
     6  * @ignore
     7  */
     8 #addPostProcessStage() {
     9     const that = this;
    10     const bias = that.#shadowMap._isPointLight ? that.#shadowMap._pointBias : that.#shadowMap._primitiveBias;
    11     const postStage = new PostProcessStage({
    12         fragmentShader: VideoShedFS,
    13         uniforms: {
    14             helsing_textureType: function () {
    15                 return that.#textureType;
    16             },
    17             helsing_texture: function () {
    18                 return that.#texture;
    19             },
    20             helsing_alpha: function () {
    21                 return that.#alpha;
    22             },
    23             helsing_visibleAreaColor: function () {
    24                 return that.#visibleAreaColor;
    25             },
    26             helsing_invisibleAreaColor: function () {
    27                 return that.#invisibleAreaColor;
    28             },
    29             shadowMap_texture: function () {
    30                 return that.#shadowMap._shadowMapTexture;
    31             },
    32             shadowMap_matrix: function () {
    33                 return that.#shadowMap._shadowMapMatrix;
    34             },
    35             shadowMap_lightPositionEC: function () {
    36                 return that.#shadowMap._lightPositionEC;
    37             },
    38             shadowMap_texelSizeDepthBiasAndNormalShadingSmooth: function () {
    39                 const t = new Cartesian2;
    40                 t.x = 1 / that.#shadowMap._textureSize.x;
    41                 t.y = 1 / that.#shadowMap._textureSize.y;
    42                 return Cartesian4.fromElements(t.x, t.y, bias.depthBias, bias.normalShadingSmooth, that.#combinedUniforms1);
    43             },
    44             shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness: function () {
    45                 return Cartesian4.fromElements(bias.normalOffsetScale, that.#shadowMap._distance, that.#shadowMap.maximumDistance, that.#shadowMap._darkness, that.#combinedUniforms2);
    46             },
    47         }
    48     });
    49     this.#postProcessStage = this.viewer.scene.postProcessStages.add(postStage);
    50 }
    复制代码

    后处理程序中的重点是向shader中传入uniforms参数,如纹理类型,可视域颜色、非可视域颜色等。最后就是重头戏着色器代码。

    复制代码
      1 export default `
      2 varying vec2 v_textureCoordinates;
      3 uniform sampler2D colorTexture;
      4 uniform sampler2D depthTexture;
      5 uniform sampler2D shadowMap_texture; 
      6 uniform mat4 shadowMap_matrix; 
      7 uniform vec4 shadowMap_lightPositionEC; 
      8 uniform vec4 shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness;
      9 uniform vec4 shadowMap_texelSizeDepthBiasAndNormalShadingSmooth;
     10 uniform int helsing_textureType;
     11 uniform sampler2D helsing_texture;
     12 uniform float helsing_alpha;
     13 uniform vec4 helsing_visibleAreaColor;
     14 uniform vec4 helsing_invisibleAreaColor;
     15 
     16 vec4 toEye(in vec2 uv, in float depth){
     17     vec2 xy = vec2((uv.x * 2.0 - 1.0),(uv.y * 2.0 - 1.0));
     18     vec4 posInCamera =czm_inverseProjection * vec4(xy, depth, 1.0);
     19     posInCamera =posInCamera / posInCamera.w;
     20     return posInCamera;
     21 }
     22 float getDepth(in vec4 depth){
     23     float z_window = czm_unpackDepth(depth);
     24     z_window = czm_reverseLogDepth(z_window);
     25     float n_range = czm_depthRange.near;
     26     float f_range = czm_depthRange.far;
     27     return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
     28 }
     29 float _czm_sampleShadowMap(sampler2D shadowMap, vec2 uv){
     30     return texture2D(shadowMap, uv).r;
     31 }
     32 float _czm_shadowDepthCompare(sampler2D shadowMap, vec2 uv, float depth){
     33     return step(depth, _czm_sampleShadowMap(shadowMap, uv));
     34 }
     35 float _czm_shadowVisibility(sampler2D shadowMap, czm_shadowParameters shadowParameters){
     36     float depthBias = shadowParameters.depthBias;
     37     float depth = shadowParameters.depth;
     38     float nDotL = shadowParameters.nDotL;
     39     float normalShadingSmooth = shadowParameters.normalShadingSmooth;
     40     float darkness = shadowParameters.darkness;
     41     vec2 uv = shadowParameters.texCoords;
     42     depth -= depthBias;
     43     vec2 texelStepSize = shadowParameters.texelStepSize;
     44     float radius = 1.0;
     45     float dx0 = -texelStepSize.x * radius;
     46     float dy0 = -texelStepSize.y * radius;
     47     float dx1 = texelStepSize.x * radius;
     48     float dy1 = texelStepSize.y * radius;
     49     float visibility = (_czm_shadowDepthCompare(shadowMap, uv, depth)
     50         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy0), depth)
     51         + _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy0), depth)
     52         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy0), depth)
     53         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, 0.0), depth) 
     54         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, 0.0), depth)
     55         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx0, dy1), depth)
     56         + _czm_shadowDepthCompare(shadowMap, uv + vec2(0.0, dy1), depth)
     57         + _czm_shadowDepthCompare(shadowMap, uv + vec2(dx1, dy1), depth)
     58     ) * (1.0 / 9.0);
     59     return visibility;
     60 }
     61 vec3 pointProjectOnPlane(in vec3 planeNormal, in vec3 planeOrigin, in vec3 point){
     62     vec3 v01 = point -planeOrigin;
     63     float d = dot(planeNormal, v01) ;
     64     return (point - planeNormal * d);
     65 }
     66 
     67 void main(){
     68     const float PI = 3.141592653589793;
     69     vec4 color = texture2D(colorTexture, v_textureCoordinates);
     70     vec4 currentDepth = texture2D(depthTexture, v_textureCoordinates);
     71     if(currentDepth.r >= 1.0){
     72         gl_FragColor = color;
     73         return;
     74     }
     75     float depth = getDepth(currentDepth);
     76     vec4 positionEC = toEye(v_textureCoordinates, depth);
     77     vec3 normalEC = vec3(1.0);
     78     czm_shadowParameters shadowParameters;
     79     shadowParameters.texelStepSize = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.xy;
     80     shadowParameters.depthBias = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.z;
     81     shadowParameters.normalShadingSmooth = shadowMap_texelSizeDepthBiasAndNormalShadingSmooth.w;
     82     shadowParameters.darkness = shadowMap_normalOffsetScaleDistanceMaxDistanceAndDarkness.w;
     83     shadowParameters.depthBias *= max(depth * 0.01, 1.0);
     84     vec3 directionEC = normalize(positionEC.xyz - shadowMap_lightPositionEC.xyz);
     85     float nDotL = clamp(dot(normalEC, -directionEC), 0.0, 1.0);
     86     vec4 shadowPosition = shadowMap_matrix * positionEC;
     87     shadowPosition /= shadowPosition.w;
     88     if (any(lessThan(shadowPosition.xyz, vec3(0.0))) || any(greaterThan(shadowPosition.xyz, vec3(1.0)))){
     89         gl_FragColor = color;
     90         return;
     91     }
     92     shadowParameters.texCoords = shadowPosition.xy;
     93     shadowParameters.depth = shadowPosition.z;
     94     shadowParameters.nDotL = nDotL;
     95     float visibility = _czm_shadowVisibility(shadowMap_texture, shadowParameters);
     96     
     97     if (helsing_textureType < 2){ // 视频或图片模式
     98         vec4 videoColor = texture2D(helsing_texture, shadowPosition.xy);
     99         if (visibility == 1.0){
    100             gl_FragColor =  mix(color, vec4(videoColor.xyz, 1.0), helsing_alpha * videoColor.a);
    101         }
    102         else{
    103             if(abs(shadowPosition.z - 0.0) < 0.01){
    104                 return;
    105             }
    106             gl_FragColor = color;
    107         }
    108     }
    109     else{ // 可视域模式
    110         if (visibility == 1.0){
    111             gl_FragColor = mix(color, helsing_visibleAreaColor, helsing_alpha);
    112         }
    113         else{
    114             if(abs(shadowPosition.z - 0.0) < 0.01){
    115                 return;
    116             }
    117             gl_FragColor = mix(color, helsing_invisibleAreaColor, helsing_alpha);
    118         }
    119     }
    120 }`;
    复制代码

    可以看出着色器代码并不复杂,而且其中大部分是Cesium中原生的,重点看我标注的部分,视频和图片模式时使用混合纹理,可视域模式时混合颜色。

    饮水思源,不忘初心。 要面包,也要有诗和远方。
  • 相关阅读:
    Vijos训练计划 1304回文数
    18.03.03 位运算作业三则
    18.03.01 codevs1014 装箱问题
    Wikioi 1020 孪生蜘蛛 Label:Floyd最短路
    TYVJ P1004 滑雪 Label:记忆化搜索
    洛谷 P1118 数字三角形游戏 Label:dfs
    TYVJ P1015 公路乘车 &&洛谷 P1192 台阶问题 Label:dp
    洛谷 P1147 连续自然数和 Label:等差数列
    洛谷 P1019 单词接龙 Label:dfs
    洛谷 P1025 数的划分 Label:dp
  • 原文地址:https://www.cnblogs.com/mazhenyu/p/15722543.html
Copyright © 2020-2023  润新知