Prince Sultan University College of Computer & Information Science Department of Computer Science CS360: Computer Graphics Practice Sheet 6 : Lighting a Sphere (GameWin class Application) Objective: The main purpose of this practice sheet is to illustrate how lighting sources and light effects are simulated in openGL. OpenGL supports up to 8 light sources that may be used to light a model: Light0, Light1, Light3, …., Light7. The light coming out of a light source is usually directional consisting of two major components: Diffuse and Specular. We add to this the global Ambient component, which is supposed to be directionless. Another major attribute of a light source is its position in 3D space. The example used in this sheet uses a sphere to illustrate the effect of light and shading on a given object. The light source and its light components are specified at initialization stage. 1 Creating a an OpenTK GameWin Application: 1- Create a normal Console Application, name it SphereLighting. 2- Add references to the following libraries: a. OpenTK, GLControl, OpenTKCompatibiliy b. System.Windows.Forms c. System.Drawing Enabling the Lighting Model and Specifying the Light Source: The Light source and its attributes are defined in the method initLight() below. The method also enables a number of capabilities including the Lighting model: private void initLight() { float[] float[] float[] float[] float[] mat_specular = { 1.0f, 0.0f, 0.0f, 1.0f }; mat_shininess = { 97.0f }; light_position = { 10.0f, 0.0f, 0.0f, 0.0f }; light_ambient = { 1.0f, 0.5f, 0.0f, 1.0f }; light_diffuse = { 0.8f, 0.8f, 0.8f, 1.0f }; GL.ClearColor(Color.CornflowerBlue); GL.ShadeModel(ShadingModel.Smooth); GL.Material(MaterialFace.Front, MaterialParameter.Specular, mat_specular); GL.Material(MaterialFace.Front, MaterialParameter.Shininess, mat_shininess); GL.Light(LightName.Light0, LightParameter.Position, light_position); GL.Light(LightName.Light0, LightParameter.Ambient, light_ambient); GL.Light(LightName.Light0, LightParameter.Diffuse, light_diffuse); GL.Light(LightName.Light0, LightParameter.Specular, mat_specular); GL.Enable(EnableCap.Lighting); GL.Enable(EnableCap.Light0); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.ColorMaterial); // GL.Enable(EnableCap.CullFace); } Note the Ambient, Diffuse and Specular Components. 2 The Full Applications: Replace the dummy code in the Console Application you created with this code: using using using using using System; System.Windows.Forms; System.Drawing; OpenTK; OpenTK.Graphics; namespace OpenTKCircle { class Program : GameWindow { Matrix4 matrixProjection, matrixModelview; float cameraRotation = 0f; private void initLight() { float[] mat_specular = { 1.0f, 0.0f, 0.0f, 1.0f }; float[] mat_shininess = { 97.0f }; float[] light_position = { 10.0f, 0.0f, 0.0f, 0.0f }; float[] light_ambient = { 1.0f, 0.5f, 0.0f, 1.0f }; float[] light_diffuse = { 0.8f, 0.8f, 0.8f, 1.0f }; GL.ClearColor(Color.CornflowerBlue); GL.ShadeModel(ShadingModel.Smooth); GL.Material(MaterialFace.Front, MaterialParameter.Specular, mat_specular); GL.Material(MaterialFace.Front, MaterialParameter.Shininess, mat_shininess); GL.Light(LightName.Light0, LightParameter.Position, light_position); GL.Light(LightName.Light0, LightParameter.Ambient, light_ambient); GL.Light(LightName.Light0, LightParameter.Diffuse, light_diffuse); GL.Light(LightName.Light0, LightParameter.Specular, mat_specular); GL.Enable(EnableCap.Lighting); GL.Enable(EnableCap.Light0); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.ColorMaterial); // GL.Enable(EnableCap.CullFace); } protected override void OnLoad(EventArgs e) { //GL.ClearColor(Color) GL.ClearColor(Color.CornflowerBlue); GL.Enable(EnableCap.DepthTest); GL.Enable(EnableCap.CullFace); GL.EnableClientState(EnableCap.VertexArray); GL.EnableClientState(EnableCap.ColorArray); initLight(); } protected override void OnResize(EventArgs e) { GL.Viewport(0, 0, Width, Height); 3 matrixProjection = Matrix4.CreatePerspectiveFieldOfView((float)Math.PI / 4, Width / (float)Height, 1f, 100f); GL.MatrixMode(MatrixMode.Projection); GL.LoadMatrix(ref matrixProjection); } public static Vertex[] CalculateVertices2(float radius, float height, byte segments, byte rings) { var data = new Vertex[segments * rings]; int i = 0; for (double y = 0; y < rings; y++) { double phi = (y / (rings - 1)) * Math.PI; for (double x = 0; x < segments; x++) { double theta = (x / (segments - 1)) * 2 * Math.PI; Vector3 v = new Vector3() { X = (float)(radius * Math.Sin(phi) * Math.Cos(theta)), Y = (float)(height * Math.Cos(phi)), Z = (float)(radius * Math.Sin(phi) * Math.Sin(theta)), }; Vector3 n = Vector3.Normalize(v); Vector2 uv = new Vector2() { X = (float)(x / (segments - 1)), Y = (float)(y / (rings - 1)) }; data[i] = new Vertex() { Position = v, Normal = n, TexCoord = uv }; i++; } } return data; } public static ushort[] CalculateElements(float radius, float height, byte segments, byte rings) { var num_vertices = segments * rings; var data = new ushort[num_vertices * 6]; ushort i = 0; for (byte y = 0; y < rings - 1; y++) { for (byte x = 0; x < segments - 1; x++) { data[i++] = (ushort)((y + 0) * segments + x); data[i++] = (ushort)((y + 1) * segments + x); 4 data[i++] = (ushort)((y + 1) * segments + x + 1); data[i++] = (ushort)((y + 1) * segments + x + 1); data[i++] = (ushort)((y + 0) * segments + x + 1); data[i++] = (ushort)((y + 0) * segments + x); } } // Verify that we don't access any vertices out of bounds: foreach (int index in data) if (index >= segments * rings) throw new IndexOutOfRangeException(); return data; } public struct Vertex { public Vector2 TexCoord; public Vector3 Normal; public Vector3 Position; } //Draw the Sphere protected override void OnRenderFrame(OpenTK.FrameEventArgs e) { base.OnRenderFrame(e); initLight(); cameraRotation = (cameraRotation < 360f) ? (cameraRotation + 1f * (float)e.Time) : 0f; // MessageBox.Show(e.Time.ToString()); Matrix4.CreateRotationY(cameraRotation, out matrixModelview); matrixModelview *= Matrix4.LookAt(0f, 0f, -5f, 0f, 0f, 0f, 0f, 1f, 0f); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref matrixModelview); GL.PolygonMode(MaterialFace.FrontAndBack, PolygonMode.Fill); Vertex[] SphereVertices = CalculateVertices2(0.5f, 0.5f, 100, 100); ushort[] SphereElements = CalculateElements(0.5f, 0.5f, 100, 100); GL.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit); GL.MatrixMode(MatrixMode.Modelview); GL.LoadMatrix(ref matrixModelview); GL.Begin(BeginMode.Triangles); foreach (var element in SphereElements) { var vertex = SphereVertices[element]; GL.TexCoord2(vertex.TexCoord); GL.Normal3(vertex.Normal); GL.Vertex3(vertex.Position); } 5 GL.End(); SwapBuffers(); } [STAThread] private static void Main(string[] args) { using (Program p = new Program()) { p.Run(60d); } } } } Answer the Following Questions: 1- Explain how the sphere vertices and indices are computed. Try to deduce the equations used. 2- What are the rings and segments used in computing the sphere elements? Use a diagram to explain. How many segments and rings have been used? 3- What is the significance of computing and using normals in drawing the model? 4- What are the transformations used in rendering this model and in what order have they been applied. 6