23

過去数日間、私はThree.jsテクスチャリングを機能させることを試みてきました。私が抱えていた問題は、ブラウザがテクスチャのロードをブロックしていたことです。これは、ここの手順に従うことで解決されました。

とにかく、私は自分のクラスの1つで、宇宙船を宇宙でナビゲートすることを示すスペースナビゲーターゲームを作成しています。だから、私はたくさんの惑星をレンダリングしています。地球はそのうちの1つです。以下に私の地球のレンダリングの写真を含めました。見た目は大丈夫ですが、私がやろうとしているのは、惑星の周りに「大気」を追加することで、よりリアルに見えるようにすることです。

周りを見回してみると、輝きを扱うとてもきれいな作品がいくつか見つかりましたが、残念ながら、それらが私の状況に当てはまるとは思いません。

そして、これが私のシーンに地球を追加するコードです(これは、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);
    }

ここに画像の説明を入力してください

4

2 に答える 2

31

古くてすでに回答された質問ですが、初心者向けの解決策を追加したいと思いました。長い間、大気散乱と GLSL に沿って遊んで、大気散乱のこの VEEERRRYYY 簡易バージョンを開発しました (アニメーションが停止した場合は、ページを更新するか、GIFをより落ち着いたもので表示します)。

[例[1]

  1. 惑星は楕円体 (中心x,y,z と半径rx,ry,rz)
  2. 大気も楕円体です(同じですが、大気の高さだけ大きくなります)
  3. すべてのレンダリングは通常どおりに行われますが、その上に近くの観測惑星用の 1 つのパスが追加されます
  4. そのパスは、画面全体をカバーする単一のクワッドです
  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] 2014.3.9 新しいソースコード

最近、zoom to mine エンジンを実装する時間があったのですが、元のソース コードは 0.002 AU を超える距離ではあまり正確ではないことがわかりました。ズームなしでは数ピクセルなので何も見えませんが、ズームするとすべてが変わるので、できる限り精度を上げようとしました。

さらに微調整した後、最大 25.0 AU まで使用でき、最大 50.0 ~ 100.0 AU の補間アーティファクトを使用できるようになりました。flat fp64頂点からフラグメントへのインターポレーターに非を渡すことができないため、これが現在のハードウェアの制限です。1 つの方法は、座標系変換をフラグメントに移動することですが、まだ試していません。変更点は次のとおりです。

  • 新しいソースは 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が、逆です。_dirが存在する場合はl2g_dir、変換が位置ではなくベクトルを処理しているため、変換がないことを意味します。は、画面を 3D (カメラ ローカル) にfromscr変換し、ベクトルを単位 1 に正規化します。何かを説明するのを忘れていないことを願っています...<-1,+1>vector_one

于 2013-10-29T13:31:50.350 に答える
18

あなたは自分の雰囲気に何を求めていますか?球体の上に別の少し大きな透明な球体をレンダリングするのと同じくらい簡単な場合もあれば、非常複雑で、実際に入射光を屈折させる場合もあります。(スキン レンダリングで使用されるサブサーフェス スキャッタリングとほとんど同じです)。

私は自分でそのような効果を試したことはありませんが、いくつかの簡単なグーグル検索でいくつかの有望な結果が示されています. たとえば、この効果はかなり良さそうだと思いますし、作者は後でさらに詳細なバリエーションを追加しました。より技術的な内訳に興味がある場合は、この手法で多くの理論的背景が詳しく説明されています。他にもあると思います。少し調べてみてください。(正直なところ、これがそれほど人気のあるレンダリング トピックであるとは知りませんでした!)

特に Three.js に適用されるこれらの手法の一部に問題がある場合は、遠慮なく質問してください。

[アップデート]

あ、すみません。ええ、それはシェーダーの予備知識なしであなたを投げ込むのは少し大変です.

2 番目のリンクのコードは実際には DirectX FX ファイルであり、コア コードは HLSL であるため、単純に WebGL にプラグインするものではありませんが、2 つのシェーダー形式は十分に類似しているため、通常、それらの間で変換することは問題になりません。あなたが実際にシェーダーを知っているなら、それはそうです。このような複雑な効果に飛び込む前に、シェーダーがどのように機能するかを読むことをお勧めします。

このチュートリアルのような単純なものから始めます。このチュートリアルでは、Three.js で基本的なシェーダーを実行する方法について簡単に説明しています。シェーダーを Three.js および GLSL チュートリアル (このようなもの) で動作させる方法を理解したら、シェーダーのしくみとそれを使用して何ができるかの基本を説明します。

前もって多くの作業が必要なように思えますが、WebGL で高度な視覚効果を実行したい場合 (これは確かに高度な効果の法案に適合します)、シェーダーを完全に理解する必要があります!

繰り返しになりますが、簡単な修正を探している場合は、私が話していた透明な球体オプションが常にあります. :)

于 2012-04-18T16:38:11.957 に答える