Source image1
Source image 2
Resulting image
Introduction
In my previous articles Mapping Images on Spherical Surfaces Using C# and Simple Ray Tracing in C# we have seen how to map textures onto spheres and as well as a simple ray tracing algorithm. But what combining ray tracing and image mapping?
Background
I recommend at first you read my previous articles, Mapping Images on Spherical Surfaces Using C# and Simple Ray Tracing in C#.
Previously we got the formula below, representing the intersection between a 3D line and a sphere:
double A = (vx * vx + vy * vy + vz * vz);
double B = 2.0 * (px * vx + py * vy + pz * vz - vx * cx - vy * cy - vz * cz);
double C = px * px - 2 * px * cx + cx * cx + py * py - 2 * py * cy + cy * cy +
pz * pz - 2 * pz *
cz + cz * cz - radius * radius;
double D = B * B - 4 * A * C;
double t = -1.0;
if (D >= 0)
{
double t1 = (-B - System.Math.Sqrt(D)) / (2.0 * A);
double t2 = (-B + System.Math.Sqrt(D)) / (2.0 * A);
if (t1 > t2)
t = t1;
else
t = t2;
}
where
- r is the sphere radius
- (cx,cy,cz) is the center of the sphere
- (vx,vy,vz) is the line direction vector
- (px,py,pz) is the viewer position
Also, the mapping equation between two different scales image pixels(integer) and floating points (latitudes and longitudes):
public static double MapCoordinate(double i1, double i2, double w1,
double w2, double p)
{
return ((p - i1) / (i2 - i1)) * (w2 - w1) + w1;
}
Now, from each intersection we will get the mapped color from the image.
The Code
System.Drawing.Image image1 = new Bitmap(Server.
MapPath("./images/worldmap4.gif"));
Bitmap imgBitmap = new Bitmap(image1);
System.Drawing.Image image2 = new Bitmap(Server.
MapPath("./images/lune-p.gif"));
Bitmap imgBitmap2 = new Bitmap(image2);
Bitmap newBitmap = new Bitmap(200, 200,
PixelFormat.Format32bppArgb);
Graphics g = Graphics.FromImage(newBitmap);
Color clrBackground = Color.Black;
g.FillRectangle(new SolidBrush(clrBackground), new Rectangle(0, 0,
200, 200));
Rectangle rect = new Rectangle(0, 0, 200, 200);
System.Collections.ArrayList obj3dArrayList;
obj3dArrayList = new System.Collections.ArrayList();
obj3dArrayList.Add(new Sphere(0.0, 0.0, 90.0, 100.0, 0.0, 0.0,
255.0));
obj3dArrayList.Add(new Sphere(70.0, 70.0, 250.0, 20.0, 255.0,
200.0,
0.0));
Graphics graphics = g;
double px = (double)Session["eyex"],
py = (double)Session["eyey"],
pz = (double)Session["eyez"];
double lpx = (double)Session["lpx"],
lpy = (double)Session["lpy"],
lpz = (double)Session["lpz"];
double lvx = (double)Session["lvx"],
lvy = (double)Session["lvy"],
lvz = (double)Session["lvz"];
double fMax = 200.0;
for (int i = rect.Left; i <= rect.Right; i++)
{
double x = Sphere.GetCoord(rect.Left, rect.Right,
-fMax, fMax, i);
for (int j = rect.Top; j <= rect.Bottom; j++)
{
double y = Sphere.GetCoord(rect.Top, rect.Bottom,
fMax, -fMax, j);
double t = 1.0E10;
double vx = x - px, vy = y - py, vz = -pz;
double mod_v = Sphere.modv(vx, vy, vz);
vx = vx / mod_v;
vy = vy / mod_v;
vz = vz / mod_v;
bool bShadow = false;
Sphere spherehit = null;
for (int k = 0; k < (int)obj3dArrayList.Count; k++)
{
Sphere sphn = (Sphere)obj3dArrayList[k];
double taux = Sphere.GetSphereIntersec(sphn.cx, sphn.cy,
sphn.cz, sphn.radius, px, py, pz,
vx, vy, vz);
if (taux < 0) continue;
if (taux > 0 && taux < t)
{
t = taux;
spherehit = sphn;
}
}
Color color = Color.FromArgb(10, 20, 10);
if (spherehit != null)
{
double itx = px + t * vx, ity = py + t * vy, itz =
pz + t * vz;
double rtx=itx-spherehit.cx, rty=ity-spherehit.cy,
rtz=itz-spherehit.cz;
Algebra.RotX(Math.PI / 2, ref rty, ref rtz);
Algebra.RotZ(1.5, ref rtx, ref rty);
double phi = Math.Acos(rtz/spherehit.radius);
double S = Math.Sqrt(rtx * rtx + rty * rty);
double theta;
if (rtx >= 0)
theta = Math.Asin(rty / S);
else
theta = Math.PI - Math.Asin(rty / S);
if (theta < 0) theta = 2.0 * Math.PI + theta;
if (spherehit.radius > 80)
{
double x1 = Algebra.MapCoordinate(0.0,
Math.PI * 2.0,
imgBitmap.Width - 1, 0.0, theta);
double y1 = Algebra.MapCoordinate(0.0, Math.PI, 0.0,
imgBitmap.Height - 1, phi);
int i1 = (int)x1, j1 = (int)y1;
if (i1 >= 0 && j1 >= 0 && i1 < imgBitmap.Width &&
j1 < imgBitmap.Height)
color1 = imgBitmap.GetPixel(i1, j1);
}
else
{
double x1 = Algebra.MapCoordinate(0.0,
Math.PI * 2.0,
imgBitmap2.Width - 1, 0.0, theta);
double y1 = Algebra.MapCoordinate(0.0, Math.PI, 0.0,
imgBitmap2.Height - 1, phi);
int i1 = (int)x1, j1 = (int)y1;
if (i1 >= 0 && j1 >= 0 && i1 < imgBitmap2.Width
&& j1 < imgBitmap2.Height)
color1 = imgBitmap2.GetPixel(i1, j1);
}
double tauxla = Sphere.GetSphereIntersec(spherehit.cx,
spherehit.cy, spherehit.cz,
spherehit.radius, lpx, lpy, lpz,
itx - lpx, ity - lpy, itz - lpz);
for (int k = 0; k < (int)obj3dArrayList.Count; k++)
{
Sphere sphnb = (Sphere)(obj3dArrayList[k]);
if (sphnb != spherehit)
{
double tauxlb = Sphere.GetSphereIntersec(
sphnb.cx,
sphnb.cy, sphnb.cz,
sphnb.radius,
lpx, lpy, lpz, itx - lpx,
ity - lpy, itz - lpz);
if (tauxlb > 0 && tauxla < tauxlb)
{
bShadow = true;
break;
}
}
}
double cost = Sphere.GetCosAngleV1V2(lvx, lvy, lvz,
itx - spherehit.cx, ity - spherehit.cy,
itz - spherehit.cz);
if (cost < 0) cost = 0;
double fact = 1.0;
if (bShadow == true) fact = 0.5; else fact = 1.0;
double rgbR = color1.R * cost * fact;
double rgbG = color1.G * cost * fact;
double rgbB = color1.B * cost * fact;
color = Color.FromArgb((int)rgbR, (int)rgbG, (int)rgbB);
}
Brush brs = new SolidBrush(color);
graphics.FillRectangle(brs, i, j, 1, 1);
brs.Dispose();
}
}
MemoryStream tempStream = new MemoryStream();
newBitmap.Save(tempStream, ImageFormat.Png);
Response.ClearContent();
Response.ContentType = "image/png";
Response.BinaryWrite(tempStream.ToArray());
Response.Flush();
In my next articles I will start to show more complex ray tracing algorithms, using recursive functions, adding transparency, reflection and more reality to the images.