COMPSCI 373 S1 C - Assignment 2 Sample Solution

Similar documents
COMPSCI 372 S2 C - Assignment 2 Due Date: Monday 22nd September 2008, 9.30pm

Books, OpenGL, GLUT, GLUI, CUDA, OpenCL, OpenCV, PointClouds, and G3D

Books, OpenGL, GLUT, CUDA, OpenCL, OpenCV, PointClouds, G3D, and Qt

// double buffering and RGB glutinitdisplaymode(glut_double GLUT_RGBA); // your own initializations

To Do. Computer Graphics (Fall 2008) Course Outline. Course Outline. Methodology for Lecture. Demo: Surreal (HW 3)

FAKULTI TEKNOLOGI MAKLUMAT DAN KOMUNIKASI BITM INTERACTIVE COMPUTER GRAPHICS LAB SESSION 4. C++ - OpenGL

C OMPUTER G RAPHICS Thursday

Basic Graphics Programming

Precept 2 Aleksey Boyko February 18, 2011

Lectures OpenGL Introduction

QUESTION 1 [10] 2 COS340-A October/November 2009

C++ is Fun Part 13 at Turbine/Warner Bros.! Russell Hanson

Graphics Programming

1 /* 4 C:\opencv\build\include. 6 C:\opencv\build\x86\vc10\lib

Duc Nguyen CSE 420 Computer Graphics 10/10/2018 Homework 1

UNIT 7 LIGHTING AND SHADING. 1. Explain phong lighting model. Indicate the advantages and disadvantages. (Jun2012) 10M

Lecture 5: Viewing. CSE Computer Graphics (Fall 2010)

CS559: Computer Graphics. Lecture 12: OpenGL Li Zhang Spring 2008

Hierarchical Modeling: Tree of Transformations, Display Lists and Functions, Matrix and Attribute Stacks,


Introduction to OpenGL

Graphics Programming. August 31, Programming of the Sierpinski gasket. Programming with OpenGL and C/C++

Lectures Transparency Case Study

Modeling with Transformations

Introduction to Computer Graphics with OpenGL/GLUT

OpenGL/GLUT Intro. Week 1, Fri Jan 12

Computer graphics Labs: OpenGL (1/3) Geometric transformations and projections

Computer graphics MN1

CS559: Computer Graphics. Lecture 12: OpenGL Transformation Li Zhang Spring 2008

Computer graphics MN1

Modeling Transform. Chapter 4 Geometric Transformations. Overview. Instancing. Specify transformation for objects 李同益

GL_COLOR_BUFFER_BIT, GL_PROJECTION, GL_MODELVIEW

API for creating a display window and using keyboard/mouse interations. See RayWindow.cpp to see how these are used for Assignment3

Basic Graphics Programming

Computer Graphics. Transformations. CSC 470 Computer Graphics 1

Computer graphics MN1

Source code: #include <iostream>

Assignment 1. Simple Graphics program using OpenGL

COMPUTER GRAPHICS LAB # 3

Lecture 4 of 41. Lab 1a: OpenGL Basics

11/1/13. Basic Graphics Programming. Teaching Assistant. What is OpenGL. Course Producer. Where is OpenGL used. Graphics library (API)

Drawing Primitives. OpenGL basics

Assignment #6 2D Vector Field Visualization Arrow Plot and LIC

Introduction to OpenGL

1 (Practice 1) Introduction to OpenGL

Using OpenGL with CUDA

6. Make use of glviewport() to display two sine curves on the same screen, one on the

Lecture 3. Understanding of OPenGL programming

Display Lists in OpenGL

3D computer graphics: geometric modeling of objects in the computer and rendering them

Outline. Other Graphics Technology. OpenGL Background and History. Platform Specifics. The Drawing Process

Graphics and Computation Introduction to OpenGL

DONALD HOUSE: CPSC 8170, FALL 2018 DESIGN OF A REAL-TIME ANIMATION PROGRAM

20 GLuint objects; 36 Scale += 0.1; 37 break; 38 case GLUT_KEY_DOWN:

Announcements OpenGL. Computer Graphics. Spring CS4815

Announcements OpenGL. Computer Graphics. Autumn 2009 CS4815

2 Transformations and Homogeneous Coordinates

Programming using OpenGL: A first Introduction

CMSC 425: Lecture 4 More about OpenGL and GLUT Tuesday, Feb 5, 2013

Introduction to MS Visual C/C++

Methodology for Lecture

Grafica Computazionale

BOUNCING BALL IMRAN IHSAN ASSISTANT PROFESSOR

IntMu.Lab5. Download all the files available from

Scientific Visualization Basics

Cameras (and eye) Ideal Pinhole. Real Pinhole. Real + lens. Depth of field

OpenGL refresher. Advanced Computer Graphics 2012

Modeling Objects by Polygonal Approximations. Linear and Affine Transformations (Maps)

Announcement. Homework 1 has been posted in dropbox and course website. Due: 1:15 pm, Monday, September 12

Interaction. CSCI 480 Computer Graphics Lecture 3

Ulf Assarsson Department of Computer Engineering Chalmers University of Technology

Graphics Pipeline & APIs

Fundamental Types of Viewing

Interaction Computer Graphics I Lecture 3

OpenGL Tutorial. Ceng 477 Introduction to Computer Graphics

Lectures Display List

ERKELEY DAVIS IRVINE LOS ANGELES RIVERSIDE SAN DIEGO SAN FRANCISCO EECS 104. Fundamentals of Computer Graphics. OpenGL

Computer Graphics Course 2005

Visualizing Molecular Dynamics

Computer Graphics, Chapt 08

OpenGL Transformations

Programming with OpenGL Part 2: Complete Programs Computer Graphics I, Fall

CSC 240 Computer Graphics. Fall 2015 Smith College

Graphics Pipeline & APIs

Computer Graphics. OpenGL

Computer Graphics (Basic OpenGL)

CS418 OpenGL & GLUT Programming Tutorial (I) Presented by : Wei-Wen Feng 1/30/2008

Lecture 2 2D transformations Introduction to OpenGL

OpenGL: Open Graphics Library. Introduction to OpenGL Part II. How do I render a geometric primitive? What is OpenGL

Ulf Assarsson Department of Computer Engineering Chalmers University of Technology

3.2 Hierarchical Modeling

Lecture 2 CISC440/640 Spring Department of Computer and Information Science

Comp 410/510 Computer Graphics Spring Programming with OpenGL Part 2: First Program

1.2 Basic Graphics Programming

by modifying the glutinitwindowsize() function you can change the screen size to whatever you please.

Order of Transformations

Exercise 1 Introduction to OpenGL

Introduction to OpenGL: Part 2

Graphics Hardware and OpenGL

Computer Graphics. Chapter 10 Three-Dimensional Viewing

Transcription:

COMPSCI 373 S1 C Assignment 2 Sample Solution 1 of 17 Computer Science COMPSCI 373 S1 C - Assignment 2 Sample Solution This assignment is worth 8.3333% of your final grade. 1. 3D Modelling and Texture Mapping with OpenGL (29 Marks) In this question you will create a little game containing a plane with grass, 20 flowers, Easter eggs falling from the sky, a robot with a basket at the end of its arm and a reflecting mirror. The objective of the game is to catch the Easter eggs with the basket. If you hit an egg with the rim of the basket or if you miss it the egg gets smashed [please feel free to extend the game and add appropriate sound effects ;-) ]. The game finishes after 50 eggs have been smashed. Download the file Ass2.zip which contains a.net solution including all necessary source files. Unzip the files and compile and run them. You should see a grass covered plane, 20 flowers, a non-reflecting mirror, and Easter eggs falling from the sky. NOTE: In order to make debugging easier and improve the understanding of texture maps, all texture images are stored in Portable Pixel Map format (ppm). This format is very inefficient to load and your program might take a long time to load when run in DEBUG mode. You can avoid this by compiling your program in RELEASE mode. (a) [2 marks] When playing the finished game you will find that it is hard to catch eggs since it is difficult to perceive the exact 3D position of an egg and where it will land. Implement the method drawpath() in the CEgg class, which draws a line from the centre of each egg to the ground. A line is coloured blue (0.3, 0.3, 1.0) if the distance to the ground is larger than 3 units, orange (1.0, 0.5, 0.3) if the distance is between 1.5 and 3 units and yellow (1.0, 1.0, 0.3) otherwise. Visibility of the lines is toggled by pressing the h key (already implemented). After completing this exercise your programs should look similar to the screen shot on the right: Solution: The path is defined as a simple line segment orthogonal to the ground (xz)-plane and hence is given by the position of an egg and its position with the y-coordinate set to 0. void CEgg::drawPath() if (position[1]>3) glcolor3f(0.3f, 0.3f, 1.0f); else if (position[1]>1.5) glcolor3f(1.0f, 0.5f, 0.3f); else glcolor3f(1.0f, 1.0f, 0.3f); glbegin(gl_lines); glvertex3fv(position); glvertex3f(position[0],0,position[2]); glend();

COMPSCI 373 S1 C Assignment 2 Sample Solution 2 of 17 (b) [6 Marks] Define the robot shown in the image below. The robot has three arms with lengths armlength and at the tip of the 3 rd arm it has a torus which is always parallel to the ground plane. The outer radius of the torus is basketradius and its inner radius is rimradius. The bottom arm is perpendicular to the ground plane and centered at the origin. The whole robot is rotated by an angle φ (phi) around its centre axis. The second arm is rotated by the angle θ (theta) with respect to the bottom arm and the third arm is rotated by 2θ with respect to the second arm as illustrated in the figure below. At the end of this exercise your robot should look like in the picture below. Note that the keys a, s, w and z are used to modify the angles φ and θ, i.e. at the end of this exercise you should be able to move the robot using these keys. NOTE: For this exercise it is sufficient to use matrix operations (translation, rotation, scaling, push, pop) and two solid objects defined in GLUT. In order to make it easier for you to test your implementation I have defined a variable basketcentre which gives you the point at the centre of the torus. You can check your code by drawing this point and by verifying that it is indeed in the centre of the basket rim of the robot. Solution: It is easiest to construct the robot using a bottom-up approach. i.e. start with the bottom arm of the robot and implement the rotation around its axis. By defining this rotation first it will be applied to all subsequent components, which is exactly what we want. Next we implement the middle arm of the robot. Note that the scaling and translation only apply to this arm (i.e. we use glpushmatrix() and gl PopMatrix()), whereas the rotation applies to this arm and all components connected to it (i.e. the top arm and the basket). Finally we define the top arm and the basket connected to it.

COMPSCI 373 S1 C Assignment 2 Sample Solution 3 of 17 void CRobot::draw() glenable(gl_normalize); glmaterialfv(gl_front, GL_SPECULAR, mat_specular); glmaterialfv(gl_front, GL_SHININESS, no_shininess); glmaterialfv(gl_front, GL_AMBIENT_AND_DIFFUSE, mat_ambient_and_diffuse); // rotate the whole robot glpushmatrix(); glrotatef(phi, 0, 1, 0); // bottom robot arm glpushmatrix(); glscalef(bottomarmwidth, armlength, bottomarmwidth); gltranslatef(0, 0.5, 0); glutsolidcube(1.0); glpopmatrix(); // Transformations moving the middle arm and the top arm attached to it glpushmatrix(); gltranslatef(0, armlength, 0); glrotatef(theta, 0, 0, 1); // middle robot arm glpushmatrix(); glscalef(toparmwidth, armlength, toparmwidth); gltranslatef(0, 0.5, 0); glutsolidcube(1.0); glpopmatrix(); // Transformations moving the top arm and basket relative to the middle arm gltranslatef(0, armlength, 0); glrotatef(180-2*theta, 0, 0, 1); // top robot arm glpushmatrix(); glscalef(toparmwidth, armlength, toparmwidth); gltranslatef(0, 0.5, 0); glutsolidcube(1.0); glpopmatrix(); // ring and basket at the top of the arm gltranslatef(0, armlength, 0); glrotatef(-90+theta, 0, 0, 1); gltranslatef(0, basketradius, 0); glrotatef(-90,0,1,0); glutsolidtorus(rimradius, basketradius, 16, 16); _drawbasket(); glpopmatrix(); glpopmatrix(); gldisable(gl_normalize); glflush(); (c) [6 Marks] Complete the function _drawbasket() and add it to the draw method of the CRobot class so that it draws a basket defined as a surface of revolution. Please precompute the vertices of the basket s surface in the method _initbasketsurface of the CRobot class and draw the surface using quad strips as indicated in the figure below. Since we later on use texture mapping for the basket it s not necessary to define surface normals. You must define the surface of revolution yourself using GLUT commands or other geometry libraries gives automatically zero marks. The dimensions and number of vertices of the surface are defined in the code by constants explained in the illustration below.

COMPSCI 373 S1 C Assignment 2 Sample Solution 4 of 17 HINT: For this question it is easiest to use a quarter ellipsoid for the profile curve! Note that you will change the profile curve in part (h) of this question, so it is recommended to design your code accordingly. At the end of this step your scene should look similar to that in the image below. Solution: We use the formula in the lecture notes to create the basket by rotating a quarter ellipses around the z-axis, i.e. the axis of the resulting torus is aligned with the z-axis. This is practical because the torus defining the ring of the basket is also by default aligned with the z-axis. The surface of revolution is defined by creating first a profile curve c(t)=(x(t),0,z(t)) which is obtained by taking the equation of an ellipsoid with the major axis basketheight and the minor axis basketradius. Then x( t) basketradius *sin(0.5πt ) c ( t) = 0 = 0 t [0,1] ( ) *cos(0.5 ) z t basketheight πt The surface of revolution is obtained by rotating each point of the profile curve around the z-axis, i.e. ( basketradius *sin(0.5πt )) cos 2πs ( basketradius *sin(0.5πt )) sin 2πs s, [0,1] x( t)cos 2πs p ( s, t) = x( t)sin 2πs = t ( ) * cos(0.5 ) z t basketheight πt

COMPSCI 373 S1 C Assignment 2 Sample Solution 5 of 17 Note that we don t need the surface normals since later on we texture map this surface. The resulting code is: // initialise vertices basket int i,j; double z,r; for(i=0;i<num_longitudinal_vertices;i++) z=basketheight*cos(pi/2*((double) i/(num_longitudinal_vertices-1))); r=basketradius*sin(pi/2*((double) i/(num_longitudinal_vertices-1))); for(j=0;j<num_circumferentiall_vertices;j++) vertices[i][j][0]=(float) (cos(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][1]=(float) (sin(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][2]=(float) z; and the draw method is void CRobot::_drawBasket() glshademodel(gl_flat); int i,j; for(i=0;i<num_longitudinal_vertices-1;i++) glbegin(gl_quad_strip); for(j=0;j<num_circumferentiall_vertices;j++) glvertex3fv(vertices[i][j]); glvertex3fv(vertices[i+1][j]); glend(); glshademodel(gl_smooth); (d) [5 marks] Use texture mapping to draw the basket at the end of the robot arm. Complete the missing code so that it loads the texture map Net.pgm and maps it onto the basket. Note that the program contains already a routine to load a texture of type Portable Pixel Map (ppm) - a Portable Gray Map (pgm) is defined similarly but has only one byte per pixel (a greyscale value in the range 0 to 255). Note that the texture image has been constructed such that the pixels on its left and ride side are identical. Consequently you should map the horizontal direction of the image onto the circumferential direction of the basket s surface in order to get a continuous texture. At the end of this step your scene should look something like this:

COMPSCI 373 S1 C Assignment 2 Sample Solution 6 of 17 Solution: Change the initialization procedures so that suitable texture coordinates are computed. The texture coordinates map the texture around the basket similarly to wrapping a paper around a cylinder. Note that the texture image will get distorted towards the apex of the basket, i.e. the black lines in it converge, which is exactly what we want to have for a realistic basket. // initialise vertices basket int i,j; double z,r; for(i=0;i<num_longitudinal_vertices;i++) z=basketheight*cos(pi/2*((double) i/(num_longitudinal_vertices-1))); r=basketradius*sin(pi/2*((double) i/(num_longitudinal_vertices-1))); for(j=0;j<num_circumferentiall_vertices;j++) vertices[i][j][0]=(float) (cos(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][1]=(float) (sin(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][2]=(float) z; texture_coordinates[i][j][1]=(double) i/(num_longitudinal_vertices-1); texture_coordinates[i][j][0]=(double) j/(num_circumferentiall_vertices-1);

COMPSCI 373 S1 C Assignment 2 Sample Solution 7 of 17 Next we complete the function for the texture initialization. It s easiest to define the texture map in the RGBA format we discussed in the lecture, i.e. the gray scale values of the input image are used as red, green and blue components of the texture image. void CRobot::_initTexture() // load net texture ifstream texturefile; char* filename="net.pgm"; texturefile.open(filename, ios::in); if (texturefile.fail()) displaymessage(error_message, "CRobot()::CRobot(): could not open file %s",filename); skipline(texturefile); texturefile >> texturewidth; texturefile >> textureheight; int numrgbvalues; texturefile >> numrgbvalues; texture = new GLubyte[textureWidth*textureHeight*4]; int k,l,c; for(k=0;k<textureheight;k++) for(l=0;l<texturewidth;l++) texturefile >> c; texture[(k*texturewidth+l)*4]=(glubyte) c; texture[(k*texturewidth+l)*4+1]=(glubyte) c; texture[(k*texturewidth+l)*4+2]=(glubyte) c; texture[(k*texturewidth+l)*4+3]=(glubyte) 255; texturefile.close(); glbindtexture(gl_texture_2d, texname); gltexparameteri(gl_texture_2d, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri(gl_texture_2d, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glteximage2d(gl_texture_2d, 0, GL_RGBA, texturewidth, textureheight, 0, GL_RGBA, GL_UNSIGNED_BYTE, texture); delete[] texture; In order to draw the basket we must define the appropriate texture coordinates for each vertex in the draw() method: glenable(gl_texture_2d); glbindtexture(gl_texture_2d, texname); gltexenvf(gl_texture_env, GL_TEXTURE_ENV_MODE, GL_REPLACE); int i,j; for(i=0;i<num_longitudinal_vertices-1;i++) glbegin(gl_quad_strip); for(j=0;j<num_circumferentiall_vertices;j++) gltexcoord2fv(texture_coordinates[i][j]); glvertex3fv(vertices[i][j]); gltexcoord2fv(texture_coordinates[i+1][j]); glvertex3fv(vertices[i+1][j]); glend();

COMPSCI 373 S1 C Assignment 2 Sample Solution 8 of 17 (e) [3 marks] Modify your code such that white pixels in the texture image become transparent. In order to do this you have to set the fourth coordinate (the alpha value ) of all white pixels to 0. You also have to enable blending and alpha testing which is done by inserting the lines glenable(gl_blend) glblendfunc(gl_src_alpha, GL_ONE_MINUS_SRC_ALPHA); glalphafunc(gl_greater, 0.9f); glenable(gl_alpha_test); before performing the texture mapping. You don t need to understand blending and alpha testing, but if you are interested feel free to read more about it in the OpenGL programming guide. Don t forget to disable blending and alpha testing at the end of the draw method. After implementing this step your scene should look something like this: like this: Solution: Change the line assigning the transparency value during texture loading to if (c==255) texture[(k*texturewidth+l)*4+3]=(glubyte) 0; else texture[(k*texturewidth+l)*4+3]=(glubyte) 255; Before drawing the texture add glshademodel(gl_flat); glenable(gl_blend); glblendfunc(gl_src_alpha, GL_ONE_MINUS_SRC_ALPHA); glalphafunc(gl_greater, 0.9f); glenable(gl_alpha_test); and after drawing the texture add gldisable(gl_alpha_test); gldisable(gl_blend); gldisable(gl_texture_2d); glshademodel(gl_smooth);

COMPSCI 373 S1 C Assignment 2 Sample Solution 9 of 17 (f) [3 marks] Modify your code so that you can zoom in and out of the scene using the +/= and -/_ keys, respectively. In order to do that you have to change one parameter of the gluperspective function and you have to set the projection matrix whenever you change that parameter. After implementing this step you should be able to obtain pictures like this: Solution: We first add the keys for zooming to the event handling routine. A zoom is achieved by changing the field-of-view (fov). A smaller fov value has the effect that the view frustum is narrower. i.e. smaller part of the scene is projected onto the view plane and the objects in that region appear enlarged. It makes sense to limit the fov value in order to avoid unnatural distortions. void handlekeypress(unsigned char key, int x, int y) // keep the method unchanged and add the two lines below if ((key=='+') (key=='=')) if (fov>1) fov/=1.05; _initview(); glutpostredisplay(); if ((key=='-') (key=='_')) if (fov<70) fov*=1.05; _initview(); glutpostredisplay(); (g) [4 marks] Modify the _initbasketsurface method of the CRobot class so that the profile curve is a Hermite curve defined by: p1 = p(0) = (basketheight, 0) // start point p4 = p(1) = (0, basketradius) // end point r1 = p'(0) = (0, 2*basketRadius) // tangent at start point r4 = p'(0) = (2*basketHeight, 0) // tangent at end point After this change the basket will look similar to the results of part (b) as illustrated below:

COMPSCI 373 S1 C Assignment 2 Sample Solution 10 of 17 Change the code so that the end tangent is rotated by the angle basketshapeangle, i.e. the new end tangent is ( -2*basketHeight*cos(2*Pi*basketShapeAngle/360.0), -2*basketHeight*sin(2*Pi*basketShapeAngle/360.0) ) Modify the event handling so that pressing the r key increases the angle basketshapeangle by one. After this change pressing the r key will allow you to rotate the end tangent around the end point resulting in the shapes illustrated below: Solution: We define a 2D profile curve (x(t), z(t)) using a Hermite curve define by the given points and tangents: x( t) c( t) = = H1(t) p1 + H 2 (t) p 4 + H 3 (t) v1 + H 4 (t) v 4 z( t) basketheight 0 = H1(t) + H 2 (t) + H 0 basketradius - 2 basketheight cos + H 4 (t) - 2 basketheight sin ( 2π basketshapeangle/360) ( 2π basketshapeangle/360) 3 0 (t) 2 basketradius t [0,1] where H 1 (t),, H 4 (t) are the Hermite basis functions (see lecture notes). Since the parameter space is the same as for the quarter ellipsoid we can define the surface-of-revolution by replacing x(t) and z(t) of the quarter ellipsoid with the values from the above Hermite curve:

COMPSCI 373 S1 C Assignment 2 Sample Solution 11 of 17 // Hermite basis functions defined at the start of Robot.cpp double H0(const double t)return t*t*(2*t-3)+1; double H1(const double t)return t*t*(3-2*t); double H2(const double t)return t*t*(t-2)+t; double H3(const double t)return t*t*(t-1); // Hermite curve representing profile curve of the basket CVec2df CRobot::_basketSilhouetteHermiteCurve(double t) CVec2df p1(basketheight, 0); CVec2df p4(0, basketradius); CVec2df r1(0, 2*basketRadius); CVec2df r4(-2*basketheight*cos(2*pi*basketshapeangle/360.0), - 2*basketHeight*sin(2*Pi*basketShapeAngle/360.0)); return H0(t)*p1+H1(t)*p4+H2(t)*r1+H3(t)*r4; // Computation of the surface of revolution // Note that only the lines for computing z and r are changed void CRobot::_initBasketSurface() // initialise vertices basket int i,j; double z,r; for(i=0;i<num_longitudinal_vertices;i++) CVec2df p=_basketsilhouettehermitecurve((double) z=p[0]; r=p[1]; for(j=0;j<num_circumferentiall_vertices;j++) vertices[i][j][0]=(float) (cos(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][1]=(float) (sin(2*pi*j/(double) (NUM_CIRCUMFERENTIALL_VERTICES-1))*r); vertices[i][j][2]=(float) z; texture_coordinates[i][j][1]=(double) i/(num_longitudinal_vertices-1); texture_coordinates[i][j][0]=(double) j/(num_circumferentiall_vertices-1); Now we only need to add a routine for event handling: // In ass1main.cpp void handlekeypress(unsigned char key, int x, int y) // keep the method unchanged and add the line below if ((key=='r') (key=='r')) robot.updatebasketshape(); // In Robot.cpp (add the function prototype to Robot.h) void CRobot::updateBasketShape() basketshapeangle+=1.0; if (basketshapeangle>360) basketshapeangle-=360.0; _initbasketsurface(); glutpostredisplay();

COMPSCI 373 S1 C Assignment 2 Sample Solution 12 of 17 2. Bezier Surfaces (16 Marks) The famous Utah teapot (rendered by the GLUT function glutsolidteapot) is defined by Bezier surfaces [http://en.wikipedia.org/wiki/utah_teapot]. In this exercise you will model and render the lid of the teapot. One quarter section of the lid is defined by 2 Bezier patches with 4x4 control points. The complete lid can be defined rendering the same patch 4 times with appropriate rotation commands. The control points and Bezier patches of one quarter of the teapot are defined in the file teapot_lid.txt. The surface consists of two Bezier patches. Each patch has 4x4 control points. However, only 22 different control points are required since the patches share control points at the common boundary and since some control points are repeated (e.g. at the tip). (a) [5 Marks] render the two cubic Bezier surfaces using a wireframe. You can write the program from scratch or modify the example program ParametricSurfaceNET from the course web page (recommended). Change the program that way that the default number of subdivision for rendering the surface is 4 (i.e. 4x4 polygons for each surface patch) and that you can increase/decrease the number of subdivisions using + / - keys. Below are some screenshots with 1,4 and 20 subdivisions and the control points rendered in blue. You don t need to render the control points, but it might make debugging easier (you might want to toggle the display of control points). Remember that Bezier surfaces and surfaces of revolution are both parametric surface and you can use the same code to display them once you have the formula for p(s,t). (b) [7 marks] Extend the program such that the surface is rendered using Gouraud shaded polygons. In order to do this you must compute vertex normals (see lecture notes chapter 6).. Note that the first control point of the first patch is repeated four times and hence the resulting mesh polygons are triangles and one of the partial derivatives is zero. Therefore you have to treat this case separately and use instead the normal (0,0,1). Extend the program such that the user can toggle between wireframe and surface representation using the l and s keys. The resulting surface (with 10 subdivisions) should look as follows.

COMPSCI 373 S1 C Assignment 2 Sample Solution 13 of 17 (c) [3 marks] Rendering the mesh by computing the polygon vertices in the display method is extremely slow. A much faster way is to use so-called display lists. Each time you change the object you want to display you define all OpenGL commands for rendering it into a display list. When you then display this object you just call the resulting display list, which executes the corresponding OpenGl commands without having to execute again all the equations necessary for computing the points and normals you display. In order to use display lists you have to change your program as follows: 1. Add a new variable displaylistid of type GLuint to the program. 2. In the initialization define displaylistid=glgenlists(1); which generates a unique ID for that display list. 3. Write a new method generatedisplaylist() which looks as follows void generatedisplaylist() glnewlist(displaylistid, GL_COMPILE); // render the Bezier patches, i.e. compute vertices // and normals for surface points and display them glendlist(); 4. Call this method once before you display the object for the first time (e.g. in the init() method) and call it each time you change the definition of the object (i.e. when you change the number of subdivisions for rendering the mesh) 5. In the display method render the object by calling its display list, i.e. use glcalllist(displaylistid); everytime you want to display the object defined in the display list (d) [1] Extend the program such that it displays the entire teapot lid. The easiest way to achieve this is by using the method/display list for the quarter teapot and just rotating it appropriately. The resulting images using wireframe and surface rendering should look as follows:

COMPSCI 373 S1 C Assignment 2 Sample Solution 14 of 17 Solution: // ***************************************************************************** // * A program to display the lid of the Utah Teapot defined by Bezier Patches * // * (c) May 2011, Burkhard Wuensche * // ***************************************************************************** // // Use 'l' to render the mesh // Use 's' to render the surface // Use '+'/'-' To increase/decrease subdivision level // Use 'c' to toggle the display of the control points #include <windows.h> #include <gl/gl.h> #include <gl/glu.h> #include <gl/glut.h> #include "Trackball.h" #include "Lighting.h" #include "Geometry.h" const int windowwidth=600; const int windowheight=600; double basis1(double t) return (1-t)*(1-t)*(1-t); double basis2(double t) return 3*(1-t)*(1-t)*t; double basis3(double t) return 3*(1-t)*t*t; double basis4(double t) return t*t*t; double db1dt(double t) return -3*(1-t)*(1-t); double db2dt(double t) return 3*(1-t)*(1-3*t); double db3dt(double t) return 3*t*(2-3*t); double db4dt(double t) return 3*t*t; typedef double (*basisfunctiontype)(double); basisfunctiontype bezierbasisfunction[]=&basis1,&basis2,&basis3,&basis4; basisfunctiontype bezierbasisfunctionderivative[]=&db1dt,&db2dt,&db3dt,&db4dt; typedef enum LINE_MODE, SURFACE_MODE RENDER_OPTION; RENDER_OPTION current_render_option=surface_mode; bool show_control_points=true; int numsegments=4; GLuint displaylistid; const int numvertices=22; CVec3df vertices[numvertices]= CVec3df(0,0,3.15f), CVec3df(0.8f,0,3.15f), CVec3df(0.8f,-0.45f,3.15f), CVec3df(0.45f,-0.8f,3.15f), CVec3df(0,-0.8f,3.15f), CVec3df(0,0,2.85f), CVec3df(0.2f,0,2.7f), CVec3df(0.2f,-0.112f,2.7f), CVec3df(0.112f,-0.2f,2.7f), CVec3df(0,-0.2f,2.7f), CVec3df(0.4f,0,2.55f), CVec3df(0.4f,-0.224f,2.55f), CVec3df(0.224f,-0.4f,2.55f), CVec3df(0,-0.4f,2.55f), CVec3df(1.3f,0,2.55f), CVec3df(1.3f,-0.728f,2.55f), CVec3df(0.728f,-1.3f,2.55f), CVec3df(0,-1.3f,2.55f), CVec3df(1.3f,0,2.4f), CVec3df(1.3f,-0.728f,2.4f), CVec3df(0.728f,-1.3f,2.4f), CVec3df(0,-1.3f,2.4f) ; const int NUM_PATCHES=2; const int BEZIER_ORDER=4; // use 4th order Bezier curves in both parameter directions int ctr_points[num_patches][bezier_order][bezier_order] = 0,0,0,0,1,2,3,4,5,5,5,5,6,7,8,9, 6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21;

COMPSCI 373 S1 C Assignment 2 Sample Solution 15 of 17 // material properties GLfloat mat_specular[4]; GLfloat mat_ambient_and_diffuse[4]; GLfloat mat_shininess[1]; CTrackball trackball; CLighting lighting; CVec3df _bicubicbezierpatchpoint(int k, double s, double t) int i,j; CVec3df p(0,0,0); for(i=0;i<bezier_order;i++) for(j=0;j<bezier_order;j++) p+=vertices[ctr_points[k][i][j]]*bezierbasisfunction[i](s) *bezierbasisfunction[j](t); return p; CVec3df _bicubicbezierpatchnormal(int k, double s, double t) int i,j; CVec3df dpdt(0,0,0); CVec3df dpds(0,0,0); for(i=0;i<bezier_order;i++) for(j=0;j<bezier_order;j++) dpdt+=vertices[ctr_points[k][i][j]]*bezierbasisfunction[i](s) *bezierbasisfunctionderivative[j](t); dpds+=vertices[ctr_points[k][i][j]]*bezierbasisfunctionderivative[i](s) *bezierbasisfunction[j](t); CVec3df n=cross(dpdt,dpds); if (dpdt.length()<0.000001 dpds.length()<0.000001) n.setvector(0,0,1); else n.normalisedestructivenoerror(); return n; void generatedisplaylist() int i,j,k; double s,t; CVec3df v[5][10]; glnewlist(displaylistid, GL_COMPILE); for(k=0;k<num_patches;k++) for(i=0;i<numsegments;i++) glbegin(gl_quad_strip); for(j=0;j<=numsegments;j++) s=(double) i/(double) numsegments; t=(double) j/(double) numsegments; glnormal3fv(_bicubicbezierpatchnormal(k,s,t).getarray()); glvertex3fv(_bicubicbezierpatchpoint(k,s,t).getarray()); s=(double) (i+1)/(double) numsegments; glnormal3fv(_bicubicbezierpatchnormal(k,s,t).getarray()); glvertex3fv(_bicubicbezierpatchpoint(k,s,t).getarray()); glend(); glendlist(); void display(void) glmatrixmode( GL_MODELVIEW ); // Set the view matrix... glloadidentity(); //... to identity. glulookat(0,0,6, 0,0,0, 0,1,0); // camera is on the z-axis trackball.tbmatrix(); // rotate scene using the trackball glclear( GL_COLOR_BUFFER_BIT GL_DEPTH_BUFFER_BIT); glenable(gl_normalize); glfrontface(gl_cw); if (current_render_option==line_mode) glpolygonmode(gl_front_and_back, GL_LINE);

COMPSCI 373 S1 C Assignment 2 Sample Solution 16 of 17 else glpolygonmode(gl_front_and_back, GL_FILL); glpushmatrix(); gltranslatef(0, 0, -3.0f); glpushmatrix(); glcalllist(displaylistid); glrotatef(90,0,0,1); glcalllist(displaylistid); glrotatef(90,0,0,1); glcalllist(displaylistid); glrotatef(90,0,0,1); glcalllist(displaylistid); glpopmatrix(); if (show_control_points) int i; glpointsize(5.0); gldisable(gl_lighting); glcolor3f(0.0, 0.0, 1.0); glbegin(gl_points); for(i=0;i<numvertices;i++) glvertex3fv(vertices[i].getarray()); glend(); glenable(gl_lighting); glpopmatrix(); glflush (); glutswapbuffers(); void init(void) // select clearing color (for glclear) glclearcolor (1,1,1,1); // RGB-value for black // enable depth buffering glenable(gl_depth_test); // initialize view (simple orthographic projection) glmatrixmode(gl_projection); glloadidentity(); gluperspective(33,1,2,10); trackball.tbinit(glut_left_button); // material properties mat_ambient_and_diffuse[0]=0.8; // red material... mat_ambient_and_diffuse[1]=0.0; mat_ambient_and_diffuse[2]=0.0; mat_ambient_and_diffuse[3]=1; mat_specular[0]=0.8f; //... with white highlights mat_specular[1]=0.8f; // if light source is reflected mat_specular[2]=0.8f; // on the material surface. mat_specular[3]=1; mat_shininess[0]=100; glmaterialfv(gl_front, GL_SPECULAR, mat_specular); glmaterialfv(gl_front, GL_AMBIENT_AND_DIFFUSE, mat_ambient_and_diffuse); glmaterialfv(gl_front, GL_SHININESS, mat_shininess); // enable shading and lighting lighting.enable(); glshademodel(gl_smooth); void reshape(int width, int height ) // Called at start, and whenever user resizes component int size = min(width, height); glviewport(0, 0, size, size); // Largest possible square trackball.tbreshape(width, height); void handlemousemotion(int x, int y) trackball.tbmotion(x, y); void handlemouseclick(int button, int state, int x, int y)

COMPSCI 373 S1 C Assignment 2 Sample Solution 17 of 17 trackball.tbmouse(button, state, x, y); void handlekeyboardevent(unsigned char key, int x, int y) switch (key) case 'l': current_render_option=line_mode; glutpostredisplay(); break; case 's': current_render_option=surface_mode; glutpostredisplay(); break; case 'c': show_control_points=!show_control_points; glutpostredisplay(); break; case '+': if (numsegments<100) numsegments++; generatedisplaylist(); glutpostredisplay(); break; case '-': if (numsegments>1) numsegments--; generatedisplaylist(); glutpostredisplay(); break; default: trackball.tbkeyboard(key); // create a double buffered colour window int main(int argc, char** argv) glutinit(&argc, argv); glutinitdisplaymode(glut_double GLUT_RGB GLUT_DEPTH); glutinitwindowsize(windowwidth, windowheight); glutinitwindowposition(100, 100); glutcreatewindow("lid of the \"Utah Teapot\""); init (); // initialise view displaylistid=glgenlists(1); generatedisplaylist(); glutmousefunc(handlemouseclick); // Set function to handle mouse clicks glutmotionfunc(handlemousemotion); // Set function to handle mouse motion glutkeyboardfunc(handlekeyboardevent); // Set function to handle keyboard input glutdisplayfunc(display); // Set function to draw scene glutreshapefunc(reshape); // Set function called if window gets resized glutmainloop(); return 0;