Log in

View Full Version : 3d textured rotating sphere in javascript



scriptmonkey
10-05-2007, 10:20 PM
I'm looking to make a 3d textured sphere in javascript. it needs to rotate at a constant rate and have the 'camera' positioned in it's center so the texture needs to be applied to the inside surface. any tips, scripts, or somewhat related scripts would help a lot. I also need to put div's with content over it. But I can do that much. I've used javascript for a lot of things due to it's portability, but this would be my first venture into using it to render 3d stuff since the debut of the html 4.0 spec.

djr33
10-05-2007, 10:36 PM
Heh. Nope. The end.
you could, I imagine, try to fake it with a circular gradient as the highlight within the bounds of a circle.
But you can't do even a circle in JS, much less a sphere that can rotate.
3D space does not exist in JS except for the z-index (depth of 'layer').

Flash or Java applets (NOT the same as javascript) are what you'd need here.

I've seen some impressive cubes that are faked with JS, but that's only because of basic math operations to fake all of the "sides".
A sphere's math would be more complex, not work the same way, be nearly impossible to display due to curves and, if you did get it working, you'd probably crash the browser anyway... it would need to be individual pixel-sized elements, when it comes down to it.

Twey
10-05-2007, 11:20 PM
I've seen some impressive cubes that are faked with JS, but that's only because of basic math operations to fake all of the "sides"."Faked?" Everything 3D we see on a 2D computer monitor is "faked." In this case, it's perfectly possible to do (if insanely complex). Look into the <canvas> element. Mozilla's upcoming Canvas3D actually has an element (http://zenit.senecac.on.ca/wiki/index.php/Canvas_3D_-_Sphere_Tag) dedicated to producing spheres.

djr33
10-05-2007, 11:34 PM
Faked in the sense that the methods used are being [mis]used to do something for which they were not designed. It's a trick, not a feature.
Only possible as a representation of pixels, within the limits that javascript presents.

The other possibilities mentioned may be fun to play with and/or full of potential, but they don't actually work yet [mostly], and a certainly not compatible with all browsers. When will IE allow the <sphere> object? :p So... nope.

Considering the idea is to have a working script, not a theoretical possibility, I'm sticking with no.

Twey
10-06-2007, 01:26 AM
Faked in the sense that the methods used are being [mis]used to do something for which they were not designed. It's a trick, not a feature.Then you mean a "hack."
The other possibilities mentioned may be fun to play with and/or full of potential, but they don't actually work yet [mostly], and a certainly not compatible with all browsers. When will IE allow the <sphere> object?<canvas> is supported by most browsers (Firefox, Safari, Opera), and can be (and has been) used to create a slow but functional 3D renderer. IE has VML, which I believe supports 3D natively.

Trinithis
10-06-2007, 02:17 AM
Even though it would not be much more difficult to implement in Javascript than something like java, do not use Javascript. The task you are describing would be way, way too slow if you programmed in in JS. Use a Java applet instead.

My gut says to use a cylindrical coordinate system to set up each pixel.
http://en.wikipedia.org/wiki/Cylindrical_coordinates
(Here's an applet that might help you understand it: http://www.pha.jhu.edu/~javalab/cylindrical/cylindrical.html)

I've made a sphere and other "3D" designs before using said method using Java, albeit not rotating. Here's the code... it might be insightful.


import java.awt.*;
import java.awt.image.*;

public class Gradient {

public static final int NONE = -1;
public static final int LINEAR = 0;
public static final int LINEAR_IN_OUT = 1;
public static final int CIRCLE = 2;
public static final int STARBURST = 3;
public static final int RADIATE = 4;

private static BufferedImage bimg;

private Gradient(){}

public static void createGradient(BufferedImage bimg, int gradientType, Color start, Color end) {
int w = bimg.getWidth();
int h = bimg.getHeight();
createGradient(bimg, gradientType, start, end, 0, 0, 0.0, Double.MAX_VALUE);
}
public static void createGradient(BufferedImage bimg, int gradientType, Color start, Color end, int cX, int cY, double rotate) {
int w = bimg.getWidth();
int h = bimg.getHeight();
createGradient(bimg, gradientType, start, end, cX, cY, rotate, Double.MAX_VALUE);
}
public static void createGradient(BufferedImage bimg, int gradientType, Color start, Color end, int cX, int cY, double rotate, double rMax) {
Gradient.bimg = bimg;
int w = bimg.getWidth();
int h = bimg.getHeight();
if(gradientType==NONE) {
int clear = new Color(0, 0, 0, 0).getRGB();
for(int i=0; i<w; ++i)
for(int j=0; j<h; ++j)
bimg.setRGB(i, j, clear);
return;
}
double r;
double[][] rs = new double[w][h];
double th, thMax = Math.PI*2, thInc = thMax/5000;
int xIndex = 0, yIndex;
double[][] zs = new double[w][h];
double z, zMin = 0, zMax = 0;
int[] colors = new int[256];
{
double red = start.getRed();
double green = start.getGreen();
double blue = start.getBlue();
double alpha = start.getAlpha();
double redInc = (end.getRed() - red) / 256.0;
double greenInc = (end.getGreen() - green) / 256.0;
double blueInc = (end.getBlue() - blue) / 256.0;
double alphaInc = (end.getAlpha() - alpha) / 256.0;
for(int i=0, n=colors.length; i<n; ++i) {
colors[i] = new Color(
(int)Math.round(red+redInc),
(int)Math.round(green+greenInc),
(int)Math.round(blue+blueInc),
(int)Math.round(alpha+alphaInc)
).getRGB();
red += redInc;
green += greenInc;
blue += blueInc;
alpha += alphaInc;
}
}
double[] colorBreakpts = new double[colors.length];
for(double x=-w/2-cX; x<w/2-cX; ++x, ++xIndex) {
yIndex = h-1;
for(double y=-w/2-cY; y<w/2-cY; ++y, --yIndex) {
r = Math.sqrt(x*x + y*y);
rs[xIndex][yIndex] = r;
if(r>rMax) continue;
th = normalizeAngle(Math.atan2(y, x) - rotate);
z = getZValue(r, th, gradientType);
zs[xIndex][yIndex] = z;
if(z<zMin) zMin = z;
else if(z>zMax) zMax = z;
}
}
double zInc = (zMax-zMin)/colorBreakpts.length;
z = zMin;
for(int i=0, n=colorBreakpts.length; i<n; ++i) {
z += zInc;
colorBreakpts[i] = z;
}
for(int x=0; x<w; ++x) {
for(int y=0; y<h; ++y) {
if(rs[x][y]<=rMax) {
z = zs[x][y];
for(int i=0, n=colorBreakpts.length; i<n; ++i) {
if(z<=colorBreakpts[i]) {
bimg.setRGB(x, y, colors[i]);
break;
}
}
}
else bimg.setRGB(x, y, Color.BLACK.getRGB());
}
}
}

private static double getZValue(double r, double th, int gradientType) {
switch(gradientType) {
case CIRCLE:
return circle(r, th);
case LINEAR:
return linear(r, th);
case LINEAR_IN_OUT:
return linearInOut(r, th);
case STARBURST:
//need to make this function
case RADIATE:
return radiate(r, th);
}
assert false;
return 0.0;
}

private static double normalizeAngle(double th) {
while(th>=Math.PI*2) th -= Math.PI*2;
while(th<0) th += Math.PI*2;
return th;
}

private static double linear(double r, double th) {
return (r*Math.cos(th)-r*Math.sin(th));
}

private static double linearInOut(double r, double th) {
if(th>Math.PI/4 && th<=Math.PI*5/4)
return (r*Math.cos(th)-r*Math.sin(th));
return -(r*Math.cos(th)-r*Math.sin(th));
}

private static double circle(double r, double th) {
return -Math.pow(r, 3/2);
}

private static double radiate(double r, double th) {
return Math.min(Math.abs(th-Math.PI/4+0.01), Math.abs(th-Math.PI*7/4-0.01));
}
}