【问题标题】:How can I render an 'atmosphere' over a rendering of the Earth in Three.js?如何在 Three.js 中渲染地球上的“大气”?
【发布时间】:2012-04-30 02:22:29
【问题描述】:

在过去的几天里,我一直在尝试让 Three.js 纹理工作。我一直遇到的问题是我的浏览器阻止了纹理的加载,按照here 的说明解决了这个问题。

无论如何,我正在为我的一个课程制作一个太空导航游戏,演示如何在太空中导航航天器。所以,我正在渲染一堆行星,地球就是其中之一。我在下面附上了我的地球渲染图。看起来不错,但我想做的是通过在地球周围添加“气氛”来让它看起来更逼真。

我环顾四周,发现了一些真正与发光有关的 neat looking creations,但不幸的是,我认为它们不适用于我的情况。

这是将地球添加到我的场景中的代码(这是我从 Three.js 教程中获得的代码的修改版本):

    function addEarth(x,y){

        var sphereMaterial =
        new THREE.MeshLambertMaterial({
            //color: 0x0000ff,
            map: earthTexture
        });

        // set up the sphere vars
        var radius = 75;
        segments = 16;
        rings = 16;

        // create a new mesh with
        // sphere geometry - we will cover
        // the sphereMaterial next!
        earth = new THREE.Mesh(

        new THREE.SphereGeometry(
        radius,
        segments,
        rings),

        sphereMaterial);

        earth.position.x = x;
        earth.position.y = y;

        // add the sphere to the scene
        scene.add(earth);
    }

【问题讨论】:

    标签: javascript html three.js


    【解决方案1】:

    您究竟在寻找什么气氛?它可以像在地球顶部渲染另一个稍大的透明球体一样简单,也可以非常非常复杂,实际上折射进入它的光。 (类似于皮肤渲染中使用的次表面散射)。

    我自己从未尝试过这样的效果,但是一些快速的谷歌搜索显示了一些有希望的结果。例如,我认为this effect 看起来还不错,后来作者甚至跟进了更多detailed variant。如果您对更多技术故障感兴趣,this technique 会详细介绍很多理论背景。我敢肯定还有更多,你只需要四处逛逛。 (说实话,我不知道这是一个如此受欢迎的渲染主题!)

    如果您在这些技术的某些方面遇到问题,特别是适用于 Three.js,请不要犹豫!

    [更新]

    啊,对不起。是的,在没有事先着色器知识的情况下,这有点让你陷入困境。

    第二个链接上的代码实际上是一个 DirectX FX 文件,核心代码是 HLSL,所以它不能简单地插入 WebGL,但两种着色器格式非常相似,通常在它们之间进行转换不是问题.如果你真的了解着色器,那就是。我建议在尝试深入研究像这样的复杂效果之前阅读着色器的工作原理。

    我将从this tutorial 之类的简单内容开始,它简单地讨论了如何使用 Three.js 运行基本着色器。一旦您知道如何使用 Three.js 和 GLSL 教程(如 this one)让着色器工作,将为您提供有关着色器如何工作以及您可以用它做什么的基础知识。

    我知道这似乎需要大量工作,但如果你想在 WebGL 中制作高级视觉效果(这当然符合高级效果的要求),你绝对必须了解着色器!

    再一次,如果您正在寻找快速解决方案,那么总是有我所说的透明球体选项。 :)

    【讨论】:

    • 哇,这些例子真令人印象深刻!但是,您应该知道我对这些东西真的很陌生。在您发布的第二个链接中,似乎有一些 C 外观的代码。这就是我将如何使用那个气氛着色器吗?这就是他们所说的'glsl'吗?如何在 Three.js 中实现这一点?对不起,我对这个问题的严重无知。
    • 感谢您的快速更新。我想我会继续尝试学习着色器,因为它们看起来很重要。但是,由于我必须在大约一周内演示这个东西,我认为我无法在那个时间范围内全部学习。但是我会尝试,然后就像你说的那样,我总是可以制作一个透明的球体。
    【解决方案2】:

    好吧,这是一个古老且已经回答的问题,但我想添加我的解决方案以供初学者考虑。玩了很久的大气散射和 GLSL,开发了这个 VEEERRRYYY 大气散射的简化版本(如果动画停止刷新页面或查看 GIF 更堕落的东西):

    [

    1. 行星是椭球体(中心 x,y,z 和半径 rx,ry,rz
    2. 大气也是椭球体(相同但大气高度更大)
    3. 所有渲染均正常完成,但除此之外,还为近观察者星球添加了 1 个通道
    4. 那个pass是单四边形覆盖整个屏幕
    5. 在片段内部,它计算像素射线与这 2 个椭圆体的交点
    6. 取可见部分(不是后面,不是地后)
    7. 计算大气层内的光线长度
    8. 将原始颜色扭曲为r,g,b 按射线长度缩放参数的函数(类似于沿路径积分)
      • 一些颜色是给定的......
      • 极大地影响颜色,因此只需几个属性就可以模拟不同的气氛
    9. 它在大气层内外都能正常工作(远距离)
    10. 可以添加近恒星作为光源(我使用最大 3 星系统)

    结果令人惊叹,见下图:

    顶点:

    /* SSH GLSL Atmospheric Ray light scattering ver 3.0
    
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE);
        use with single quad covering whole screen
    
        no Modelview/Projection/Texture matrixes used
    
        gl_Normal   is camera direction in ellipsoid space
        gl_Vertex   is pixel in ellipsoid space
        gl_Color    is pixel pos in screen space <-1,+1>
    
        const int _lights=3;
        uniform vec3 light_dir[_lights];     // direction to local star in ellipsoid space
        uniform vec3 light_col[_lights];     // local star color * visual intensity
        uniform vec4 light_posr[_lights];    // local star position and radius^-2 in ellipsoid space
        uniform vec4 B0;                     // atmosphere scattering coefficient (affects color) (r,g,b,-)
    
        [ToDo:]
        add light map texture for light source instead of uniform star colide parameters
        - all stars and distant planets as dots
        - near planets ??? maybe too slow for reading pixels
        aspect ratio correction
    */
    
    varying vec3 pixel_nor;       // camera direction in ellipsoid space
    varying vec4 pixel_pos;       // pixel in ellipsoid space
    
    void main(void)
        {
        pixel_nor=gl_Normal;
        pixel_pos=gl_Vertex;
        gl_Position=gl_Color;
        }
    

    片段:

    varying vec3 pixel_nor;              // camera direction in ellipsoid space
    varying vec4 pixel_pos;              // pixel in ellipsoid space
    
    uniform vec3 planet_r;               // rx^-2,ry^-2,rz^-2 - surface
    uniform vec3 planet_R;               // Rx^-2,Ry^-2,Rz^-2 - atmosphere
    uniform float planet_h;              // atmoshere height [m]
    uniform float view_depth;            // max. optical path length [m] ... saturation
    
    // lights are only for local stars-atmosphere ray colision to set start color to star color
    const int _lights=3;
    uniform vec3 light_dir[_lights];     // direction to local star in ellipsoid space
    uniform vec3 light_col[_lights];     // local star color * visual intensity
    uniform vec4 light_posr[_lights];    // local star position and radius^-2 in ellipsoid space
    uniform vec4 B0;                     // atmosphere scattering coefficient (affects color) (r,g,b,-)
    
    // compute length of ray(p0,dp) to intersection with ellipsoid((0,0,0),r) -> view_depth_l0,1
    // where r.x is elipsoid rx^-2, r.y = ry^-2 and r.z=rz^-2
    float view_depth_l0=-1.0,view_depth_l1=-1.0;
    bool _view_depth(vec3 p0,vec3 dp,vec3 r)
        {
        float a,b,c,d,l0,l1;
        view_depth_l0=-1.0;
        view_depth_l1=-1.0;
        a=(dp.x*dp.x*r.x)
         +(dp.y*dp.y*r.y)
         +(dp.z*dp.z*r.z); a*=2.0;
        b=(p0.x*dp.x*r.x)
         +(p0.y*dp.y*r.y)
         +(p0.z*dp.z*r.z); b*=2.0;
        c=(p0.x*p0.x*r.x)
         +(p0.y*p0.y*r.y)
         +(p0.z*p0.z*r.z)-1.0;
        d=((b*b)-(2.0*a*c));
        if (d<0.0) return false;
        d=sqrt(d);
        l0=(-b+d)/a;
        l1=(-b-d)/a;
        if (abs(l0)>abs(l1)) { a=l0; l0=l1; l1=a; }
        if (l0<0.0)          { a=l0; l0=l1; l1=a; }
        if (l0<0.0) return false;
        view_depth_l0=l0;
        view_depth_l1=l1;
        return true;
        }
    // determine if ray (p0,dp) hits a sphere ((0,0,0),r)
    // where r is (sphere radius)^-2
    bool _star_colide(vec3 p0,vec3 dp,float r)
        {
        float a,b,c,d,l0,l1;
        a=(dp.x*dp.x*r)
         +(dp.y*dp.y*r)
         +(dp.z*dp.z*r); a*=2.0;
        b=(p0.x*dp.x*r)
         +(p0.y*dp.y*r)
         +(p0.z*dp.z*r); b*=2.0;
        c=(p0.x*p0.x*r)
         +(p0.y*p0.y*r)
         +(p0.z*p0.z*r)-1.0;
        d=((b*b)-(2.0*a*c));
        if (d<0.0) return false;
        d=sqrt(d);
        l0=(-b+d)/a;
        l1=(-b-d)/a;
        if (abs(l0)>abs(l1)) { a=l0; l0=l1; l1=a; }
        if (l0<0.0)          { a=l0; l0=l1; l1=a; }
        if (l0<0.0) return false;
        return true;
        }
    
    // compute atmosphere color between ellipsoids (planet_pos,planet_r) and (planet_pos,planet_R) for ray(pixel_pos,pixel_nor)
    vec3 atmosphere()
        {
        const int n=8;
        const float _n=1.0/float(n);
        int i;
        bool b0,b1;
        vec3 p0,p1,dp,p,c,b;
        // c - color of pixel from start to end
    
        float l0,l1,l2,h,dl;
        c=vec3(0.0,0.0,0.0);
        b0=_view_depth(pixel_pos.xyz,pixel_nor,planet_r);
        if ((b0)&&(view_depth_l0>0.0)&&(view_depth_l1<0.0)) return c;
        l0=view_depth_l0;
        b1=_view_depth(pixel_pos.xyz,pixel_nor,planet_R);
        l1=view_depth_l0;
        l2=view_depth_l1;
    
        dp=pixel_nor;
        p0=pixel_pos.xyz;
    
        if (!b0)
            {                                       // outside surface
            if (!b1) return c;                      // completly outside planet
            if (l2<=0.0)                            // inside atmosphere to its boundary
                {
                l0=l1;
                }
            else{                                   // throu atmosphere from boundary to boundary
                p0=p0+(l1*dp);
                l0=l2-l1;
                }
            // if a light source is in visible path then start color is light source color
            for (i=0;i<_lights;i++)
            if (light_posr[i].a<=1.0)
            if (_star_colide(p0-light_posr[i].xyz,dp,light_posr[i].a))
            c+=light_col[i];
            }
        else{                                       // into surface
            if (l0<l1) b1=false;                    // atmosphere is behind surface
            if (!b1)                                // inside atmosphere to surface
                {
                l0=l0;
                }
            else{                                   // from atmosphere boundary to surface
                p0=p0+(l1*dp);
                l0=l0-l1;
                }
            }
        dp*=l0;
        p1=p0+dp;
        dp*=_n;
    /*
        p=normalize(p1);
        h=0.0; l2=0.0;
        for (i=0;i<_lights;i++)
         if (light_posr[i].a<=1.0)
            {
            dl=dot(pixel_nor,light_dir[i]);         // cos(ang: light-eye)
            if (dl<0.0) dl=0.0;
            h+=dl;
            dl=dot(p,light_dir[i]);                 // normal shading
            if (dl<0.0) dl=0.0;
            l2+=dl;
            }
        if (h>1.0) h=1.0;
        if (l2>1.0) l2=1.0;
        h=0.5*(2.0+(h*h));
    */
        float qqq=dot(normalize(p1),light_dir[0]);
    
    
        dl=l0*_n/view_depth;
        for (p=p1,i=0;i<n;p-=dp,i++)                // p1->p0 path throu atmosphere from ground
            {
            _view_depth(p,normalize(p),planet_R);   // view_depth_l0=depth above atmosphere top [m]
            h=exp(view_depth_l0/planet_h)/2.78;
    
            b=B0.rgb*h*dl;
            c.r*=1.0-b.r;
            c.g*=1.0-b.g;
            c.b*=1.0-b.b;
            c+=b*qqq;
            }
        if (c.r<0.0) c.r=0.0;
        if (c.g<0.0) c.g=0.0;
        if (c.b<0.0) c.b=0.0;
        h=0.0;
        if (h<c.r) h=c.r;
        if (h<c.g) h=c.g;
        if (h<c.b) h=c.b;
        if (h>1.0)
            {
            h=1.0/h;
            c.r*=h;
            c.g*=h;
            c.b*=h;
            }
        return c;
        }
    
    void main(void)
        {
        gl_FragColor.rgb=atmosphere();
        }
    

    抱歉,它是我的一个非常古老的来源......应该可能转换为核心配置文件

    [编辑 1] 抱歉忘记添加地球大气的输入散射常数

        double view_depth=1000000.0;    // [m] ... longer path is saturated atmosphere color
        double ha=40000.0;              // [m] ... usable atmosphere height (higher is too low pressure)
    
    //  this is how B0 should be computed (for real atmospheric scattering with nested volume integration)
    //  const float lambdar=650.0*0.000000001; // wavelengths for R,G,B rays
    //  const float lambdag=525.0*0.000000001;
    //  const float lambdab=450.0*0.000000001;
    //  double r=1.0/(lambdar*lambdar*lambdar*lambdar); // B0 coefficients
    //  double g=1.0/(lambdag*lambdag*lambdag*lambdag);
    //  double b=1.0/(lambdab*lambdab*lambdab*lambdab);
    
    //  and these are my empirical coefficients for earth like 
    //  blue atmosphere with my simplified integration style
    //  images above are rendered with this:
        float r=0.198141888310295;
        float g=0.465578010163675;
        float b=0.862540960504986;
        float B0=2.50000E-25;
        i=glGetUniformLocation(ShaderProgram,"planet_h");   glUniform1f(i,ha);
        i=glGetUniformLocation(ShaderProgram,"view_depth"); glUniform1f(i,view_depth);
        i=glGetUniformLocation(ShaderProgram,"B0");     glUniform4f(i,r,g,b,B0);
    //  all other atributes are based on position and size of planet and are 
    //  pretty straightforward so here is just the earth size i use ...
        double r_equator=6378141.2; // [m]
        double r_poles=6356754.8;   // [m]
    

    [edit2] 3.9.2014 新源代码

    我最近有一些时间来实现缩放到我的引擎,并发现原始源代码从 0.002 AU 以上的距离不是很精确。没有缩放,它只有几个像素,所以什么都看不到,但是缩放所有变化,所以我尽量提高准确性。

    经过更多调整后,我发现它可以使用高达 25.0 AU,并且插值伪影高达 50.0-100.0 AU。这是当前硬件的限制,因为我不能将非 flat fp64 传递给从顶点到片段的插值器。一种解决方法可能是将坐标系变换移动到片段但尚未尝试过。以下是一些变化:

    • 新源使用 64 位浮点数
    • 并添加uniform int lights,这是已用灯的计数
    • B0 的含义也发生了一些变化(它们不再是依赖于波长的常数,而是颜色),因此您需要稍微更改 CPU 代码中的统一值填充。
    • 添加了一些性能改进

    [顶点]

    /* SSH GLSL Atmospheric Ray light scattering ver 3.1
    
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
        use with single quad covering whole screen
    
        no Modelview/Projection/Texture matrixes used
    
        gl_Normal   is camera direction in ellipsoid space
        gl_Vertex   is pixel in ellipsoid space
        gl_Color    is pixel pos in screen space <-1,+1>
    
        const int _lights=3;
        uniform int  lights;                 // actual number of lights
        uniform vec3 light_dir[_lights];     // direction to local star in ellipsoid space
        uniform vec3 light_col[_lights];     // local star color * visual intensity
        uniform vec4 light_posr[_lights];    // local star position and radius^-2 in ellipsoid space
        uniform vec4 B0;                     // atmosphere scattering coefficient (affects color) (r,g,b,-)
    
        [ToDo:]
        add light map texture for light source instead of uniform star colide parameters
        - all stars and distant planets as dots
        - near planets ??? maybe too slow for reading pixels
        aspect ratio correction
    */
    
    varying vec3 pixel_nor;       // camera direction in ellipsoid space
    varying vec4 pixel_pos;       // pixel in ellipsoid space
    varying vec4 pixel_scr;       // pixel in screen space <-1,+1>
    
    varying vec3 p_r;               // rx,ry,rz
    uniform vec3 planet_r;          // rx^-2,ry^-2,rz^-2 - surface
    
    void main(void)
        {
        p_r.x=1.0/sqrt(planet_r.x);
        p_r.y=1.0/sqrt(planet_r.y);
        p_r.z=1.0/sqrt(planet_r.z);
        pixel_nor=gl_Normal;
        pixel_pos=gl_Vertex;
        pixel_scr=gl_Color;
        gl_Position=gl_Color;
        }
    

    [片段]

    #extension GL_ARB_gpu_shader_fp64 : enable
    double abs(double x) { if (x<0.0) x=-x; return x; }
    
    varying vec3 pixel_nor;              // camera direction in ellipsoid space
    varying vec4 pixel_pos;              // pixel in ellipsoid space
    varying vec4 pixel_scr;              // pixel in screen space
    varying vec3 p_r;                    // rx,ry,rz                        
    
    uniform vec3 planet_r;               // rx^-2,ry^-2,rz^-2 - surface
    uniform vec3 planet_R;               // Rx^-2,Ry^-2,Rz^-2 - atmosphere
    uniform float planet_h;              // atmoshere height [m]
    uniform float view_depth;            // max. optical path length [m] ... saturation
    
    // lights are only for local stars-atmosphere ray colision to set start color to star color
    const int _lights=3;
    uniform int  lights;                 // actual number of lights
    uniform vec3 light_dir[_lights];     // direction to local star in ellipsoid space
    uniform vec3 light_col[_lights];     // local star color * visual intensity
    uniform vec4 light_posr[_lights];    // local star position and radius^-2 in ellipsoid space
    uniform vec4 B0;                     // atmosphere scattering color coefficients (r,g,b,ambient)
    
    // compute length of ray(p0,dp) to intersection with ellipsoid((0,0,0),r) -> view_depth_l0,1
    // where r.x is elipsoid rx^-2, r.y = ry^-2 and r.z=rz^-2
    const double view_depth_max=100000000.0; // > max view depth
    double view_depth_l0=-1.0, // view_depth_l0 first hit
           view_depth_l1=-1.0; // view_depth_l1 second hit
    bool  _view_depth_l0=false;
    bool  _view_depth_l1=false;
    bool _view_depth(vec3 _p0,vec3 _dp,vec3 _r)
        {
        dvec3 p0,dp,r;
        double a,b,c,d,l0,l1;
        view_depth_l0=-1.0; _view_depth_l0=false;
        view_depth_l1=-1.0; _view_depth_l1=false;
        // conversion to double
        p0=dvec3(_p0);
        dp=dvec3(_dp);
        r =dvec3(_r );
        // quadratic equation a.l.l+b.l+c=0; l0,l1=?;
        a=(dp.x*dp.x*r.x)
         +(dp.y*dp.y*r.y)
         +(dp.z*dp.z*r.z);
        b=(p0.x*dp.x*r.x)
         +(p0.y*dp.y*r.y)
         +(p0.z*dp.z*r.z); b*=2.0;
        c=(p0.x*p0.x*r.x)
         +(p0.y*p0.y*r.y)
         +(p0.z*p0.z*r.z)-1.0;
        // discriminant d=sqrt(b.b-4.a.c)
        d=((b*b)-(4.0*a*c));
        if (d<0.0) return false;
        d=sqrt(d);
        // standard solution l0,l1=(-b +/- d)/2.a
        a*=2.0;
        l0=(-b+d)/a;
        l1=(-b-d)/a;
        // alternative solution q=-0.5*(b+sign(b).d) l0=q/a; l1=c/q; (should be more accurate sometimes)
    //  if (b<0.0) d=-d; d=-0.5*(b+d);
    //  l0=d/a;
    //  l1=c/d;
        // sort l0,l1 asc
        if ((l0<0.0)||((l1<l0)&&(l1>=0.0))) { a=l0; l0=l1; l1=a; }
        // exit
        if (l1>=0.0) { view_depth_l1=l1; _view_depth_l1=true; }
        if (l0>=0.0) { view_depth_l0=l0; _view_depth_l0=true; return true; }
        return false;
        }
    
    // determine if ray (p0,dp) hits a sphere ((0,0,0),r)
    // where r is (sphere radius)^-2
    bool _star_colide(vec3 _p0,vec3 _dp,float _r)
        {
        dvec3 p0,dp,r;
        double a,b,c,d,l0,l1;
        // conversion to double
        p0=dvec3(_p0);
        dp=dvec3(_dp);
        r =dvec3(_r );
        // quadratic equation a.l.l+b.l+c=0; l0,l1=?;
        a=(dp.x*dp.x*r)
         +(dp.y*dp.y*r)
         +(dp.z*dp.z*r);
        b=(p0.x*dp.x*r)
         +(p0.y*dp.y*r)
         +(p0.z*dp.z*r); b*=2.0;
        c=(p0.x*p0.x*r)
         +(p0.y*p0.y*r)
         +(p0.z*p0.z*r)-1.0;
        // discriminant d=sqrt(b.b-4.a.c)
        d=((b*b)-(4.0*a*c));
        if (d<0.0) return false;
        d=sqrt(d);
        // standard solution l0,l1=(-b +/- d)/2.a
        a*=2.0;
        l0=(-b+d)/a;
        l1=(-b-d)/a;
        // alternative solution q=-0.5*(b+sign(b).d) l0=q/a; l1=c/q; (should be more accurate sometimes)
    //  if (b<0.0) d=-d; d=-0.5*(b+d);
    //  l0=d/a;
    //  l1=c/d;
        // sort l0,l1 asc
        if (abs(l0)>abs(l1)) { a=l0; l0=l1; l1=a; }
        if (l0<0.0)          { a=l0; l0=l1; l1=a; }
        if (l0<0.0) return false;
        return true;
        }
    
    // compute atmosphere color between ellipsoids (planet_pos,planet_r) and (planet_pos,planet_R) for ray(pixel_pos,pixel_nor)
    vec4 atmosphere()
        {
        const int n=8;
        const float _n=1.0/float(n);
        int i;
        bool b0,b1;
        vec3 p0,p1,dp,p,b;
        vec4 c;     // c - color of pixel from start to end
    
        float h,dl,ll;
        double l0,l1,l2;
        bool   e0,e1,e2;
        c=vec4(0.0,0.0,0.0,0.0);    // a=0.0 full background color, a=1.0 no background color (ignore star)
        b1=_view_depth(pixel_pos.xyz,pixel_nor,planet_R);
        if (!b1) return c;                          // completly outside atmosphere
        e1=_view_depth_l0; l1=view_depth_l0;        // first atmosphere hit
        e2=_view_depth_l1; l2=view_depth_l1;        // second atmosphere hit
        b0=_view_depth(pixel_pos.xyz,pixel_nor,planet_r);
        e0=_view_depth_l0; l0=view_depth_l0;        // first surface hit
        if ((b0)&&(view_depth_l1<0.0)) return c;    // under ground
        // set l0 to view depth and p0 to start point
        dp=pixel_nor;
        p0=pixel_pos.xyz;
        if (!b0)                                    // outside surface
            {
            if (!e2)                                // inside atmosphere to its boundary
                {
                l0=l1;
                }
            else{                                   // throu atmosphere from boundary to boundary
                p0=vec3(dvec3(p0)+(dvec3(dp)*l1));
                l0=l2-l1;
                }
            // if a light source is in visible path then start color is light source color
            for (i=0;i<lights;i++)
             if (_star_colide(p0.xyz-light_posr[i].xyz,dp.xyz,light_posr[i].a*0.75)) // 0.75 is enlargment to hide star texture corona
                {
                c.rgb+=light_col[i];
                c.a=1.0; // ignore already drawed local star color
                }
            }
        else{                                       // into surface
            if (l1<l0)                              // from atmosphere boundary to surface
                {
                p0=vec3(dvec3(p0)+(dvec3(dp)*l1));
                l0=l0-l1;
                }
            else{                                   // inside atmosphere to surface
                l0=l0;
                }
            }
        // set p1 to end of view depth, dp to intergral step
        p1=vec3(dvec3(p0)+(dvec3(dp)*l0)); dp=p1-p0;
        dp*=_n;
    
        dl=float(l0)*_n/view_depth;
        ll=B0.a; for (i=0;i<lights;i++)             // compute normal shaded combined light sources into ll
         ll+=dot(normalize(p1),light_dir[0]);
        for (p=p1,i=0;i<n;p-=dp,i++)                // p1->p0 path throu atmosphere from ground
            {
    //      _view_depth(p,normalize(p),planet_R);   // too slow... view_depth_l0=depth above atmosphere top [m]
    //      h=exp(view_depth_l0/planet_h)/2.78;
    
            b=normalize(p)*p_r;                     // much much faster
            h=length(p-b);
            h=exp(h/planet_h)/2.78;
    
            b=B0.rgb*h*dl;
            c.r*=1.0-b.r;
            c.g*=1.0-b.g;
            c.b*=1.0-b.b;
            c.rgb+=b*ll;
            }
        if (c.r<0.0) c.r=0.0;
        if (c.g<0.0) c.g=0.0;
        if (c.b<0.0) c.b=0.0;
        h=0.0;
        if (h<c.r) h=c.r;
        if (h<c.g) h=c.g;
        if (h<c.b) h=c.b;
        if (h>1.0)
            {
            h=1.0/h;
            c.r*=h;
            c.g*=h;
            c.b*=h;
            }
        return c;
        }
    
    void main(void)
        {
        gl_FragColor.rgba=atmosphere();
        }
    

    [统一值]

    // Earth
    re=6378141.2         // equatoreal radius r.x,r.y
    rp=6356754.79506139 // polar radius r.z
    planet_h=60000      // atmosphere thickness R(r.x+planet_h,r.y+planet_h,r.z+planet_h)
    view_depth=250000   // max view distance before 100% scattering occur 
    B0.r=0.1981         // 100% scattered atmosphere color
    B0.g=0.4656
    B0.b=0.8625
    B0.a=0.75           // overglow (sky is lighter before Sun actually rise) it is added to light dot product
    
    // Mars
    re=3397000
    rp=3374919.5
    ha=30000
    view_depth=300000
    B0.r=0.4314
    B0.g=0.3216
    B0.b=0.196
    B0.a=0.5
    

    有关更多信息(和更新的图像),另请参阅相关:

    [编辑3]

    这里有一个小的 CPU 辅助代码,我在引擎中使用上面的着色器来渲染气氛:

    if (sys->_enable_bodya) // has planet atmosphere?
     if (view_depth>=0.0)
        {
        glColor4f(1.0,1.0,1.0,1.0);
        double a,b,p[3],d[3];
        sys->shd_engine.unbind();
        sys->shd_scatter.bind(); // this is the atmospheric shader
        if (1) //*** GLSL_uniform_supported (leftover from old GL engine version)
            {
            int j;
            double *w;
            AnsiString s;
            a=re; b=rp; a=divide(1.0,a*a); b=divide(1.0,b*b); // radius of planet re equatoral and rp polar and ha is atmosphere thickness
            sys->shd_scatter.set3f("planet_r",a,a,b);
            a=re+ha; b=rp+ha;   a=divide(1.0,a*a); b=divide(1.0,b*b);
            sys->shd_scatter.set3f("planet_R"  ,a,a,b);
            sys->shd_scatter.set1f("planet_h"  ,ha);
            sys->shd_scatter.set1f("view_depth",view_depth); // visibility distance
            sys->shd_scatter.set4f("B0",B0[0],B0[1],B0[2],B0[3]); // saturated atmosphere color and overglow
            sys->shd_scatter.set1i("lights",sys->local_star.num); // local stars
            for (j=0;j<sys->local_star.num;j++)
                {
                a=sys->local_star[j].r;
                w=sys->local_star[j].p;
                s=AnsiString().sprintf("light_posr[%i]",j);
                sys->shd_scatter.set4f(s,w[0],w[1],w[2],divide(1.0,a*a));
    
                w=sys->local_star[j].d;
                s=AnsiString().sprintf("light_dir[%i]",j);
                sys->shd_scatter.set3f(s,w[0],w[1],w[2]);
    
                vector_mul(p,sys->local_star[j].col,10.0);
                s=AnsiString().sprintf("light_col[%i]",j);
                sys->shd_scatter.set3f(s,p[0],p[1],p[2]);
                }
            }
        glEnable(GL_BLEND);
        glBlendFunc(GL_ONE,GL_ONE_MINUS_SRC_ALPHA);
        a=1.0;
        b=-2.0*view.scr->views[view.scr->view].znear;
        // color  = pixel pos in screen space <-1,+1> ... no Projection/ModelView is used :)
        // vertex = pixel pos in elypsoid space
        // normal = eye-pixel direction in elypsoid space
        zsort.rep0.g2l_dir(d,zsort.obj_pos0);
        glDepthMask(0);
        glBegin(GL_QUADS);
        a=divide(1.0,view.zoom);
        glColor4d(-1.0,-1.0,0.0,1.0); vector_ld(p,-a,-a,b); view.scr->fromscr(p,p); view.eye0.l2g(q,p); zsort.rep0.g2l_dir(q,q); vector_sub(p,q,d); vector_one(q,q); glNormal3dv(q); glVertex3dv(p);
        glColor4d(+1.0,-1.0,0.0,1.0); vector_ld(p,+a,-a,b); view.scr->fromscr(p,p); view.eye0.l2g(q,p); zsort.rep0.g2l_dir(q,q); vector_sub(p,q,d); vector_one(q,q); glNormal3dv(q); glVertex3dv(p);
        glColor4d(+1.0,+1.0,0.0,1.0); vector_ld(p,+a,+a,b); view.scr->fromscr(p,p); view.eye0.l2g(q,p); zsort.rep0.g2l_dir(q,q); vector_sub(p,q,d); vector_one(q,q); glNormal3dv(q); glVertex3dv(p);
        glColor4d(-1.0,+1.0,0.0,1.0); vector_ld(p,-a,+a,b); view.scr->fromscr(p,p); view.eye0.l2g(q,p); zsort.rep0.g2l_dir(q,q); vector_sub(p,q,d); vector_one(q,q); glNormal3dv(q); glVertex3dv(p);
        glEnd();
        glDepthMask(1);
        glDisable(GL_BLEND);
        sys->shd_scatter.unbind();
        sys->shd_engine.bind();
        }
    

    它是从我的引擎中提取的,所以它使用了很多你没有的东西,但你知道这些东西是如何使用的......顺便说一句,l2g 意味着从局部坐标转换到全局坐标,g2l 是另一种方式。如果_dirl2g_dir 一样存在,则表示变换正在处理矢量而不是位置,因此没有平移。 fromscr 将屏幕 &lt;-1,+1&gt; 转换为 3D(相机本地),vector_one 将矢量标准化为单元一。希望我没有忘记解释一些事情......

    【讨论】:

    • 你能提供一个演示链接吗?
    • 你在这里:ulozto.sk/xewsQkcE/atmosphericscattering-rar 只需解压到 bin 文件夹读取 txt(键功能)并运行 exe。小心它模拟真实的物理 + 船的 ctrl 系统,所以设置速度并等到船匹配它......并且不要忘记在进入大气层之前放慢速度,这样你就不会飞快(没有崩溃或碰撞测试...)(雷达上的蓝色气泡表示停止距离,如果您靠近它开始减速)太阳正在围绕地球快速旋转以进行日/夜测试,因此您无需飞来飞去或等待...
    • 顺便说一句,最近有一个想法通过小型球形大气地图来改进(更现实)。这将允许我需要一些额外的东西 bud 没有时间实施和测试它,至少一年内不会有:( 以前有太复杂的付费项目......
    • @user2070775 抱歉忘记添加@user...没有它有时没有响应通知...
    • @Ealrann 是的椭球空间平均行星中心是 (0,0,0) 并且轴与 x,y,z 对齐(它简化了很多事情并提高了准确性)......和是的,法线必须归一化,否则计算的长度将不正确,并且输出颜色将是错误的......以防万一我使用此着色器从我的引擎中提取的代码将 [Edit2] 添加到我的答案中...所以你可以检查你是否有类似的......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-09-25
    • 2020-03-28
    • 2015-02-22
    • 2020-06-04
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    相关资源
    最近更新 更多