首页 > 代码库 > SharpGL学习笔记(十三) 光源例子:环绕二次曲面球体的光源

SharpGL学习笔记(十三) 光源例子:环绕二次曲面球体的光源

这是根据徐明亮《OpenGL游戏编程》书上光灯一节的一个例子改编的.

从这个例子可以学习到二次曲面的参数设置,程序中提供了两个画球的函数,一个是用三角形画出来的,一个是二次曲面构成的.

你会发现,跟三角形版本不同,二次曲面要做一些设定,否则画出来的球体无法接受光照.

 

先上代码:

  1 using System;  2 using System.Collections.Generic;  3 using System.ComponentModel;  4 using System.Data;  5 using System.Drawing;  6 using System.Linq;  7 using System.Text;  8 using System.Windows.Forms;  9 using SharpGL; 10  11 namespace light2 12 { 13     /// <summary> 14     /// 原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/ 15     /// </summary> 16     public partial class SharpGLForm : Form 17     { 18         private float rotation = 0.0f; 19         float m_bReadX, m_bReadY; 20         float m_bGreenX, m_bGreenY; 21         float m_bBlueX, m_bBlueY; 22  23         //3个光源位置 24         float[] lightPosR = new float[] { 0f, 0f, 2f, 1f }; 25         float[] lightPosG = new float[] { 0f, 0f, 2f, 1f }; 26         float[] lightPosB = new float[] { 0f, 0f, 2f, 1f }; 27  28         //3个光源漫射光 29         float[] diffLightR = { 1f, 0f, 0f, 1f }; 30         float[] diffLightG = { 0f, 1f, 0f, 1f }; 31         float[] diffLightB = { 0f, 0f, 1f, 1f }; 32  33         //定义3个光源我镜面光 34         float[] specLightR = { 1f, 0f, 0f, 1f }; 35         float[] specLightG = { 0f, 1f, 0f, 1f }; 36         float[] specLightB = { 0f, 0f, 1f, 1f }; 37  38         //默认的光源, 灰色光源,用于默认照明 39         float[] defDiffLight = new float[] { 0.8f, 0.8f, 0.8f, 1f }; 40         float[] defSpecLight = new float[] { 1f, 1f, 1f, 1f }; 41         float[] defLightPos = new float[] { 0f, 0f, 10f, 1f }; 42  43  44  45         public SharpGLForm() 46         { 47             InitializeComponent(); 48         } 49  50         private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 51         { 52             OpenGL gl = openGLControl.OpenGL; 53             gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 54             gl.LoadIdentity(); 55             gl.Rotate(rotation, 0.0f, 1.0f, 0.0f); 56  57             draw(gl); 58             rotation += 3.0f; 59  60             update(gl); 61         } 62  63         void update(OpenGL gl) 64         { 65             gl.Enable(OpenGL.GL_LIGHT1); 66             m_bReadX += 16; 67             m_bReadY += 12; 68             gl.Enable(OpenGL.GL_LIGHT2); 69             m_bGreenX += 10; 70             m_bGreenY += 6; 71             gl.Enable(OpenGL.GL_LIGHT3); 72             m_bBlueX += 2; 73             m_bBlueY += 4; 74         } 75  76         void draw(OpenGL gl) 77         { 78             gl.PushMatrix(); 79             //旋转红光 80             gl.Rotate(m_bReadX, 1f, 0f, 0f); 81             gl.Rotate(m_bReadY, 0f, 1f, 0f); 82             //设置红光的位置 83             gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, lightPosR);    84             //绘制光球 85             gl.Translate(lightPosR[0], lightPosR[1], lightPosR[2]); 86             gl.Color(1f, 0f, 0f); 87             gl.PushAttrib(OpenGL.GL_LIGHTING_BIT); 88             gl.Disable(OpenGL.GL_LIGHTING); 89             drawSphere(gl,lightPosR[0], lightPosR[1], lightPosR[2],0.2f,10,10,false); 90             gl.Enable(OpenGL.GL_LIGHTING); 91             gl.PopAttrib(); 92             gl.PopMatrix(); 93  94  95             gl.PushMatrix(); 96             //旋转绿光 97             gl.Rotate(m_bGreenX, 1f, 0f, 0f); 98             gl.Rotate(m_bGreenY, 0f, 1f, 0f); 99             //设置绿光的位置100             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_POSITION, lightPosG);101             //绘制光球102             gl.Translate(lightPosG[0], lightPosG[1], lightPosG[2]);103             gl.Color(0f, 1f, 0f);104             gl.PushAttrib(OpenGL.GL_LIGHTING_BIT);105             gl.Disable(OpenGL.GL_LIGHTING);106             drawSphere(gl, lightPosG[0], lightPosG[1], lightPosG[2], 0.2f, 10, 10 ,false);107             gl.Enable(OpenGL.GL_LIGHTING);108             gl.PopAttrib();109             gl.PopMatrix();110 111 112             gl.PushMatrix();113             //旋转蓝光114             gl.Rotate(m_bBlueX, 1f, 0f, 0f);115             gl.Rotate(m_bBlueY, 0f, 1f, 0f);116             //设置蓝光的位置117             gl.Light(OpenGL.GL_LIGHT3, OpenGL.GL_POSITION, lightPosB);118             //绘制光球119             gl.Translate(lightPosB[0], lightPosB[1], lightPosB[2]);120             gl.Color(0f, 0f, 1f);121             gl.PushAttrib(OpenGL.GL_LIGHTING_BIT);122             gl.Disable(OpenGL.GL_LIGHTING);123             drawSphere(gl, lightPosB[0], lightPosB[1], lightPosB[2], 0.2f, 10, 10,false);124             gl.Enable(OpenGL.GL_LIGHTING);125             gl.PopAttrib();126             gl.PopMatrix();127 128 129             //绘制球体130             gl.PushMatrix();131             gl.Rotate(rotation, 1f, 0f, 0f);132             gl.Rotate(rotation, 0f, 1f, 0f);133             gl.Rotate(rotation, 0f, 0f, 1f);134             drawSphere(gl, 0, 0, 0, 3, 40, 40,false);135 136             gl.PopMatrix();137 138             gl.Flush();139 140         }141 142         //二次曲面球体143         void drawSphere(OpenGL gl,float x,float y,float z, double radius, int segx, int segy, bool isLines)144         {145             gl.PushMatrix();146             gl.Translate(x, y, z);147             var sphere = gl.NewQuadric();148             if (isLines)149                 gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES);150             else151                 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS);152             gl.QuadricNormals(sphere, OpenGL.GLU_NONE);   //GLU_NONE,GLU_FLAT,GLU_SMOOTH153             gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE);  //GLU_OUTSIDE,GLU_INSIDE154             gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE);  //GL_TRUE,GLU_FALSE155             gl.Sphere(sphere, radius, segx, segy);156             gl.DeleteQuadric(sphere);157             gl.PopMatrix();158         }159 160         //球心坐标为(x,y,z),球的半径为radius,M,N分别表示球体的横纵向被分成多少份161         void drawSphere1(OpenGL gl, float xx, float yy, float zz, float radius, float M, float N,bool isLines)162         {163             const float PI = 3.1415926f;164             float step_z = (float)Math.PI / M;165             float step_xy = 2 * PI / N;166             float[] x = new float[4] { 0, 0, 0, 0 };167             float[] y = new float[4] { 0, 0, 0, 0 };168             float[] z = new float[4] { 0, 0, 0, 0 };169 170             float angle_z = 0.0f;171             float angle_xy = 0.0f;172             int i = 0, j = 0;173             gl.Begin(OpenGL.GL_QUADS);174             for (i = 0; i < M; i++)175             {176                 angle_z = i * step_z;177                 for (j = 0; j < N; j++)178                 {179                     angle_xy = j * step_xy;180 181                     x[0] = (float)(radius * Math.Sin(angle_z) * Math.Cos(angle_xy));182                     y[0] = (float)(radius * Math.Sin(angle_z) * Math.Sin(angle_xy));183                     z[0] = (float)(radius * Math.Cos(angle_z));184 185                     x[1] = (float)(radius * Math.Sin(angle_z + step_z) * Math.Cos(angle_xy));186                     y[1] = (float)(radius * Math.Sin(angle_z + step_z) * Math.Sin(angle_xy));187                     z[1] = (float)(radius * Math.Cos(angle_z + step_z));188 189                     x[2] = (float)(radius * Math.Sin(angle_z + step_z) * Math.Cos(angle_xy + step_xy));190                     y[2] = (float)(radius * Math.Sin(angle_z + step_z) * Math.Sin(angle_xy + step_xy));191                     z[2] = (float)(radius * Math.Cos(angle_z + step_z));192 193                     x[3] = (float)(radius * Math.Sin(angle_z) * Math.Cos(angle_xy + step_xy));194                     y[3] = (float)(radius * Math.Sin(angle_z) * Math.Sin(angle_xy + step_xy));195                     z[3] = (float)(radius * Math.Cos(angle_z));196 197                     for (int k = 0; k < 4; k++)198                     {199                         gl.Vertex(xx + x[k], yy + y[k], zz + z[k]);200                     }201                 }202             }203             gl.End();204         }205 206         private void openGLControl_OpenGLInitialized(object sender, EventArgs e)207         {208             OpenGL gl = openGLControl.OpenGL;209             setLight(gl);210             //gl.Enable(OpenGL.GL_NORMALIZE);211             gl.ClearColor(0, 0, 0, 0);212         }213 214         private void setLight(OpenGL gl)215         {216             //0号灯光,默认灯光217             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, defDiffLight);218             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, defSpecLight);219             gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, defLightPos);220 221             //1号灯光222             gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, diffLightR);223             gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_SPECULAR, specLightR);224             gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, lightPosR);225 226             //2号灯光227             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_DIFFUSE, diffLightG);228             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_SPECULAR, specLightG);229             gl.Light(OpenGL.GL_LIGHT2, OpenGL.GL_POSITION, lightPosG);230 231             //3号灯光232             gl.Light(OpenGL.GL_LIGHT3, OpenGL.GL_DIFFUSE, diffLightB);233             gl.Light(OpenGL.GL_LIGHT3, OpenGL.GL_SPECULAR, specLightB);234             gl.Light(OpenGL.GL_LIGHT3, OpenGL.GL_POSITION, lightPosB);235 236             gl.Enable(OpenGL.GL_LIGHTING);237             gl.Enable(OpenGL.GL_LIGHT0);  //启用默认光源238 239         }240 241         private void openGLControl_Resized(object sender, EventArgs e)242         {243             OpenGL gl = openGLControl.OpenGL;244             gl.MatrixMode(OpenGL.GL_PROJECTION);245             gl.LoadIdentity();246             gl.Perspective(70.0f, (double)Width / (double)Height, 0.01, 100.0);247             gl.LookAt(-5, 5, -5, 0, 0, 0, 0, 1, 0);248             gl.MatrixMode(OpenGL.GL_MODELVIEW);249         }250         251     }252 }

 

截取了一帧的效果如下图:

有三个光球围绕球体旋转,三组光分别为红,绿,蓝,因此它们的组合可以在球面上生成所有可能的颜色效果.

技术分享

 

函数drawSphere是二次曲面球体,函数drawSphere1是三角形构成的球体.

下面我们研究一下二次曲面的几个关键的需要注意的设置函数:

 

(1) QuadricDrawStyle(IntPtr quadObject, uint drawStyle);

第一个参数是二次方程对象状态的指针,第二个参数的枚举值如下表:

常量描述
GLU_FILL二次方程对象画成实体
GLU_LINE二次方程对象画成线框
GLU_POINT二次方程对象画成一组顶点的集合
GLU_SILHOUETTE类似于线框,但相邻的多边形的边不被绘制。

 

 

 

 

(2) QuadricNormals(IntPtr quadricObject, uint normals);

这个函数指定二次方程对象如何生成法线。第二个参数可以是:GLU_NONE不生成法线,GLU_FLAT扁平法线,GLU_SMOOTH平滑法线。

(3) QuadricOrientation(IntPtr quadricObject, int orientation);

这个函数可以指定法线的朝向,指向外面还是只想里面。orientation可以是GLU_OUTSIDE或者是GLU_INSIDE这两个值。OpenGL默认是以GL_CCW逆时针为正方向的

(4) QuadricTexture(IntPtr quadricObject, int textureCoords);

这个函数可以指定二次方程表面的纹理坐标,textureCoords这个参数可以是GL_TRUE或者GL_FALSE.当为球体和圆柱体生成纹理坐标时,纹理是对称地环绕在球体和圆柱体的表面的。如果应用到圆盘上,那么纹理的中心就是圆盘的中心,然后以线性插值的方式扩展到圆盘的边界。

 

读者可以尝试改变这些函数的参数,会发现受光效果是不同的.

也可以尝试用drawSphere1()函数替换掉drawSphere()函数,它是不需要做任何设定,就有很好的效果.

 

 

本节源代码下载

 

原创文章,出自"博客园, 猪悟能‘S博客" : http://www.cnblogs.com/hackpig/

 

SharpGL学习笔记(十三) 光源例子:环绕二次曲面球体的光源