Cesium大气散射效果

由于做全球体积云效果的需要,再来研究下大气散射效果

和体积云类似,关于大气散射颜色计算的过程也仅发生在这两个球体之间。如图所示。

计算从相机出发的视线与球壳的交点,如果不相交,则该视线方向上不会发生大气散射,直接返回透明色。

javascript 复制代码
// 检查与大气外层球体的相交
 vec2 atmo_intersect = raySphereIntersect(start, dir, dynamic_atmo_radius);
 vec2 planet_intersect = raySphereIntersect(start, dir, planet_radius);
          
// 如果不相交大气层,直接返回
if (atmo_intersect.x < 0.0) return scene_color;

这段代码是大气散射物理模型的核心部分,用于模拟光线在大气中的散射行为。

学术解释:真实物理现象的数学建模 ,不是简单的视觉效果,而是基于光学理论的准确模拟。

简单来说:

  • 瑞利散射:让天空变蓝
  • 米氏散射:让太阳周围发光
  • mu:决定你看向太阳的角度对散射强度的影响
javascript 复制代码
// 散射计算
float mu = dot(dir, light_dir);
          
// 瑞利散射相函数
float phase_ray = 0.75 * (1.0 + mu * mu);
          
// 米氏散射相函数 (简化)
float phase_mie = 1.5 * ((1.0 - 0.9 * 0.9) / (2.0 + 0.9 * 0.9)) * (1.0 + mu * mu) / pow(1.0 + 0.9 * 0.9 - 2.0 * 0.9 * mu, 1.5);

最重要的一步 就是光线步进

javascript 复制代码
   for (int i = 0; i < MARCH_STEPS; i++) {
              // 当前采样点位置
              float t = ray_start + (float(i) + 0.5) * step_size;
              vec3 sample_pos = start + dir * t;
              
              // 计算当前高度和大气密度
              float height = length(sample_pos) - planet_radius;
              float density_ray = exp(-height / 8000.0);  // 瑞利散射标度高度:8km
              float density_mie = exp(-height / 1200.0);  // 米氏散射标度高度:1.2km
              
              // 计算光学深度(光线衰减)
              float optical_depth_step = (density_ray + density_mie) * step_size;
              total_optical_depth += optical_depth_step;
              
              // 计算透射率(光线在大气中的衰减)
              float transmittance = exp(-total_optical_depth * 0.00001); // 衰减系数
              
              // 🌟 太阳光到当前点的衰减(次级光线步进 - 简化版)
              float sun_optical_depth = 0.0;
              vec2 sun_intersect = raySphereIntersect(sample_pos, light_dir, dynamic_atmo_radius);
              if (sun_intersect.y > 0.0) {
                  float sun_ray_length = sun_intersect.y;
                  // 简化的太阳光衰减计算
                  float avg_height = height + sun_ray_length * 0.5 * light_dir.y;
                  sun_optical_depth = exp(-avg_height / 8000.0) * sun_ray_length;
              }
              float sun_transmittance = exp(-sun_optical_depth * 0.00001);
              
              // 计算当前步的散射贡献
              vec3 step_ray_scattering = beta_ray * density_ray * phase_ray * 
                                        transmittance * sun_transmittance * step_size;
              vec3 step_mie_scattering = beta_mie * density_mie * phase_mie * 
                                        transmittance * sun_transmittance * step_size;
              
              // 累积散射
              total_ray_scattering += step_ray_scattering;
              total_mie_scattering += step_mie_scattering;
          }

最终效果如图所示 左侧为没有处理的,ceisum自带的大气效果,右侧为添加散射的效果

其实只看图片的话,可能大部分人会觉得太阳都看不清楚有什么必要加,但这个散射在体积云中效果就很明显了,场景也明细更加贴近显示大气效果。

todo:将大气和全球体积云效果融合