Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Shading Subdivide Sphere

3.11/5 (6 votes)
23 Mar 2008CPOL 1   1.2K  
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)