SharpGL学习笔记(十一) 光源创建的综合例子:光源参数可调节的测试场景
灯光的测试例子:光源参数可以调节的测试场景
先看一下测试场景和效果。
场景中可以切换视图, 以方便观察三维体和灯光的位置。环境光,漫射光,镜面反射光都可以在四种颜色间切换。
灯光位置和摄像机位置(LookAt)可以输入数值或者点动调节,也可以按键盘的QEWASD六个键进行调节。
你还会注意到:球体对光的效果要敏感柔和些,而那个六面体BOX看来效果不好。这是因为灯光对顶点发生作用。在程序里面,球休的顶点数量有20*10,而BOX只有4*6个,而且还重合了一些顶点。
这一点,在3dsmax的全局光照里面表现很明显,做为墙壁的box顶点数量越大,计算出来光照效果越好。
勘误:box在创建的时候没有指定法线,这个也是重要原因,请参考例子SharpGL学习笔记(十五) 纹理映射 ,那里的box指定了法线,效果就很好了。(2016/9/4)
还有,界面上灯光位置是 -1,5,1,1 前三个是x,y,z, 后面的一个1不是坐标,它取值0或者1,表示灯光是定向光源(directonal),还是定位光源(positional)。
代码如下:
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 SharpGLWinformsApplication1 12 { 13 14 public partial class SharpGLForm : Form 15 { 16 private float rotation = 0.0f; 17 private bool isRotate = false; 18 private bool isLines = false; 19 private bool isFrontView = false; 20 private bool isLeftView = false; 21 private bool isTopView = false; 22 private bool isPerspective = true; 23 private float[] lightPos = new float[] { -1, -3, 1, 1 }; 24 private float[] lightSphereColor = new float[] { 1f, 1f, 1f }; 25 private IList<float[]> lightColor = new List<float[]>(); 26 private double[] lookatValue = { 1, 1, 2, 0, 0, 0, 0, 1, 0 }; 27 private IList<double[]> viewDefaultPos = new List<double[]>(); 28 public SharpGLForm() 29 { 30 InitializeComponent(); 31 } 32 33 private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 34 { 35 OpenGL gl = openGLControl.OpenGL; 36 gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 37 gl.LoadIdentity(); 38 gl.Rotate(rotation, 0.0f, 1.0f, 0.0f); 39 40 drawGrid(gl); 41 DrawCube(ref gl, 1.5f,-1f, -2f, isLines); 42 drawOneSphere(ref gl,-3,-2,-4,isLines); 43 if (isRotate) 44 rotation += 3.0f; 45 } 46 47 private void setLightColor(OpenGL gl) 48 { 49 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[0]); 50 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[1]); 51 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[2]); 52 } 53 54 private void openGLControl_OpenGLInitialized(object sender, EventArgs e) 55 { 56 OpenGL gl = openGLControl.OpenGL; 57 58 //四个视图的缺省位置 59 viewDefaultPos.Add(new double[] { 1, 1, 2, 0, 0, 0, 0, 1, 0 }); //透视 60 viewDefaultPos.Add(new double[] { 0, 0, 2, 0, 0, 0, 0, 1, 0 }); //前视 61 viewDefaultPos.Add(new double[] { 5, 0, 0, 0, 0, 0, 0, 1, 0 }); //左视 62 viewDefaultPos.Add(new double[] { 0, 13, 0, -1, 0, 0, 0, 1, 0 }); //顶视 63 lookatValue =(double[])viewDefaultPos[0].Clone(); 64 65 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //环境光(ambient light) 66 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //漫射光(diffuse light) 67 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //镜面反射光(specular light) 68 69 setLightColor(gl); 70 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); 71 72 gl.Enable(OpenGL.GL_LIGHTING); 73 gl.Enable(OpenGL.GL_LIGHT0); 74 75 gl.ClearColor(0, 0, 0, 0); 76 } 77 78 private void drawOneSphere(ref OpenGL gl, float xPos, float yPos, float zPos, bool isLine) 79 { 80 gl.PushMatrix(); 81 { 82 gl.Translate(xPos, yPos, zPos); 83 gl.Color(lightSphereColor); 84 drawSphere(gl,1,20,10,isLine); 85 } 86 gl.PopMatrix(); 87 } 88 89 private void openGLControl_Resized(object sender, EventArgs e) 90 { 91 92 OpenGL gl = openGLControl.OpenGL; 93 gl.MatrixMode(OpenGL.GL_PROJECTION); 94 gl.LoadIdentity(); 95 gl.Perspective(40.0f, (double)Width / (double)Height, 0.01, 100.0); 96 97 98 gl.LookAt(lookatValue[0], lookatValue[1], lookatValue[2], 99 lookatValue[3], lookatValue[4], lookatValue[5], 100 lookatValue[6], lookatValue[7], lookatValue[8]); 101 102 gl.MatrixMode(OpenGL.GL_MODELVIEW); 103 updateLabInfo(); 104 } 105 106 internal void DrawCube(ref OpenGL Gl, float xPos, float yPos, float zPos, bool isLine) 107 { 108 Gl.PushMatrix(); 109 Gl.Translate(xPos, yPos, zPos); 110 if (isLine) 111 Gl.Begin(OpenGL.GL_LINE_STRIP); 112 else 113 Gl.Begin(OpenGL.GL_POLYGON); 114 115 /** 顶面 */ 116 Gl.Vertex(0.0f, 0.0f, 0.0f); 117 Gl.Vertex(0.0f, 0.0f, -1.0f); 118 Gl.Vertex(-1.0f, 0.0f, -1.0f); 119 Gl.Vertex(-1.0f, 0.0f, 0.0f); 120 121 /** 前面 */ 122 Gl.Vertex(0.0f, 0.0f, 0.0f); 123 Gl.Vertex(-1.0f, 0.0f, 0.0f); 124 Gl.Vertex(-1.0f, -1.0f, 0.0f); 125 Gl.Vertex(0.0f, -1.0f, 0.0f); 126 127 /** 右面 */ 128 Gl.Vertex(0.0f, 0.0f, 0.0f); 129 Gl.Vertex(0.0f, -1.0f, 0.0f); 130 Gl.Vertex(0.0f, -1.0f, -1.0f); 131 Gl.Vertex(0.0f, 0.0f, -1.0f); 132 133 /** 左面*/ 134 Gl.Vertex(-1.0f, 0.0f, 0.0f); 135 Gl.Vertex(-1.0f, 0.0f, -1.0f); 136 Gl.Vertex(-1.0f, -1.0f, -1.0f); 137 Gl.Vertex(-1.0f, -1.0f, 0.0f); 138 139 /** 底面 */ 140 Gl.Vertex(0.0f, 0.0f, 0.0f); 141 Gl.Vertex(0.0f, -1.0f, -1.0f); 142 Gl.Vertex(-1.0f, -1.0f, -1.0f); 143 Gl.Vertex(-1.0f, -1.0f, 0.0f); 144 145 146 /** 后面 */ 147 Gl.Vertex(0.0f, 0.0f, 0.0f); 148 Gl.Vertex(-1.0f, 0.0f, -1.0f); 149 Gl.Vertex(-1.0f, -1.0f, -1.0f); 150 Gl.Vertex(0.0f, -1.0f, -1.0f); 151 Gl.End(); 152 Gl.PopMatrix(); 153 } 154 155 void drawGrid(OpenGL gl) 156 { 157 //绘制过程 158 gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性 159 gl.PushMatrix(); //压入堆栈 160 gl.Translate(0f, -2f, 0f); 161 gl.Color(0f, 0f, 1f); 162 163 //在X,Z平面上绘制网格 164 for (float i = -50; i <= 50; i += 1) 165 { 166 //绘制线 167 gl.Begin(OpenGL.GL_LINES); 168 { 169 if (i == 0) 170 gl.Color(0f, 1f, 0f); 171 else 172 gl.Color(0f, 0f, 1f); 173 174 //X轴方向 175 gl.Vertex(-50f, 0f, i); 176 gl.Vertex(50f, 0f, i); 177 //Z轴方向 178 gl.Vertex(i, 0f, -50f); 179 gl.Vertex(i, 0f, 50f); 180 181 } 182 gl.End(); 183 } 184 gl.PopMatrix(); 185 gl.PopAttrib(); 186 } 187 188 189 void drawSphere(OpenGL gl,double radius,int segx,int segy,bool isLines) 190 { 191 gl.PushMatrix(); 192 gl.Translate(2f, 1f, 2f); 193 var sphere = gl.NewQuadric(); 194 if (isLines) 195 gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES); 196 else 197 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS); 198 gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH); 199 gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE); 200 gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE); 201 gl.Sphere(sphere, radius, segx, segy); 202 gl.DeleteQuadric(sphere); 203 gl.PopMatrix(); 204 } 205 206 private void moveObject(int obj,string keyName) 207 { 208 //obj==0移动视图 209 switch (keyName) 210 { 211 case "btnQ": 212 if (obj == 0) ++lookatValue[1]; //y 213 else 214 ++lightPos[1]; 215 break; 216 case "btnE": 217 if (obj == 0) --lookatValue[1]; 218 else 219 --lightPos[1]; 220 break; 221 case "btnW": 222 if (obj == 0) --lookatValue[2]; //z 223 else 224 --lightPos[2]; 225 break; 226 case "btnS": 227 if (obj == 0) ++lookatValue[2]; 228 else 229 ++lightPos[2]; 230 break; 231 case "btnA": 232 if (obj == 0) --lookatValue[0]; //X 233 else 234 --lightPos[0]; 235 break; 236 case "btnD": 237 if (obj == 0) ++lookatValue[0]; 238 else 239 ++lightPos[0]; 240 break; 241 } 242 } 243 244 private void rbPerspective_CheckedChanged(object sender, EventArgs e) 245 { 246 switch (((RadioButton)sender).Name) 247 { 248 case "rbPerspective": 249 isPerspective = !isPerspective; 250 isFrontView = false; 251 isTopView = false; 252 isLeftView = false; 253 break; 254 case "rbLeft": 255 isLeftView = !isLeftView; 256 isFrontView = false; 257 isPerspective = false; 258 isTopView = false; 259 break; 260 case "rbFront": 261 isFrontView = !isFrontView; 262 isTopView = false; 263 isPerspective = false; 264 isLeftView = false; 265 break; 266 case "rbTop": 267 isTopView = !isTopView; 268 isPerspective = false; 269 isLeftView = false; 270 isFrontView = false; 271 break; 272 default: 273 return; 274 } 275 setViewDefaultValue(); 276 openGLControl_Resized(null, null); 277 } 278 279 private void cbxRotate_CheckedChanged(object sender, EventArgs e) 280 { 281 var cbx=((CheckBox)sender); 282 switch (cbx.Name) 283 { 284 case "cbxRotate": 285 isRotate = cbx.Checked; 286 break; 287 case "cbxLines": 288 isLines = cbx.Checked; 289 break; 290 case "cbxLightOff": 291 if (!cbx.Checked) 292 this.openGLControl.OpenGL.Enable(OpenGL.GL_LIGHT0); 293 else 294 this.openGLControl.OpenGL.Disable(OpenGL.GL_LIGHT0); 295 break; 296 } 297 } 298 299 private void SharpGLForm_Load(object sender, EventArgs e) 300 { 301 this.cbxLightType.SelectedIndex = 0; 302 updateLabInfo(); 303 } 304 305 private void updateLabInfo() 306 { 307 tbLightPos.Text = string.Format("{0},{1},{2},{3}", lightPos[0], lightPos[1], lightPos[2], lightPos[3]); 308 tbLookAt.Text = string.Format("{0},{1},{2},{3},{4},{5},{6},{7},{8}", lookatValue[0], lookatValue[1], lookatValue[2], 309 lookatValue[3], lookatValue[4], lookatValue[5], lookatValue[6], lookatValue[7], lookatValue[8]); 310 } 311 312 private void rbWhite_CheckedChanged(object sender, EventArgs e) 313 { 314 var rad = ((RadioButton)sender); 315 var lightType = this.cbxLightType.SelectedIndex; 316 if (rad.Checked) 317 { 318 switch (rad.Name) 319 { 320 case "rbWhite": 321 lightColor[lightType][0] = 1f; 322 lightColor[lightType][1] = 1f; 323 lightColor[lightType][2] = 1f; 324 lightColor[lightType][3] = 1f; 325 break; 326 case "rbRed": 327 lightColor[lightType][0] = 1f; 328 lightColor[lightType][1] = 0f; 329 lightColor[lightType][2] = 0f; 330 lightColor[lightType][3] = 1f; 331 break; 332 case "rbGreen": 333 lightColor[lightType][0] = 0f; 334 lightColor[lightType][1] = 1f; 335 lightColor[lightType][2] = 0f; 336 lightColor[lightType][3] = 1f; 337 break; 338 case "rbBlue": 339 lightColor[lightType][0] = 0f; 340 lightColor[lightType][1] = 0f; 341 lightColor[lightType][2] = 1f; 342 lightColor[lightType][3] = 1f; 343 break; 344 } 345 setLightColor(openGLControl.OpenGL); 346 } 347 } 348 349 private void cbxLightType_SelectedIndexChanged(object sender, EventArgs e) 350 { 351 var lightType = this.cbxLightType.SelectedIndex; 352 if (lightType >= 0) 353 judgeColor(lightColor[lightType]); 354 } 355 356 private void judgeColor(float[] color) 357 { 358 if (color[0] == 1f && color[1] == 1f && color[2] == 1f && color[3] == 1f) 359 rbWhite.Checked = true; 360 else if (color[0] == 1f && color[1] == 0f && color[2] == 0f && color[3] == 1f) 361 rbRed.Checked = true; 362 else if (color[0] == 0f && color[1] == 1f && color[2] == 0f && color[3] == 1f) 363 rbGreen.Checked = true; 364 else if (color[0] == 0f && color[1] == 0f && color[2] == 1f && color[3] == 1f) 365 rbBlue.Checked = true; 366 } 367 368 private void btnQ_Click(object sender, EventArgs e) 369 { 370 moveObject(radioButton1.Checked ? 0 : 1,((Button)sender).Name); 371 openGLControl_Resized(null, null); 372 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); 373 } 374 375 private void setViewDefaultValue() 376 { 377 if (isPerspective) 378 { 379 lookatValue = (double[])viewDefaultPos[0].Clone(); 380 } 381 else if (isFrontView) 382 { 383 lookatValue = (double[])viewDefaultPos[1].Clone(); 384 } 385 else if (isLeftView) 386 { 387 lookatValue = (double[])viewDefaultPos[2].Clone(); 388 } 389 else if (isTopView) 390 { 391 lookatValue = (double[])viewDefaultPos[3].Clone(); 392 } 393 } 394 395 private void btnDefaultPOS_Click(object sender, EventArgs e) 396 { 397 if (radioButton1.Checked) 398 { 399 setViewDefaultValue(); 400 } 401 else 402 { 403 lightPos = new float[] { -1, -3, 1, 1 }; 404 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); 405 } 406 openGLControl_Resized(null, null); 407 } 408 409 private void openGLControl_KeyDown(object sender, KeyEventArgs e) 410 { 411 string name = string.Empty; 412 switch (e.KeyCode) 413 { 414 case Keys.W: 415 name = "btnW"; 416 break; 417 case Keys.A: 418 name = "btnA"; 419 break; 420 case Keys.S: 421 name = "btnS"; 422 break; 423 case Keys.D: 424 name = "btnD"; 425 break; 426 case Keys.Q: 427 name = "btnQ"; 428 break; 429 case Keys.E: 430 name = "btnE"; 431 break; 432 } 433 moveObject(radioButton1.Checked ? 0 : 1, name); 434 openGLControl_Resized(null, null); 435 } 436 437 private void btnSetPos_Click(object sender, EventArgs e) 438 { 439 if (radioButton1.Checked) 440 { 441 double[] ary = tbLookAt.Text.Split(',').Select(s => Convert.ToDouble(s)).ToArray(); 442 lookatValue = ary; 443 openGLControl_Resized(null, null); 444 } 445 else 446 { 447 float[] ary = tbLightPos.Text.Split(',').Select(s => Convert.ToSingle(s)).ToArray(); 448 lightPos = ary; 449 openGLControl.OpenGL.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, ary); 450 } 451 452 } 453 454 455 } 456 }
有关灯光的代码如下粗体显示部分:
1 private void setLightColor(OpenGL gl) 2 { 3 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, lightColor[0]); 4 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, lightColor[1]); 5 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, lightColor[2]); 6 } 7 8 private void openGLControl_OpenGLInitialized(object sender, EventArgs e) 9 { 10 OpenGL gl = openGLControl.OpenGL; 11 12 //四个视图的缺省位置 13 viewDefaultPos.Add(new double[] { 1, 1, 2, 0, 0, 0, 0, 1, 0 }); //透视 14 viewDefaultPos.Add(new double[] { 0, 0, 2, 0, 0, 0, 0, 1, 0 }); //前视 15 viewDefaultPos.Add(new double[] { 5, 0, 0, 0, 0, 0, 0, 1, 0 }); //左视 16 viewDefaultPos.Add(new double[] { 0, 13, 0, -1, 0, 0, 0, 1, 0 }); //顶视 17 lookatValue =(double[])viewDefaultPos[0].Clone(); 18 19 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //环境光(ambient light) 20 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //漫射光(diffuse light) 21 lightColor.Add(new float[] { 1f, 1f, 1f, 1f }); //镜面反射光(specular light) 22 23 setLightColor(gl); 24 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, lightPos); 25 26 gl.Enable(OpenGL.GL_LIGHTING); 27 gl.Enable(OpenGL.GL_LIGHT0); 28 29 gl.ClearColor(0, 0, 0, 0); 30 }
第3,4,5行是创建灯光三个部分 环境光,漫射光,镜面反射光。
第24行是设定灯光的位置。
第26,27是让灯光开,有效。
灯光代码确实比较简单,这段代码其它部分没什么好说的,笔者按界面功能直述其意,朋友们应该很容易懂。
唯一要关注下的是:视图和灯光的参数修改是如何实时生效的。
原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/