开发者生态
morning
渲染天空、日落和行星
2026-05-12
1 阅读
ibobev
关于渲染天空、日落和行星 2026 年 5 月 12 日 2026 年 5 月 12 日 这张照片在我的灵感板上已经放了一段时间了,这张照片是奋进号航天飞机,日落时分悬浮在近地轨道的太空中。它以地球的高层大气为背景,具有美丽、多彩的层次,从深橙色到蓝色,然后消失在太空的深黑色中。这种颜色渐变不仅美观,而且一旦您开始研究它的工作原理以及如何重现它,这些颜色背后的现象(大气散射)就更成为一个有趣的话题。航天飞机剪影 https://www.nasa.gov/image-article/shuttle-silhouette-2/ 我想用着色器构建我自己的这种效果版本,直接在浏览器中渲染天空独特的蓝色以及逼真的日落和日出。我的目标是尽可能接近那张照片,同时也朝着游戏和其他基于着色器的媒体中常见的大气渲染迈进。以下是这个为期一个月的旅程的结果汇编,所有内容都是实时运行的:我最初并没有打算写这个主题,但最近阿尔忒弥斯二号任务的热情,加上我自己对太空万物的兴趣,让我觉得值得深入探索。这也是构建互动体验的绝佳机会,可以让该主题更容易理解。在本文中,我们将了解如何逐步实现大气散射着色器后处理效果,从实现不同的构建块(光线行进、瑞利和米氏散射以及臭氧吸收)开始,以渲染真实的天空穹顶,然后调整结果以将其渲染为行星周围的大气壳。最后,我们将研究 Sebastian Hillaire 基于 LUT 的方法以获得更高效的结果,或者至少是我尝试实现它的方法,因为这在很大程度上是我在这个项目中走出舒适区阶段的一步。如何渲染天空 您可能曾经在某些时候尝试过在您的一些作品后面添加蓝色渐变背景,试图给它一个更“大气”的外观,然后就到此为止了,但很快您就发现这样做总感觉不太对劲 1 。为了更真实地实现,我们必须将天空及其颜色视为光与空气及其成分相互作用的结果,同时考虑几个变量,例如观察者的高度、灰尘量、一天中的时间等,所有这些都在一个体积中。确定了这一点后,我们第一部分的目标就是以此为指导原则,为我们的大气着色器奠定基础,并在一天中的任何时间获得与真实天空几乎没有区别的结果。大气密度采样 就像我们处理体积云或体积光的方式一样,对大气进行采样的一种简单方法是通过光线行进。我们可以将光线从相机位置投射到场景中并穿过透明介质来回答以下两个问题:有多少光线在穿过大气层时幸存下来?这是透射率项。每个样本有多少光被重定向到相机?也称为散射。为了回答第一个问题,我们需要累积沿光线遇到的大气密度,以获得所谓的光学深度。我们将使用瑞利密度函数对此进行建模,该函数告诉我们在给定高度 h 处有多少“空气”。考虑到随着海拔的增加,大气层会变得越来越稀薄,这一点很重要。采样瑞利密度并累积光学深度 1 const float RAYLEIGH_SCALE_HEIGHT = 8.0 ; // km 2 const float ATMOSPHERE_HEIGHT = 100.0 ; // km - 卡门线 3 const float VIEW_DISTANCE = 200.0 ; // 公里 4 const int PRIMARY_STEPS = 24 ; 5 const vec3 SUN_DIRECTION = 标准化 ( vec3 ( 0.0 , 1.0 , 1.0 ) ) ; 6 7 float rayleighDensity ( float h ) { 8 return exp ( - max ( h , 0.0 ) / RAYLEIGH_SCALE_HEIGHT ) ; 9 } 10 11 void main ( ) { 12 vec2 p = vUv * 2.0 - 1.0 ; 13 14 vec3 颜色 = vec3(0.0); 15 vec3 viewDir = 标准化 ( vec3 ( p . x , p . y , 1.0 ) ) ; 16 vec3 skyDir = 标准化 ( vec3 ( viewDir . x , max ( viewDir . y , 0.0 ) , viewDir . z ) ) ; 17 18 浮点步长= VIEW_DISTANCE / 浮点(PRIMARY_STEPS); 19 浮点数 viewOpticalDepth = 0.0 ; 20 21 for ( int i = 0 ; i < PRIMARY_STEPS ; i ++ ) { 22 float t = ( float ( i ) + 0.5 ) * stepSize ; 23 浮点数 h = t * skyDir 。 y ; 24 25 如果 (h < 0.0) 中断; 26 if ( h > ATMOSPHERE_HEIGHT ) 中断 ; 27 28 float dR = rayleighDensity ( h ) ; 29 视图光学深度 += dR * 步长; 30 31 // ... 32 } 33 34 //... 35 36 颜色 = ACESFilm ( 颜色 ) ; 37 38 fragColor = vec4(颜色,1.0); 39 } 然后,从光学深度,我们可以计算沿光线给定点的透射率 T:穿过大气层时幸存的光的比例。 T=1.0 意味着有