XD-1. A WebGL dynamic, multi-layer, offline FITS viewer.

Proof of concept beta version

Go to the new devel version....
Scale Angle Tx Ty
Pointer :

About XD-1

This early beta version has several limitations. Reload the page to load a new image. It is possible to render up to 4 different image layers coming from different FITS files. It will be possible also to load the data from a single FITS datacube (todo).

The (actual) ugly look of the page and lack of controls will be changed into something more modern and practic, allowing full-screen view, image PNG/JPEG export of the WebGL canvas, WebGL computed histogram for fast automatic cut computation and selection, a selected column/line 1D plot, FITS data unit header view and probably many more, that will potentially make this tool a complete multi-platform, fast replacement of the good old ds9 program most astronomers are still using now.

This viewer makes use of astro.js FITS library for JavaScript FITS file loading.

How does it work?

Nowaday's OpenGL rendering is done in parallel for every pixel of the screen (in practice the html canvas area) by programs called shaders, executed in the GPU by an increasingly big number of hard-silicium processing cores.

These little program's aim was originally to compute the specific colors of the pixels produced by (parts of) a given 3D scene (for examples, famous shaders to produce light and reflection (computing of normals), shadow or fire effects in 3D video games).

We would want, in theory, to use this additional processing power for whatever computing needs we want, and possibly not only for specific pixel color rendering; but the specific hardware, drivers, programming interface paradigm's lack made this impossible, until now. If you see and can manipulate the cat's eye nebula layer's colors above, then this is finally possible, even for a simple downloaded text-script on your browser. Unfortunately, now in 2014, only browsers with openGL interface capable of floating point textures will work. This will be standard soon.

Shader code

This is the fragment shader code where all the color/geometry computations are done. Optimization on memory use is done by using a single floating point rgba texture to represent all the data. The four 'channels' of a pixel element represent four different image layers. Geometry transformations are computed by the algorithm using a single texture. Each of the (R,G,B,A) components can be used to store data for individual layers. This is why this rendering program handles at most four image layers.

var fragment_shader_src="precision mediump float;"+ //can change to higher precision here
                        "uniform float u_zoom;"+  //the passed zoom value from the html ui
                        "uniform float u_angle;"+ //the passed rotation value from the html ui
                        "uniform vec2 u_tr;"+ //Translations (x,y) vector[2] from the html ui
			"uniform ivec4 u_layer_enabled;"+ //4 layers
			"uniform vec4 u_test[2];"+
			"uniform vec4 u_pvals[8];"+
			"uniform ivec4 u_ncolors;"+ //Number of colours in the 4 layers

			"uniform sampler2D u_image;"+ //The four layer float data texture
			"uniform sampler2D u_cmap_fracs;"+ //Positions of the colour on the colormap € [0,1] for each layer
			"uniform sampler2D u_cmap_colors;"+//Color vector representing the colormaps for each layer.
			"uniform vec2 u_resolution;"+
			"void main() {"+
			"  vec4 cmap_col=vec4(0.0,0.0,0.0,1.0);"+ //This is the final colour for this pixel, we set it initially to opaque black.
			"  mat2 rmg =mat2(cos(u_angle),sin(u_angle),-sin(u_angle),cos(u_angle));"+//Set up the global view rotation matrix
			"  for(int l=0; l<4; l++){"+ //Looping on the 4 layers 
			"   if(u_layer_enabled[l]==1){"+//If the layer is visible ...
			"    float lpos=(float(l)+.5)/4.0;"+
			"    float alpha=u_pvals[2*l+1][1];"+//this layer's angle
			"    mat2 rm =mat2(cos(alpha),sin(alpha),-sin(alpha),cos(alpha));"+//this layer's rotation matrix
			"vec2 trl=vec2(u_pvals[2*l][2],u_pvals[2*l][3]);"+//this layer's translation (x,y) vector
			"  vec2 texCoord = rmg*( (gl_FragCoord.xy/u_resolution-.5)/u_zoom) +u_tr/u_resolution +vec2(.5,.5);"+ //Setting the 2-vector (Tx,Ty)€[0,1] : the texture position for this layer, applying global transform.
			"texCoord = rm*(texCoord-trl/u_resolution-.5)/u_pvals[2*l+1][0]+vec2(.5,.5);"+//Applying layer's geometrical transforms.
			"    if(texCoord[0]>=0.0 && texCoord[0]<=1.0 && texCoord[1]>=0.0 && texCoord[1]<=1.0){"+
			"      float c=(texture2D(u_image, texCoord)[l]-u_pvals[2*l][0])/(u_pvals[2*l][1]-u_pvals[2*l][0]);"+
			"      if(c<=0.0){cmap_col+=u_pvals[2*l+1][2]*texture2D(u_cmap_colors, vec2(0.5/128.0, lpos));} else {"+
			"        if(c>=1.0){cmap_col+=u_pvals[2*l+1][2]*texture2D(u_cmap_colors, vec2( (float(u_ncolors[l])-.5)/128.0, lpos));}else{"+
			"        float frl=0.0,frr; vec4 rc; vec4 lc;"+
			"        for(int i=0;i< 128 ;i++){ "+
			"          if(i==u_ncolors[l]){ break;}"+
			"          frr = texture2D(u_cmap_fracs, vec2((float(i)+.5)/128.0, lpos)).r;"+	
			"          rc = texture2D(u_cmap_colors, vec2((float(i)+.5)/128.0, lpos));"+
			"          if(frr>c){"+
			"            float dc=frr-frl;"+
			"            rc=(c-frl)/dc*rc;"+
			"            lc=(frr-c)/dc*lc;"+
			"            cmap_col+=(lc+rc)*u_pvals[2*l+1][2];"+
			"            break;"+
			"          }else{ frl=frr; lc=rc; }"+
			"        }"+//end for
			"      }"+//end else
			"     }"+//end else
			"   }"+ //end if (pixel visible)
			"  }"+ //end if (layer enabled)
			" }"+//end for
			" cmap_col[3]=.5;"+
			"for(int i=0;i<3;i++)if( cmap_col[i]>1.0)cmap_col[i]=1.0;"+ //if the color is too bright, set to max.
			" gl_FragColor = cmap_col;"+ //setting final fragment color
			"}"; //end main

    

More

If you want to know how this web page works, look at the source html/javascript files, they are available in two clicks on your browser.

XD-1 ?

This is the code-name of the spaceship Discovery One appearing in the film 2001: A Space Odissey and controlled by the Hal-9000 crazy computer.