How can I render an 'atmosphere' over a rendering of the Earth in Three.js?

前端 未结 2 1481
一生所求
一生所求 2020-11-22 14:26

For the past few days, I\'ve been trying to get Three.js texturing to work. The problem I\'ve been having is that my browser was blocking textures from loading, which was so

相关标签:
2条回答
  • 2020-11-22 15:03

    Well an old and already answered question but I wanted to add my solution for beginner consideration out there. Have playing along Atmospheric scattering and GLSL for a long time and developed this VEEERRRYYY Simplified version of Atmospheric scattering (if animation stops refresh page or view the GIF in something more decend):

    [example[1]

    1. planet is and ellipsoid (center x,y,z and radiuses rx,ry,rz)
    2. atmosphere is also ellipsoid (the same but bigger by atmosphere height)
    3. all render is done normally but on top of that is added 1 pass for near observer planet
    4. that pass is single quad covering whole screen
    5. inside fragment it computes the intersection of pixel ray with these 2 ellipsoids
    6. take the visible part (not behind, not after ground)
    7. compute the ray length inside atmosphere
    8. distort original color as function of r,g,b scaled params by ray length (something like integrating along the path)
      • some color is taken some given ...
      • greatly affects color so its possible to simulate different atmospheres by just few attributes
    9. it work well inside and also outside the atmosphere (from distance)
    10. can add close stars as light source (i use max 3 star system)

    the result is stunning see images below:

    enter image description here enter image description here enter image description here enter image description here enter image description here

    Vertex:

    /* 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;
        }
    

    Fragment:

    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();
        }
    

    Sorry but its a really old source of my ... should be probably converted to core profile

    [Edit 1] sorry forget to add my input scattering constants for Earth atmosphere

        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 new source code

    I had some time recently to implement zoom to mine engine and figured out that original source code is not very precise from distance above 0.002 AU. Without Zoom it is just a few pixels so nothing is seen, but with zoom all changes so I tried to improve accuracy as much as I could.

    • here ray and ellipsoid intersection accuracy improvement is the related question to this

    After some more tweaks I get it to be usable up to 25.0 AU and with interpolation artifacts up to 50.0-100.0 AU. That is limit for current HW because I can not pass non flat fp64 to interpolators from vertex to fragment. One way around could be to move the coordinate system transform to fragment but haven't tried it yet. Here are some changes:

    • new source uses 64 bit floats
    • and add uniform int lights which is the count of used lights
    • also some changes in B0 meaning (they are no longer wavelength dependent constant but color instead) so you need to change uniform value fill in CPU code slightly.
    • some performance improvements was added

    [vertex]

    /* 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;
        }
    

    [fragment]

    #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();
        }
    

    [uniform values]

    // 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
    

    For more info (and newer images) see also related:

    • Is it possible to make realistic n-body solar system simulation in matter of size and mass?

    [Edit3]

    Here a small CPU side code that I use in my engine to render atmosphere using shader above:

    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();
        }
    

    It is extracted from mine engine so it uses a lot of stuff you do not have, but you get the idea how the stuff is used... btw l2g means transform from local to global coordinate, g2l is the other way around. If _dir is present like l2g_dir it means the transform is handling vector instead of position so no translations. The fromscr converts screen <-1,+1> to 3D (camera local) and vector_one normalizes a vector to unit one. Hope I did not forget to explain something...

    0 讨论(0)
  • 2020-11-22 15:16

    What exactly are you looking for in your atmosphere? It could be as simple as rendering another slightly larger transparent sphere over the top of your globe, or it could be very very complex, actually refracting light that enters it. (Almost like subsurface scattering used in skin rendering).

    I've never tried such an effect myself, but some quick Googling shows some promising results. For example, I think this effect looks fairly nice, and the author even followed it up with a more detailed variant later on. If you're interested in a more technical breakdown this technique details a lot of the theoretical background. I'm sure there's more, you've just got to poke around a bit. (Truth be told I wasn't aware this was such a popular rendering topic!)

    If you're having trouble with some aspect of those techniques specifically as applies to Three.js don't hesitate to ask!

    [UPDATE]

    Ah, sorry. Yeah, that's a bit much to throw you into without prior shader knowledge.

    The code on the second link is actually a DirectX FX file, the core code being HLSL, so it's not something that would simply plug into WebGL but the two shader formats are similar enough that it's typically not an issue to translate between them. If you actually know shaders, that is. I would recommend reading up on how shaders work before trying to dive into a complicated effect like this.

    I'd start with something simple like this tutorial, which simply talks about how to get a basic shader running with Three.js. Once you know how to get a shader working with Three.js and GLSL tutorials (like this one) will give you the basics of how a shader works and what you can do with it.

    I know that seems like a lot of work up front, but if you want to do advanced visual effects in WebGL (and this certainly fits the bill of advanced effects) you absolutely must understand shaders!

    Then again, if you're looking for a quick fix there's always that transparent sphere option I was talking about. :)

    0 讨论(0)
提交回复
热议问题