這是根據徐明亮《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 else 151 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS); 152 gl.QuadricNormals(sphere, OpenGL.GLU_NONE); //GLU_NONE,GLU_FLAT,GLU_SMOOTH 153 gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE); //GLU_OUTSIDE,GLU_INSIDE 154 gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE); //GL_TRUE,GLU_FALSE 155 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/