How to calculate rocket? - javascript

So I got a 3d system and some coordinates:
Start coordinates (x, y, z) of a rocket (on the ground)
Target coordinates (x, y, z) of the rockets target (also on the ground)
I got some initialize values like:
maximum_velocityZ = 0.5
maximum_resVelocityXY = 0.3
gravity factor = 9.81
How can I calculate the flight velocitys (velocityX, velocityY and velocityZ) for every update frame?
let maximum_velocityZ = 0.5
let maximum_resVelocityXY = 0.3
let gravity_factor = 9.81
let rocketPosition = {
x: 3,
y: 0,
z: 2
}
let rocketTarget = {
x: 7,
y: 5,
z: 8
}
let rocketVelocity = {
x: 0,
y: 0,
z: 0
}
let update = function() {
rocketPosition.x += rocketVelocity.x
rocketPosition.y += rocketVelocity.y
rocketPosition.z += rocketVelocity.z
let distanceX = (rocketTarget.x - rocketPosition.x)
let distanceY = (rocketTarget.y - rocketPosition.y)
let distanceZ = (rocketTarget.z - rocketPosition.z)
let factorXY = Math.abs(distanceX / distanceY)
rocketVelocity.x = maximum_resVelocityXY / Math.sqrt((1 / factorXY ** 2) + 1) * (distanceX > 0 ? 1 : -1)
rocketVelocity.y = maximum_resVelocityXY / Math.sqrt((factorXY ** 2) + 1) * (distanceY > 0 ? 1 : -1)
rocketVelocity.z = maximum_velocityZ * distanceZ;
rocketVelocity.z /= gravity_factor;
console.log("x:", Math.round(rocketPosition.x), "y:", Math.round(rocketPosition.y), "z:", Math.round(rocketPosition.z))
}
setInterval(update, 300)
This code is what I've developed so far. I'm sure I'm on the right track. X and Y seem to be more or less right. Only the Velocity Z can't be calculated the way I tried. In 3D space the trajectory doesn't really look realistic. So by "not really" I mean "not realistic at all"...
I would be happy for help. Thanks and a happy new year - matching to the rocket - of course!

I do not know what is you coordinate system
plane
sphere
ellipsoid like WGS84
My guess is your ground is planar (induced from your constants however your positions suggest something else)... so I will stick with that for now... You got 2 problems:
Newton/D'Alembert physics
Yours is weird as you got no dt multiplication so it works only if your update is 1 Hz. take a look at this:
Can't flip direction of ball without messing up gravity
you do not need speed limiter as air friction will do it for you and you should drive with accelerations ... or Force if you want to account for mass changes too.
However as you are working with ground/ground then I assume atmospheric flight instead of Newtonian so in such case you need to handle the heading control not by acceleration but by turning the integrated velocity. The main thruster should still be handled as acceleration.
The collisions are not necessary in your case (unless your ground is not planar or have obstacles along the way).
Rocket guiding system
I suggest to use 3 state (Markovov model) rocket control.
launch
Rise the rocket to safe altitude first to avoid obstacles, conserve fuel and maximize speed
cruise
Travel to the target area (while still keep its altitude). Simply compute heading projected on the ground plane and apply correction to the heading of the rocket to match it (still going parallel to ground).
hit
Hit the target while descending. Almost the same as #2 but this time you need to change altitude too...
On top of these you can add strategies to avoid detection/destruction or obstacles etc ... You can also make a fake approach from different heading to keep the launch position hidden ...
Here a simple C++ example of this approach:
//---------------------------------------------------------------------------
void vector_one(double *c,double *a)
{
double l=sqrt((a[0]*a[0])+(a[1]*a[1])+(a[2]*a[2]));
if (l>1e-10) l=1.0/l; else l=0.0;
c[0]=a[0]*l;
c[1]=a[1]*l;
c[2]=a[2]*l;
}
//---------------------------------------------------------------------------
// Z=0 plane is ground, Z+ is up
const double g=9.81; // [m/s^2] Earth's gravity
const double acc0=20.0; // [m/s^2] rocket main thruster acceleration
const double kv2 =0.002; // [-] rocket air friction coeff (speed limiter)
const double alt0=50.0; // [m] rocket safe altitude
const double dis0=100.0; // [m] rocket safe distance to target
const double dis1= 10.0; // [m] rocket explosion distance to target
const double dang0=375.0*M_PI/180.0;// [rad/s] rocket turn speed per yaw/roll/pitch
// Rocket
double dst[3]={+90.0,-50.0,0.0}; // [m] target position
double pos[3]={-100.0,200.0,0.0}; // [m] rocket position
double vel[3]={ 0.0, 0.0,0.0}; // [m/s] rocket velocity
double acc[3]={ 0.0, 0.0,0.0}; // [m/s^2] rocket acceleration
enum{
_state_none=0,
_state_launch, // rise to alt0
_state_cruise, // get near target but maintain alt0
_state_hit, // descend and hit
};
int state=_state_launch;
void update(double dt) // update rocket after dt [sec] has passed
{
int i;
double v,a,hdg[3],tar[3];
// guiding system
if (state==_state_none)
{
for (i=0;i<3;i++) vel[i]=0.0;
for (i=0;i<3;i++) acc[i]=0.0;
return;
}
if (state==_state_launch)
{
// init heading to Up
for (i=0;i<3;i++) hdg[i]=0.0; hdg[2]=1.0;
if (pos[2]>=alt0) state=_state_cruise;
}
v=sqrt((vel[0]*vel[0])+(vel[1]*vel[1])+(vel[2]*vel[2]));// |vel|
if ((state==_state_cruise)||(state==_state_hit))
{
vector_one(hdg,vel); // heading
for (i=0;i<3;i++) tar[i]=dst[i]-pos[i]; // to target
a=sqrt((tar[0]*tar[0])+(tar[1]*tar[1])+(tar[2]*tar[2])); // distance to target
if (state==_state_cruise)
{
tar[2]=0; // no altitude change
if (a<=dis0) state=_state_hit;
}
else{
if (a<=dis1) state=_state_none; // here you shoul add exlosion code
}
vector_one(tar,tar);
// a = angle between hdg and tar [rad]
for (a=0.0,i=0;i<3;i++) a+=hdg[i]*tar[i];
a=fabs(acos(a));
// approximate turn up to dang0
if (a>1e-10) a=dt*dang0/a; else a=0.0;
for (i=0;i<3;i++) hdg[i]=hdg[i]+a*(tar[i]-hdg[i]);
vector_one(hdg,hdg); // new heading
for (i=0;i<3;i++) vel[i]=v*hdg[i]; // new vel
}
// physics
for (i=0;i<3;i++) acc[i] =-kv2*vel[i]*v; // air friction (k*|vel|^2)
for (i=0;i<3;i++) acc[i]+=hdg[i]*acc0; // rocket thrust
acc[2]-=g; // gravity
// Newton/D'Alembert simulation
for (i=0;i<3;i++) vel[i]+=acc[i]*dt;
for (i=0;i<3;i++) pos[i]+=vel[i]*dt;
}
//---------------------------------------------------------------------------
You might need to tweak the constants a bit to match your sizes and game needs. As you can see you can customize the rocket quite a lot which is ideal for game (tech upgrades).
The physics is straight forward Newton/D'Alembert (apart the vel turning due to wings) and the guiding system works as described above. In first state the rocket just rise to alt0 then it try to turn towards target with dang0 turn speed while maintaining altitude and when closer than dis0 it start also descending. If closer than dis1 the rocket should explode ...
Here preview (top view):
The white line is line from ground to verify the altitude of rocket ... Rocket is Blue and target is Red.
The turning math is like this:
so I just scaled tar-hdg to approximately match dang0*dt and add that to original hdg. now the new heading is turned towards target by up to dang0*dt so just I normalize it back to unit size and recompute velocity to this new direction (as wings are turning velocity instead of accelerating)
Beware of the units
All the units used must be compatible I am using SI. Your 9.81 constant suggest the same but your position and target values makes no sense if in meters ... Why shoot rocket if target is just few meters away? Also the values suggest your coordinates are either not cartessian or ground is not planar/flat. Also the values suggest integers hope you have floats/doubles instead...

The trajectory will be a parabola. The basic equations of which are explained quite well here: https://courses.lumenlearning.com/boundless-physics/chapter/projectile-motion/
The 3D problem (x, y, z) can be easily be transformed to a 2D (single plane) problem (horizontal, vertical), for the equations then back to 3D for the problem.

Related

How can I convert a periodic range into one range

I'm searching for a function to convert sound frequency into a light frequency, or in other words a sound into a color.
For the moment I can achieve this by a simple formula that transposes the sound frequency to the light frequency range:
const lightFreq = frequency * Math.pow( 2, 40 );
Now if I want to have the wave length in nanometers I only need to do
const waveLength = 1 / lightFreq / 10000000;
One common analogy between sound and color would be to replicate this corresponding wavelength to each octaves. This would imply that if 440Hz is equal to a wavelength of approximately 640nm then the octave which is 880Hz would also be 640nm.
Using the function could look like:
soundFreqToLightWaveLength( 440 ) //approx.640
soundFreqToLightWaveLength( 880 ) //approx.640
soundFreqToLightWaveLength( 1760 ) //approx.640
waveLength range [380,780]
In order to divide a frequency range into 9 divisions you will have to find the 9th root of the factor from lower to higher frequency:
let a=20,b=20000,n=9;
const fact=Math.pow(b/a,1/n);
console.log(a);
for (i=0; i<n; i++) console.log(a*=fact)
Update, referring to the updated question:
The following snippet translates sound frequencies to light frequencies with an equivalence of 440HZ = 640nm. Within one octave: the higher the sound pitch, the shorter the wave length will be:
sound frequency / Hz
wave length / nm
440
640
660
426.6666666666667
879.9999
320.0000363636406
880
640
1320
426.6666666666667
1760
640
let a=20,b=20000,n=12;
const A=440, lnA=Math.log2(A)%1;
const L=640; // base wave length
if(1)for (let n=35,f=20,f1=20000,fact=Math.pow(f1/f,1/n);n-->-1; f*=fact) {
let l=(lnA-Math.log2(f))%1;
let wl=Math.pow(2,l)*L // light wave length in nm
console.log(f,wl)
}
The snippet goes throught the frequency range 20 ... 20000Hz in 35 steps (this can be changed to any value). The light wave lengths are mapped only for the fractional part (l=(lnA-Math.log2(f))%1) of the frequencies as the will repeat in each octave.
Clarification/question
Looking at OP's latest comment I now assume that the wave-length calculations should be done according to the following diagram:
In the above example we can see that the whole frequency range (from lower value f0 to higher value f1) has been divided into 6 divisions (instead of octaves!), within each of which the sound frequencies will be mapped to the wave length in the range defined by wl0 (longest wave length) and wl1 (shortest wave length).
OP, is that what you had in mind?
If so, then the following would work for you:
function calcWaveLength([f0,f1],[wl0,wl1],n){
const lf0=Math.log(f0), lfstep=Math.log(f1/f0)/n,
lwl0=Math.log(wl0), llrange=Math.log(wl1/wl0);
return function(freq){ // return the actual calc-function here
lf=Math.log(freq)
return Math.exp( lwl0 + (lf-lf0)/lfstep % 1 * llrange )
}
}
// set up the calc function (once) for each frequency range:
const calc=calcWaveLength([20,20000],[640,460],3);
console.log("frequency, wavelength");
// test for the following frequencies:
[20,35.56558820077846,63.245553203367585,112.46826503806982,199.999,
200,355.65588200778456,632.4555320336758,1124.682650380698,1999.99,
2000,3556.558820077845,6324.555320336758,11246.82650380698,19999.9,
20000].forEach(f=>
console.log(f.toFixed(3),calc(f).toFixed(3))
)
The snippet calculates what the diagram demands, but the result is something like a sawtooth wave function, having sharp inclines at the end of each division:
// outputs a number between 0 and 1, depending on whether the pitch
// is at the lower or upper end of an octave
// each semitone will be 1/12th of the way between 0 and 1
// the octave starts with an A note. Change the number 440
// to the frequency of another note if you want the octave
// to start at a different note
function freqToOctavePosition(freq) {
return ((Math.log(freq/440)/Math.log(2) % 1) + 1) % 1
}
// linearly converts the position within the octave to a position within a range
function freqToRange(freq, rangeStart, rangeEnd) {
return (freqToOctavePosition(freq) * (rangeEnd-rangeStart)) + rangeStart
}
console.log(freqToRange(262, 400, 700))

Output/Generate a set of points from individual equations - Python, JS, Matlab

I began with a set of coordinates, which I then approximated a function to represent them (Fourier series). The function produced is a sum of sin and cos waves:
0.3sin(2x) + 1.7(sin5x) + 1.8(sin43x)...
I would like to take this new function that I generated and produce a new set of coordinates. How can I generate points for every [INTEGER X Value] say from 0-400?
Note: I have 2 complex (2D) functions.
GOAL: Take a function --> Generate Points from this function for every whole integer.
This uses a function handle and (:) to force a column vector ((:).' forces a row vector).
The code simply uses the given equation (summing sines and cosines) to calculate a corresponding y coordinate for each given x coordinate.
% MATLAB R2018b
X = 0:400; % x = 0, 1, 2, ..., 400
fh = #(x) 0.3*sin(2*x) + 1.7*sin(5*x) + 1.8*sin(43*x);
Y = fh(X);
P = [X(:) Y(:)];
Note that size(P) returns 401 x 2. You'll see Y takes on whatever size X is, which is a row vector. X can be declared as as column vector with X = (0:400).' using .' which performs a transpose.
Recommend taking a look at MATLAB's documentation, specifically the Getting Started and Language Fundamentals.
Relevant MATLAB functions: sin, cos.
Matlab Code
X = 0:400;
fh = #(x) 0.3*sin(2*x) + 1.7*sin(5*x) + 1.8*sin(43*x);
Y = fh(X);
P = [X, Y]

Is it possible to draw function like tangens wihout knowing its domain? [duplicate]

I am making a graphing program in C++ using the SFML library. So far I have been able to draw a function to the screen. I have run into two problems along the way.
The first is a line which seems to return to the origin of my the plane, starting from the end of my function.
You can see it in this image:
As you can see this "rogue" line seems to change colour as it nears the origin. My first question is what is this line and how may I eradicate it from my window?
The second problem which is slightly unrelated and more mathematical can be seen in this image:
As you can see the asymptotes which are points where the graph is undefined or non continuous are being drawn. This leads me to my second question: is there a way ( in code ) to identify an asymptote and not draw it to the window.
My code for anything drawn to the window is:
VertexArray axis(Lines, 4);
VertexArray curve(PrimitiveType::LinesStrip, 1000);
axis[0].position = Vector2f(100000, 0);
axis[1].position = Vector2f(-100000, 0);
axis[2].position = Vector2f(0, -100000);
axis[3].position = Vector2f(0, 100000);
float x;
for (x = -pi; x < pi; x += .0005f)
{
curve.append(Vertex(Vector2f(x, -tan(x)), Color::Green));
}
I would very much appreciate any input : )
Update:
Thanks to the input of numerous people this code seems to work fine in fixing the asymptote problem:
for (x = -30*pi; x < 30*pi; x += .0005f)
{
x0 = x1; y0 = y1;
x1 = x; y1 = -1/sin(x);
a = 0;
a = fabs(atan2(y1 - y0, x1 - x0));
if (a > .499f*pi)
{
curve.append(Vertex(Vector2f(x1, y1), Color::Transparent));
}
else
{
curve.append(Vertex(Vector2f(x1, y1), Color::Green));
}
}
Update 2:
The following code gets rid of the rogue line:
VertexArray curve(Lines, 1000);
float x,y;
for (x = -30 * pi; x < 30 * pi; x += .0005f)
{
y = -asin(x);
curve.append(Vertex(Vector2f(x, y)));
}
for (x = -30 * pi + .0005f; x < 30 * pi; x += .0005f)
{
y = -asin(x);
curve.append(Vertex(Vector2f(x, y)));
}
The first problem looks like a wrong polyline/curve handling. Don't know what API are you using for rendering but some like GDI need to start the pen position properly. For example if you draw like this:
Canvas->LineTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...
Then you should do this instead:
Canvas->MoveTo(x[0],y[0]);
Canvas->LineTo(x[1],y[1]);
Canvas->LineTo(x[2],y[2]);
Canvas->LineTo(x[3],y[3]);
...
In case your API needs MoveTo command and you are not setting it then last position is used (or default (0,0)) which will connect start of your curve with straight line from last draw or default pen position.
Second problem
In continuous data you can threshold the asymptotes or discontinuities by checking the consequent y values. If your curve render looks like this:
Canvas->MoveTo(x[0],y[0]);
for (i=1;i<n;i++) Canvas->LineTo(x[i],y[i]);
Then you can change it to something like this:
y0=y[0]+2*threshold;
for (i=0;i<n;i++)
{
if (y[1]-y0>=threshold) Canvas->MoveTo(x[i],y[i]);
else Canvas->LineTo(x[i],y[i]);
y0=y[i];
}
The problem is selection of the threshold because it is dependent on x density of sampled points and on the first derivation of your y data by x (slope angles)
If you are stacking up more functions the curve append will create your unwanted line ... instead handle each data as separate draw or put MoveTo command in between them
[Edit1]
I see it like this (fake split):
double x0,y0,x1,y1,a;
for (e=1,x = -pi; x < pi; x += .0005f)
{
// last point
x0=x1; y0=y1;
// your actual point
x1=x; y1=-tan(x);
// test discontinuity
if (e) { a=0; e=0; } else a=fabs(atan2(y1-y0,x1-x0));
if (a>0.499*M_PI) curve.append(Vertex(Vector2f(x1,y1), Color::Black));
else curve.append(Vertex(Vector2f(x1,y1), Color::Green));
}
the 0.499*M_PI is you threshold the more closer is to 0.5*M_PIthe bigger jumps it detects... I faked the curve split by black color (background) it will create gaps on axis intersections (unless transparency is used) ... but no need for list of curves ...
Those artifacts are due to the way sf::PrimitiveType::LinesStrip works (or more specific lines strips in general).
In your second example, visualizing y = -tan(x), you're jumping from positive infinity to negative infinity, which is the line you're seeing. You can't get rid of this, unless you're using a different primitive type or splitting your rendering into multiple draw calls.
Imagine a line strip as one long thread you're pinning with pushpins (representing your vertices). There's no (safe) way to go from positive infinity to negative infinity without those artifacts. Of course you could move outside the visible area, but then again that's really specific to this one function.

HTML Canvas game: 2D collision detection

I need to program a very simple 2D HTML canvas game with a character and a few walls. The map (top view) is a multidimensional array (1=walls)
map = [
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
[1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,0,0,0],
[1,0,0,1,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,1],
[1,0,0,1,0,0,0,0,1,0,1,1,1,1,1,1,0,0,0,0,0,0,0,1],
[1,0,0,1,0,0,0,0,1,0,0,0,1,1,0,0,0,0,0,0,0,0,0,1],
[1,0,0,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
[1,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1],
[1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,0,1,1],
[1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,1,1,0,0,1,1,0,1,1],
[1,1,1,0,1,1,1,1,1,1,0,1,1,1,1,0,0,0,0,1,1,0,1,1],
[0,0,0,0,1,1,1,1,1,1,0,1,1,1,1,0,0,1,1,1,1,0,1,1],
[1,1,1,0,1,1,1,1,0,0,0,1,1,1,0,0,0,1,1,1,1,0,1,1],
[1,1,1,0,0,0,0,0,0,0,0,1,1,0,0,1,1,1,0,0,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1,0,1,1,0,0,0,0,0,0,1,0,0,1,1],
[1,1,1,1,1,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,1,1,1,1],
[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
];
The character shouldn't be able to walk over the walls... so he should only walk on the "0"s. I already got the map rendering and the walking part of the character working just fine, but I can't quite figure out how to check for collisions yet.
A very simple version can be found on JSBin. You can either use the arrow keys or WASD to move around (black square).
I already tried to do a very simple collision detection by using something like this:
function checkCollision( x, y ) {
if ( map[ Math.round( x ) ][ Math.round( y ) ] !== 0 ) {
return true; // Collision
}
return false;
}
But this doesn't quite work (see JSBin). With Math.round the character and wall overlap... if I use Math.ceil or Math.floor it's even worse.
Is there any way that I can improve this "collision detection", so that the character can't walk over the red walls?
There are a few problems:
First you should avoid using 0.1 as coordinate step because it's a "bad number" in floating point (it's periodic when expressed in binary). Much better is 0.125 (1/8). Adding/substracting 1/8 will guarantee your numbers will always remain exact multiples of 1/8, not accumulating any error.
You should define an "ok(x, y)" function checking if the (possibly fractional) point (x, y) is valid or inside a wall... a simple implementation could be:
return map[y|0][x|0] == 0; // <expr>|0 is used to convert to integer
Finally you should compute new_charX and new_charY and only accept moving from charX, charY to the new position if all four points:
(new_charX+s, new_charY+s)
(new_charX+1-s, new_charY+s)
(new_charX+s, new_charY+1-s)
(new_charX+1-s, new_charY+1-s)
are valid with s = 1/16 (i.e. half of the moving step).
Example: http://jsbin.com/wipimidije/edit?js,output
Your player can potentially be overlapping four squares in the map at any time. So you need to check for collision in all four squares (corresponding to the top-left, top-right, bottom-left, bottom-right corners of the character). To allow it to 'squeeze' through corridors (given that the character is the same size as a tile) you may also need to adjust this by one or two pixels (hence the 1 / config.tileSize in the following).
function checkCollision( x, y ) {
var x1 = Math.floor(x + 1 / config.tileSize),
y1 = Math.floor(y + 1 / config.tileSize),
x2 = Math.floor(x + 1 - 1 / config.tileSize),
y2 = Math.floor(y + 1 - 1 / config.tileSize);
if (map[y1][x1] !== 0 || map[y2][x1] !== 0 || map[y1][x2] !== 0 ||
map[y2][x2] !== 0) {
return true; // Collision
}
return false;
}
See this version of the JSBin
My answer is a little different. Instead of having a 2D array I'd use a simple array and calculate the rows (if you even need them). Now you only have to check the map array at the one index that is the position of the actor:
var map = [0,0,0,1,1,0,0,1,1,0,0,...];
//heres how to calculate the x/y if you need it for something else
var pos_x = position % NUM_OF_ROWS;
var pos_y = Math.floor(position / NUM_OF_ROWS);
//for collisions now you just check the value of the array at that index
leftKey.addEventListener('keypress', function() {
test(position - 1);
});
rightKey.addEventListener('keypress', function() {
test(position + 1);
});
upKey.addEventListener('keypress', function() {
test(position + NUM_OF_ROWS);
});
downKey.addEventListener('keypress', function() {
test(position - NUM_OF_ROWS);
});
function test(n) {
if (map[n] === 0) {
//if there's no collision, update the position.
position = n;
} else {
console.log('Collided!');
}
}
You need to consider two aspects: the first is collision detection, the second is response. Let's start with the detection. You are checking a single point, but in reality you have a tile size, so there is thickness which you must consider. The coordinate of the character, and the coordinate of your tiles is the top-left corner. It is not sufficient to compare the top-left corners, you must also check the other corners. The right-hand side of the player's square for example is at charX + config.tileSize.
The second aspect is the collision response. The simplest mechanism you can use here is to check the next position of the character for collisions, and only move the character if there are none. You should preferably check the two axes separately to allow the character to "slide" along walls (otherwise it till get stuck in walls in moving diagonally into the wall).
First of all I would change the tiles "value" if you change the character to walk in 1's but in 0's you can check if a tile is walkable by typing
If(tile[x][y])...
Then, I would, first calculate the next position and then make the move if the player is able to...
Var nextpos = new position;
If(KEYLEFT){
Nextpos.x = currpos - 1;
}
If(nextpos > 0 && nextpos < mapsize && tile[nextpos.x][nextpos.y])
Player.pos = nextpos;

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

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 solved by following the instructions here.
Anyways, I'm making a space-navigator game for one of my classes that demonstrates navigating spacecraft through space. So, I'm rendering a bunch of planets, the Earth being one of them. I've included a picture of my Earth rendering below. It looks okay, but what I'm trying to do is make it look more realistic by adding an 'atmosphere' around the planet.
I've looked around, and I've found some really neat looking creations that deal with glow, but I don't think they apply to my situation, unfortunately.
And here's the code that adds the earth to my scene (it's a modified version of code I got from a Three.js tutorial):
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);
}
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):
[
planet is and ellipsoid (center x,y,z and radiuses rx,ry,rz)
atmosphere is also ellipsoid (the same but bigger by atmosphere height)
all render is done normally but on top of that is added 1 pass for near observer planet
that pass is single quad covering whole screen
inside fragment it computes the intersection of pixel ray with these 2 ellipsoids
take the visible part (not behind, not after ground)
compute the ray length inside atmosphere
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
it work well inside and also outside the atmosphere (from distance)
can add close stars as light source (i use max 3 star system)
the result is stunning see images below:
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...
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. :)

Categories

Resources