效果图如上:
功能:按“m”“t”“u”3个按键,可以控制运行状态。
步骤:略。
实际代码如下:
main.c
1 /********************************************************************** 2 3 Fountain 4 5 June, 7th, 2000 6 7 This tutorial was written by Philipp Crocoll 8 Contact: 9 philipp.crocoll@web.de 10 www.codecolony.de 11 12 Every comment would be appreciated. 13 14 If you want to use parts of any code of mine: 15 let me know and 16 use it! 17 18 The used texture water.bmp is from www.freetextures.com 19 20 *********************************************************************** 21 22 23 ESC : Exit 24 m : turn the movement up/down on/off 25 t : turn the turning around the fountain on/off 26 u : turn the updating of the scene on/off 27 28 **********************************************************************/ 29 30 #pragma comment ( lib , "glaux.lib" ) 31 32 #include <GL\glut.h> //includes gl.h and glu.h 33 #include <GL\glaux.h> //load the texture 34 #include <stdlib.h> //random function 35 #include <math.h> //sine and cosine functions 36 37 #define PI 3.1415265359 38 #define RandomFactor 2.0 //cannot be variable, because it is only use in InitFountain(), 39 //zero for perfect physical 40 41 GLint ListNum; //The number of the diplay list 42 43 GLfloat OuterRadius = 1.2; 44 GLfloat InnerRadius = 1.0; 45 GLint NumOfVerticesStone = 16; //only a quarter of the finally used vertices 46 GLfloat StoneHeight = 0.5; 47 GLfloat WaterHeight = 0.45; 48 49 GLint Turned = 0; 50 bool DoTurn = true; 51 bool DoMoveUp = true; 52 bool DoUpdateScene = true; 53 GLfloat MoveUp = 0.8; 54 GLfloat ChangeMoveUp = 0.01; 55 56 //The variables for the fountain are below 57 58 59 60 //////////////////////////////////////////////////////////////////////////////// 61 62 63 struct SVertex 64 { 65 GLfloat x,y,z; 66 }; 67 68 //It's not the best style to put classes into the main file, 69 //but here it is easier for you and me! 70 class CDrop 71 { 72 private: 73 GLfloat time; //How many steps the drop was "outside", when it falls into the water, time is set back to 0 74 SVertex ConstantSpeed; //See the doc for explanation of the physics 75 GLfloat AccFactor; 76 public: 77 void SetConstantSpeed (SVertex NewSpeed); 78 void SetAccFactor(GLfloat NewAccFactor); 79 void SetTime(GLfloat NewTime); 80 void GetNewPosition(SVertex * PositionVertex); //increments time, gets the new position 81 }; 82 83 void CDrop::SetConstantSpeed(SVertex NewSpeed) 84 { 85 ConstantSpeed = NewSpeed; 86 } 87 88 void CDrop::SetAccFactor (GLfloat NewAccFactor) 89 { 90 AccFactor = NewAccFactor; 91 } 92 93 void CDrop::SetTime(GLfloat NewTime) 94 { 95 time = NewTime; 96 } 97 98 void CDrop::GetNewPosition(SVertex * PositionVertex) 99 { 100 SVertex Position; 101 time += 0.2; 102 Position.x = ConstantSpeed.x * time; 103 Position.y = ConstantSpeed.y * time - AccFactor * time * time; 104 Position.z = ConstantSpeed.z * time; 105 PositionVertex->x = Position.x; 106 PositionVertex->y = Position.y + WaterHeight; 107 PositionVertex->z = Position.z; 108 if (Position.y < 0.0) 109 { 110 /*the drop has fallen into the water. The problem is now, that we cannot 111 set time to 0.0, because if there are more "DropsPerRay" than "TimeNeeded" (See InitFountain()) 112 several drops would be seen as one. Check it out. 113 */ 114 time = time - int(time); 115 if (time > 0.0) time -= 0.2; 116 } 117 118 } 119 120 //////////////////////////////////////////////////////////////////////////////// 121 122 CDrop * FountainDrops; 123 SVertex * FountainVertices; 124 GLint Steps = 3; //a fountain has several steps, each with its own height 125 GLint RaysPerStep = 4; 126 GLint DropsPerRay = 10; 127 GLint DropsComplete = Steps * RaysPerStep * DropsPerRay; 128 GLfloat AngleOfDeepestStep =60; 129 GLfloat AccFactor = 0.016; 130 131 //////////////////////////////////////////////////////////////////////////////// 132 133 void CreateList(void) 134 { 135 GLuint ID; 136 _AUX_RGBImageRec *Image; 137 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 138 glGenTextures(1,&ID); //开辟一个纹理内存,内存指向ID 139 glBindTexture( GL_TEXTURE_2D, ID);//将创建的纹理内存指向的内容绑定到纹理对象GL_TEXTURE_2D上,经过这句代码后,以后对 140 //GL_TEXTURE_2D的操作的任何操作都同时对应与它所绑定的纹理对象 141 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_REPEAT); // s方向,即x轴方向,重复 142 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_REPEAT); //t方向,即y轴方向,重复 143 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_NEAREST); //当所显示的纹理比加载进来的纹理大时,采用GL_LINEAR的方法来处理 144 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_NEAREST); //当所显示的纹理比加载进来的纹理小时,采用GL_LINEAR的方法来处理 145 glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); // 贴图模式:光照无效 146 Image = auxDIBImageLoadA( "water.bmp" ); 147 gluBuild2DMipmaps( GL_TEXTURE_2D, 3, 148 Image->sizeX, 149 Image->sizeY, 150 GL_RGB, 151 GL_UNSIGNED_BYTE, 152 Image->data); 153 delete Image; 154 SVertex * Vertices = new SVertex[NumOfVerticesStone*4]; //allocate mem for the required vertices 保存石头围的总块数的4个顶点 155 ListNum = glGenLists(1); 156 GLint i; 157 for (i = 0; i<NumOfVerticesStone; i++) 158 { 159 Vertices[i].x = cos(2.0 * PI / NumOfVerticesStone * i) * OuterRadius; 160 Vertices[i].y = StoneHeight; //Top 161 Vertices[i].z = sin(2.0 * PI / NumOfVerticesStone * i) * OuterRadius; 162 } 163 for (i = 0; i<NumOfVerticesStone; i++) 164 { 165 Vertices[i + NumOfVerticesStone*1].x = cos(2.0 * PI / NumOfVerticesStone * i) * InnerRadius; 166 Vertices[i + NumOfVerticesStone*1].y = StoneHeight; //Top 167 Vertices[i + NumOfVerticesStone*1].z = sin(2.0 * PI / NumOfVerticesStone * i) * InnerRadius; 168 } 169 for (i = 0; i<NumOfVerticesStone; i++) 170 { 171 Vertices[i + NumOfVerticesStone*2].x = cos(2.0 * PI / NumOfVerticesStone * i) * OuterRadius; 172 Vertices[i + NumOfVerticesStone*2].y = 0.0; //Bottom 173 Vertices[i + NumOfVerticesStone*2].z = sin(2.0 * PI / NumOfVerticesStone * i) * OuterRadius; 174 } 175 for (i = 0; i<NumOfVerticesStone; i++) 176 { 177 Vertices[i + NumOfVerticesStone*3].x = cos(2.0 * PI / NumOfVerticesStone * i) * InnerRadius; 178 Vertices[i + NumOfVerticesStone*3].y = 0.0; //Bottom 179 Vertices[i + NumOfVerticesStone*3].z = sin(2.0 * PI / NumOfVerticesStone * i) * InnerRadius; 180 } 181 glNewList(ListNum, GL_COMPILE); 182 183 glBegin(GL_QUADS); 184 //ground quad: // 用于衬托的四方形地板 185 glColor3f(0.6,0.6,0.6); 186 glVertex3f(-OuterRadius*1.3,0.0,OuterRadius*1.3); 187 glVertex3f(-OuterRadius*1.3,0.0,-OuterRadius*1.3); 188 glVertex3f(OuterRadius*1.3,0.0,-OuterRadius*1.3); 189 glVertex3f(OuterRadius*1.3,0.0,OuterRadius*1.3); 190 //stone: // 用于围绕的石头 191 for (int j = 1; j < 3; j++) 192 { 193 if (j == 1) glColor3f(0.8,0.4,0.4); 194 if (j == 2) glColor3f(0.4,0.2,0.2); 195 for (i = 0; i<NumOfVerticesStone-1; i++) // 石头围的外侧壁和顶层。石围块的总个数。 196 { 197 glVertex3fv(&Vertices[i+NumOfVerticesStone*j].x); 198 glVertex3fv(&Vertices[i].x); 199 glVertex3fv(&Vertices[i+1].x); 200 201 glVertex3fv(&Vertices[i+NumOfVerticesStone*j+1].x); 202 } 203 glVertex3fv(&Vertices[i+NumOfVerticesStone*j].x); 204 glVertex3fv(&Vertices[i].x); 205 glVertex3fv(&Vertices[0].x); 206 glVertex3fv(&Vertices[NumOfVerticesStone*j].x); 207 } 208 glColor3f(0.4,0.2,0.2); 209 for (i = 0; i<NumOfVerticesStone-1; i++) // 石头围的内侧壁 210 { 211 glVertex3fv(&Vertices[i+NumOfVerticesStone*3].x); 212 glVertex3fv(&Vertices[i+NumOfVerticesStone].x); 213 glVertex3fv(&Vertices[i+NumOfVerticesStone+1].x); 214 glVertex3fv(&Vertices[i+NumOfVerticesStone*3+1].x); 215 } 216 glVertex3fv(&Vertices[i+NumOfVerticesStone*3].x); 217 glVertex3fv(&Vertices[i+NumOfVerticesStone].x); 218 glVertex3fv(&Vertices[NumOfVerticesStone].x); 219 glVertex3fv(&Vertices[NumOfVerticesStone*3].x); 220 221 glEnd(); 222 223 //The "water": 224 glTranslatef(0.0,WaterHeight - StoneHeight, 0.0); // 从石头围的高度下降到水面的高度 225 glBindTexture(GL_TEXTURE_2D, ID); 226 glEnable(GL_TEXTURE_2D); 227 glBegin(GL_POLYGON); 228 for (i = 0; i<NumOfVerticesStone; i++) // 白色的水波圈 229 { 230 glTexCoord2f( 0.5+cos(i/GLfloat(NumOfVerticesStone)*360.0*PI/180.0)/2.0, 231 0.5-sin(i/GLfloat(NumOfVerticesStone)*360.0*PI/180.0)/2.0); 232 233 glVertex3fv(&Vertices[i+NumOfVerticesStone].x); 234 } 235 236 glEnd(); 237 glDisable(GL_TEXTURE_2D); 238 239 glEndList(); 240 } 241 242 GLfloat GetRandomFloat(GLfloat range) 243 { 244 return (GLfloat)rand() / (GLfloat)RAND_MAX * range * RandomFactor; 245 } 246 247 void InitFountain(void) 248 { 249 //This function needn't be and isn't speed optimized 250 FountainDrops = new CDrop [ DropsComplete ]; // 掉落物 251 FountainVertices = new SVertex [ DropsComplete ]; // 最高点 252 SVertex NewSpeed; 253 GLfloat DropAccFactor; //different from AccFactor because of the random change // 随机数 254 GLfloat TimeNeeded; 255 GLfloat StepAngle; //Angle, which the ray gets out of the fountain with // 喷泉的向下射线 256 GLfloat RayAngle; //Angle you see when you look down on the fountain 257 GLint i,j,k; 258 for (k = 0; k <Steps; k++) 259 { 260 for (j = 0; j < RaysPerStep; j++) 261 { 262 for (i = 0; i < DropsPerRay; i++) 263 { 264 DropAccFactor = AccFactor + GetRandomFloat(0.0005); 265 StepAngle = AngleOfDeepestStep + (90.0-AngleOfDeepestStep) 266 * GLfloat(k) / (Steps-1) + GetRandomFloat(0.2+0.8*(Steps-k-1)/(Steps-1)); 267 //This is the speed caused by the step: 268 NewSpeed.x = cos ( StepAngle * PI / 180.0) * (0.2+0.04*k); 269 NewSpeed.y = sin ( StepAngle * PI / 180.0) * (0.2+0.04*k); 270 //This is the speed caused by the ray: 271 RayAngle = (GLfloat)j / (GLfloat)RaysPerStep * 360.0; 272 //for the next computations "NewSpeed.x" is the radius. Care! Dont swap the two 273 //lines, because the second one changes NewSpeed.x! 274 NewSpeed.z = NewSpeed.x * sin ( RayAngle * PI /180.0); 275 NewSpeed.x = NewSpeed.x * cos ( RayAngle * PI /180.0); 276 //Calculate how many steps are required, that a drop comes out and falls down again 277 TimeNeeded = NewSpeed.y/ DropAccFactor; 278 FountainDrops[i+j*DropsPerRay+k*DropsPerRay*RaysPerStep].SetConstantSpeed ( NewSpeed ); 279 FountainDrops[i+j*DropsPerRay+k*DropsPerRay*RaysPerStep].SetAccFactor (DropAccFactor); 280 FountainDrops[i+j*DropsPerRay+k*DropsPerRay*RaysPerStep].SetTime(TimeNeeded * i / DropsPerRay); 281 } 282 } 283 } 284 285 286 //Tell OGL that we'll use the vertex array function 287 glEnableClientState(GL_VERTEX_ARRAY); // 开启顶点数组 288 //Pass the date position 289 glVertexPointer( 3, //x,y,z-components 290 GL_FLOAT, //data type of SVertex 291 0, //the vertices are tightly packed 292 FountainVertices); 293 294 } 295 296 297 void DrawFountain(void) 298 { 299 glColor4f(0.8,0.8,0.8,0.8); 300 if (DoUpdateScene) 301 for (int i = 0; i < DropsComplete; i++) 302 { 303 FountainDrops[i].GetNewPosition(&FountainVertices[i]); 304 } 305 glDrawArrays( GL_POINTS, 306 0, 307 DropsComplete); 308 309 } 310 311 static long long times = 0; 312 void Display(void) 313 { 314 times++; // 延迟 315 if(times>1000000) 316 times =0; 317 if(times% 1000000== 0) 318 { 319 320 if (DoTurn) Turned += 2; 321 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 322 glLoadIdentity(); //Load a new modelview matrix -> we can apply new transformations 323 glTranslatef(0.0,0.0,-5.0); 324 //glRotatef(90.0,1.0,0.0,0.0); //Enable this line to look down on the fountain 325 glRotatef((GLfloat)Turned,0.0,1.0,0.0); 326 if (DoMoveUp) 327 { 328 MoveUp += ChangeMoveUp; 329 if (MoveUp>= 1.5 || MoveUp<=0.8) ChangeMoveUp = -ChangeMoveUp; 330 } 331 glTranslatef(0.0,-MoveUp,0.0); 332 glPushMatrix(); 333 glCallList(ListNum); 334 glPopMatrix(); 335 DrawFountain(); 336 glFlush(); //Finish rendering 337 glutSwapBuffers(); //Swap the buffers ->make the result of rendering visible 338 339 }// timeout 340 } 341 void Reshape(int x, int y) 342 { 343 if (y == 0 || x == 0) return; //Nothing is visible then, so return 344 //Set a new projection matrix 345 glMatrixMode(GL_PROJECTION); 346 glLoadIdentity(); 347 //Angle of view:40 degrees 348 //Near clipping plane distance: 0.5 349 //Far clipping plane distance: 20.0 350 gluPerspective(40.0,(GLdouble)x/(GLdouble)y,0.5,20.0); 351 glMatrixMode(GL_MODELVIEW); 352 glViewport(0,0,x,y); //Use the whole window for rendering 353 //Adjust point size to window size 354 glPointSize(GLfloat(x)/200.0); 355 } 356 void KeyDown(unsigned char key, int x, int y) 357 { 358 switch(key) 359 { 360 case 27: //ESC 361 exit(0); 362 break; 363 case 't': 364 DoTurn = !DoTurn; 365 break; 366 case 'm': 367 DoMoveUp = !DoMoveUp; 368 break; 369 case 'u': 370 DoUpdateScene = !DoUpdateScene; 371 break; 372 } 373 } 374 375 int main(int argc, char **argv) 376 { 377 //Initialize GLUT 378 glutInit(&argc, argv); 379 //Lets use doublebuffering, RGB(A)-mode and a depth buffer 380 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); 381 glutInitWindowSize(300,300); 382 //Create a window with rendering context and everything else we need 383 glutCreateWindow("Fountain"); // 喷泉 384 //Init some state variables: 385 glEnable(GL_DEPTH_TEST); 386 glClearColor(0.1,0.1,0.1,0.0); // 背景颜色 387 388 glHint(GL_POINT_SMOOTH,GL_NICEST); //告诉opengl以显示效果为重,速度不重要 (+) 389 390 glEnable(GL_POINT_SMOOTH); // 开启点平滑 391 glEnable(GL_BLEND); // 混合,在做透明效果时,必须要启用 392 393 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // 表示用1.0减去源颜色的alpha值来作为因子。 394 glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); //also try GL_LINE GL_FILL 用于控制多边形的显示方式 395 //Init the foundtain 396 InitFountain(); 397 //Create the display list 398 CreateList(); 399 400 //Assign the event-handling routines 401 glutDisplayFunc(Display); 402 glutReshapeFunc(Reshape); 403 glutKeyboardFunc(KeyDown); 404 glutIdleFunc(Display); //If there is no msg, we have to repaint 405 //Let GLUT get the msgs and tell us the ones we need 406 glutMainLoop(); 407 return 0; 408 }
原始代码来源: www.codecolony.de