The API
About the Pure Perl Renderer API
Ok. Here are some notes on the existing code. The idea is to have available an easy to use API in order to apply typical 3D Computer Graphics techniques in Perl.
THIS IS NO COMPLETE DOCUMENTATION BY ANY MEANS. No every aspect is covered, just some basic notes to help you study, tweak, make use of the RayCaster and get you started. Do not expect a complete API for doing 3D CG (Computer Graphics) in Pure Perl RayCaster, but just a set of classes to perform many fundamental 3D CG operations. I suggest to study the source after these notes so as to have a better understanding of all you may need. There are several things on the existing source that can be improved.
ObjectReader::FileRaw Class
This class is used by RendererCanvas constructor (RendererCanvas is instantiated by the .pl script) in order to read the vertices from the RAW File. The class is responsible for setting up and returning an Object3D class with its Polygons and Vertices. Vertices normals are also calculated in this class (the rest of the application expects a ready to use object). The last step before returning the object to reader is to call Object's InitObjectScene(). You can create other Classes in ObjectReader directory for reading other formats if you want (especially plain text files are very easy to be parsed using Perl). If you want you may contribute any new ObjectReader (for other formats) you have written.
AffineTransform Class
This is a class to handle Affine Transformations. You get a 4x4 matrix that can be applied on Vertices, Vectors etc.
Typical usage:
my AffineTransform $newTransform = new AffineTransform; #Transformations follow (they are stacked in the newTransform object) $newTransform->rotateX($theta); $newTransform->rotateZ($phi); $newTransform->rotateY(-$theta); #$aVertex is a Vertex Object, translate to its (x,y,z) values $newTransform->translate(-10,300,20); #coordinates $newTransform->translate2Vertex($aVertex); $newTransform->scale($zoom); #stacks an arbitrary matrix ($myMatrix is an array ref.) to $newTransform $newTransform->arbitraryMatrix($myMatrix); #combine all requested transformations (in reverse order) #and create the final 4x4 matrix (as an array ref.) that represents all #requested transformations my $T = $newTranform->getFinalTransformMatrix(); #apply the matrix $T... #to vertices $myObject->transformVertices($T); #to faces normals $myObject->transformFacesNormals($T); #to visible vertices (after back face culling) $myObject->transformVisible($T); #to vertices normals $myObject->transformVerticesNormals($T);
Polygon, Vertex and Object3D Classes
Implement the Polygons (triangles in the application), the Vertices and the 3D Objects.
Typical usage:
#create three 3D points (Vertices) from simple scalars my Vertex $point1 = new Vertex ($p1x,$p1y,$p1z); my Vertex $point2 = new Vertex ($p2x,$p2y,$p2z); my Vertex $point3 = new Vertex ($p3x,$p3y,$p3z); #create a new vertex based on those three 3D points my Polygon $aPolygon = new Polygon ($point1,$point2,$point3); #add this polygon to our object my Object3D $obj = $Object3D->addPolygon($aPolygon); #After adding vertices and polygons to an object, #call the following method to initialize it. #In this method we position lights and the camera to #our scene and we make copies of all vertices and lights #to be able to restore everything in their initial state #Right now, you must change the source in this method to add #or move lights and your camera position $anotherObj->initObjectScene(); #apply a 4x4 transform (created by AffineTransform) #to a vertex $point1->transform($T); #to its normal $point1->transformNormal($T); #to all vertices of face $aPolygon->transform($T); #to face normal $aPolygon->transformNormal($T); #apply $T only if face is not hidden $aPolygon->transformVisible($T); #to all vertices $Object3D->transformVertices($T); #strigification of our objects print "Vertex \$point1 is: $point1\n" print "Polygon \$aPolygon (all vertices) is: $aPolygon\n" print "Object3D \$obj (all polygons with their vertices) is: $obj\n" #add,subtract and calc. product with a scalar of 2 vertices $point2->add($point1); #p2 = p2 + p1 $point3->subtract($point1); #p3 = p3 - p1 $point1->product(34.2); #p1 = p1 * 34.2 #get distance between $point2 and $point1: my $dist_21 = $point2->distance($point1); #add one vertex to $anotherPolygon $aPolygon->addVertices($anotherVertex); #compute polygon normal $anotherPolygon->computeNormal(); #transform only tagged as non 'hidden' vertices by 4x4 Matrix ref. $T $anotherPolygon->transformVisible($T); #transform a polygon's normal by 4x4 Matrix array ref. $T #(yours or returned by an AffineTransform) $aPolygon->transformNormal($T); #get a reference to all vertices of a polygon. my $polyVertices = $anotherPolygon->getVertices(); #test and set the hidden attribute of a polygon #(if polygon is not visible to current view) $anotherPolygon->checkIfHidden(); #test and set the hidden attribute of all object's polygons #(if polygon is not visible to current view) $anotherObject->hidePolygons(); #calculates and stores as object fields the following scalars #x_max, y_max, z_max, x_min, y_min, z_min and x_center,y_center,z_center $anotherObj->getBoundingBoxAndCenter(); #Resets all polygons vertices to original polygons vertices #which were copied when object was initialized. #Use it after transformations to get Object at initial state $anotherObj->resetObject(); #do the raycasting drawing on 2 Device Context objects (DCs) #passed as arguments to the method #$app reference is used just to Yield() in our tight time consuming loop #to avoid "not-responding" windows. #We draw on 2 device contexts now (one in memory), #(implementing double buffering, 'blitting' memory to #real one on request $anotherObj->render($memoryDC, $dc, $app); #draw the wireframe on device context $dc $anotherObj->drawWireframe($dc);
Ray Class
Simple class representing rays. Rays have the following fields, which you can set/get. Rays can be stringified:
- origin
- destination
- direction
Camera Class
Represents the camera in our World.
Important fields are:- cPosition: the camera position
- lookAt: Is the lookat Vector
- vec_N: Viewing Direction Vector (normal to view plane)
- vec_VUV: View Up Vector
- vec_U: VUV x N (direction of increasing 'x' in camera space)
- vec_V: N x U
- focalLength, backClipping, scrW, scrH, rayCastScrW, rayCastScrH, frameW frameH: Parameters of your viewport (where W means width, H means Height, scr means screen)
- orbit(Object3D $obj, $phi, $theta, $zoom): method allows you to orbit camera around object, by applying the opposite of the requested translation the object.
- world2Camera(Object3D $obj): Important coordinate system change: transform WCS to CS
- perspectiveTransform(Object3D $obj): Performs the perspective transformation of the object to display it on wireframe rendering.
- getRay($i, $j): Returns a ray directed from camera Origin to pixel ($i,$j) on window (you will find this method inlined in Object3d::render()
RayCast Class
Represents and Implements the RayCasting.
Important methods:- rayTriangleIntersection(Ray $ray, Polygon $triangle) - Tests whether $ray intersects $triangle and returns the intersection Vertex if found. Look at the FAQ for basic info on the algorithm being used.
- findClosestIntersection(Ray $ray, Object3D $obj) - Find the closest intersection of o $ray to an $object. Returns the intersection Vertex and $intersectionTriangle Polygon if found.
- intersectsBoundingBox(Ray $ray, Object3D $obj) - Returns true if ray intersects bounding box.
- calculateLocalColour(..): in case of an intersection between a ray and the object, send a shadow filler ray to see if intersection vertex is in shadow. If needed calls the localShadeModel to get the object's color for the given intersection vertex.
- localShadeModel(..): calculates color for given intersection Vertex on Object using the Phong model and lookup on the Color Table (RayCast::colourTable)
- calcBarycentricCoords($isectVertex, $isectPoly): Calculates barycentric coordinates for given vertex in polygon
Other info
- Arguments are being processed by Getopt::Std in the .pl file.
- You can perform clipping of polygon lines to near and far clipping plane by using the methods Polygon::clipLine of Polygons and Object3D::clipObject.
The WxPerl side
WxPerl is being used to create the GUI of the application. WxWindows (wxPerl) components are being used in the following places:- RendererCanvas Class:
Implements the Canvas on which we draw (as WxWindows ScrolledWindows objects). Handles the events (eg. keystrokes). and calls on user requests methods from the rest of Pure Perl RayCaster (Object3D methods, to draw wireframe or perform raycasting). Handles most user generated events. -
pureRayCaster.pl
This is our executable script. Initializes the WxPerl application, sets the GUI (and GUI events).