Link to home
Start Free TrialLog in
Avatar of jack_p50
jack_p50

asked on

3d rotate of 2d image

How to rotate 2d!!! image in 3d (rotate it's surface)
Avatar of The_Brain
The_Brain

you will have to elaborate this is terribly unclear :)  just set out what you want to achieve.  

no doubt that you are going to need trigs.
Merry Christmas
Avatar of jack_p50

ASKER

I have image (BMP for example). I want to rotate it's surface in 3d (i.e. :
surface : -----------
             |          |
             -----------
image is on it; i want to do so that transformed image will fit in new surface, i.e.
         ----
         |   ---
         |      ---
         |         ---
         ---------------
I have image (BMP for example). I want to rotate it's surface in 3d (i.e. :
surface : -----------
             |          |
             -----------
image is on it; i want to do so that transformed image will fit in new surface, i.e.
         ----
      --     ---
    --          ---
  --              ---
(same then, i'm too lazy to draw it)
It's the same as 2d rotate, but i want surface to be 3d rotated    
Still not exactly sure what you need...

Let's say you rotate the image in 3D so it looks like this when see from aside...

front side of image ->   |     <- Back side of image...
                                        |
                                        |

Is this what you wanna do??

-Viktor
Yes. I.e. when rotating on 90 degrees by x-axis or y-axis, image will convert to line.
hmmm,  
this is a bit tricky
I could try to work it out.

If I get it, can you raise the points just a little, don't wanna sound greedy but this is really difficult.
:)
perspective or no perspecitive
(hope fully no perspective. :)
This is what I have worked out so far

The new height of the object if you tip it back x degrees
is AB is the height.

AB= DC.Cos(x) = new Height;
for the width
AB2 = DC2.Cos(y) = New Width;

where DC is the orig Height 2. width.

but that won't really help the image, that is just the dimentions for in which the new image will fit.
   
Ok, I just thought about this.

Every pixel has a position on this picture.

every pixel should be stored in buffer with their original cordinates.

now the image is cleared. (this requires some mega processing.
but You can really speed it up by doing This)


Give the Picture a record:  (pascal)
or just keep 2 sets of arrays open.

Set is a record type which I am very unfimiliar with exept using their members;

Record (do what ever you have to do)
 Set
   Begin
    Bx,By :integer;
    Ex,Ey :integer;
end;

Rows : array[1..Height] of Set;
Column : array[1..Width] of Set;


Begin
  now this is not code form, but this what you have to do.

 

 

end.

But I to work on that. good luck,
Try this in Delphi....It's not what you want but it's fun :))
==================
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  ExtCtrls;

type
  TForm1 = class(TForm)
    Timer1: TTimer;
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

const
      pts = 3;

type TPoints = array [1..pts] of TPoint;

const
      Orig : TPoints = (
         (X : -100; Y : -50),
         (X :  100; Y : -50),
         (X :    0; Y : 100)
      );

var
      ang : double;
   Now : TPoints;

procedure PlotPoints(points : TPoints);
var
      i : integer;
   trans : TPoints;
begin
      for i := 1 to pts do begin
            trans[i].x := points[i].x + (Form1.Width shr 1);
         trans[i].y := points[i].y + (Form1.Height shr 1);
   end;
   Form1.Canvas.Polygon(trans);
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
      i : integer;
   old : TPoints;
begin
      Old := Now;
      for i := 1 to pts do begin
            Now[i].x := round(Orig[i].x * cos(ang) - Orig[i].y * sin(ang));
      Now[i].y := round(Orig[i].x * sin(ang) + Orig[i].y * cos(ang));
   end;
   Canvas.Brush.Style := bsClear;
   Canvas.Pen.Color := Color;
   PlotPoints(Old);
   Canvas.Pen.Color := clBlack;
   PlotPoints(Now);
        ang := ang + 0.1;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
      ang := 0;
end;

end.
==========
-Viktor
--Ivanov
I can adjust points only a little (lack of them). Please give me some transformation formulas. I think, better rotate is so
  for y:=0 to maxy do
    for x:=0 to maxx do
      putpixel2Destbufer(x,y,getSourceColor(x,y,rotation_params));

It just goes on each point of Destination image and sees which Source point corresponds to it (rotating dest. coords back) and then plotting a point with color of such coords.
For 2d rotate it's easy, but I don't know how to do getSourceColor for 3d rotate.
The 3D color is easy also..
Give me this func., please
ok, in DOS (VGA) when you put a pixel on the screen you do this...

NewX = (X * 256) / (Z - ZOff) + XOff
NewY = (Y * 256) / (Z - ZOff) + YOff

where x, y, and z are the 3D points... Now when you plot the NewX and NewY on the screen it will be in prespective.. The color depend on you.. YOu should decide how to work out the color....

Ok, when you put the pixel on the screen in 3D you do this...

PutPixel(NewX, NewY, Color);

and when you get the color you do this..

Color := GetPixel(NewX, NewY);

where NewX and NewY are the results from the formulas we did before...

Now you know how to get the color in 3D....

Here is a fast starfield in 3D in C++, if you want me to convert it to Pascal let me know... It's quite easy,,,,
==============
#include <conio.h>
#include <stdlib.h>
#include <utils.h>    //This is my own .H file that declares the types like unsgined char BYTE
#include <mem.h>
#include <time.h>
#include <dos.h>
#include <iostream.h>

#define num 200     //Number of stars
#define speed 7    //This speed that in which the stars are moving
#define vga 0xA000   //The address of VGA

typedef struct {
      int x;
      int y;
      int z;
} _3D;

typedef struct {
      int x;
      int y;
} _2D;

void pal(BYTE, BYTE, BYTE, BYTE);
void SetMode(WORD);
void Init();
void CalcStars();
void putpixel(int, int, BYTE, WORD);
void DrawStars();
void ClearStars();
void MoveStars(bool);
void WaitRetrace();
void GoThroughSpace();
void DisplayInfo();

_3D stars[num];
_2D pos[num];

int main()
{
      time_t t;
      srand(unsigned(time(&t)));
      DisplayInfo();
      SetMode(0x0013);
      Init();
      GoThroughSpace();
      SetMode(0x0003);
      return 0;
}

void SetMode(WORD mode)
{
      asm mov ax, [mode]
      asm int 10h
}

void pal(BYTE col, BYTE r, BYTE g, BYTE b)
{
      outp(0x3c8, col);
      outp(0x3c9, r);
      outp(0x3c9, g);
      outp(0x3c9, b);
}

void Init()
{
      for (int i = 0; i < num; i++) {
            do {
                  stars[i].x = rand() % 320 - 160;
                  stars[i].y = rand() % 200 - 100;
                  stars[i].z = i + 1 ;
            } while (!stars[i].x && !stars[i].y);    //until stars.x and stars.y <> 0
      }
//Set a few colors' RGBs
      pal(19, 5, 5, 5);
      pal(18, 25, 25, 25);
      pal(17, 32, 32, 32);
      pal(16, 63, 63, 63);
}

void CalcStars()
{
      int x, y;
      for (int i = 0; i < num; i++) {
            x = ((stars[i].x << 7) / stars[i].z) + 160;
            y = ((stars[i].y << 7) / stars[i].z) + 100;
//The following two lines declare the NewX and NewY from the example I gave you above...
            pos[i].x = x;
            pos[i].y = y;
      }
}

void putpixel(int x, int y, BYTE col, WORD where)
{
      asm {
            mov ax, [where]
            mov es, ax
            mov bx, [x]
            mov dx, [y]
            mov di, bx
            mov bx, dx
            shl dx, 8
            shl bx, 6
            add dx, bx
            add di, dx
            mov al, [col]
            stosb
      }
}

void DrawStars()
{
//Draw the stars and see what is their Z coordinate so you can calculate the color
      int x, y;
      for (int i = 0; i < num; i++) {
            x = pos[i].x;
            y = pos[i].y;
            if ((x > 0) && (x < 320) && (y > 0) && (y < 200)) {
                  if (stars[i].z > 400) putpixel(x, y, 18, vga);
                  else if (stars[i].z > 300) putpixel(x, y, 18, vga);
                  else if (stars[i].z > 200) putpixel(x, y, 17, vga);
                  else if (stars[i].z > 100) putpixel(x, y, 17, vga);
                  else putpixel(x, y, 16, vga);
            }
      }
}

void ClearStars()
{
//Clear the stars
      for (int i = 0; i < num; i++)
            putpixel(pos[i].x, pos[i].y, 0, vga);
}

void MoveStars(bool foreward)
{
//Move the stars towards you...
      if (foreward) {
            for (int i = 0; i < num; i++) {
                  stars[i].z -= speed;
                  if (stars[i].z < 1)
                        stars[i].z = num;
            }
      } else {
//Move the stars in opposite way...
            for (int i = 0; i < num; i++) {
                  stars[i].z += speed;
                  if (stars[i].z > num)
                        stars[i].z = 1;
            }
      }
}
//Wait vertical retrace
void WaitRetrace()
{
      asm mov dx,3DAh
 l1:
      asm in al,dx
      asm and al,08h
      asm jnz l1
 l2:
      asm in al,dx
      asm and al,08h
      asm jz  l2
}

void GoThroughSpace()
{
//Repeat moving the stars until the ESC key is pressed
      char ch;
      CalcStars();
      DrawStars();
      do {
            WaitRetrace();
            ClearStars();
            CalcStars();
            WaitRetrace();
            DrawStars();
            if (kbhit())
                  ch = getch();
            ch == 32 ? MoveStars(FALSE) : MoveStars(TRUE);
      } while (ch != 27);
}

void DisplayInfo()
{
//Display some info about the keys you use/...
      clrscr();
      cout << "This is a simple demo of a starfield.\n\n"
              << "Space Bar     ->  Moves stars backwards.\n"
              << "Any other key ->  Moves starts towards you.\n"
              << "Esc           ->  Exit";
      getch();
}
==================
The real formula is this,,,

void CalcStars()
{
      int x, y;
      for (int i = 0; i < num; i++) {
//The following 2 lines calculate the formula....
            x = ((stars[i].x << 7) / stars[i].z) + 160;
            y = ((stars[i].y << 7) / stars[i].z) + 100;

            pos[i].x = x;
            pos[i].y = y;
      }
}

In pascal it would be like this....

x := (((stars[i].x shl 7) div stars[i].z) + 160;
y := ((stars[i].y shl 7) div stars[i].z) + 100;

I did

X SHL 7 which is the same as X * 256... It's just a lot faster then multiplying by 256...

If you need more help understanding anything let me know...

btw- I put the comments just now and they might be a bit clumsy.. Anyway, tell me if you don't understand something...

-Viktor
--Ivanov
Viktor, please, pixels are easy in 2d or 3d no case, but I want imageS!
Plz give me the function getSourceColor
Same thing goes for images....ok when you plot pixels in 3D prespective it looks like this...


|\
|  \
|   |
|  /
|/

when you plot it, so for the image it works the same way...depends in the form the image takes when you plot it... I gotta try it my self sometime and tell you if it works, but no now since I dont have much time right now... maybe in a few days or a week or something...
Ok
Let's have a look at the problem:
There're two (at least) possibilities to rotate and write a flat image (a pixel map) to the screen:

The first method is something like "rendering": You check for every point of the screen (let's say 640 x 480) if a ray starting at this point hits the image which is somewhere in the room.

have a look at this poor (2d-) drawing:

xxxxxxxxxxxxxxxx         <-  every x is a pixel
        \      /
         \    /  <- one ray per pixel
          \  /
           X  <- like some lens
          /  \
         /    \
        /      \
       /  ##### <- the image plane
      /
     / <- this one haven't hit the image plane
             
I haven't thought much about this method because it's very slow. But on the other side it's the most exact method. Maybe you can increase speed by calculating only the area where the picture is located at.

The second method is to check for each point of the IMAGE (let's say 100x50) where it is located after rotating. Then you calulate some perspective and put a pixel with the given color to the calculated location.

Out coordinate system:
coordinates are in this format: x1/x2/x3

            X3
             |
             |
             |
             |
             |________________ X2
            /
          /  
        /
     X1

The plane is located here:
the center is at 0/0/0,
the left/top edge is at 0/-50/-25, the right/bottom edge is at 0/50/-25

            X3
             |
             |
+-----------|------------+
 |           |             |
 |           |_______ | _________ X2
 |          /              |
 |        /                |
+------/-----------------+
     X1

Let's take a random point in the 2d image map (there the top/left pixel is at 0/0). Ok, let's say we take the pixel at 15/30. The unrotated 3d-coordinates are caluclated as follows:

x1=0   (as always)
x2=15-50=-35           (2d-coordinates - center)
x3=30-25=5
so, if not rotated, the pixel would be at 0/-35/5 in the room
Now, we'll rotate around the X1-Axis where rotate_x1 is the angle (clockwise when looking at 0/0/0 from the positive x1-side).
let's have a look directly from the x1-axis to the point we want to rotate: x=out point at 0/-35/5

rotate that way around 0/0/0
       ---->                   x3
   /                            ^                        
 /                              |  
 |                              |  
 x                             |              
                                |  
-------------------------------+---------------------------> x2
                                |  
                                |              
                                |  
                                |              
                                |  

That's how it should look like:

new_x1=x1                                                          (doesn't change!)
new_x2=x2*cos(rotate_x1)+x3*sin(rotate_x1)
new_x3=x3*cos(rotate_x1)-x2*sin(rotate_x1)
you have to continue calculating with these coordinates
x1=new_x1
x2=new_x2
x3=new_x3

(If you want me to explain this, please tell me, 'cause it'll take too much time right now)

Ok, when got the new coordinates of our point. If you understand how to rotate around X1, you can do this for X2 and X3 - only if you want to. If not, just leave the coordinates.

Let's calculate the perspective (that's easy!). camera_x3 is where you're looking from, use 100 or so. Use zoom to zoom ;-). Vary with these params to get best pictures.

pixel_x=round(zoom*x2/(camera_x3-x3))
pixel_y=round(zoom*x3/(camera_x3-x3))
(there's no pixel_z - the screen is 2d !)

Now you know where to put the pixel with the color defined in the image. Just add the center of the screen to the coordinates, let's say for 640x480:

pixel_x=pixel_x+320
pixel_y=            240-pixel_y             (because top is 0 and bottom is 480 - just inverted!)

Do this for every point in the image.

the only problem is: if you zoom into the picture, there'll be holes, because there's to pixel for this point. But I'm sure this problem can be solved: when zooming to deep, you calculate not only -35/5 but also -35.0/5.5 , -35.5/5.0 and -35/5.5, always using the color of -35/5 or even average of -35/5 and the neighbour-pixel.

But to do rotations in realtime you have to use lots of tricks to increase to calculation.

Good luck,
 
 flobecker

Let's have a look at the problem:
There're two (at least) possibilities to rotate and write a flat image (a pixel map) to the screen:

The first method is something like "rendering": You check for every point of the screen (let's say 640 x 480) if a ray starting at this point hits the image which is somewhere in the room.

have a look at this poor (2d-) drawing:

xxxxxxxxxxxxxxxx         <-  every x is a pixel
        \      /
         \    /  <- one ray per pixel
          \  /
           X  <- like some lens
          /  \
         /    \
        /      \
       /  ##### <- the image plane
      /
     / <- this one haven't hit the image plane
             
I haven't thought much about this method because it's very slow. But on the other side it's the most exact method. Maybe you can increase speed by calculating only the area where the picture is located at.

The second method is to check for each point of the IMAGE (let's say 100x50) where it is located after rotating. Then you calulate some perspective and put a pixel with the given color to the calculated location.

Out coordinate system:
coordinates are in this format: x1/x2/x3

            X3
             |
             |
             |
             |
             |________________ X2
            /
          /  
        /
     X1

The plane is located here:
the center is at 0/0/0,
the left/top edge is at 0/-50/-25, the right/bottom edge is at 0/50/-25

            X3
             |
             |
+-----------|------------+
 |           |             |
 |           |_______ | _________ X2
 |          /              |
 |        /                |
+------/-----------------+
     X1

Let's take a random point in the 2d image map (there the top/left pixel is at 0/0). Ok, let's say we take the pixel at 15/30. The unrotated 3d-coordinates are caluclated as follows:

x1=0   (as always)
x2=15-50=-35           (2d-coordinates - center)
x3=30-25=5
so, if not rotated, the pixel would be at 0/-35/5 in the room
Now, we'll rotate around the X1-Axis where rotate_x1 is the angle (clockwise when looking at 0/0/0 from the positive x1-side).
let's have a look directly from the x1-axis to the point we want to rotate: x=out point at 0/-35/5

rotate that way around 0/0/0
       ---->                   x3
   /                            ^                        
 /                              |  
 |                              |  
 x                             |              
                                |  
-------------------------------+---------------------------> x2
                                |  
                                |              
                                |  
                                |              
                                |  

That's how it should look like:

new_x1=x1                                                          (doesn't change!)
new_x2=x2*cos(rotate_x1)+x3*sin(rotate_x1)
new_x3=x3*cos(rotate_x1)-x2*sin(rotate_x1)
you have to continue calculating with these coordinates
x1=new_x1
x2=new_x2
x3=new_x3

(If you want me to explain this, please tell me, 'cause it'll take too much time right now)

Ok, when got the new coordinates of our point. If you understand how to rotate around X1, you can do this for X2 and X3 - only if you want to. If not, just leave the coordinates.

Let's calculate the perspective (that's easy!). camera_x3 is where you're looking from, use 100 or so. Use zoom to zoom ;-). Vary with these params to get best pictures.

pixel_x=round(zoom*x2/(camera_x3-x3))
pixel_y=round(zoom*x3/(camera_x3-x3))
(there's no pixel_z - the screen is 2d !)

Now you know where to put the pixel with the color defined in the image. Just add the center of the screen to the coordinates, let's say for 640x480:

pixel_x=pixel_x+320
pixel_y=            240-pixel_y             (because top is 0 and bottom is 480 - just inverted!)

Do this for every point in the image.

the only problem is: if you zoom into the picture, there'll be holes, because there's to pixel for this point. But I'm sure this problem can be solved: when zooming to deep, you calculate not only -35/5 but also -35.0/5.5 , -35.5/5.0 and -35/5.5, always using the color of -35/5 or even average of -35/5 and the neighbour-pixel.

But to do rotations in realtime you have to use lots of tricks to increase to calculation.

Good luck,
 
 flobecker

Good comment, I prefer 1st method (check 640x480 pixels, because 2nd is super-bad quality (there're holes in image, and other ****). So it's better for each point of screen (640x480) do rotation in back direction and see if it hits into source image, if yes, get it's color and plot to point we're checking. Please, Flobecker, give me formulas for such back direction rotation (as answer).
Hi I have working pascal code for tranlating a rectangle 2D image to any qadrilateral ie 4 corners anywhere on screen without any trig so its fast
Sounds like texture mapping problem :)
You might find some texture mapping sample at
http://www.hornet.org

basically the best site to do demo coding.  tons of sample and tutorial too.
Why do it the hard way looking at all sources you were getting. Look at the picture as one big polygon based picture. You know how to draw polygons ? If you it this way your picture becomes divided in small textures (polygon) which you can move up and down like a flag rotate etc just with such a ease bill gates looks like a nerd.

However I have sources from various demos I made, but they are in C/C++ (Watcom), and in assembler (32bit). If you want them please let me know.

A friend of mine God_Ares says to me jack_p50 has his pascal unit xvga which contains just the routines to do this. semi 32bit.

regards,

Cono
First give me what I need, then answer. Thanx.
That's a short(!) scheme how to do it:
You better copy this text to a editor, so the graphics (?!) are readable.

We have to use lots of vectors - I hope you know how to use them.
By the way: You're working on a game or so, right? In this case, I would use
the other method I described and would try to improve the quality. "Rendering"
is realy SLOW!

Well, but that's what I found out about this (very interesting) topic:
This time I'm using an other system of axises (I read that this one is used
for 3d graphics). It's called the left-hand-coordinate-system or so.

       +Y
       |  +Z
       | /
       |/
 ------*------- +X
      /|
     / |
       |

Note: p0,p1,p2,r0,r1 are all 3d-vectors: <x,y,z>
The image we want to show is located on a infinitve plane with its center at:
 p0 = <plane_x, plane_y, plane_z>
and which is defined by like this:
 <x,y,z> = p0 + i*p1 + j*p2
 <x,y,z> = <plane_x, plane_y, plane_z> + i * <1,0,0> + j * <0,1,0>
i and j are parameters. There's one pair of i and j for each point on the
plane. To rotate the plane, just rotate the vectors p1 and p2 (I've got the
forumlas for this, just have to find them, if not: it's not too hard to find
it)

The camera is located somewhere on the z-axis (makes life much easier!) at
 r0 = <0,0,camera_z>
A ray is defined by
 <x,y,z> = r0 + k*r1
k is another parameter to describe every point on the ray.
r1 is the direction of the ray. This one is calculated like this:

 r1 = <pixel_x,pixel_y,focus>

pixel_x and pixel_y are the coordinates on the screen, while 0/0 is the
center. focus is the way between the

xxXxxxxxxxxxxxxxXxxxx   <---- screen
   \           /
    \         /
     \       /
      \     /
       \   /
        \ /
       *              <---- camera center
        / \
       /   \
      /     \
--------===========---- <---- the image plane:
                              = means this area of the plane is defined by some colors
                              - means this area of the plane is undefined, so we just look through and take the background color

Ok, now we've defined the plane and the ray:

ray:   <x,y,z> = r0 + k*r1
plane: <x,y,z> = p0 + i*p1 + j*p2

Now we have to check wether the ray crosses the plane for each pixel of the
screen. If yes, we have to check, where exactly this happens and if a color is
defined for this point of the image plane (hey, this part is easy!) or if the
background color is used instead. If the ray doesn't cross the plane, then we
just use the background color.

To cross the plane with the ray we just have to solve this equation
 r0 + k*r1 = p0 + i*p1 + j*p2
     [ray] = [plane]
Looks easy, doesn't it? It isn't! That means tons of lines and tons of
CPU-time!!! for EACH pixel!
Anyway, there're three possible cases how the solving may end:
 1. the ray doesn't touch the plane at all, there's no solution, so take
     take background color
 2. there's a infinitve number of solutions, we're looking at the plane from
     the side, what color shall we take now? we may check, from which side
     we're looking at and take the border pixel's color or we may use a common
     border color. That's another problem, I'm working on.
 3. the best case: there's exactly one solution, giving each parameters i,j,k
     exactly one value. With i and j are pointing directly on the image plane
     while i is the x-coordinate and j is the y-coordinate. You just have to
     check wether the coordinates are in the range where the colors are defined
     on the image plane (let's say i in [-50..+49] for a image width of 100).
     Sure you have to round() the i/j values or even calculate an average
     color of two pixels

I'm working on solving this equations but you better use the other method I
described in the other comment.

Anyway, I found some notes I made how to 3d-rotate a point at <X,Y,Z> around a
center at <0,0,0>, while x_rot, y_rot and z_rot are the angles by which we
rotate around the x-, y- and z-axis (in this order). We rotate clockwise around
the axis when looking from the positive side of the axis to <0,0,0>. You may
change the order of rotating or use just one or two rotations. You better
precalculate the values for SIN(x_rot), COS(x_rot), SIN(x_rot) and so on.
You may even precalculate a SIN-table with integers to increase speed.

Rotation around the X-axis
  X_new=X
  Y_new=Y*cos(x_rot)-Z*sin(x_rot)
  Z_new=Z*cos(x_rot)+Y*sin(x_rot)
 copy the new coordinates back to X,Y,Z
  X=X_new
  Y=X_new
  Z=Z_new

Rotation around the Y-axis
  X_new=X*cos(y_rot)+Z*sin(y_rot)
  Y_new=Y
  Z_new=Z*cos(y_rot)-X*sin(y_rot)
 the same again (looks better!)
  X_new=X_new
  Y_new=X_new
  Z_new=Z_new

Rotation around the Z-axis
  X_new=X*cos(z_rot)-Y*sin(z_rot)
  Y_new=Y*cos(z_rot)+X*sin(z_rot)
  Z_new=Z
 we did it!
  X=X_new
  Y=X_new
  Z=Z_new

You will have to use these formulas for rotating the vectors p1 and p2 (and
so the image-plane).

Give me some days (haven't got much time) then you'll get the other formulas
- I hope so :-)

 flobecker

Here it is!
that's what I wrote down:

IMAGE PLANE DEFINITION
plane: p0 + i*p1 + j*p2
middle of image plane
p0.x=plane_x
p0.y=plane_y
p0.z=plane_z
direction1 (x-axis), unrotated
p1.x=1
p1.y=0
p1.z=0
direction2 (y-axis), unrotated
p2.x=0
p2.y=1
p2.z=0

RAY DEFINITION
ray: r0 + k*r1
start
r0.x=0
r0.y=0
r0.z=camera_z
direction
r1.x=pixel_x/focus
r1.y=pixel_y/focus
r1.z=1

again, p0,p1,r0,r1,r2 are 3d-vectors:

                  / p0.x \
p0 = |  p0.y  |
                  \ p0.z /

alle we have to do is to solve this:

                   p0+r*p1+s*p2=r0+t*r1

in other words:
p0.x + i * p1.x + j * p2.x = r0.x + k * r1.x
p0.y + i * p1.y + j * p2.y = r0.y + k * r1.y
p0.z + i * p1.z + j * p2.z = r0.z + k * r1.z

i,j,k to one side:
i * p1.x + j * p2.x - k * r1.x = r0.x - p0.x
i * p1.y + j * p2.y - k * r1.y = r0.y - p0.y
i * p1.z + j * p2.z - k * r1.z = r0.z - p0.z

the matrix
p1.x  p2.x -r1.x | r0.x - p0.x
p1.y  p2.y -r1.y | r0.y - p0.y
p1.z  p2.z -r1.z | r0.z - p0.z

replace some numbers which will never change
[ i ] [ j ] [ k ]
p1.x  p2.x  -r1.x | 0        - p0.x   (1)
p1.y  p2.y  -r1.y | 0        - p0.y   (2)
p1.z  p2.z   -1   | camera_z - p0.z   (3)

let's start solving, we'll start with this:
(4)=(1)-(3)*r1.x
(5)=(2)-(3)*r1.y

our new matrix:
p1.x-p1.z*r1.x   p2.x-p2.z*r1.x  0  | -p0.x - (camera_z-p0.z) * r1.x    (4)
p1.y-p1.z*r1.y   p2.y-p2.z*r1.y  0  | -p0.y - (camera_z-p0.z) * r1.y    (5)
p1.z             p2.z            -1 | camera_z - p0.z                   (3)

I'll substitute the matrix elements like this:
a1 a2 a3 | a4
b1 b2 b3 | b4
c1 c2 c3 | c4

      a1:=p1.x-p1.z*r1.x;
      a2:=p2.x-p2.z*r1.x;
      a3:=0;
      a4:=-p0.x-(camera_z-p0.z)*r1.x;

      b1:=p1.y-p1.z*r1.y;
      b2:=p2.y-p2.z*r1.y;
      b3:=0;
      b4:=-p0.y-(camera_z-p0.z)*r1.y;

      c1:=p1.z;
      c2:=p2.z;
      c3:=-1;
      c4:=camera_z-p0.z;

We have to check, whether any value we want to divide by isn't zero!
      if a1=0 then
            if a2=0 then
                  if a4=0 then
                        begin
                              {infinitve solutions - ray hits plane directly from the side}
                              color:=getbordercolor;
                        end
                  else
                        begin
                              {no solutions - ray doesn't hit the plane}
                              color:=backgroundcolor;
                        end
            else
                  begin
                        j:=a4/a2;
                        if b1=0 then
                              begin
                                    {infinitve solutions - ray hits plane directly from the side}
                                    color:=getbordercolor;
                              end
                        else
                              begin
                                    {exactly one solution - let's calculate i and j and get the color}
                                    i:=(b4-b2*j)/b1;
                                    color:=getimagecolor;
                              end;
                  end
      else
            if a2=0 then
                  begin
                        i:=a4/a1;
                        if b2=0 then
                              begin
                                    {infinitve solutions - ray hits plane directly from the side}
                                    color:=getbordercolor;
                              end
                        else
                              begin
                                    {exactly one solution - let's calculate i and j and get the color}
                                    j:=(b4-b1*i)/b2;
                                    color:=getimagecolor;
                              end;
                  end
            else
                  {a1<>0 and a2<>0}
                  if b1=0 then
                        if b2=0 then
                              if b4=0 then
                                    begin
                                          {infinitve solutions - ray hits plane directly from the side}
                                          color:=getbordercolor;
                                    end
                              else
                                    begin
                                          {no solutions - ray doesn't hit the plane}
                                          color:=backgroundcolor;
                                    end
                        else
                              begin
                                    {exactly one solution - let's calculate i and j and get the color}
                                    j:=b4/b2;
                                    i:=(a4-j*a2)/a1;
                                    color:=getimagecolor;
                              end
                  else
                        {a1<>0 and a2<>0 and b1<>0}
                        if b2=0 then
                              begin
                                    {exactly one solution - let's calculate i and j and get the color}
                                    i:=b4/b1;
                                    j:=(a4-a1*i)/a2;
                                    color:=getimagecolor;
                              end
                        else
                              {a1<>0 and a2<>0 and b1<>0 and b2<>0}
                              begin
                                    {exactly one solution - let's calculate i and j and get the color}
                                    i:=(a4-b4*(a2/b2))/(a1-b1*(a2/b2));
                                    j:=(b4-b1*i)/b2;
                                    color:=getimagecolor;
                              end;

Calculate k like this:
k:=(c4-i*c1-j*c2)/c3
k:=-(camera_z-p0.z-i*p1.z-g*p2.z)

You may calculate the distance between the point where the ray starts and
the point where the ray crosses the plane:

distance:=k * sqrt(sqr(r1.x)+sqr(r1.y)+sqr(r1.z));



Any questions? please ask!
Following is a pascal program I wrote using these formulas. It works!
I used a picture I converted to a 256 grey value palette and exported to RAW format with Paintshop Pro. Don't forget to correct image_height and image_width.
It's slow and there're lots of useless lines, but I guess it helps understanding how it works.
Again: If you're realy writing a game, you better use the other method and NO floating point!

ASKER CERTIFIED SOLUTION
Avatar of flobecker
flobecker

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanx, flo, you're really a man of action. Sorry for don't accepting it so long, I wasn't in net for a week. I'll use it for game, but only when creating graphics for it.
The only thing I want to know is why it's not rotating around x-axis (it works and doesn't care what is plane_xrot). ??? Please, answer it, I want to rotate around x-axis too.
You're right. I didn't implement rotating around the X-axis because I thought two rotations shall be enough. Replace the following lines in the PRECALC-routine
  p1.x:=2*cos(plane_zrot)*cos(plane_yrot);
  p1.y:=2*sin(plane_zrot);
  p1.z:=2*cos(plane_zrot)*sin(plane_yrot);
  p2.x:=-2*sin(plane_zrot)*cos(plane_yrot);
  p2.y:=+2*cos(plane_zrot);
  p2.z:=-2*sin(plane_zrot)*sin(plane_yrot);
with those:
  p1.x:=1*cos(plane_yrot)*cos(plane_zrot);
  p1.y:=1*cos(plane_yrot)*sin(plane_zrot);
  p1.z:=-1*sin(plane_yrot);
  p2.x:=1*sin(plane_xrot)*sin(plane_yrot)*cos(plane_zrot)-1*cos(plane_xrot)*sin(plane_zrot);
        p2.y:=1*cos(plane_xrot)*cos(plane_zrot)+1*sin(plane_xrot)*sin(plane_yrot)*sin(plane_zrot);
  p2.z:=1*sin(plane_xrot)*cos(plane_yrot);

Now the plane will be rotated clockwiese around the axis (when looking from the positive half of the axis to <0,0,0> in this order: X - Y - Z
Hope it works, but the tests looked good.
Maybe you can send me a copy of your game when finished :-)

flobecker
BIG thanx.