cesium 实现体积光 (Volumetric Light) 圣光 (Godrays)

2 篇文章 0 订阅
1 篇文章 0 订阅
本文介绍了如何使用WebGL框架(如three.js、Babylon和Cesium)在3D渲染中创建体积光效果,包括获取径向模糊的太阳图像,遮挡并提取阳光部分,以及融合正常画面和太阳特写的过程。作者提供了Cesium1.106版本的代码示例。
摘要由CSDN通过智能技术生成

介绍:

体积光,也被称作神光(Godrays)、阳光尘或光束效果(crepuscular rays),是一种视觉效果,经常被用于增强3D渲染图、视频游戏和电影中的自然光照效果。体积光效果通过模拟光线在三维空间中传播时遇到各种障碍(如云、尘埃或水雾等)而产生的散射现象,来产生一种戏剧性和真实感十足的视觉体验。

常用的webgl框架均有实现:

three.js 实现:

babylon 实现:

cesium 实现:

获取一帧只有太阳,径向模糊的画面

获取一帧只有太阳,径向模糊的画面


获取一帧正常的画面


两个画面融合
 

附上实现代码:(使用cesium最新的版本1.106) 如有问题可以私信我

第一步:使用后处理,对画面进行径向模糊

const blurShader = `
        uniform sampler2D UserMapSampler;
        uniform vec2 lightPositionOnScreen;
        uniform float decay;
        uniform float exposure;
        uniform float density;
        uniform float weight;
        in vec2 v_textureCoordinates; 
        bool isSunVisible(vec2 position) {
            return position.x > 0.0 && position.x < 1.0 && position.y > 0.0 && position.y < 1.0;
        }
        void main()
        {   
            vec2 tc = v_textureCoordinates; 
            int NUM_SAMPLES = 100;
            vec2 deltaTexCoord = (tc - lightPositionOnScreen.xy);
            deltaTexCoord *= 1.0 / float(NUM_SAMPLES) * density;
            float illuminationDecay = 1.0;
            vec4 color =texture(UserMapSampler, tc)*0.4;
            for(int i=0; i < NUM_SAMPLES ; i++)
            {
                tc -= deltaTexCoord;
                vec4 mysample = texture(UserMapSampler, tc)*0.4;
                mysample *= illuminationDecay * weight;
                color += mysample;
                illuminationDecay *= decay;
            }
            out_FragColor = vec4(color.rgb * exposure, 1.0);
        }
      `;

第二步:对遮挡太阳的进行遮挡,得到一个只有阳光的图

    `
          #version 300 es
          uniform sampler2D passTexture;
          uniform sampler2D depthTexture; // 场景深度纹理
          uniform bool isSkyAtmosphereVisible; // 大气是否显示
          in vec2 v_textureCoordinates; // 来自顶点着色器的纹理坐标

          float getDepth(in vec4 depth) {
                float z_window = czm_unpackDepth(depth);
                z_window = czm_reverseLogDepth(z_window);
                float n_range = czm_depthRange.near;
                float f_range = czm_depthRange.far;
                return (2.0 * z_window - n_range - f_range) / (f_range - n_range);
          }

          void main() {
              vec4 color;
              vec4 depthColor = texture(depthTexture, v_textureCoordinates);
              float depth = getDepth(depthColor);
              if (depth >= 1.0) {
                color = texture(passTexture, v_textureCoordinates);
              } else {
                color = vec4(0.0, 0.0, 0.0, 1.0);
              }
              out_FragColor = color;
          }
        `

第三步、对获取到的两张图进行融合

`
              uniform sampler2D baseTexture;
              uniform sampler2D blendTexture;
              in vec2 v_textureCoordinates;
              float blendScreen(float base, float blend) {
                return 1.0-((1.0-base)*(1.0-blend));
              }

              vec3 blendScreen(vec3 base, vec3 blend) {
                return vec3(blendScreen(base.r,blend.r),blendScreen(base.g,blend.g),blendScreen(base.b,blend.b));
              }

              void main()
              {           
                  vec4 base = texture(baseTexture, v_textureCoordinates);
                  vec4 blend = texture(blendTexture, v_textureCoordinates);

                  vec3 sceneColor = blend.xyz;
                  sceneColor = czm_RGBToHSB(sceneColor);
                  if (sceneColor.z > 0.8) {
                    sceneColor.z -= 0.2; // 降低亮度

                    // 限制亮度值在0到1之间
                    sceneColor.z = max(sceneColor.z, 0.0);
                  }
                  sceneColor = czm_HSBToRGB(sceneColor);

                  float opacity = 1.0;
                  vec3 blendColorDodge = (blendScreen(vec3(base.rgb), sceneColor)) * opacity + vec3((base.rgb) * (1.0 - opacity));
                  out_FragColor = vec4(blendColorDodge, base.a);
              }
            `

  • 21
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 20
    评论
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值