Как я могу отобразить "атмосферу" поверх изображения Земли в Three.js?

Последние несколько дней я пытался заставить работать текстурирование Three.js. Проблема, с которой я столкнулся, заключается в том, что мой браузер блокировал загрузку текстур, что было решено с помощью следующих инструкций.

В любом случае, я делаю космическую навигацию для одного из моих классов, которая демонстрирует навигацию космического корабля в космосе. Итак, я рисую кучу планет, одна из которых - Земля. Я включил изображение моего рендеринга Земли ниже. Это выглядит хорошо, но я пытаюсь сделать его более реалистичным, добавив "атмосферу" вокруг планеты.

Я огляделся по сторонам и обнаружил некоторые действительно опрятные создания, которые имеют дело со свечением, но, к сожалению, я не думаю, что они применимы к моей ситуации.

А вот код, который добавляет землю к моей сцене (это модифицированная версия кода, которую я получил из учебника 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);
    }

2 ответа

Решение

Что именно вы ищете в своей атмосфере? Это может быть так же просто, как рендеринг другой немного большей прозрачной сферы поверх вашего земного шара, или это может быть очень очень сложный, фактически преломляющий свет, который входит в него. (Почти как подповерхностное рассеяние, используемое при рендеринге кожи).

Я никогда не пробовал такой эффект сам, но быстрый поиск в Google показывает некоторые многообещающие результаты. Например, я думаю, что этот эффект выглядит довольно хорошо, и автор даже разработал более подробный вариант позже. Если вы заинтересованы в более технической разбивке, эта техника подробно описывает теоретические основы. Я уверен, что есть еще кое-что, ты просто должен немного побродить. (По правде говоря, я не знал, что это была такая популярная тема рендеринга!)

Если у вас возникли проблемы с каким-либо аспектом этих техник, особенно в отношении Three.js, не стесняйтесь спрашивать!

[ОБНОВИТЬ]

Ах, прости. Да, это немного, чтобы бросить вас без предварительного знания шейдеров.

Код на второй ссылке на самом деле представляет собой файл DirectX FX, основной код которого - HLSL, так что это не то, что просто подключается к WebGL, но два формата шейдеров достаточно схожи, и обычно между ними не возникает проблем с переводом. Если вы действительно знаете шейдеры, то это так. Я бы порекомендовал прочитать о том, как работают шейдеры, прежде чем пытаться погрузиться в такой сложный эффект, как этот.

Я бы начал с чего-то простого, такого как этот учебник, в котором просто говорится о том, как запустить базовый шейдер с Three.js. Как только вы узнаете, как заставить шейдер работать с Three.js и учебными пособиями по GLSL (как этот), вы узнаете основы работы шейдера и что вы можете с ним сделать.

Я знаю, что это кажется большой работой заранее, но если вы хотите создавать расширенные визуальные эффекты в WebGL (и это, безусловно, соответствует требованиям продвинутых эффектов), вы обязательно должны понимать шейдеры!

Опять же, если вы ищете быстрое решение, всегда есть опция прозрачной сферы, о которой я говорил.:)

Хорошо старый и уже отвеченный вопрос, но я хотел добавить свое решение для начинающего рассмотрения там. Проследите за атмосферным рассеянием и GLSL в течение длительного времени и перейдите к этой упрощенной версии VEEERRRYYY (если анимация останавливает обновление страницы или просмотр GIF- файла в более приземленном виде):

[Пример [1

  1. планета и эллипсоид (центр x,y,z и радиусы rx,ry,rz)
  2. атмосфера тоже многоточия (такая же, но больше по высоте атмосферы)
  3. все рендеринг выполняется как обычно, но в дополнение к этому добавляется 1 проход для планеты ближнего наблюдателя.
  4. этот проход - один квад, покрывающий весь экран
  5. внутри фрагмента он вычисляет пересечение пиксельного луча с этими двумя эллипсоидами
  6. взять видимую часть (не позади, не после земли)
  7. вычислить длину луча в атмосфере
  8. искажать исходный цвет как функцию от r, g, b масштабированных параметров по длине луча (что-то вроде интегрирования по пути)
    • какой-то цвет взят какой-то дано...
    • сильно влияет на цвет, поэтому можно моделировать различные атмосферы всего лишь несколькими атрибутами
  9. хорошо работает как внутри, так и вне атмосферы (на расстоянии)
  10. Можно добавить близкие звезды в качестве источника света (я использую макс. 3 звездные системы)

Результат потрясающий, смотрите изображения ниже:

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

Фрагмент:

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 а.е. Без Zoom это всего несколько пикселей, поэтому ничего не видно, но с Zoom все изменения, поэтому я старался максимально повысить точность.

После еще нескольких настроек я использую его до 25,0 AU и с артефактами интерполяции до 50,0-100,0 AU. Это предел для текущего HW, потому что я не могу передать не flat fp64 интерполяторы от вершины к фрагменту. Одним из способов может быть преобразование системы координат во фрагмент, но еще не пробовал. Вот некоторые изменения:

  • новый источник использует 64-битные числа с плавающей точкой
  • и добавить uniform int lights это количество использованных огней
  • также некоторые изменения в значении B0 (они больше не являются зависимой от длины волны константой, а вместо этого цветом), поэтому вам нужно немного изменить равномерное заполнение кода процессора.
  • были добавлены некоторые улучшения производительности

[Вершина]

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

Для получения дополнительной информации (и новых изображений) см. Также связанные:

Другие вопросы по тегам