Building and interacting with a 3D world

Building and interacting with a 3D world Computer Graphics COMP 770 (236) Spring 2007 Instructor: Brandon Lloyd 1/31/07 1 From last time… ■ Render...
Author: Patience Park
0 downloads 1 Views 696KB Size
Building and interacting with a 3D world Computer Graphics COMP 770 (236) Spring 2007 Instructor: Brandon Lloyd

1/31/07

1

From last time… ■ Rendering pipeline ■ Vector multiplication ° Dot product, cross product, tensor product

■ Modeling transformations ° Translation, rotation, scale

■ Viewing transformation ■ Projections ° Orthographic, perspective

1/31/07

2

Outline ■ Representing 3D objects with a mesh ■ Loading objects in the OBJ file format ■ Interaction ° pan, dolly, trackball

■ Picking and selection ■ Transformation hierarchies ■ Assignment #1

1/31/07

3

Primitive 3D ■ How do we specify 3D objects? ° Simple mathematical functions, z = f(x,y) ° Parametric functions, (x(u,v), y(u,v), z(u,v)) ° Implicit functions, f(x,y,z) = 0

■ Build up from simple primitives ° Point – nothing really to see ° Lines – nearly see through ° Planes – a surface

1/31/07

4

Simple planes ■ Surfaces modeled as connected planar facets ° N (>3) vertices, each with 3 coordinates ° Minimally a triangle

1/31/07

5

Specifying a face ■ Face or facet Face [v0.x, v0.y, v0.z] [v1.x, v1.y, v1.z] [v2.x, v2.y, v2.z] … [vN.x, vN.y, vN.z]

■ Sharing vertices via indirection Vertex[0] = [v0.x, v0.y, v0.z] Vertex[1] = [v1.x, v1.y, v1.z] Vertex[2] = [v2.x, v2.y, v2.z] : Vertex[N] = [vN.x, vN.y, vN.z] Face v0, v1, v2, … vN

1/31/07

v0

v3 v1

v2

6

Vertex specification ■ Where ° Geometric coordinates [x, y, z]

■ Attributes ° Color values [r, g, b] ° Texture Coordinates [u, v]

■ Orientation ° Inside vs. Outside ° Encoded implicitly in ordering

■ Geometry Nearby ° Often we’d like to “fake” a more complex shape than our true faceted (piecewise-planar) model ° Required for lighting and shading in OpenGL

1/31/07

7

Smoothing ■ Normals ° First-Order Taylor-series approximation of surface ° Normals provide derivative information ° A unit-vector perpendicular to the actual surface a the specified vertex ° 3 coordinates – 2 degrees of freedom [nx, ny, nz] ° Normalized:

nˆ =

1/31/07

[nx ,ny ,nz ] n2x +n2y +n2z

8

Drawing faces in OpenGL glBegin(GL_POLYGON); foreach (Vertex v in Face) { glColor4d(v.red, v.green, v.blue, v.alpha); glNormal3d(v.norm.x, v.norm.y, v.norm.z); glTexCoord2d(v.texture.u, v.texture.v); glVertex3d(v.x, v.y, v.z); } glEnd();

■ Heavy-weight model ° attributes specified for every vertex

■ Redundant ° vertex positions often shared by at least 3 faces ° “vertex” attributes are often face attributes (e.g. face normal) 1/31/07

9

Aside: Dual graph ■ Why do I say that a vertex is generally shared by 3 or more faces? ■ Constructing a “dual” graph representation of our mesh. ° Replace each face with a point ° Insert an edge between every pair of faces that share an edge ° Where are vertices in this “dual” representation?

f0

f1

f4 f2

f3

■ In what cases are there fewer than 3 faces sharing a vertex? 1/31/07

10

Decoupling vertex and face attributes ■ Case for: ° Most of the time vertices will be consistent

■ Exceptions: ° Regions where the surface changes materials ° Regions of high curvature (a crease)

■ This is possible with ‘Heavyweight’ vertices, but less efficient

1/31/07

11

Polygon soup ■ A collection of ° Vertices ° Normals ° Vertex colors & Texture coordinates

■ Connected by faces

Vertices Faces

Normals

Colors & Texture

1/31/07

12

3D File formats ■ Typically textbooks invent something ■ MAX – Studio Max ■ DXF – AutoCAD ° supports 2-D and 3-D; binary

■ 3DS – 3D studio ° flexible; binary

■ VRML – Virtual reality modelling language ° ASCII – Human readable (and writeable)

■ OBJ – Wavefront OBJ format ° ASCII ° Extremely simple ° widely supported 1/31/07

13

OBJ Basics ■ The most common Wavefront obj file tokens are listed below.

# some text Rest of line is a comment

v float float float A single vertex’s geometric position in space.

vn float float float A normal.

vt float float A texture coordinate.

1/31/07

14

OBJ face varieties f int int int ...

(vertex only)

or

f int/int int/int int/int . . .

(vertex & texture)

or

f int/int/int int/int/int int/int/int …

(vertex, texture, & normal)

or

f int//int int//int int//int …

(vertex & normal)

■ The arguments are 1-based indices into the arrays ° vertex positions ° texture coordinates ° and normals, respectively.

■ One set of indices for each vertex 1/31/07

15

OBJ extras g string group specification for the following faces. The string is the name of the group

s int smoothing group ID for the following faces. Faces in the same smoothing group share vertex normals. Used if normals must be estimated.

1/31/07

16

Obj Example ■ Vertices followed by faces ° Faces reference previous vertices by integer index ° 1-based ° Co-planarity of vertices is assumed

1/31/07

# v v v v v v v v f f f f f f

A simple cube 1 1 1 1 1 -1 1 -1 1 1 -1 -1 -1 1 1 -1 1 -1 -1 -1 1 -1 -1 -1 1 3 4 2 5 6 8 7 1 2 6 5 3 7 8 4 1 5 7 3 2 4 8 6

17

OBJ sources ■ Avalon – Viewpoint (http://avalon.viewpoint.com/) old standards ■ 3D Café – (http://www.3dcafe.com/asp/meshes.asp) Nice thumbnail index ■ Others ■ Most modeling programs will export .OBJ files ■ Most rendering packages will read in .OBJ files

1/31/07

18

Code: Vertex class Vertex: def __init__(self, *args): if (type(args[0]) is list): self.x, self.y, self.z = args[0] else: self.x, self.y, self.z = args def __sub__(self, v): return Vertex(self.x - v.x, self.y - v.y, self.z - v.z)

1/31/07

19

Code: Normal class Normal: def __init__(self, *args): if (type(args[0]) is list): nx, ny, nz = args[0] else: nx, ny, nz = args l = math.sqrt(nx*nx + ny*ny + nz*nz) if (l != 0): l = 1/l self.nx, self.ny, self.nz = nx*l, ny*l, nz*l

1/31/07

20

Code: TexCoord class TexCoord: def __init__(self, *args): if (type(args[0]) is list): self.tu, self.tv = args[0] else: self.tu, self.tv = args

1/31/07

21

Code: Face class Face: def __init__(self): self.vList = [] def add(self, vertIndex, normIndex = 0, texIndex = 0): self.vList.append((vertIndex-1, normIndex-1, texIndex-1)) def vertices(self): return len(vList)

1/31/07

22

Code: WaveFrontOBJ class WaveFrontOBJ: def __init__(self): self.v = [] self.n = [] self.t = [] self.f = [] self.extents = None self.isFlat = 0 self.mode = GL_POLYGON

1/31/07

The soup bowl

23

WaveFrontOBJ parser def readfile(self, filename): for line in open(filename).xreadlines(): line = line[0:line.find('#')] # strip off comments and return token = line.split() # break up lines into tokens if (len(token) == 0): continue if (token[0] == 'v'): # vertex self.v.append(Vertex([float(val) for val in token[1:]])) elif (token[0] == 'vn'): # vertex normal self.n.append(Normal([float(val) for val in token[1:]])) elif (token[0] == 'vt'): # texture coordinate self.t.append(TexCoord([float(val) for val in token[1:]])) elif (token[0] == 'f'): # face

See next slide… elif (token[0] == 'g'): None elif (token[0] == 's'): None elif (token[0] == ''): None else: print line self.findExtents() 1/31/07

# group # smoothing group # blank line

24

WaveFrontOBJ parser (cont.) elif (token[0] == 'f'): face = Face() for i in token[1:]: idxList = i.split('/') if (len(idxList) == 1): face.add(int(idxList[0])) elif (len(idxList) == 2): face.add(int(idxList[0]), texIndex = int(idxList[1])) elif (len(idxList) == 3): if (idxList[1] == ''): face.add(int(idxList[0]), normIndex = int(idxList[2])) else: face.add(int(idxList[0]), int(idxList[1]), int(idxList[2])) self.f.append(face)

Face parsing code

1/31/07

25

WaveFrontOBJ Draw() def draw(self): for face in self.f: vNum = 0 glBegin(self.mode) for vertData in face.vList: vIndex, nIndex, tIndex = vertData if (nIndex >= 0) and (not self.isFlat): norm = self.n[nIndex] glNormal3d(norm.nx, norm.ny, norm.nz) elif (vNum == 0): norm = self.normal(face) #compute face normal glNormal3d(norm.nx, norm.ny, norm.nz) if (tIndex >= 0): texel = self.t[tIndex] glTexCoord2d(texel.tu, texel.tv) vert = self.v[vIndex] glVertex3d(vert.x, vert.y, vert.z) vNum += 1 glEnd() 1/31/07

26

Picking and selection ■ Basic idea: Identify objects selected by the user ° Click-selection: select one object at a time ° Sweep-selection: select objects within a bounding rectangle

■ Several ways to implement selection: ° ° ° °

1/31/07

Find screen space bounding boxes contained in pick region Compute pick ray. Ray trace to find intersections OpenGL selection buffers Render to back buffer using colors that encode object IDs. Return ID under pick point

27

OpenGL selection buffers ■ Hit record generated for each primitive that falls within the view volume ■ Hit records returned in selection buffer ■ Hit record reports the contents of name stack when primitive was rendered ° Good for hierarchical models, e.g. Car 1, Wheel 3, Bolt 7

■ Basic set-up: ° ° ° ° °

1/31/07

Set up selection buffer Set rendering mode to selection Set up picking view volume with gluPickMatrix() Render the scene, assigning names to primitives Examine hit records to determine selection

28

OpenGL selection buffers ■ Setup selection buffer glSelectBuffer(bufferSize, intArray)

■ Setup selection render mode (default = GL_RENDER) glRenderMode(GL_SELECT)

■ Setup pick region glMatrixMode(GL_PROJECTION) glPushMatrix() glGetFloatv(GL_PROJECTION_MATRIX, projection) glGetIntegerv(GL_VIEWPORT, viewport) glLoadIdentity() gluPickMatrix(x, height - y, width, height, viewport) glMultMatrixd(projection) glMatrixMode(GL_MODELVIEW)

■ Initialize name stack glInitNames() 1/31/07

29

Rendering to selection buffer def display(): setCamera() glLoadName(1) drawFloor() glLoadName(2) drawCow() glLoadName(3) drawBeethoven() glFlush()

ƒ Hit records appear in the selection buffer in the order they are drawn, regardless of whether they are visible (assuming they fall in the pick window) ƒ Name stack to support hierarchies: glPushName(intVal) glPopName() glLoadName(intVal)

Hit record format: NameCount depth1 depth2 Name1 Name2 Name3… 1/31/07

30

Selection with the back buffer ■ Selects only objects that are visible ■ Render objects to back buffer with color that encodes ID ■ Use glReadPixels() to read the pixel at the pick point ■ Back buffer is never seen

1/31/07

31

Encoding object IDs def munge(x): r, g, b = 0, 0, 0 for i in xrange(8): r = (r >= 1 g = (g >= 1 b = (b >= 1 r = r/float(255) g = g/float(255) b = b/float(255) return (r, g, b)

def unmunge(r, g, b): x = 0 for i in xrange(8): x = (x >= 1 x = (x >= 1 x = (x >= 1 return x

if (selectMode): glDisable(GL_LIGHTING) r, g, b = munge(objID) glColor3d(r, g, b) else: glColor3d( 1, 0.2, 0.4 ) 1/31/07

Object rendering code fragment

32

Encoding object IDs ■ A simpler, faster, but less colorful munger def munge(x): r = (x & 255)/float(255) g = ((x >> 8) & 255)/float(255) b = ((x >> 16) & 255)/float(255) return (r, g, b) def unmunge(r, g, b): return (r + (g