javascript html5 three.js

javascript - ¿Cómo puedo representar una ''atmósfera'' sobre una representación de la Tierra en Three.js?



html5 (2)

Durante los últimos días, he estado tratando de que la textura de Three.js funcione. El problema que he tenido es que mi navegador estaba bloqueando la carga de texturas, que se resolvió siguiendo las instrucciones que se encuentran here .

De todos modos, estoy haciendo un juego de navegador de espacio para una de mis clases que demuestra cómo navegar naves espaciales a través del espacio. Entonces, estoy renderizando un montón de planetas, la Tierra es uno de ellos. He incluido una imagen de mi representación de la Tierra a continuación. Se ve bien, pero lo que estoy tratando de hacer es hacerlo más realista al agregar una ''atmósfera'' alrededor del planeta.

He mirado a mi alrededor, y he encontrado algunas creaciones realmente bonitas que tratan con el brillo, pero desafortunadamente no creo que se apliquen a mi situación.

Y aquí está el código que agrega la tierra a mi escena (es una versión modificada del código que obtuve de un tutorial de 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); }


¿Qué es exactamente lo que buscas en tu ambiente? Podría ser tan simple como representar otra esfera transparente un poco más grande sobre la parte superior de su globo, o podría ser muy muy compleja, en realidad refractando la luz que entra en ella. (Casi como la dispersión subsuperficial utilizada en el renderizado de la piel).

Nunca he intentado tal efecto, pero un rápido Google Googling muestra algunos resultados prometedores. Por ejemplo, creo que este efecto parece bastante bueno, y el autor incluso lo siguió con una variante más detallada más adelante. Si está interesado en un desglose más técnico, esta técnica detalla muchos de los antecedentes teóricos. Estoy seguro de que hay más, solo tienes que hurgar un poco. (A decir verdad, ¡no sabía que este era un tema de representación tan popular!)

Si está teniendo problemas con algún aspecto de esas técnicas específicamente como se aplica a Three.js, ¡no dude en preguntar!

[ACTUALIZAR]

Oh, lo siento. Sí, eso es un poco demasiado para lanzarte sin el conocimiento previo del sombreador.

El código en el segundo enlace es en realidad un archivo DirectX FX, el código central es HLSL, por lo que no es algo que simplemente se conecte a WebGL, pero los dos formatos de sombreado son lo suficientemente similares como para que no sea un problema traducir entre ellos. Si realmente sabes shaders, eso es. Recomendaría leer sobre cómo funcionan los shaders antes de intentar sumergirse en un efecto complicado como este.

Comenzaría con algo tan simple como este tutorial , que simplemente habla de cómo hacer funcionar un sombreador básico con Three.js. Una vez que sepa cómo hacer que un shader trabaje con Three.js y GLSL, los tutoriales (como este ) le brindarán los conceptos básicos de cómo funciona un shader y qué puede hacer con él.

Sé que parece mucho trabajo por adelantado, pero si quieres hacer efectos visuales avanzados en WebGL (y esto ciertamente encaja en la lista de efectos avanzados) ¡ debes entender los sombreadores!

Por otra parte, si está buscando una solución rápida, siempre estaba esa opción de esfera transparente de la que estaba hablando. :)


Bueno, una pregunta antigua y ya contestada, pero quería agregar mi solución para que la consideren un principiante. Tener plaing a lo largo de la dispersión atmosférica y GLSL por un largo tiempo y venir a esta versión simplificada VEEERRRYYY (si la animación deja de actualizar la página o ver el GIF en algo más decendente):

El

  1. planeta es y elipsoide (centro x, y, z y radios rx, ry, rz)
  2. La atmósfera también es elipsis (igual pero más grande por la altura de la atmósfera)
  3. todo el render se realiza normalmente, pero además de eso se agrega 1 pase para el planeta observador cercano
  4. Ese pase es un quad individual que cubre toda la pantalla.
  5. en el fragmento interno se calcula la intersección de rayos de píxeles con estos 2 elipsoides
  6. tomar la parte visible (no detrás, no después de tierra)
  7. calcular la longitud del rayo dentro de la atmósfera
  8. distorsiona el color original como función de r, g, b params escalados por la longitud del rayo (algo así como la integración a lo largo del camino)
    • un poco de color se toma algo dado ...
    • Afecta mucho el color, por lo que es posible simular diferentes atmósferas con solo unos pocos atributos.
  9. Funciona bien dentro y también fuera de la atmósfera (desde la distancia).
  10. puede agregar estrellas cercanas como fuente de luz (uso un sistema máximo de 3 estrellas)

el resultado es impresionante ver imágenes a continuación:

Vértice:

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

Fragmento:

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

Lo siento, pero es una fuente muy antigua de mi ... probablemente debería convertirse al perfil principal

[Editar 1] perdón olvide agregar las constantes de dispersión de entrada para la atmósfera de la Tierra

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 nuevo código fuente

Recientemente, tuve algo de tiempo para implementar el zoom en el motor de la mina y descubrí que el código fuente original no es muy preciso desde una distancia superior a 0.002 UA. Sin Zoom, solo quedan unos pocos píxeles para que no se vea nada, pero con el zoom todos los cambios, así que intenté mejorar la precisión tanto como pude.

  • Aquí la mejora de la precisión de la intersección de rayos y elipsoides es la pregunta relacionada con esto.

Después de algunos ajustes más, puedo usarlo hasta 25.0 UA y con artefactos de interpolación hasta 50.0-100.0 UA. Ese es el límite para el HW actual porque no puedo pasar el flat fp64 no flat fp64 a los interpoladores de vértice a fragmento. Una forma de hacerlo podría ser mover la transformación del sistema de coordenadas a un fragmento, pero aún no lo he probado. Aquí hay algunos cambios:

  • nueva fuente utiliza flotadores de 64 bits
  • y agregar uniform int lights que es el recuento de luces usadas
  • también algunos cambios en el significado de B0 (ya no son una constante dependiente de la longitud de onda sino un color), por lo que debe cambiar un poco el valor uniforme y completar el código de la CPU.
  • Se agregaron algunas mejoras de rendimiento.

[vértice]

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

[fragmento]

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

[valores uniformes]

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

Para más información (y nuevas imágenes) ver también relacionados:

  • ¿Es posible realizar una simulación realista del sistema solar n-body en cuestión de tamaño y masa?

[Edit3]

Aquí hay un pequeño código del lado de la CPU que uso en mi motor para representar la atmósfera usando el sombreador de arriba:

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

Se extrae del motor de la mina, por lo que usa muchas cosas que no tiene, pero se le da una idea de cómo se usan las cosas ... por cierto l2g significa transformación de coordenadas locales a globales, g2l es al revés. Si _dir está presente como l2g_dir , significa que la transformación está manejando el vector en lugar de la posición, por lo que no hay traducciones. La pantalla fromscr convierte <-1,+1> a 3D (cámara local) y vector_one normaliza un vector a la unidad uno. Espero no haber olvidado explicar algo ...