【游戏课】技术片段之——使用BillBoard技术进行快速绘制

来源:互联网 发布:ug nx11编程新功能 编辑:程序博客网 时间:2024/06/18 08:01

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 <windows.h>#include <stdio.h>#include <stdlib.h>#include <gl/glut.h>#include <gl/glext.h>#define BITMAP_ID 0x4D42//about multitexturePFNGLMULTITEXCOORD1FARBPROC     glMultiTexCoord1fARB = NULL;PFNGLMULTITEXCOORD2FARBPROC     glMultiTexCoord2fARB = NULL;PFNGLACTIVETEXTUREARBPROC       glActiveTextureARB = NULL;PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL;//texture identifiersstatic GLuint texture[1];//for scene movementGLfloat fRotate;//whether perspective or orthographicbool bPersp = true;//whether to animatebool bAnim = false;//whether wire or solidbool bWire = false;//whether to combine texure and lighteningbool bTexLit = false;//view port sizeint wHeight = 512;int wWidth = 512;unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader){    FILE *filePtr;    BITMAPFILEHEADER bitmapFileHeader;    unsigned char *bitmapImage;    intimageIdx = 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 lighting    GLfloat 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 multitexture    glMultiTexCoord1fARB = (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 texture    glActiveTextureARB(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);    //top    glBegin(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 port    glViewport(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 positionGLdouble eye[] = { 0, 0, 8 };//where it is aimedGLdouble 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 background    glDisable(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;}



0 0