NEHE,估计知道OpenGL的人应该都会了解那么一些的。虽然导师让我们从OpenGL转向用VTK实现三维显示,不过这两个的原理其实差不多,因为都是基于计算机图形学的嘛。
Moving Bitmaps In 3D Space,其实实现这个“旋转星空”的效果(这个是我自己命名的,哈哈)主要就是以下步骤:
1.读入BMP图像,这里就是一个星星的图像;
2.将其变为二维纹理;
3.设置混合模式,因为BMP图像是四四方方的,要想弄出个星星的效果,必须让一部分变得透明;
4.0K,接下来就是在虚拟的三维空间里,在虚拟的矩形上贴这个星星生成的二维纹理了。
下面是详细的步骤说明,一起说说NEHE所使用的“算法”(姑且用算法吧,其实也够不上,因为星星移动其实不复杂的):
在实现以下4步之前,还是像往常一样啦,去洞庭散人的博客里,按照《基于MFC的OpenGL编程》Part 2 Setting up OpenGL on Windows把基本的东西先弄弄好。
1.好了,可以读BMP了,这是NEHE写的,感觉很严谨,或者说程序的健壮性比较好吧。
{
FILE * File = NULL;
if ( ! Filename)
{
return NULL;
}
File = fopen(Filename, " r " );
if (File)
{
fclose(File);
return auxDIBImageLoad(Filename);
}
return NULL;
}
2.可以开始生成纹理的工作了,这个其实也没什么可说的,除了本人菜鸟,刚开始不知道怎么把CString转化为char*,呵呵,后来百度了下,用了其中最简单的一个方法,const char* ch = (LPCTSTR)Picname
{
bool Status = FALSE;
AUX_RGBImageRec * TextureImage[ 1 ];
memset(TextureImage, 0 , sizeof ( void * ) * 1 );
const char * ch = (LPCTSTR)Picname;
if (TextureImage[ 0 ] = LoadBMP(( char * )ch))
{
Status = TRUE;
glGenTextures( 1 ,texture);
glBindTexture(GL_TEXTURE_2D,texture[ 0 ]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,
GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,
GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0 , 3 ,TextureImage[ 0 ] -> sizeX,
TextureImage[ 0 ] -> sizeY, 0 ,GL_RGB,GL_UNSIGNED_BYTE,
TextureImage[ 0 ] -> data);
}
if (TextureImage[ 0 ])
{
if (TextureImage[ 0 ] -> data)
{
free(TextureImage[ 0 ] -> data);
}
free(TextureImage[ 0 ]);
}
return Status;
}
3.\(^o^)/,设置混合模式啦,搞出个透明的效果的代码就在这里啦。貌似在NEHE的教程中没有glDepthMask(GL_FALSE)这一句,结果就不透明了,呵呵,但是它的程序直接拷贝到一个win32 console application里编译运行的时候又是一切OK的,不过这个没什么关系,参考了下红宝书,找出上面这句话添上,就没问题了。
{
if ( ! LoadGLTextures( " Star.bmp " )) // Jump To Texture Loading Routine
{
return FALSE; // If Texture Didn't Load Return FALSE
}
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations
glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Set The Blending Function For Translucency
glEnable(GL_BLEND);
glDepthMask (GL_FALSE);
for ( int loop = 0 ; loop < num; loop ++ )
{
star[loop].angle = 0.0f ;
star[loop].dist = ( float (loop) / num) * 5.0f ;
star[loop].r = rand() % 256 ;
star[loop].g = rand() % 256 ;
star[loop].b = rand() % 256 ;
}
return TRUE; // Initialization Went OK
}
4.在做显示在前嘛,先看看其中使用的数据结构吧。旁边的注释说得很清楚了。
{
int r, g, b; // Stars Color
GLfloat dist, // Stars Distance From Center
angle; // Stars Current Angle
}
stars;
进入正题吧,显示之。我觉得他用的显示过程还是应该说一下的。首先,glLoadIdentity(),这个函数,我以前都是不注意的,虽然很多地方都出现过,都没有去查,这个函数其实相当于初始化,将表示坐标的那个Matrix变成对角线为1,其余为0的4X4矩阵;然后转到屏幕后深度15的地方;接着绕X轴旋转tilt = 90°,绕Y轴旋转,这个角度,是各个STAR随机生成的。角度转好了之后再移动,距离dist也因星星而异,因为图片是片面的所以角度需要转回去。dist是从5.0递减到0,然后再重复。为了显示旋转的效果,坐标又绕Z轴旋转spin度,这个参数的设置时让所有的星星都旋转起来,即星星旋转的角速度。然后画星星。这部分的代码的功能,就是让星星从距离(0,0,-15)为5的地方向(0,0,-15)移动。在这个代码后面贴上运动控制和随机数生成的代码吧。


for ( int loop = 0 ; loop < num; loop ++ ) // Loop Through All The Stars
{
glLoadIdentity(); // Reset The View Before We Draw Each Star
glTranslatef( 0.0f , 0.0f , - 15.0f ); // Zoom Into The Screen (Using The Value In 'zoom')
glRotatef(tilt, 1.0f , 0.0f , 0.0f ); // Tilt The View (Using The Value In 'tilt')
glRotatef(star[loop].angle, 0.0f , 1.0f , 0.0f ); // Rotate To The Current Stars Angle
glTranslatef(star[loop].dist, 0.0f , 0.0f ); // Move Forward On The X Plane
glRotatef( - star[loop].angle, 0.0f , 1.0f , 0.0f ); // Cancel The Current Stars Angle
glRotatef( - tilt, 1.0f , 0.0f , 0.0f ); // Cancel The Screen Tilt
glRotatef(spin, 0.0f , 0.0f , 1.0f );
glColor4ub(star[loop].r,star[loop].g,star[loop].b, 255 );
glBegin(GL_QUADS);
glTexCoord2f( 0.0f , 0.0f ); glVertex3f( - 1.0f , - 1.0f , 0.0f );
glTexCoord2f( 1.0f , 0.0f ); glVertex3f( 1.0f , - 1.0f , 0.0f );
glTexCoord2f( 1.0f , 1.0f ); glVertex3f( 1.0f , 1.0f , 0.0f );
glTexCoord2f( 0.0f , 1.0f ); glVertex3f( - 1.0f , 1.0f , 0.0f );
glEnd();
}
NEHE所写的程序并不是基于MFC的,我在这里把变量的变化放在OnTimer里面,这样的话程序可以正常运行。如果照搬NEHE,写在上面的代码后面,图像是动不了的。
{
// TODO: Add your message handler code here and/or call default
for ( int loop = 0 ; loop < num; loop ++ )
{
spin += 0.01f ;
star[loop].angle += float (loop) / num;
star[loop].dist -= 0.01f ;
if (star[loop].dist < 0.0f )
{
star[loop].dist += 5.0f ;
star[loop].r = rand() % 256 ;
star[loop].g = rand() % 256 ;
star[loop].b = rand() % 256 ;
}
}
InvalidateRect(NULL,FALSE);
CView::OnTimer(nIDEvent);
}
效果图也贴上吧,和NEHE的其实一样。