按下b,然后同时按下down和right方向键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 |
#include "stdafx.h" #include <iostream> #define GLUT_DISABLE_ATEXIT_HACK #include "glut.h" #include "glaux.h" using namespace std; bool g_bBlend;// 是否混合? bool g_bKeyB = false;// B 键按下了么? bool g_bLight =false;// 光源的开/ 关 bool g_bKeyL =false;// L键按下了么? bool g_bKeyF =false;// F键按下了么? GLfloat g_fXrot =5; // X 旋转 GLfloat g_fYrot =5;// Y 旋转 GLfloat g_fXSpeed =0;// X 旋转速度 GLfloat g_fYSpeed =0;// Y 旋转速度 GLfloat g_fZ =-5.0f;// 深入屏幕的距 // 接着设置用来创建光源的数组。我们将使用两种不同的光。第一种称为环境光。环境光来自于四面 // 八方。所有场景中的对象都处于环境光的照射中。第二种类型的光源叫做漫射光。漫射光由特定的 // 光源产生,并在您的场景中的对象表面上产生反射。处于漫射光直接照射下的任何对象表面都变得 // 很亮,而几乎未被照射到的区域就显得要暗一些。这样在我们所创建的木板箱的棱边上就会产生的很不错的阴影效果。 // 创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。 // 因此,下面的代码我们得到的是半亮(0.5f)的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。 GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };// 环境光参数 //下一行代码我们生成最亮的漫射光。所有的参数值都取成最大值1.0f 。它将照在我们木板箱的前面,看起来挺好。 GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };// 漫射光参数 // 最后我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。由于 // 我们想要光线直接照射在木箱的正面,所以XY轴上的位移都是0.0f 。第三个值是Z 轴上的位移。为 // 了保证光线总在木箱的前面,所以我们将光源的位置朝着观察者( 就是您哪。) 挪出屏幕。我们通常 // 将屏幕也就是显示器的屏幕玻璃所处的位置称作Z 轴的0.0f 点。所以Z 轴上的位移最后定为2.0f 。假 // 如您能够看见光源的话,它就浮在您显示器的前方。当然,如果木箱不在显示器的屏幕玻璃后面的 // 话,您也无法看见箱子。 GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };// 光源位置 // filter 变量跟踪显示时所采用的纹理类型。 //第一种纹理(texture[0])使用gl_nearest(不光滑) 滤波方式构建。 //第二种纹理 (texture[1])使用gl_linear(线性滤波)方式,离屏幕越近的图像看起来就越光滑。 //第三种纹理 (texture[2])使用mipmapped滤波方式,这将创建一个外观十分优秀的纹理。 //根据我们的使用类型,filter 变量的值分别等于0,1或2。下面我们从第一种纹理开始。 //GLuint texture[3] 为三种不同纹理分配储存空间。它们分别位于在 texture[0], texture[1]和texture[2] 中。 GLuint filter; // 滤波类型 GLuint texture[3]; // 3种纹理的储存空间 //现在载入一个位图,并用它创建三种不同的纹理。这一课使用glaux辅助库来载入位图,因此在编译 //时您应该确认是否包含了glaux库。我知道Delphi 和VC++都包含了glaux库,但别的语言不能保证都 //有。『译者注:glaux是OpenGL辅助库,根据OpenGL的跨平台特性,所有平台上的代码都应通用。 //但辅助库不是正式的OpenGL标准库,没有出现在所有的平台上。但正好在Win32平台上可用。 AUX_RGBImageRec *TextureImage=new AUX_RGBImageRec; //辅助函数 AUX_RGBImageRec *LoadBMP(char* FileName) { FILE *file = 0; if(!FileName) return 0; file = fopen(FileName,"r"); if(file) { fclose(file); cout<<"start open!"; return auxDIBImageLoad(FileName); //如果此处出现Fail to open DIB是字符串的问题 //修改项目配置,字符集中将Unicode改成多级字符即可 } return 0; } void LoadTexture() { //图像的宽和高必须是2的n次方 memset(TextureImage,0,sizeof(void *)*1); //现在载入位图,并将其转换为纹理。 TextureImage = LoadBMP("E:\\pic\\box200.bmp"); if(0 == TextureImage) { cout<<"error bmp"<<endl; return; } //告诉OpenGL我们要创建三个纹理,它们将存放在texture[0],texture[1]和texture[2]中 glGenTextures(3,&texture[0]); //第六课中我们使用了线性滤波的纹理贴图。这需要机器有相当高的处理能力,但它们看起来很不 //错。这一课中,我们接着要创建的第一种纹理使用 GL_NEAREST 方式。从原理上讲,这种方式没 //有真正进行滤波。它只占用很小的处理能力,看起来也很差。唯一的好处是这样我们的工程在很快 //和很慢的机器上都可以正常运行。 //您会注意到我们在 MIN 和 MAG 时都采用了GL_NEAREST,你可以混合使用 GL_NEAREST和 //GL_LINEAR。纹理看起来效果会好些,但我们更关心速度,所以全采用低质量贴 //图。MIN_FILTER在图像绘制时小于贴图的原始尺寸时采用。MAG_FILTER在图像绘制时大于贴图 //的原始尺寸时采用。 //###创建 Nearest 滤波贴图### glBindTexture(GL_TEXTURE_2D, texture[0]); // 告诉OpenGL将纹理名字texture绑定到纹理目标上。 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); //glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data); //###创建线性滤波纹理### glBindTexture(GL_TEXTURE_2D, texture[1]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data); //glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data); //下面是创建纹理的新方法。 Mipmapping! //您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会 //丢失。刚才还很不错的图案变得很难看。当您告诉OpenGL创建一个 mipmapped 的纹理 //后,OpenGL将尝试创建不同尺寸的高质量纹理。当您向屏幕绘制一个 mipmapped 纹理的时 //候,OpenGL将选择它已经创建的外观最佳的纹理( 带有更多细节) 来绘制,而不仅仅是缩放原先的图 //像( 这将导致细节丢失) 。 //我曾经说过有办法可以绕过OpenGL对纹理宽度和高度所加的限制——64、128、256,等等。 //办法就是 gluBuild2DMipmaps 。据我的发现,您可以使用任意的位图来创建纹理。OpenGL将自动将它缩放到正常的大小。 //因为是第三个纹理,我们将它存到texture[2] 。这样本课中的三个纹理全都创建好了。 glBindTexture(GL_TEXTURE_2D,texture[2]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST); //下面一行生成 mipmapped 纹理。 gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage->sizeX, TextureImage->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage->data); //释放前面用来存放位图数据的内存 if(TextureImage) { if(TextureImage->data) { free(TextureImage->data); } free(TextureImage); } } void DrawScene(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);//清除屏幕及其缓存 glLoadIdentity();//重置当前模型观察矩阵 glTranslatef(0.0f,0.0f,g_fZ); // 移入/ 移出屏幕 glRotatef(g_fXrot,1.0f,0.0f,0.0f);// 绕X轴旋转 glRotatef(g_fYrot,0.0f,1.0f,0.0f);// 绕Y轴旋转 g_fXrot += g_fXSpeed; g_fYrot += g_fYSpeed; // 下一行代码选择我们使用的纹理。如果您在您的场景中使用多个纹理,您应该使用来 // glBindTexture(GL_TEXTURE_2D, texture[ 所使用纹理对应的数字 ]) 选择要绑定的纹理。当您想改变 // 纹理时,应该绑定新的纹理。有一点值得指出的是,您不能在 glBegin() 和 glEnd() 之间绑定纹理, // 必须在 glBegin() 之前或 glEnd() 之后绑定。注意我们在后面是如何使用 glBindTexture 来指定和绑定纹理的。 glBindTexture(GL_TEXTURE_2D, texture[filter]);// 选择纹理 // 为了将纹理正确的映射到四边形上,您必须将纹理的右上角映射到四边形的右上角,纹理的左上角 // 映射到四边形的左上角,纹理的右下角映射到四边形的右下角,纹理的左下角映射到四边形的左下 // 角。如果映射错误的话,图像显示时可能上下颠倒,侧向一边或者什么都不是。 glBegin(GL_QUADS);//////////////////////////// //glNormal3f 是这一课的新东西。Normal就是法线的意思,所谓法线是指经过面( 多边形)上的一点且 //垂直于这个面多边形的直线。使用光源的时候必须指定一条法线。法线告诉 这个多边形的 //朝向,并指明多边形的正面和背面。如果没有指定法线,什么怪事情都可能发生:不该照亮的面被 //照亮了,多边形的背面也被照亮....。对了,法线应该指向多边形的外侧。 // //看着木箱的前面您会注意到法线与Z 轴正向同向。这意味着法线正指向观察者-您自己。这正是我 //们所希望的。对于木箱的背面,也正如我们所要的,法线背对着观察者。如果立方体沿着X 或Y 轴 //转个180度的话,前侧面的法线仍然朝着观察者,背面的法线也还是背对着观察者。换句话说,不 //管是哪个面,只要它朝着观察者这个面的法线就指向观察者。由于光源紧邻观察者,任何时候法线 //对着观察者时,这个面就会被照亮。并且法线越朝着光源,就显得越亮一些。如果您把观察点放到 //立方体内部,你就会法线里面一片漆黑。因为法线是向外指的。如果立方体内部没有光源的话,当 //然是一片漆黑。 //前侧面 glNormal3f( 0.0f, 0.0f, 1.0f);// 法线指向观察者 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); //纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); //纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); //左下 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); //右下 //后侧面 glNormal3f( 0.0f, 0.0f,-1.0f);// 法线背向观察者 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); //右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); //右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 //顶面 glNormal3f( 0.0f, 1.0f, 0.0f); // 法线向上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 // 底面 glNormal3f( 0.0f,-1.0f, 0.0f); // 法线朝下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 // 右面 glNormal3f( 1.0f, 0.0f, 0.0f);// 法线朝右 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 // 左面 glNormal3f(-1.0f, 0.0f, 0.0f); // 法线朝左 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glEnd();/////////////////////////////// glFlush(); } void ReSizeFunc(int width,int height) { glViewport(0,0,width,height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45,width/height,0.1,100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } void KeyBoardFunc(unsigned char key, int x, int y) { cout<<"curren key"<<(int)key<<" what:"<<key<<endl; if(VK_ESCAPE == key) { exit(0); } if((key == 'l') && !g_bKeyL) { g_bKeyL = true; g_bLight = !g_bLight; if(!g_bLight) { glDisable(GL_LIGHTING);// 禁用光源 cout<<"禁用光源"<<endl; } else { glEnable(GL_LIGHTING);// 启用光源 cout<<"启用光源"<<endl; } } //下面的代码查看是否松开了"L" 键。如果松开,变量lp 将设为false 。这意味着"L" 键没有按下。如果 //不作此检查,光源第一次打开之后,就无法再关掉了。计算机会以为"L" 键一直按着呢。 if(key != 'l') { g_bKeyL = false; } if((key == 'f') && !g_bKeyF) { g_bKeyF = true; filter += 1; if(filter>2) { filter = 0; } cout<<"filter="<<filter<<endl; } if(key != 'f') { g_bKeyF = false; } if(('b' == key)&&!g_bKeyB)// B 健按下且bp为 FALSE 么? { g_bKeyB = true;// 若是, bp 设为 TRUE g_bBlend = !g_bBlend;// 切换混合选项的 TRUE / FALSE if(g_bBlend)// 混合打开了么? { glEnable(GL_BLEND);// 打开混合 glDisable(GL_DEPTH_TEST);// 关闭深度测试 } else { glDisable(GL_BLEND);// 关闭混合 glEnable(GL_DEPTH_TEST);// 关闭混合 } } if('b'!=key)// B 键松开了么? { g_bKeyB = false;// 若是, bp 设为 FALSE } DrawScene(); } void SpecialKeys(int key, int x, int y) { cout<<"curren special key"<<(int)key<<" what:"<<key<<endl; //现在检查方向键。按下左右方向键xspeed 相应减少或增加。按下上下方向键yspeed 相应减少或增 //加。记住在以后的教程中如果xspeed 、yspeed 的值增加的话,立方体就转的更快。如果一直按着某 //个方向键,立方体会在那个方向上转的越快。 if(GLUT_KEY_UP == key) { g_fXSpeed -= 0.01; } if(GLUT_KEY_DOWN == key) { g_fXSpeed += 0.01; } if(GLUT_KEY_LEFT == key) { g_fYSpeed -= 0.01; } if(GLUT_KEY_RIGHT == key) { g_fYSpeed += 0.01; } //接着四行检查PageDown键是否按下,若是的话,增加z 变量的值。这样DrawGLScene 函数中包含 //的glTranslatef(0.0f,0.0f,z) 调用将使木箱向着观察者移近一点。 if(GLUT_KEY_PAGE_DOWN == key) { g_fZ+=0.02; } DrawScene(); } int Init() { LoadTexture(); glEnable(GL_TEXTURE_2D);// 启用纹理映射 glShadeModel(GL_SMOOTH);// 启用阴影平滑 glClearColor(0,0,0,0); // 黑色背景 glClearDepth(1); // 设置深度缓存 glEnable(GL_DEPTH_TEST);// 启用深度测试 glDepthFunc(GL_LEQUAL); // 所作深度测试的类型 glHint(GL_PERSPECTIVE_CORRECTION_HINT,GL_NICEST);// 真正精细的透视修正 glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // 设置环境光 glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // 设置漫射光 glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); // 设置光源位置 //我们启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对 //光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。 glEnable(GL_LIGHT1); //第一行以全亮度绘制此物体,并对其进行50% 的alpha混合( 半透 // 明) 。当混合选项打开时,此物体将会产生50% 的透明效果。第二行设置所采用的混合类型。 // Rui Martins 的补充: alpha 通道的值为 0.0 意味着物体材质是完全透明的。1.0 则意味着完全不透明。 glColor4f(1,1,1,0.5);// 全亮度, 50% Alpha 混合 glBlendFunc(GL_SRC_ALPHA,GL_ONE);// 基于源象素alpha通道值的半透明混合函数 return 0; } int main(int argc, char *argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowPosition(100, 100); glutInitWindowSize(500, 500); glutCreateWindow("第八个OpenGL程序"); glutReshapeFunc(ReSizeFunc); glutDisplayFunc(DrawScene); glutKeyboardFunc(KeyBoardFunc); glutSpecialFunc(SpecialKeys); Init(); glutMainLoop(); return 0; } |