Keywords: SVG, 3D, Graphic, Multimedia, Virtual reality, X3D, DOM, Namespaces, Metadata, JavaScript, GUI, Content conversion, Conversion, XML
Biography
After receiving his Ph.D. in Mathematical Physics from Yale University in 1989, Philip spent a year as Assistant Professor of Physics at Knox College, followed by four years as Assistant Professor of Mathematics at the University of Toronto. His background in Differential Geometry and in computer modelling of physical phenomena served as unorthodox preparation for his subsequent move into industry as a Software Engineer with an emphasis on Computer Graphics. By 1997 Philip was in charge of a software research team creating early Web technologies based on HTML, XML, CSS and Java. Philip now lives and works in Vancouver, Canada, where he is President of SchemaSoft (http://www.schemasoft.com/), a software development consulting company he co-founded in 1999. He is an Advisory Committee Representative of the World Wide Web Consortium (http://www.w3.org/), and has been a member of the W3C Scalable Vector Graphics Working Group (http://www.w3.org/Graphics/SVG/) since its inception in 1998. Philip is Chair of the BC Advanced Systems Institute International Scientific Advisory Board (http://www.asi.bc.ca/). He is also a Director of the Vancouver XML Developers Association (http://www.vanx.org/), an organization that he co-founded in 2000. He regularly writes and lectures on topics related to software engineering, XML and SVG.
Biography
Buke Otkunc graduated with honors from Istanbul Technical University, Turkey with a B.Sc. in Control and Computer Engineering. After a year of industry experience she continued her academic studies at the University of British Columbia to receive her M.A.Sc. degree in Electrical and Computer Engineering. The focus of her Masters work was on real-time operating systems. She has been employed as a software developer with Schema Software Inc. in Vancouver BC for the last three years. Among many other tasks she had in this position, Buke was involved in a project for converting a presentation file format to SVG.
Three-dimensional modelling is not built into today's most popular browsers, and is perhaps considered too specialized to be added in the near future. Although browser plug-ins have been created for formats such as VRML [VRML] and X3D [X3D] , such plug-ins are far from ubiquitous. This creates a problem for anyone wishing to publish 3D content online. The current paper offers a practical solution that takes full advantage of XML and related standards.
3D viewing software generally provides users with the ability to explore a scene by changing viewpoint. The scene is typically made up of polyhedra or multi-faceted surfaces while the view is constructed by transformation to 2D and application of a lighting model. The 2D requirement arises as a physical limitation of typical computer display devices. However, it also begs the question, "Is a 2D format sufficient for the viewing of 3D content?"
As long as 2D renditions can be generated on the fly in response to changing viewpoint, the behaviour of a 3D viewer can be reproduced. A Web application that generates these renditions on the server would be too unresponsive to be practical, so there is a need to interactively generate views on the client. Our solution is to use Scalable Vector Graphics [SVG] , in conjunction with compatible W3C standards for client-side processing. In particular,
Although SVG comes with an implicit z-order as well as filter effects designed to simulate a three-dimensional appearance, these effects assume an unchanging view direction, and have to be adapted for use on re-computed geometry.
We present an online 3D viewer implementation called Aquila, demonstrate its applicability to arbitrary 3D content, and discuss further enhancements that can add to the realism of the rendered output. Along the way we illustrate how to use SVG to achieve standard 3D algorithms such as backface culling, shadows, Gouraud shading and Phong shading.
1. Motivation
1.1 Viewing 3D on the Web
1.2 Using SVG and ECMAScript for Displaying 3D Data
1.3 Using XML to Store 3D Data
1.4 Rendering 3D Data with Client-Side Script
2. Three-Dimensional Model
2.1 world3D Schema
2.2 Example
3. Transformation to SVG
3.1 Two-Dimensional Projection
3.1.1 Projection Method
3.1.2 Lighting Model
3.2 Managing the SVG DOM
4. Visible Surface Detection
4.1 Data Preparation
4.2 Back Face Culling
4.3 Overlap Detection
4.3.1 Point in Polygon
4.3.2 Point in Front of Face
4.4 Face Pair Sorting
4.5 Topological Sort
5. User Interface
5.1 Changing the View
5.2 Demonstration
6. Further Work
6.1 Modelling Enhancements
6.2 SVG Enhancements
7. Conclusion
Bibliography
Viewers and file formats that have been developed for the Web have been mainly applicable to 2D. This is in contrast with the 3D world of our experience. Further, the general interest in viewing 3D on the Internet has shown itself with the emergence of standards such as VRML [VRML] and X3D [X3D] . Unfortunately, these open formats have not been natively supported in Browsers.
It is possible to create and view 3D images using CAD and 3D design applications. However, these applications require proprietary formats, and the specialized Browser plug-ins that process them do not have a wide install base. Authors are reluctant to publish Web content unless most client browsers are capable of rendering this content without downloading a new plug-in.
On the other hand, SVG is already widely supported through deployed plug-ins and browsers, and has the potential to become a universal, general-purpose drawing API for delivering a wide range of more specialized graphical formats. With the use of SVG and ECMAScript [ECMA] we will demonstrate that it is possible to provide a widely accessible viewing solution for non-proprietary 3D data stored in an appropriate XML format.
SVG is an XML format that is designed to be mixed with other XML namespaces, therefore the use of XML for storing the 3D data is a natural choice. Some modelling tools such as AutoCAD now have functionality to save user-created 3D images as XML. XML data can be stored in memory as DOM nodes that can be manipulated using ECMAScript.
3D rendering can be computationally expensive, and client-side script threatens to be the bottleneck of the rendering pipeline we are considering. On the other hand, the 2D rendering is done for us for free by the Browser's SVG viewing code (whether native or plug-in), and it is presumably optimized. As long as we stick to conversions from 3D to 2D objects in script, we avoid the time-consuming pixel painting processes. This project presents an interesting exercise in division of processor labour, and some nontraditional approaches to rendering optimization emerge.
An important part of a 3D viewer is providing a format for defining and storing 3D objects. We introduce the world3D XML grammar for representing 3D data. The world3D XML grammar is similar to the X3D standard but much simpler and therefore more appropriate for illustrative purposes. world3D can encode a scene of multiple polyhedra illuminated by a light source of any colour and position viewed from any viewpoint and orientation.
Here is the file
world3D.xsd
, containing the XML Schema for the world3D format:
<?xml version="1.0" encoding="UTF-8"?>
<schema targetNamespace="http://www.schemasoft.com/schema/world3D.xsd"
xmlns:poly="http://www.schemasoft.com/schema/world3D.xsd"
xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified"
attributeFormDefault="unqualified">
<element name="world3D">
<complexType>
<sequence>
<element name="viewPlane">
<complexType>
<sequence>
<element name="vector" type="poly:vectorType"/>
<element name="vector" type="poly:vectorType"/>
</sequence>
<attribute name="zoom" type="decimal" default="1"/>
<attribute name="originX" type="decimal" default="0"/>
<attribute name="originY" type="decimal" default="0"/>
</complexType>
</element>
<element name="lightSource">
<complexType>
<sequence>
<element name="vector" type="poly:vectorType"/>
<element name="color" type="poly:colorType"/>
</sequence>
</complexType>
</element>
<element name="edge">
<complexType>
<sequence>
<element name="color" type="poly:colorType"/>
</sequence>
<attribute name="show" type="boolean" default="false"/>
</complexType>
</element>
<element name="polyhedra">
<complexType>
<sequence minOccurs="0" maxOccurs="unbounded">
<element name="face">
<complexType>
<sequence minOccurs="3" maxOccurs="unbounded">
<element name="vector" type="poly:vectorType"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
<complexType name="vectorType">
<attribute name="x" type="decimal" use="required"/>
<attribute name="y" type="decimal" use="required"/>
<attribute name="z" type="decimal" use="required"/>
</complexType>
<complexType name="colorType">
<attribute name="red" type="unsignedByte" use="required"/>
<attribute name="green" type="unsignedByte" use="required"/>
<attribute name="blue" type="unsignedByte" use="required"/>
</complexType>
</schema>
|
Our "Hello world3D" example is a simple cube, given in the listing below:
<?xml version="1.0" encoding="UTF-8"?>
<world3D
xmlns="http://www.schemasoft.com/schema/world3D.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.schemasoft.com/schema/world3D.xsd
world3D.xsd">
<viewPlane zoom="187.1" originX="0" originY="100">
<vector x="-0.5774" y="0.5774" z="-0.5774"/>
<vector x="-0.7715" y="-0.1543" z="0.6172"/>
</viewPlane>
<lightSource>
<vector x="0.2673" y="0.8018" z="0.5345"/>
<color red="255" green="55" blue="155"/>
</lightSource>
<edge show="true">
<color red="0" green="0" blue="255"/>
</edge>
<polyhedra>
<face>
<vector x="0" y="0" z="0"/>
<vector x="0" y="1" z="0"/>
<vector x="1" y="1" z="0"/>
<vector x="1" y="0" z="0"/>
</face>
<face>
<vector x="0" y="0" z="0"/>
<vector x="0" y="0" z="1"/>
<vector x="0" y="1" z="1"/>
<vector x="0" y="1" z="0"/>
</face>
<face>
<vector x="0" y="0" z="0"/>
<vector x="1" y="0" z="0"/>
<vector x="1" y="0" z="1"/>
<vector x="0" y="0" z="1"/>
</face>
<face>
<vector x="0" y="0" z="1"/>
<vector x="1" y="0" z="1"/>
<vector x="1" y="1" z="1"/>
<vector x="0" y="1" z="1"/>
</face>
<face>
<vector x="1" y="0" z="0"/>
<vector x="1" y="1" z="0"/>
<vector x="1" y="1" z="1"/>
<vector x="1" y="0" z="1"/>
</face>
<face>
<vector x="0" y="1" z="0"/>
<vector x="0" y="1" z="1"/>
<vector x="1" y="1" z="1"/>
<vector x="1" y="1" z="0"/>
</face>
</polyhedra>
</world3D>
|
One of the main tasks of a 3D viewer is converting the object, which is represented in real world coordinates, into a 2D coordinate system. This is done through a projection method. A projection method is an approximate representation of what a person would see, perhaps through the lens of a camera.
In addition to projecting faces, it is also necessary to determine what parts of a face are visible by the observer. This information is used to remove faces that do not need to be processed by the viewer, and to ensure that partially obscured faces are drawn before the faces that obscure them.
Another important expectation from a 3D viewer is the ability to represent a lighting model. Applying a lighting model to the 2D view of the object enhances the real-world look and feel of the object. A lighting model must appropriately represent the colours reflected at each location under specified illumination.
The specific transformation models that we have chosen are described in the following subsections.
In order to represent 3D points in SVG format we need to use a 3D to 2D planar projection method. For this prototype we chose the parallel projection [COMP97] , [COMP90] method. Parallel projection an approximation of perspective transformation in the limit that the viewpoint is far away. Perspective transformation is modelled after the behaviour of a camera.
A great way to enhance the viewing of objects in 3D is through the use of a lighting model. For this prototype we chose a free directional lighting [COMP97] , [COMP90] model. This model represents a global type of light, which is considered to be infinitely far away. The direction of the light is used to determine the angle at which the light intersects with a surface. If the surface is perpendicular to the light rays it will be rendered brightest. The more oblique the surface to the light rays the less it is illuminated, with the light's energy flux being proportional to the dot product of the illumination vector and the surface normal.
After 3D data is transformed, the 2D data is stored as SVG. Since these are transformations of XML, it might seem natural to take an XSLT-based approach as discussed in [GS] . However, an XSLT solution is not amenable to user interactivity. For example, altering viewpoint requires re-calculation of the transformation to 2D. Therefore we chose to use ECMAScript to construct the SVG representation of the projected image in memory as a tree of DOM nodes.
Figure 1 shows Aquila's view of the world3D cube introduced in Section 2.2 .
This SVG rendition includes axes and a GUI, but if we were to serialize just the portion of the SVG DOM representing the cube we would get the following:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="-600 -600 1200 1200"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<desc>cube.xml transformed to SVG</desc>
<g id="ThreeDimensionalShape" transform="scale(1,-1)"
stroke-width="2" stroke-linejoin="bevel" stroke="#0000ff">
<polygon fill="#880088" points="141.96846,315.47812 33.93691,171.13047
141.96846,142.26094 250,286.60859"/>
<polygon fill="#440044" points="141.96846,55.65235 250,26.782820
141.96846,142.26094 33.93691,171.13047"/>
<polygon fill="#CC00CC" points="358.03154,171.13047 250,286.60859
141.96846,142.26094 250,26.782820"/>
</g>
</svg>
|
In this example, the SVG group element with the id "ThreeDimensionalShape" represents the shape. Each polygon element corresponds to one projected face of the cube. The fill attribute values are calculated using the free directional lighting model. Three of the six faces have been excluded since they are completely hidden. Section 4.2 will describe the technique used to identify these hidden faces.
|
NOTE: SVG follows a simple painter's model in which each visible element is drawn over the previous element in tree serialization order. Therefore the order in which nodes are inserted in the SVG DOM determines the order in which face projections are stacked. |
When surfaces overlap each other, as in Figure 2 , it is important that we draw these surfaces in the right order. This is a nontrivial exercise, since there is no consistent way to order the surfaces from "furthest" to "nearest", given that each surface spans a range of distances from the observer. The methods used to solve this problem are explained in the following sections.
Consider a directed graph whose nodes are the polygonal faces in our world3D model and whose edges represent the "overlap" relation. If there are any cycles on this graph then it is impossible to draw the 2-D projection properly using a linear drawing order. Figure 3 and Figure 4 show examples of a 2-cycle and a 3-cycle that cannot be depicted just by choosing a drawing order for P, Q and R.
A solution to this conundrum is to chop up the faces of an N-cycle wherever the planar extensions of one face intersect another face. When you chop face P with an extension of face Q, this divides P into parts P1 and P2 that appear on either side of Q regardless of viewpoint.
Although subdividing faces in this manner can be accomplished at run time, this is inefficient, particularly in ECMAScript. Because the subdivision algorithm is independent of viewpoint, it can instead be performed on the data in advance, by the 3D authoring software used to create the models. The authoring software should prepare its world3D description to be as efficient as possible when run, allowing the world3D processor to assume that no cycles remain in the overlap graph. The result is a DAG (Directed Acyclic Graph) , which can be ordered using the procedure discussed in Section 4.5 .
Those surfaces on a 3D object that are completely obscured from view need not be rendered at all, and need not be inserted into the DOM. Before deciding drawing order, we detect and remove hidden faces for run-time efficiency. Our viewer uses the back face culling method for eliminating these faces [COMP97] , [COMP90] . In this method, if the outward normal of a polyhedral face is pointing away from the observer (i.e. its dot product with the viewpoint vector is negative) then the face is hidden and should not be rendered. This method relies on the assumption that all planar faces are part of a closed surface, which follows from an assumption about how we model tangible reality: faces are used to represent the exterior of solids rather than representing infinitely thin "solids" in their own right.
Before we can sort the DAG , we will have to construct it! Each graph edge encodes an overlap, so we need to be able to determine when one face overlaps another. This happens as a result of two tests:
Face P overlaps face Q, or vice-versa, if and only if a vertex of P lies inside Q when the two are projected into the view plane, or vice-versa. Therefore we first project all vertices to the view plane, then use the "Crossings Test" to determine if a vertex of P lies inside Q. The Crossings Test checks each vertex of P to determine whether a ray emanating in a fixed direction (say, the positive X-direction of the projection plane) intersects the sides of Q an odd number of times (where special attention has to be paid to vertex intersections, a degenerate case). Figure 5 shows an example of this.
We employ an optimization due to MacMartin [PtPoly] : An intersecting side must have positive y value at one of its vertices and negative y value at the other, so we need only check the places on the perimeter of Q where y changes sign.
Once we have found a point of face P whose view-plane projection is inside the projection of face Q, it is straightforward to determine whether that point is in front of Q or behind Q simply by taking the dot product of the point with Q's normal and comparing the result of the same calculation performed on one of Q's vertices.
As a result of the foregoing algorithms, for every pair of faces {P,Q} we know either (P overlaps Q), (Q overlaps P) or neither. This allows us to construct the DAG in software by imbuing each face object with an array of references to the face objects it overlaps.
It is possible to place a linear order on all the nodes of a DAG that is consistent with the pairwise ordering of each graph edge. The implications of this are that we can draw the faces of the polyhedra in an order that correctly shows all overlaps.
The algorithm for finding a linear order is called "Topological Sort", summarized as follows: For each root node of the DAG , start at that node and perform a depth-first traversal, only stopping at child nodes that have not already been visited. Whenever returning to a parent node because all its children have been visited, mark that parent node as next in the linear sequence. Figure 6 shows an example of this sort algorithm applied to the practical problem of dressing oneself in the morning.
Figure 6: Topological Sort Example
Top — Graph showing the order in which pairs of garments must be donned. The numbers next to each node indicate the sequence in which each is first visited and last visited in a depth-first traversal.
Bottom — The same graph laid out in order of decreasing last-visited time. This sequence solves the problem of getting dressed without mishap.
For the full 3D model to be accessible in our viewer, it must be possible to adjust one's viewpoint interactively. For that reason, Aquila provides the ability to rotate along any of 3 axes, to translate, and to and zoom. Both the user interface and the projected views are 100% SVG. User feedback is received as DOM events on SVG elements.
Aquila provides the user with buttons for rotation and zooming. The mouse events are captured and the SVG nodes are modified with ECMAScript to reflect the changes that the user requested. ECMAScript handlers of DOM events modify DOM nodes in order to create the effect of a shifting view in response to user input. Since SVG supports external script files, it is possible to separate the code completely from the world3D content, allowing the viewer code to be easily re-used on arbitrary world3D content.
Figure 7 through Figure 14 show an initial view of a simple table in Aquila, followed by successive snapshots after the user has interacted with the various scripted buttons in the SVG. In particular, we include views that result from the successive operations of moving the table up, moving it left, rotating about the Z axis, rotating about the Y axis, rotating about the X axis, zooming in and changing the colour.
3D graphics is a rich topic in which many technological advances have been made. While Aquila demonstrates the principle of using SVG for a 3D application, there remain many opportunities to extend the 3D model, add to the features, specialize to specific use-cases and optimize the performance. For example,
SVG has a number of features that can be used to add to the realism of the 2D views. These can be listed as follows:
In order to view 3D object models on the Web a 3D viewer is required. Our implementation of Aquila proves that it is sufficient to have SVG support in your browser; no additional 3D language support is necessary. Via the scripting and DOM capabilities of SVG it is possible to provide both the user interface and the interactive display required of such a viewer. Additionally, SVG provides features that are directly applicable for further enhancing such a viewer. Among these features are gradients, filters and texture. Since the 3D viewer code has been completely factored from the world3D content it acts upon, Aquila can be re-used on any world3D content without change.
XHTML rendition created by gcapaper Web Publisher v2.1, © 2001-3 Schema Software Inc.