Post by CGHow can I draw simple 3d shapes (like say a cube) using just
2d-line primitives? I am not worried about hidden surface elimination
etc or filling etc.
I just want to draw basic 3d shapes (even 1 shape like a cube is
sufficient).
Let's say I have 8 points (with x,y,z cordinates for each point) & 12
vertex definitions (formed with the eight points) how do I draw a cube
given that I have the following primitives.
- a drawpixel kind of function.
- a drawline (x1, y1, x2, y2) kind of function.
This is what I want to do
- Accept 8 points & 12 vertices (from a file).
- Draw the cube
- Accept a transformation (say rotate by 45 degrees about X axis).
- Transform the co-ordinates & draw the cube again.
I can write the code which can do the matrix multiplication transforms.
However, I am not able to figure out an easy way to draw a wireframe
kind of cube or any other 3d shape.
I wanted to display 3D objects on my 2D screen, too. Thinking I would
solve the problem by using matrix multiplications to do all the
transformations was one way. But about 8 years ago I stumbled on a web
page that delighted me with its easy-to-understand (at least, for me)
use of vectors to figure out the position on the computer screen that
corresponds to a point in space as seen from a camera positioned
anywhere in space. Take a look at
http://members.tripod.com/~Paul_Kirby/vector/Vplanelineint.html
as a start. Unfortunately, the two pictures seem to have moved so they
don't show. I have them on my hard drive, though, so if you want to see
them I'll try to get them to you. While this vector method quite easily
generates a vector 'r' that, when applied at the origin, lands directly
at the point on the 2D screen that should be addressed, the conversion
of 'r' to screen coordinates is not intuitive. The author of the web
page simply says "Add a little programming and this is our pixel" and
does not solve this crucial second step! In my case I solved the "little
programming" final step by using direction cosines for converting from
one coordinate system (real 3D space) to another (screen space). Here's
my 'C' code for plotting a line in 3D space to a line on the screen's 2D
space...
//----------------------------------------------------------------------------------------------------------------
int draw_line_from_to_screen_xy(POINT *p1,POINT *p2,unsigned long color) {
/*
Given two points in 3D space, the camera position, and screen
location as a rectangular portion of a plane,
compute the computer screen coordinates and draw a straight line
between the points on the screen.
*/
float tstart,tfinish,t1,t2;
VECTOR c,a,n,b,tempv1,tempv2,rstart,rfinish;
double a11,a12,a13,a21,a22,a23,a31,a32,a33; //Direction cosines
int X_pixels,Y_pixels; //Number of pixels for each
axis of the display screen
VECTOR screen_z; //The normal to the screen
(computed from 'screen')
VECTOR v; //The position vector of our
arbitrary point on the screen in real-world basis vectors
VECTOR screen_corner; //Generated position vector
for screen's origin point in real-world basis vectors
VECTOR s; //Computed vector
'in-the-screen' from screen's origin in real-world basis vectors
VECTOR screen_coords; //Converted vector
'in-the-screen' from screen's origin in screen basis vectors
int X_pos_start,Y_pos_start; //Actual pixel position on
the screen based on screen's resolution
int X_pos_finish,Y_pos_finish; //Actual pixel position on
the screen based on screen's resolution
VECTOR real_x_axis={1.0,0.0,0.0}; //The real-world unit-length
basis vectors
VECTOR real_y_axis={0.0,1.0,0.0};
VECTOR real_z_axis={0.0,0.0,1.0};
if (SCREEN_METHOD==2) { //If calculating 'screen' from camera
direction, distance, and field of view
/*
tan(45/2) = 1/2-the-diagonal-distance /
distance-from-camera-to-screen
Therefore...
the-diagonal-distance = 2 * tan(45/2) *
distance-from-camera-to-screen [call this result D]
If the two sides of the screen are X and Y, then the aspect
ratio of the screen is X:Y, its value
is computed as X/Y, and we'll call this result R. Also, X^2 +
Y^2 = D^2 according to the Pythagorean
theorem. We can now solve for X and Y as from these two equations...
X^2 + Y^2 = D^2 [Eq 1]
X/Y = R [Eq 2]
X = R * Y [Rearrange Eq 2]
(R * Y)^2 + Y^2 = D^2 [Subst into Eq 1]
R^2 * Y^2 + Y^2 = D^2 [Mpy]
(R^2 + 1) * Y^2 = D^2 [Factor]
Y^2 = D^2 / (R^2 + 1) [Rearrange]
Y = SQRT(D^2 / (R^2 + 1) ) [Take SQRT]
X = Y * R [From Eq 2]
*/
}
/*
Since our screen is described with the 'plane_pt_2v' method we don't
have the normal to the screen as
required by the logic I found on the internet for determining the
position vector of an object's vertex
on an arbitrary screen. Generate it now by taking the cross product
of the two screen definition vectors.
*/
if (cross_product(&screen.pv1,&screen.pv2,&screen_z)) { //Generate
normal to the screen, length not crucial
fprintf(stderr,"draw_line_from_to_screen_xy: invalid screen
definition\n");
return(1); //Screen's 2 vectors are invalid (null or parallel)
}
/*
Use the logic gleaned from the internet to compute the vector 'r',
in real world coordinates, of a point lying
on the surface of a display screen. This internet logic describes
the display screen as a plane that has a
given normal vector 'n' and goes through a given real world point
'c'. The point 'c' is exactly 'screen_dist'
in front of the camera.
This internet logic does *not* compute the coordinates needed to
access the display screen. This is done in a
second step that does a coordinate transformation of the 'r' vector
from the real world basis vectors to
screen world basis vectors.
*/
//CONVERSION OF FIRST POINT TO A POSITION VECTOR THAT ENDS ON THE SCREEN
vector_1_point(p1,&a); //Establish vector 'a', the
real point imaged
vector_1_point(&camera_loc,&b); //Establish vector 'b'
n=screen_z; //Establish vector 'n' as the
screen's normal, previously calculated
vector_1_point(&screen.pp,&c); //'c' is constructed from the
point that defines the screen
vector_sub(&c,&a,&tempv1); //'c' - 'a'
t1=dot_product(&tempv1,&n); //( 'c' - 'a' ) . 'n'
vector_sub(&b,&a,&tempv2); //'b' - 'a'
/*
If the dot product of 'b'-'a' & 'n' goes to zero then either (1) the
point 'a' is at the camera
location (i.e. b=a) or (2) the screen is *essentially* parallel to
the camera direction. In either
case we want 'rstart' to equal 'a', so set tstart to 0.0.
*/
if ((t2=dot_product(&tempv2,&n))==0.0) //( 'b' - 'a' ) . 'n'
tstart=0.0;
else
tstart=t1/t2; //( ( 'c' - 'a' ) . 'n' ) / (
( 'b' - 'a' ) . 'n' ) )
vector_mpy(&tempv2,tstart); //t ( 'b' - 'a' )
vector_add(&a,&tempv2,&rstart); //'r' = 'a' + t ( 'b' - 'a' )
//CONVERSION OF SECOND POINT TO A POSITION VECTOR THAT ENDS ON THE SCREEN
vector_1_point(p2,&a); //Establish vector 'a', the
real point imaged
vector_1_point(&camera_loc,&b); //Establish vector 'b'
n=screen_z; //Establish vector 'n' as the
screen's normal, previously calculated
vector_1_point(&screen.pp,&c); //'c' is constructed from the
point that defines the screen
vector_sub(&c,&a,&tempv1); //'c' - 'a'
t1=dot_product(&tempv1,&n); //( 'c' - 'a' ) . 'n'
vector_sub(&b,&a,&tempv2); //'b' - 'a'
if ((t2=dot_product(&tempv2,&n))==0.0) //( 'b' - 'a' ) . 'n'
tfinish=0.0;
else
tfinish=t1/t2; //( ( 'c' - 'a' ) . 'n' ) / (
( 'b' - 'a' ) . 'n' ) )
vector_mpy(&tempv2,tfinish); //t ( 'b' - 'a' )
vector_add(&a,&tempv2,&rfinish); //'r' = 'a' + t ( 'b' - 'a' )
//NEW LOGIC USING DIRECTION COSINES TO CONVERT FROM REAL-WORLD BASIS
VECTORS TO SCREEN-WORLD BASIS VECTORS
/*
We now have the vectors 'rstart' and 'rfinish', which are the
position vectors in real-world coordinates to the
start and end of the line we wish to draw on the screen's plane. My
latest "direction cosines" method of
calculating the screen X and Y pixel addresses for points requires a
"plane_from_pt_2v" description of the
screen. Eventually I'd like this logic to be inside VCTRSUPPORT.h
but it's compact enough for now to be coded
right in the application. Notice there is no clipping to screen
dimensions so I am relying on X-Window to
clip for me. This should be fixed but works OK for now.
*/
//CONVERSION OF FIRST POINT'S POSITION VECTOR TO SCREEN PIXEL ADDRESS
X_pixels=image.size_hints.width; //Get the screen's width in pixels
Y_pixels=image.size_hints.height; //Get the screen's height in pixels
a11=dot_product(&real_x_axis,&screen.pv1)/vector_length(&screen.pv1);
//Compute the nine direction cosines
a12=dot_product(&real_x_axis,&screen.pv2)/vector_length(&screen.pv2);
a13=dot_product(&real_x_axis,&screen_z) /vector_length(&screen_z );
a21=dot_product(&real_y_axis,&screen.pv1)/vector_length(&screen.pv1);
a22=dot_product(&real_y_axis,&screen.pv2)/vector_length(&screen.pv2);
a23=dot_product(&real_y_axis,&screen_z) /vector_length(&screen_z );
a31=dot_product(&real_z_axis,&screen.pv1)/vector_length(&screen.pv1);
a32=dot_product(&real_z_axis,&screen.pv2)/vector_length(&screen.pv2);
a33=dot_product(&real_z_axis,&screen_z) /vector_length(&screen_z );
vector_1_point(&screen.pp,&screen_corner); //Generate
position vector for screen's corner point
vector_sub(&rstart,&screen_corner,&s); //Compute
'in-the-screen' vector to our point in real-world basis vectors
screen_coords.x = a11 * s.x + a21 * s.y + a31 * s.z; //Convert basis
to screen basis vectors
screen_coords.y = a12 * s.x + a22 * s.y + a32 * s.z;
screen_coords.z = a13 * s.x + a23 * s.y + a33 * s.z;
X_pos_start=screen_coords.x/vector_length(&screen.pv1)*(double)X_pixels;
//Compute X pixel using point's position along X-axis
Y_pos_start=screen_coords.y/vector_length(&screen.pv2)*(double)Y_pixels;
//Compute Y pixel using point's position along Y-axis
//CONVERSION OF SECOND POINT'S POSITION VECTOR TO SCREEN PIXEL ADDRESS
vector_sub(&rfinish,&screen_corner,&s); //Compute
'in-the-screen' vector to our point in real-world basis vectors
screen_coords.x = a11 * s.x + a21 * s.y + a31 * s.z; //Convert basis
to screen basis vectors
screen_coords.y = a12 * s.x + a22 * s.y + a32 * s.z;
screen_coords.z = a13 * s.x + a23 * s.y + a33 * s.z;
X_pos_finish=screen_coords.x/vector_length(&screen.pv1)*(double)X_pixels; //Compute
X pixel using point's position along X-axis
Y_pos_finish=screen_coords.y/vector_length(&screen.pv2)*(double)Y_pixels; //Compute
Y pixel using point's position along Y-axis
//DRAW THE LINE ON THE SCREEN BETWEEN THE TWO POINTS
gcval.foreground=revamp(color);
if ((stat=XChangeGC(xserv,image.gc,GCForeground,&gcval))!=1) {
printf("xscrunch: XChangeGC returned %d\n",stat);
}
XDrawLine(xserv,image.window,image.gc,X_pos_start,Y_pos_start,X_pos_finish,Y_pos_finish);
XDrawLine(xserv,image.pixmap,image.gc,X_pos_start,Y_pos_start,X_pos_finish,Y_pos_finish);
return(0);
}
//----------------------------------------------------------------------------------------------------------------