Understanding Hierarchical vs. Non-Hierarchical Models in Computer Graphics
Dive into the concepts of hierarchical and non-hierarchical modeling in computer graphics. Explore how hierarchical models represent complex objects with explicit sub-part dependencies, while non-hierarchical models treat objects independently. Understand the benefits and challenges of each approach, and learn about symbol-instance tables, scene graphs, and graph structures in graphics systems design.
Download Presentation
Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author. Download presentation by click this link. If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
E N D
Presentation Transcript
Computer Graphics Dr. Chih-Kuo Yeh Email: simpson.ycg@gmail.com
Hierarchical Models Hierarchical models used to represent complex objects explicit dependency between sub-parts of an object object-oriented approach to implementation e.g. Articulated objects (robot arm) Scene hierarchical uses to represent all objects in as a hierarchy shapes/lights/viewpoints/transforms/attributes Scene Graph Scenes can be represented non-hierarchically leads to difficulties in scaling to large scale complex scenes all functions explicit in display() function inflexible Design of graphics systems with multiple objects hierarchical models object-oriented design scene graphs
Non-Hierarchical Modelling Treat object independently reference object by a unique symbol i.e. a,b,c . Object initially defined in local object coordinates Transform each object instance from local to world coordinates: OpenGL display function: 1. display(){ 2. .. 3. Scalef( ); 4. Rotatef( ); 5. Translatef(...); 6. draw_object(); 7. .. 8. };
Non-Hierarchical Modelling All objects are treated independently display() function transforms/draws each object explicitly No interrelations between objects Can represent objects by a table structure: each object has a symbol each object has corresponding translation/rotation/scale each object has set of attributes colour/material properties etc. render object by calling drawing each symbol in turn with specified transformation/attributes
Hierarchical Models Consider a more complex model composed of several sub-objects car = chassis + 4 wheels Representation 1: Treat all parts independently (non-hierarchical) apply transformation to each part independently chassis: translate, draw chassis wheel 1: rotate, translate, draw wheel 1 wheel 2: rotate, translate, draw wheel 2 . redundant, repeated computation of translate no explicit representation of dependence between chassis and wheels
Representation 2: Group parts hierarchically exploit relation between parts exploit similarity i.e. wheels are identical (just translated)
Graph Structures Graph Representation - nodes: objects + attributes? + transforms? - edges: dependency between objects parent-child relation between nodes Directed-Graph edges have a direction associated with them Tree - directed graph with no closed-loops i.e. cannot return to the same point in the graph - root node : no entering edges - Intermediate nodes have one parent and one or more children - leaf node : no children Parameters such as location & attributes may be stored either in nodes or edges
Hierarchical Models We represent such models using transformations Each transformation represents a relative change from one scaling, position and orientation to another
Example: a small solar system A sun Two planets A moon around each planet
Example: a small solar system Every primitive is drawn as a sphere DrawSolidSphere(...); The use of PushMatrix() and PopMatrix() allow for Using the present model-view matrix to place objects preserving the model-view matrix for drawing other objects
Example: Solar system Relationships The sun stands still. Planets rotate around the sun and spin around their y-axis The moons rotate around their planet spin around their y-axis Rotate around the sun (together with their planet)
Just one planet and one moon void draw() { ... // set the projection and the camera here (see labs) // draw the scene DrawSolidSphere(...); // sun Rotate(angle_1p, 0, 1, 0); Translate(radius_1p); DrawSolidSphere(...); // first planet Rotate(angle_1m, 0, 1, 0); Translate(radius_1m); DrawSolidSphere(...); // moon around first planet }
Adding another planet with a moon void draw() { ... // set the projection and the camera here (see labs) // draw the scene DrawSolidSphere(...); // sun PushMatrix(); // save the model-view matrix into the transformation stack Rotate(angle_1p, 0, 0, 1); Translate(radius_1p); DrawSolidSphere(...); // first planet Rotate(angle_1m, 0, 0, 1); Translate(radius_1m); DrawSolidSphere(...); // moon around first planet PopMatrix(); // restore the model-view matrix (pop from stack) Rotate(angle_2p, 0, 0, 1); Translate(radius_2p); DrawSolidSphere(...); // second planet Rotate(angle_2m, 0, 0, 1); Translate(radius_2m); DrawSolidSphere(...); // moon around second planet }
Making one planet spin around its own axis void draw() { ... // set the projection and the camera here (see labs) // draw the scene DrawSolidSphere(...); // sun PushMatrix(); // save the model-view matrix into the transformation stack Rotate(angle_1p, 0, 0, 1); Translate(radius_1p); PushMatrix(); Rotate(angle_1rot, 0, 1, 0); // spin! DrawSolidSphere(...); // first planet PopMatrix(); Rotate(angle_1m, 0, 0, 1); Translate(radius_1m); SolidSphere(...); // moon around first planet PopMatrix(); // restore the model-view matrix (pop from stack) ... // draw the second planet here }
Example: Robot Arm Represented by a tree with a single chain Explicit hierarchical implementation (i) Base: Rotate about base R( 1) M1= R( 1) (ii) Upper-arm: translate & rotate M2= M1T(l2)R( 2) (iii) lower-arm: translate & rotate M3= M2 T(l3)R( 3) (iv) end-effector: translate & rotate M4= M3T(l4)R( 4) Base OpenGL: display(){ draw_base() Rotatef( 1,0,0,1); draw_upperarm(); Translatef(0,l2,0); Rotatef( 2,0,0,1); draw_lowerarm(); } Upper-arm Lower-arm End-effector
This example demonstrates an explicit hierarchy hard-coded in display function hierarchy cannot be changed (inflexible) Object-oriented hierarchical tree data structure Each node object store (1) Transformation of object M (2) Pointer to function to draw object (3) Pointers to children
OpenGL pseudo code for single chain tree display(){ draw_arm(root); /* single call to recursive function */ } draw_arm(node){ Transform(node.M); /* apply model transform */ node.draw(); /* draw this part */ draw_arm(node.child); /* recursive call to children */ }
Example: Torso This figure consists of a torso and connected part, each arm and leg has two parts, but each arm and leg depend on the location & orientation of the torso, but not each other. Lets assume we can build the individual parts head(), torso(), left_upper_arm() etc. Each part can be located w.r.t its parent by a translation and one or more rotations.
Example: Torso The display callback must traverse the tree i.e. visit every node, drawing the object for that node, using the correct model-view matrix A standard pre-order traversal (that travels down the left of the tree, visiting each node) is used
First draw torso. It only has one angle associated with it that allows it to rotate about y. Then we go to the head, however note we have to come back up to the torso to get to the arms and legs Any matrix that we apply to draw the head is not required for the arms or legs. Rather than recomputed the matrix that we apply to the torso node we can save it on the stack with a PushMatrix(). We can then go to the node for the head, changing the model-view matrix as necessary to draw the head. When we come back up to the torso node, we recover the model-view matrix with a PopMatrix() We have to come back up the torso after dealing with the left arm so we must go to a PushMatrix() immediately after the pop to keep a copy of the same model-view matrix
Simple! Although it appears convoluting, the rule is simple every time we go to the left at a node with another unvisited right child we do a push; every time we return to the node we do a pop. Note we must do a pop at the end so the total number of pushes and pops is the same
Rotatef(theta[0], 0.0, 1.0, 0.0); DrawTorso(); PushMatrix(); //save current model-view matrix Translatef(0.0, TORSO_HEIGHT+0.5*HEAD_HEIGHT, 0.0); Rotatef(theta[1], 1.0, 0.0, 0.0); Rotatef(theta[2], 0.0, 1.0, 0.0); Translatef(0.0, -0.5*HEAD_HEIGHT, 0.0); DrawHead(); PopMatrix(); //we have drawn the head so go back up to torso PushMatrix(); //but now want to draw left arm so save the torso matrix again Translatef(-(TORSO_RADIUS+UPPER_ARM_RADIUS), 0.9*TORSO_HEIGHT, 0.0); Rotatef(theta[3], 1.0, 0.0, 0.0); DrawLeft_upper_arm(); Translatef(0.0, UPPER_ARM_HEIGHT, 0.0); Rotatef(theta[4], 1.0, 0.0, 0.0); DrawLeft_lower_arm();
PopMatrix(); //left arm done, go back up to torso PushMatrix(); //but we are going to draw the right arm so save the torso matrix again Translatef(TORSO_RADIUS+UPPER_ARM_RADIUS, 0.9*TORSO_HEIGHT, 0.0); Rotatef(theta[5], 1.0, 0.0, 0.0); DrawRight_upper_arm(); Translatef(0.0, UPPER_ARM_HEIGHT, 0.0); Rotatef(theta[6], 1.0, 0.0, 0.0); DrawRight_lower_arm(); PopMatrix(); //back up to torso PushMatrix(); //save it we are going to draw the left leg Translatef(-(TORSO_RADIUS+UPPER_LEG_RADIUS), 0.1*UPPER_LEG_HEIGHT, 0.0); Rotatef(theta[7], 1.0, 0.0, 0.0); DrawLeft_upper_leg(); Translatef(0.0, UPPER_LEG_HEIGHT, 0.0); Rotatef(theta[8], 1.0, 0.0, 0.0); DrawLeft_lower_leg();
PopMatrix(); //back to torso PushMatrix(); //save it as we are going to draw right leg Translatef(TORSO_RADIUS+UPPER_LEG_RADIUS, 0.1*UPPER_LEG_HEIGHT, 0.0); Rotatef(theta[9], 1.0, 0.0, 0.0); DrawRight_upper_leg(); Translatef(0.0, UPPER_LEG_HEIGHT, 0.0); Rotatef(theta[10], 1.0, 0.0, 0.0); Right_lower_leg(); PopMatrix(); //pop so that the total number of pushes = total number of pops!
Example: Skeleton Represent transformation matrices between each parent and child each matrix is the transformation of the object in local coordinates into the parents coordinates How do we traverse the tree to draw the figure? Any order i.e. depth-first, breadth-first 2 methods to implement traversal: (1) Stack based - use matrix stack to store required matrices (2) Recursive - store matrix within nodes of data structure
(1) Stack-based tree traversal draw_figure(){ PushMatrix(); /* torso transform */ draw_torso(); Translatef( ); /* transform of head relative to torso */ Rotatef(...); draw_head(); PopMatrix(); /* restore torso transform */ PushMatrix(); Translate(); /* left_arm */ Rotate(); draw_upperarm(); Translate(); Rotate(); draw_lowerarm(); PopMatrix(); /* restore torso transform */ PushMatrix(); Translate(); /* right arm */ ... } use matrix stack to store intermediate matrices current ModelView matrix M determines position of figure in scene
Can also use Push/Pop values from attribute stack i.e. color etc. PushAttrib(); PopAttrib(); Limitation of stack-based approach: explicit representation of tree in single function relies on application programmer to push/pop matrices hard-coded/inflexible source code must be changed for different hierarchical structure no clear distinction between building a model and rendering it
(2) Recursive tree data-structures typedef struct treenode { Glfloat m[16]; void (*draw)(); int nchild; struct treenode *children; } treenode; each node is a recursive structure with pointers to children use a standard tree structure to represent hierarchy render via tree traversal algorithm (independent of model) void draw_tree(treenode *node){ PushMatrix(); /* save transform*/ MultMatrixf(node->m); node->draw(); for (i=0;i<node->nchild;i++) .. draw_tree(node->children[i]); PopMatrix(); /* restore transform */ }