Click here to Skip to main content
Licence CPOL
First Posted 23 Mar 2008
Views 22,921
Downloads 512
Bookmarked 15 times

Shading Subdivide Sphere

By | 23 Mar 2008 | Article
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

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)

About the Author

salim_chaf

Web Developer

Algeria Algeria

Member



Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
QuestionChanging size and position of sphere Pinmembersafee ullah16:56 21 Jul '11  
GeneralMissing dll Pinmemberdave.kelly0:07 26 Mar '08  
csgl.native.dll is missing from the project, which causes an initialization exception.

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

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.5.120529.1 | Last Updated 24 Mar 2008
Article Copyright 2008 by salim_chaf
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid