Click here to Skip to main content
15,878,959 members
Articles / Programming Languages / C#

Shading Subdivide Sphere

Rate me:
Please Sign up or sign in to vote.
3.11/5 (6 votes)
23 Mar 2008CPOL 50K   1.2K   16   3
Flat and Smooth Shading
im04.PNG

Introduction

Polygonal Shading

  • Curved surfaces are approximated by polygons
  • How do we shade?
    • Flat shading
    • Smooth shading
  • Two questions:
    • How do we determine normals at vertices?
    • How do we calculate shading at interior points?

Steps

im01.PNG

im02.PNG

im03.PNG

Using the Code

C#
using CsGL.OpenGL;

namespace GL_shading_subdivide
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            Text += " - Press S F Left Right";
            float X = 0.525731112119133696f;
            float Z = 0.850650808352039932f;

            /* vertex data array */
            vdata = new float[12][] {
  new float[3]{-X, 0.0f, Z},  new float[3]{X, 0.0f, Z}, 
	new float[3] {-X, 0.0f, -Z}, new float[3] {X, 0.0f, -Z},
  new float[3]{0.0f, Z, X}, new float[3] {0.0f, Z, -X}, 
	new float[3] {0.0f, -Z, X}, new float[3] {0.0f, -Z, -X},
  new float[3] {Z, X, 0.0f}, new float[3] {-Z, X, 0.0f}, 
	new float[3] {Z, -X, 0.0f}, new float[3] {-Z, -X, 0.0f}};

            /* triangle indices */
            tindices = new int[20][]{
  new int[3] {1,4,0}, new int[3] {4,9,0}, new int[3] {4,5,9}, 
	new int[3] {8,5,4}, new int[3] {1,8,4},
  new int[3] {1,10,8}, new int[3] {10,3,8}, new int[3] {8,3,5}, 
	new int[3] {3,2,5}, new int[3] {3,7,2},
  new int[3] {3,10,7}, new int[3] {10,6,7}, new int[3]  {6,11,7},  
	new int[3]{6,0,11}, new int[3] {6,1,0},
  new int[3] {10,1,6}, new int[3] {11,0,9}, new int[3] {2,11,9}, 
	new int[3] {5,2,9}, new int[3] {11,2,7}};

            ClientSize = new Size(640, 480);
            init();
            reshape(640, 480);
        }

        /* vertex data array */
        float[][] vdata = new float[12][];

        /* triangle indices */
        int[][] tindices = new int[20][];

        float[] mat_specular = { 0.5f, 0.5f, 0.5f, 1.0f };
        float[] mat_diffuse = { 0.8f, 0.6f, 0.4f, 1.0f };
        float[] mat_ambient = { 0.8f, 0.6f, 0.4f, 1.0f };
        float mat_shininess = 20.0f;    // unused if specular is 0 

        float[] light_ambient = { 0.2f, 0.2f, 0.2f, 1.0f };
        float[] light_diffuse = { 1.0f, 1.0f, 1.0f, 1.0f };
        float[] light_specular = { 0.4f, 0.4f, 0.4f, 1.0f };

        //   float[] light_position = { 100f, 50f, -1.0f, 0.0f }; // directional

        int flat = 1;            /* 0 = smooth shading, 1 = flat shading */
        int subdiv = 0;            /* number of subdivisions */

        private void openGLControl1_Paint(object sender, PaintEventArgs e)
        {
            Draw();
        }

        void Draw()
        {
            GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT);
            GL.glLoadIdentity();
            GL.glTranslatef(0f, 0.0f, 0);
            //  GL.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, light_position);

            /* drawIco(); */
            /* drawSphere(); */
            GL.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, light_ambient);
            GL.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, light_diffuse);
            GL.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, light_specular);

            GL.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, mat_specular);
            GL.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, mat_ambient);
            GL.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, mat_diffuse);
            GL.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, mat_shininess);

            GL.glEnable(GL.GL_LIGHTING);    /* enable lighting */
            GL.glEnable(GL.GL_LIGHT0);        /* enable light 0 */

            for (int i = 0; i < 20; i++)
            {
                subdivide(ref vdata[tindices[i][0]],
                          ref vdata[tindices[i][1]],
                          ref vdata[tindices[i][2]],
                             subdiv);
            }

            GL.glDisable(GL.GL_LIGHTING);    /* glDisable lighting */
            GL.glDisable(GL.GL_LIGHT0);        /* glDisable light 0 */

            GL.glFlush();
        }

        private void timer_Tick(object sender, EventArgs e)
        {
            openGLControl1.Invalidate();
        }

        void init()
        {
            GL.glShadeModel(GL.GL_SMOOTH);    /* enable smooth shading */

            GL.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            GL.glClearDepth(1.0f);                    // Depth Buffer Setup

            GL.glEnable(GL.GL_DEPTH_TEST);            // Enables Depth Testing
            GL.glDepthFunc(GL.GL_LEQUAL);             // The Type Of Depth Testing To Do
        }
        void reshape(int w, int h)
        {
            float aspect = (float)w / (float)h;
            GL.glViewport(0, 0, w, h);

            GL.glMatrixMode(GL.GL_PROJECTION);
            GL.glLoadIdentity();
            if (w <= h)
                GL.glOrtho(-1.25, 1.25, -1.25 * aspect, 1.25 * aspect, -2.0, 2.0);
            else
                GL.glOrtho(-1.25 * aspect, 1.25 * aspect, -1.25, 1.25, -2.0, 2.0);
            GL.glMatrixMode(GL.GL_MODELVIEW);

            GL.gluLookAt(0.5, 0.5, -1.5, /* eye */
                    0.0, 0.0, 0.0,  /* at */
                0.0, 1.0, 0.0); /* up */

            GL.glMatrixMode(GL.GL_MODELVIEW);
            GL.glLoadIdentity();
        }

        /* normalize a vector of non-zero length */
        void normalize(float[] v)
        {
            float d = (float)Math.Sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
            /* omit explicit check for division by zero */

            v[0] /= d; v[1] /= d; v[2] /= d;
        }

        /* normalized cross product of non-parallel vectors */
        void normCrossProd(float[] u, float[] v, float[] n)
        {
            n[0] = u[1] * v[2] - u[2] * v[1];
            n[1] = u[2] * v[0] - u[0] * v[2];
            n[2] = u[0] * v[1] - u[1] * v[0];
            normalize(n);
        }

        void normFace(float[] v1, float[] v2, float[] v3)
        {
            float[] d1 = new float[3], d2 = new float[3], n = new float[3];
            int k;
            for (k = 0; k < 3; k++)
            {
                d1[k] = v1[k] - v2[k];
                d2[k] = v2[k] - v3[k];
            }
            normCrossProd(d1, d2, n);
            GL.glNormal3fv(n);
        }

        /* draw triangle using face normals */
        void drawTriangleFlat(float[] v1, float[] v2, float[] v3)
        {
            GL.glBegin(GL.GL_TRIANGLES);
            normFace(v1, v2, v3);
            GL.glVertex3fv(v1);
            GL.glVertex3fv(v2);
            GL.glVertex3fv(v3);
            GL.glEnd();
        }

        /* draw triangle using sphere normals */
        void drawTriangleSmooth(float[] v1, float[] v2, float[] v3)
        {
            GL.glBegin(GL.GL_TRIANGLES);
            GL.glNormal3fv(v1);
            GL.glVertex3fv(v1);
            GL.glNormal3fv(v2);
            GL.glVertex3fv(v2);
            GL.glNormal3fv(v3);
            GL.glVertex3fv(v3);
            GL.glEnd();
        }

        /* recursively subdivide face depth times */
        /* and draw the resulting triangles */
        void subdivide(ref float[] v1, ref float[] v2, ref float[] v3, int depth)
        {
            float[] v12 = new float[3], v23 = new float[3], v31 = new float[3];

            if (depth == 0)
            {
                if (flat == 1)
                    drawTriangleFlat(v1, v2, v3);
                else
                    drawTriangleSmooth(v1, v2, v3);
            }

            /* calculate midpoints of each side */
            for (int i = 0; i < 3; i++)
            {
                v12[i] = (v1[i] + v2[i]) / 2.0f;
                v23[i] = (v2[i] + v3[i]) / 2.0f;
                v31[i] = (v3[i] + v1[i]) / 2.0f;
            }
            // extrude midpoints to lie on unit sphere 
            normalize(v12);
            normalize(v23);
            normalize(v31);

            // recursively subdivide new triangles 
            if (depth != 0)
            {
                subdivide(ref v1, ref v12, ref v31, depth - 1);
                subdivide(ref v2, ref v23, ref v12, depth - 1);
                subdivide(ref v3, ref v31, ref v23, depth - 1);
                subdivide(ref v12, ref v23, ref v31, depth - 1);
            }
        }

        protected override bool ProcessDialogKey(Keys keyData)
        {
            if (keyData == Keys.Left)
            {
                subdiv++;
                if (subdiv > 6)
                    subdiv = 6;
            }
            if (keyData == Keys.Right)
            { 
                subdiv--;
                if (subdiv < 0)
                    subdiv = 0;
            }

            if (keyData == Keys.F)
                flat = 1;

            if (keyData == Keys.S)
                flat = 0;

            return base.ProcessDialogKey(keyData);
        }
    }
}

History

  • 24th March, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Algeria Algeria
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionGreat article Pin
Yuval Rivlin6-Nov-13 4:33
Yuval Rivlin6-Nov-13 4:33 
QuestionChanging size and position of sphere Pin
safee ullah21-Jul-11 16:56
safee ullah21-Jul-11 16:56 
GeneralMissing dll Pin
dave.kelly26-Mar-08 0:07
professionaldave.kelly26-Mar-08 0:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.