【游戏课】技术片段之——使用BillBoard技术进行快速绘制
BillBoard技术是计算机图形学领域中进行快速绘制的一种方法。在类似游戏这种对实时性要求较高的场景下采取BillBoard技术可以大大加快绘制的速度从而提高画面的流畅性。
那么什么是BillBoard技术,BillBoard技术的原理是什么呢?
“BillBoard技术采用一个带有纹理的四边形,其纹理图像为该BillBoard所代表的物体的图像,即用带有该物体图像的长方形,代替生成该物体的图形画面。BillBoard放置于所代表物体的位置中心,并随相机的运动而变化,始终面对用户。将BillBoard技术与Alpha纹理和动画技术相结合,可以模拟很多自然现象,如树、烟、火、爆炸、云等。”——《计算机游戏程序设计》
简单地说,就是把3D的物体用2D来表示,然后让该物体始终朝向镜头。比如场景中的一棵树,对于整个场景来说不是主要物体,因此无需花费大量的时间去计算树的每一部分的细节。通常的做法是首先准备好一张树的照片,然后镜头运动的时候使得树始终正对着镜头,我们看到的始终是树的正面。
BillBoard四边形法向和向上向量的设置方式对应着不同的BillBoard技术,分为平行屏幕的BillBoard技术、平行物体的BillBoard技术、视点朝向的BillBoard技术和轴向BillBoard技术。下面实现的是平行屏幕的BillBoard技术。
采用OpenGL实现的思路是,在绘制树的时候将ModelView矩阵的左上角的3*3矩阵置为单位阵。
核心代码:
float mat[16];glGetFloatv(GL_MODELVIEW_MATRIX, mat);// Identify the 3*3 sub matrix in Top-left corner mat[1] = mat[2] = mat[6] = 0;mat[4] = mat[8] = mat[9] = 0;mat[0] = mat[5] = mat[10] = 1;glLoadMatrixf(mat);
为了演示BillBoard技术,我们假设场景有一个茶壶和一棵树(好怪异的搭配)。达到的效果应该是视点转动时茶壶不动,树始终跟人的视线垂直。
在OpenGL中当视点发生变化时,实际上我们的视线是不动的,始终朝向电脑屏幕,而场景在做相对运动。所以实际上绘制出的效果应该是树始终处于画面中朝向保持不变,茶壶会发生转动。
场景一:
场景二(视点变化):
项目的代码如下:
控制:WSAD控制视点上下左右移动,ZC控制视点前后移动。
#define GLUT_DISABLE_ATEXIT_HACK
#define _CRT_SECURE_NO_WARNINGS
#include
#include
#include
#include
#include #define BITMAP_ID 0x4D42//about multitexture
PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1fARB = NULL;
PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL;
PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL;
PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL;//texture identifiers
static GLuint texture[1];//for scene movement
GLfloat fRotate;
//whether perspective or orthographic
bool bPersp = true;
//whether to animate
bool bAnim = false;
//whether wire or solid
bool bWire = false;
//whether to combine texure and lightening
bool bTexLit = false;//view port size
int wHeight = 512;
int wWidth = 512;unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader)
{FILE *filePtr;BITMAPFILEHEADER bitmapFileHeader;unsigned char *bitmapImage;int imageIdx = 0;unsigned char tempRGB;filePtr = fopen(filename, "rb");if (filePtr == NULL) return NULL;fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr);if (bitmapFileHeader.bfType != BITMAP_ID) {fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n");return NULL;}fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr);fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET);bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage];if (!bitmapImage) {fprintf(stderr, "Error in LoadBitmapFile: memory error\n");return NULL;}fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr);if (bitmapImage == NULL) {fprintf(stderr, "Error in LoadBitmapFile: memory error\n");return NULL;}for (imageIdx = 0;imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) {tempRGB = bitmapImage[imageIdx];bitmapImage[imageIdx] = bitmapImage[imageIdx + 2];bitmapImage[imageIdx + 2] = tempRGB;}fclose(filePtr);return bitmapImage;
}void TextLoad(int i, char *filename)
{BITMAPINFOHEADER bitmapInfoHeader;unsigned char* bitmapData;bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader);glBindTexture(GL_TEXTURE_2D, texture[i]);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, GL_RGB, bitmapInfoHeader.biWidth,bitmapInfoHeader.biHeight, 0, GL_RGB, GL_UNSIGNED_BYTE,bitmapData);
}void Init()
{glClearColor(0.0, 0.0, 0.0, 0.0);glShadeModel(GL_SMOOTH);// initialize OpenGL lightingGLfloat lightPos[] = { 0.0, 0.0, 1.0, 1 };GLfloat lightAmb[4] = { 1.0, 1.0, 1.0, 1.0 };GLfloat lightDiff[4] = { 1.0, 1.0, 1.0, 1.0 };GLfloat lightSpec[4] = { 1.0, 1.0, 1.0, 1.0 };//glLightfv(GL_LIGHT0, GL_POSITION, lightPos);glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiff);glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpec);glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, lightPos);glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL_EXT, GL_SEPARATE_SPECULAR_COLOR_EXT);GLfloat black[] = { 0.0, 0.0, 0.0, 1.0 };//glLightModelfv(GL_LIGHT_MODEL_AMBIENT, black);glEnable(GL_LIGHT0);glEnable(GL_LIGHTING);glEnable(GL_DEPTH_TEST);glPixelStorei(GL_UNPACK_ALIGNMENT, 1);glGenTextures(1, texture);TextLoad(0, "tree.bmp");//define multitextureglMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC)wglGetProcAddress("glMultiTexCoord1fARB");glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC)wglGetProcAddress("glMultiTexCoord2fARB");glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC)wglGetProcAddress("glActiveTextureARB");glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC)wglGetProcAddress("glClientActiveTextureARB");
}void Draw_Teapot()
{glPushMatrix();glTranslatef(0, -1.5, 0);glutSolidTeapot(0.5);glPopMatrix();
}void Draw_Tree()
{glPushMatrix();float mat[16];glGetFloatv(GL_MODELVIEW_MATRIX, mat);// Identify the 3*3 sub matrix in Top-left corner mat[1] = mat[2] = mat[6] = 0;mat[4] = mat[8] = mat[9] = 0;mat[0] = mat[5] = mat[10] = 1;glLoadMatrixf(mat);//choose textureglActiveTextureARB(GL_TEXTURE0_ARB);glEnable(GL_TEXTURE_2D);glBindTexture(GL_TEXTURE_2D, texture[0]);glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT);glTexEnvf(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE);//topglBegin(GL_QUADS);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1);//bottom glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 1); glVertex3f(2, 2, 0);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 1, 0); glVertex3f(2, -2, 0);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 0); glVertex3f(-2, -2, 0);glMultiTexCoord2fARB(GL_TEXTURE0_ARB, 0, 1); glVertex3f(-2, 2, 0);glEnd();glTranslatef(0, 0, 0);glActiveTextureARB(GL_TEXTURE0_ARB);glDisable(GL_TEXTURE_2D);glPopMatrix();
}void DrawScene()
{Draw_Teapot();Draw_Tree();
}void updateView(int width, int height)
{// Reset The Current View portglViewport(0, 0, (GLsizei)width, (GLsizei)height);glMatrixMode(GL_PROJECTION);glLoadIdentity();float whRatio = (GLfloat)width / (GLfloat)height;if (bPersp) {gluPerspective(45.0f, whRatio, 0.1f, 100.0f);}else {glOrtho(-3, 3, -3, 3, -100, 100);}glMatrixMode(GL_MODELVIEW);
}void reshape(int width, int height)
{if (height == 0){height = 1;}wHeight = height;wWidth = width;updateView(wHeight, wWidth);
}void idle()
{glutPostRedisplay();
}//eye position
GLdouble eye[] = { 0, 0, 8 };
//where it is aimed
GLdouble center[] = { 0, 0, 0 };void key(unsigned char k, int x, int y)
{switch (k){case 27:case 'q': {exit(0); break; }case 'p': {bPersp = !bPersp; break; }case ' ': {bAnim = !bAnim; break; }case 'o': {bWire = !bWire; break; }case 'a': {eye[0] += 0.2f;//center[0] += 0.2f;break;}case 'd': {eye[0] -= 0.2f;//center[0] -= 0.2f;break;}case 'w': {eye[1] -= 0.2f;//center[1] -= 0.2f;break;}case 's': {eye[1] += 0.2f;//center[1] += 0.2f;break;}case 'z': {eye[2] -= 0.2f;center[2] -= 0.2f;break;}case 'c': {eye[2] += 0.2f;center[2] += 0.2f;break;}case 'l':{bTexLit = !bTexLit;break;}}updateView(wHeight, wWidth);
}void begin_window_coords()
{glMatrixMode(GL_PROJECTION);glPushMatrix();glLoadIdentity();glOrtho(0.0, wWidth, 0.0, wHeight, -1.0, 1.0);glMatrixMode(GL_MODELVIEW);glLoadIdentity();
}void end_window_coords()
{glMatrixMode(GL_PROJECTION);glPopMatrix();glMatrixMode(GL_MODELVIEW);
}void display()
{glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// gradient backgroundglDisable(GL_DEPTH_TEST);glDisable(GL_LIGHTING);begin_window_coords();glBegin(GL_QUADS);glColor3f(0.2, 0.4, 0.8);glVertex2f(0.0, 0.0);glVertex2f(wWidth, 0.0);glColor3f(0.05, 0.1, 0.2);glVertex2f(wWidth, wHeight);glVertex2f(0, wHeight);glEnd();end_window_coords();glLoadIdentity();gluLookAt(eye[0], eye[1], eye[2],center[0], center[1], center[2],0, 1, 0);if (bWire) {glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);}else {glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);}glEnable(GL_DEPTH_TEST);glEnable(GL_LIGHTING);glPushMatrix();glRotatef(fRotate, 0, 1, 0);DrawScene();glPopMatrix();if (bAnim){fRotate += 0.5f;}glutSwapBuffers();
}int main(int argc, char *argv[])
{glutInit(&argc, argv);glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE);glutInitWindowPosition(400, 200);glutInitWindowSize(512, 512);glutCreateWindow("BillBoard Illustration");Init();glutDisplayFunc(display);glutReshapeFunc(reshape);glutKeyboardFunc(key);glutIdleFunc(idle);glutMainLoop();return 0;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
