SharpGL学习笔记(十二) 光源例子:解决光源场景中的常见问题
笔者学到光源这一节,遇到的问题就比较多了,收集了一些如下所述:
- (1) 导入的3ds模型,如果没有材质光照效果很奇怪.
如下图 -
- (2) 导入的3ds模型,有材质,灯光效果发暗,材质偏色,效果也很奇怪.
下图中是有灯光的,但效果惨不忍睹. -
- (3) 场景引入灯光后,场景中的物体的颜色就全部消失了,即合引入颜色材质,效果也是怪怪的.
如下图中的栅格,它原本应该是蓝色的. -
- (4) 场景中有物体引入材质后,整个场景的颜色就变得很奇怪
下图中球体引入材质后,整个场景的颜色就变得很奇怪了. -
(5) 导入的3ds模型,贴图颜色失真。
-
下图中的茶壶,柱体,地板的贴图分别对应上图材质图片,可以看到经过纹理映射后,贴图颜色失真。
这种问题并不是光源的问题,但是我也在这里一并列出来。 -
像这些问题,因为不好形容,网上也找不到合适的答案.群里的高手们也不屑回答这些菜鸟问题,因此只好自力更生了.
我先上一段演示场景的代码:
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 /// <summary> 14 /// 原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/ 15 /// </summary> 16 public partial class SharpGLForm : Form 17 { 18 private float rotation = 0.0f,rotation2=0f,rotation3=0f; 19 SharpGL.SceneGraph.Assets.Texture textureBox = new SharpGL.SceneGraph.Assets.Texture(); 20 21 float[] fLightPosition = new float[4] { 16f, 9f, -18f, 0f };// 光源位置 22 float[] fLightAmbient = new float[4] { 1f, 1f, 1f, 0f };// 环境光参数 23 float[] fLightDiffuse = new float[4] { 1f, 1f, 1f,0f };// 漫射光参数 24 25 float[] fLightPosition2 = new float[4] { -7f, 5f, 2f, 0f };// 光源位置 26 float[] fLightAmbient2 = new float[4] { 0f, 0f, 1f, 0f };// 环境光参数 27 float[] fLightDiffuse2 = new float[4] { 0f, 0f, 1f, 0f };// 漫射光参数 28 29 bool f1 = false; 30 31 public SharpGLForm() 32 { 33 InitializeComponent(); 34 } 35 36 private void openGLControl_OpenGLDraw(object sender, PaintEventArgs e) 37 { 38 OpenGL gl = openGLControl.OpenGL; 39 gl.Clear(OpenGL.GL_COLOR_BUFFER_BIT | OpenGL.GL_DEPTH_BUFFER_BIT); 40 gl.LoadIdentity(); 41 gl.Rotate(rotation3, 0, 1, 0); 42 drawGrid(gl); 43 drawLightPT(gl); 44 drawLightPT2(gl); 45 drawTextrueBox(gl, 1, 0, 2); 46 drawSphere(gl, 2, 20, 20, false); 47 48 moveLightA(gl); 49 rotation3+=0.1f; 50 } 51 52 53 private void moveLightA(OpenGL gl) 54 { 55 if (!f1) 56 --fLightPosition[0]; 57 else 58 ++fLightPosition[0]; 59 if (fLightPosition[0] > 15f) 60 { 61 f1 = !f1; 62 } 63 else if (fLightPosition[0] < -25f) 64 { 65 f1 = !f1; 66 } 67 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, fLightPosition);//光源位置 68 } 69 70 71 72 private void drawLightPT(OpenGL gl) 73 { 74 gl.PushMatrix(); 75 { 76 gl.Disable(OpenGL.GL_TEXTURE_2D); 77 gl.Color(0f,1f, 1f); 78 gl.Scale(0.2, 0.2, 0.2); 79 gl.Translate(fLightPosition[0]-5 , fLightPosition[1]+5, fLightPosition[2]); 80 drawBox(gl, 0, 0, 0); 81 } 82 gl.PopMatrix(); 83 } 84 85 private void drawLightPT2(OpenGL gl) 86 { 87 88 rotation2+=4f; 89 gl.PushMatrix(); 90 { 91 gl.LoadIdentity(); 92 gl.Disable(OpenGL.GL_TEXTURE_2D); 93 gl.Color(0f, 1f, 1f); 94 gl.Scale(0.2, 0.2, 0.2); 95 gl.Rotate(rotation2, 0, 1, 0); 96 gl.Translate(-28 , 8 , 5); 97 98 drawBox(gl, 0, 0, 0); 99 } 100 gl.PopMatrix(); 101 } 102 103 private void drawTextrueBox(OpenGL gl, float xPos, float yPos, float zPos) 104 { 105 rotation += 3.0f; 106 gl.PushMatrix(); 107 { 108 textureBox.Bind(gl); 109 gl.Enable(OpenGL.GL_TEXTURE_2D); 110 gl.Rotate(rotation, 0, 1, 0); 111 gl.Translate(-1, 2, -5); 112 gl.Scale(3, 3, 3); 113 drawBox(gl,xPos, yPos, zPos); 114 } 115 gl.PopMatrix(); 116 } 117 118 private void drawBox(OpenGL gl, float xPos, float yPos, float zPos) 119 { 120 gl.PushMatrix(); 121 gl.Translate(xPos, yPos, zPos); 122 123 gl.Begin(OpenGL.GL_QUADS); 124 125 //前 126 gl.TexCoord(0, 0); gl.Vertex(0, 0, 0); 127 gl.TexCoord(1, 0); gl.Vertex(-1, 0, 0); 128 gl.TexCoord(1, 1); gl.Vertex(-1, -1, 0); 129 gl.TexCoord(0, 1); gl.Vertex(0, -1, 0); 130 131 //底 132 gl.TexCoord(0, 0); gl.Vertex(0, 0, 0); 133 gl.TexCoord(1, 0); gl.Vertex(0, 0, -1); 134 gl.TexCoord(1, 1); gl.Vertex(-1, 0, -1); 135 gl.TexCoord(0, 1); gl.Vertex(-1, 0, 0); 136 137 //左 138 gl.TexCoord(0, 0); gl.Vertex(-1, 0, 0); 139 gl.TexCoord(1, 0); gl.Vertex(-1, 0, -1); 140 gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1); 141 gl.TexCoord(0, 1); gl.Vertex(-1, -1, 0); 142 143 //右 144 gl.TexCoord(0, 0); gl.Vertex(0, 0, 0); 145 gl.TexCoord(1, 0); gl.Vertex(0, 0, -1); 146 gl.TexCoord(1, 1); gl.Vertex(0, -1, -1); 147 gl.TexCoord(0, 1); gl.Vertex(0, -1, 0); 148 149 //后 150 gl.TexCoord(0, 0); gl.Vertex(0, 0, -1); 151 gl.TexCoord(1, 0); gl.Vertex(-1, 0, -1); 152 gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1); 153 gl.TexCoord(0, 1); gl.Vertex(0, -1, -1); 154 155 //顶 156 gl.TexCoord(0, 0); gl.Vertex(0, -1, 0); 157 gl.TexCoord(1, 0); gl.Vertex(0, -1, -1); 158 gl.TexCoord(1, 1); gl.Vertex(-1, -1, -1); 159 gl.TexCoord(0, 1); gl.Vertex(-1, -1, 0); 160 161 162 gl.End(); 163 gl.PopMatrix(); 164 165 } 166 167 void drawSphere(OpenGL gl, double radius, int segx, int segy, bool isLines) 168 { 169 170 gl.PushMatrix(); 171 gl.Disable(OpenGL.GL_TEXTURE_2D); 172 gl.Translate(-7f, -1f, 2f); 173 var sphere = gl.NewQuadric(); 174 175 if (isLines) 176 gl.QuadricDrawStyle(sphere, OpenGL.GL_LINES); 177 else 178 gl.QuadricDrawStyle(sphere, OpenGL.GL_QUADS); 179 gl.QuadricNormals(sphere, OpenGL.GLU_SMOOTH); 180 gl.QuadricOrientation(sphere, (int)OpenGL.GLU_OUTSIDE); 181 gl.QuadricTexture(sphere, (int)OpenGL.GLU_FALSE); 182 gl.Sphere(sphere, radius, segx, segy); 183 gl.DeleteQuadric(sphere); 184 gl.PopMatrix(); 185 186 } 187 188 189 190 191 void drawGrid(OpenGL gl) 192 { 193 //关闭纹理和光照 194 gl.Disable(OpenGL.GL_TEXTURE_2D); 195 gl.Disable(OpenGL.GL_LIGHTING); 196 197 //绘制过程 198 gl.PushAttrib(OpenGL.GL_CURRENT_BIT); //保存当前属性 199 gl.PushMatrix(); //压入堆栈 200 gl.Translate(0f, -2f, 0f); 201 gl.Color(0f, 0f, 1f); 202 203 //在X,Z平面上绘制网格 204 for (float i = -50; i <= 50; i += 1) 205 { 206 //绘制线 207 gl.Begin(OpenGL.GL_LINES); 208 { 209 if (i == 0) 210 gl.Color(0f, 1f, 0f); 211 else 212 gl.Color(0f, 0f, 1f); 213 214 //X轴方向 215 gl.Vertex(-50f, 0f, i); 216 gl.Vertex(50f, 0f, i); 217 //Z轴方向 218 gl.Vertex(i, 0f, -50f); 219 gl.Vertex(i, 0f, 50f); 220 221 } 222 gl.End(); 223 } 224 gl.PopMatrix(); 225 gl.PopAttrib(); 226 gl.Enable(OpenGL.GL_LIGHTING); 227 } 228 229 private void openGLControl_OpenGLInitialized(object sender, EventArgs e) 230 { 231 OpenGL gl = openGLControl.OpenGL; 232 textureBox.Create(gl, "image.bmp"); 233 234 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, fLightAmbient);//环境光源 235 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, fLightDiffuse);//漫射光源 236 gl.Light(OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, fLightPosition);//光源位置 237 238 gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_AMBIENT, fLightAmbient2);//环境光源 239 gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_DIFFUSE, fLightDiffuse2);//漫射光源 240 gl.Light(OpenGL.GL_LIGHT1, OpenGL.GL_POSITION, fLightPosition2);//光源位置 241 242 gl.Enable(OpenGL.GL_LIGHTING);//开启光照 243 gl.Enable(OpenGL.GL_LIGHT0); 244 gl.Enable(OpenGL.GL_LIGHT1); 245 246 gl.Enable(OpenGL.GL_NORMALIZE); 247 gl.ClearColor(0, 0, 0, 0); 248 249 } 250 251 private void openGLControl_Resized(object sender, EventArgs e) 252 { 253 OpenGL gl = openGLControl.OpenGL; 254 gl.MatrixMode(OpenGL.GL_PROJECTION); 255 gl.LoadIdentity(); 256 gl.Perspective(60.0f, (double)Width / (double)Height, 0.01, 100.0); 257 gl.LookAt(-2, 3, -7, -2, 0, 0, 0, 1, 0); 258 gl.MatrixMode(OpenGL.GL_MODELVIEW); 259 } 260 261 262 263 } 264 }
效果如下图:
场景中有两个光源,一个在X方向左右运行,一个绕点点在转圈.
Box上了材质贴图,球体没有材质.
从效果上看,已经解决了开头所述的问题(3),(4),我们提下关键点在哪里:
1. 第194,195行必须有,否则画栅格时会受到场景中的灯光,或者材质设定的影响,栅格原来颜色就没有了.
这个其实就是因为OpenGL是个状态机,其它部分代码改变了某些状态,画栅格时就会继承改变.
2. 同样原道理,第171行必须关闭纹理,否会受到Box材质设置状态的影响,球就不会是白色的了.
3. 第246行必须有,它用来自动归一化法线方向,因为光照效果由顶点和法线方向决定.
这就是问题(1),(2)之所以有问题的原因.
而导入的3ds模型,其画三角形的函数中也需要注意Normal()函数的向量值有没有方向问题.
下图是修正了法线后的光照效果,可以看到是正常的.
4. 对于问题5,因为在检查了灯光与贴图后都是正常的, 所以问题只会在出现在贴图的时候。
经检测,是在导入3ds模形的代码中,关于读取贴图的一个函数Build2DMipmaps()中,把RGB换BGR即可。
GL.Build2DMipmaps(OpenGL.GL_TEXTURE_2D, 3, image.Width, image.Height, OpenGL.GL_BGR, OpenGL.GL_UNSIGNED_BYTE, bitmapdata.Scan0);
可以看到,效果正常了。
5. 最后一个困绕笔者的问题是物体旋转中心点的问题.这个话题跟灯光无关,在这个场景中恰好碰到了这个问题,发现原来是知识上的一个盲点.
演示场景中,你会看到Box并不是绕世界坐标系的原点(绿色线的交汇点)在转,而是沿指定位置为轴心在转.
是绕世界坐标原点转,还是绕你指定的坐标为轴转动,关键在于你是先Translate(),还是先Rotate(). 读者可以参考下演示代码,然后自己尝试一下就知道了.
原创文章,出自"博客园, 猪悟能'S博客" : http://www.cnblogs.com/hackpig/