阅读 3

OpenGL NeHe on Linux 5

剪裁平面,蒙板缓存和反射

#define TRUE 1
#define FALSE 0

/*
** 利用freeimage加载bmp图像
** 此函数在Linux系统上可以作为常用util调用
*/
GLboolean Lesson26::LoadBmp(const char *filename,
                            AUX_RGBImageRec *texture_image) {
    FREE_IMAGE_FORMAT fifmt = FreeImage_GetFileType(filename, 0);
    FIBITMAP *dib = FreeImage_Load(fifmt, filename, 0);
    dib = FreeImage_ConvertTo24Bits(dib);
    int width = FreeImage_GetWidth(dib);
    int height = FreeImage_GetHeight(dib);
    BYTE *pixels = (BYTE *) FreeImage_GetBits(dib);
    int pix = 0;
    if (texture_image == NULL)
        return FALSE;
    texture_image->data = (BYTE *) malloc(width * height * 3);
    texture_image->sizeX = width;
    texture_image->sizeY = height;
    for (pix = 0; pix < width * height; pix++) {
        texture_image->data[pix * 3 + 0] = pixels[pix * 3 + 2];
        texture_image->data[pix * 3 + 1] = pixels[pix * 3 + 1];
        texture_image->data[pix * 3 + 2] = pixels[pix * 3 + 0];
    }
    FreeImage_Unload(dib);
    return TRUE;
}

/**
 * 下一部分代码载入位图(调用上面的代码)并转换成纹理。
 * @return
 */
int Lesson26::LoadGLTextures() {
    int Status = FALSE;

    AUX_RGBImageRec *TextureImage, *TextureImage2, *TextureImage3;
    TextureImage = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage2 = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));
    TextureImage3 = (AUX_RGBImageRec *) malloc(sizeof(AUX_RGBImageRec));

    if (LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Envwall.bmp", TextureImage)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/Ball.bmp", TextureImage2)
        && LoadBmp("/home/zpw/Documents/CLionProjects/opengl_study/Data/EnvRoll.bmp", TextureImage3)) {
        Status = TRUE;
        glGenTextures(3, &texture[0]);


        glBindTexture(GL_TEXTURE_2D, texture[0]);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage->sizeX, TextureImage->sizeY, 0, GL_RGB,
                     GL_UNSIGNED_BYTE, TextureImage->data);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glBindTexture(GL_TEXTURE_2D, texture[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage2->sizeX, TextureImage2->sizeY, 0, GL_RGB,
                     GL_UNSIGNED_BYTE, TextureImage2->data);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

        glBindTexture(GL_TEXTURE_2D, texture[2]);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage3->sizeX, TextureImage3->sizeY, 0, GL_RGB,
                     GL_UNSIGNED_BYTE, TextureImage3->data);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    }
    return Status;
}


/**
 * 下面的代码的作用是重新设置OpenGL场景的大小,而不管窗口的大小是否已经改变(假定您没有使用全屏模式)。
 * 甚至您无法改变窗口的大小时(例如您在全屏模式下),它至少仍将运行一次--在程序开始时设置我们的透视图。
 * OpenGL场景的尺寸将被设置成它显示时所在窗口的大小。
 * @param width
 * @param height
 * @return
 */
GLvoid Lesson26::ReSizeGLScene(GLsizei width, GLsizei height)                // 重置OpenGL窗口大小
{
    if (height == 0)                                // 防止被零除
    {
        height = 1;                            // 将Height设为1
    }

    glViewport(0, 0, width, height);                    // 重置当前的视口
    /**
     * 下面几行为透视图设置屏幕。意味着越远的东西看起来越小。这么做创建了一个现实外观的场景。
     * 此处透视按照基于窗口宽度和高度的45度视角来计算。0.1f,100.0f是我们在场景中所能绘制深度的起点和终点。
     *
     * glMatrixMode(GL_PROJECTION)指明接下来的两行代码将影响projection matrix(投影矩阵)。投影矩阵负责为我们的场景增加透视。
     * glLoadIdentity()近似于重置。它将所选的矩阵状态恢复成其原始状态。调用 glLoadIdentity()之后我们为场景设置透视图。
     *
     * glMatrixMode(GL_MODELVIEW)指明任何新的变换将会影响 modelview matrix(模型观察矩阵)。模型观察矩阵中存放了我们的物体讯息。最后我们重置模型观察矩阵。
     */
    glMatrixMode(GL_PROJECTION);                        // 选择投影矩阵
    glLoadIdentity();                            // 重置投影矩阵

    // 设置视口的大小
    gluPerspective(45.0f, (GLfloat) width / (GLfloat) height, 0.1f, 100.0f);

    glMatrixMode(GL_MODELVIEW);                        // 选择模型观察矩阵
    glLoadIdentity();                            // 重置模型观察矩阵
}

/**
 * 接下的代码段中,我们将对OpenGL进行所有的设置。我们将设置清除屏幕所用的颜色,打开深度缓存,启用smooth shading(阴影平滑),等等。这个例程直到OpenGL窗口创建之后才会被调用。
 * if (!LoadGLTextures()) 这行代码调用上面讲的子例程载入位图并生成纹理。如果因为任何原因 LoadGLTextures() 调用失败,接着的一行返回FALSE。如果一切OK,并且纹理创建好了,我们启用2D纹理映射。
 * 如果您忘记启用的话,您的对象看起来永远都是纯白色,这一定不是什么好事。
 * @return
 */
int Lesson26::InitGL(GLvoid)                                // 此处开始对OpenGL进行所有设置
{
    if (!LoadGLTextures())								// If Loading The Textures Failed
    {
        return FALSE;									// Return False
    }
    glShadeModel(GL_SMOOTH);							// Enable Smooth Shading
    glClearColor(0.2f, 0.5f, 1.0f, 1.0f);				// Background
    glClearDepth(1.0f);									// Depth Buffer Setup
    glClearStencil(0);									// Clear The Stencil Buffer To 0
    glEnable(GL_DEPTH_TEST);							// Enables Depth Testing
    glDepthFunc(GL_LEQUAL);								// The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);	// Really Nice Perspective Calculations
    glEnable(GL_TEXTURE_2D);							// Enable 2D Texture Mapping

    glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmb);			// Set The Ambient Lighting For Light0
    glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDif);			// Set The Diffuse Lighting For Light0
    glLightfv(GL_LIGHT0, GL_POSITION, LightPos);		// Set The Position For Light0

    glEnable(GL_LIGHT0);								// Enable Light 0
    glEnable(GL_LIGHTING);								// Enable Lighting

    q = gluNewQuadric();								// Create A New Quadratic
    gluQuadricNormals(q, GL_SMOOTH);					// Generate Smooth Normals For The Quad
    gluQuadricTexture(q, GL_TRUE);						// Enable Texture Coords For The Quad

    glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);	// Set Up Sphere Mapping
    glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);	// Set Up Sphere Mapping
    return TRUE;                                // 初始化 OK
}

void Lesson26::DrawObject() {
    glColor3f(1.0f, 1.0f, 1.0f);						// Set Color To White
    glBindTexture(GL_TEXTURE_2D, texture[1]);			// Select Texture 2 (1)
    gluSphere(q, 0.35f, 32, 16);						// Draw First Sphere

    glBindTexture(GL_TEXTURE_2D, texture[2]);			// Select Texture 3 (2)
    glColor4f(1.0f, 1.0f, 1.0f, 0.4f);					// Set Color To White With 40% Alpha
    glEnable(GL_BLEND);									// Enable Blending
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);					// Set Blending Mode To Mix Based On SRC Alpha
    glEnable(GL_TEXTURE_GEN_S);							// Enable Sphere Mapping
    glEnable(GL_TEXTURE_GEN_T);							// Enable Sphere Mapping

    gluSphere(q, 0.35f, 32, 16);						// Draw Another Sphere Using New Texture
    // Textures Will Mix Creating A MultiTexture Effect (Reflection)
    glDisable(GL_TEXTURE_GEN_S);						// Disable Sphere Mapping
    glDisable(GL_TEXTURE_GEN_T);						// Disable Sphere Mapping
    glDisable(GL_BLEND);								// Disable Blending
}

void Lesson26::DrawFloor() {
    GLfloat num = 4.5;
    glBindTexture(GL_TEXTURE_2D, texture[0]);			// Select Texture 1 (0)
    glBegin(GL_QUADS);									// Begin Drawing A Quad
    glNormal3f(0.0, 1.0, 0.0);						// Normal Pointing Up

    glTexCoord2f(0.0f, 1.0f);					// Bottom Left Of Texture
    glVertex3f(-num, 0.0, num);					// Bottom Left Corner Of Floor

    glTexCoord2f(1.0f, 1.0f);					// Bottom Right Of Texture
    glVertex3f( num, 0.0, num);					// Bottom Right Corner Of Floor

    glTexCoord2f(1.0f, 0.0f);					// Top Right Of Texture
    glVertex3f( num, 0.0,-num);					// Top Right Corner Of Floor

    glTexCoord2f(0.0f, 0.0f);					// Top Left Of Texture
    glVertex3f(-num, 0.0,-num);					// Top Left Corner Of Floor

    glEnd();											// Done Drawing The Quad
}

/**
 * 下一段包括了所有的绘图代码。任何您所想在屏幕上显示的东东都将在此段代码中出现。以后的每个教程中我都会在例程的此处增加新的代码。
 * 如果您对OpenGL已经有所了解的话,您可以在 glLoadIdentity()调用之后,返回TRUE值之前,试着添加一些OpenGL代码来创建基本的形。
 * @return
 */
int Lesson26::DrawGLScene(GLvoid)                                // 从这里开始进行所有的绘制
{
    // Clear Screen, Depth Buffer & Stencil Buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    // Clip Plane Equations
    double eqr[] = {0.0f,-1.0f, 0.0f, 0.0f};			// Plane Equation To Use For The Reflected Objects

    glLoadIdentity();									// Reset The Modelview Matrix
    glTranslatef(0.0f, -0.6f, zoom);					// Zoom And Raise Camera Above The Floor (Up 0.6 Units)
    glColorMask(0,0,0,0);								// Set Color Mask

    /**
     * 绘制一个地面,当然现在你什么也看不到,它只是把覆盖地面的蒙板缓存区中的相应位置设为1。
     */
    glEnable(GL_STENCIL_TEST);							// Enable Stencil Buffer For "marking" The Floor
    glStencilFunc(GL_ALWAYS, 1, 1);						// Always Passes, 1 Bit Plane, 1 As Mask
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);			// We Set The Stencil Buffer To 1 Where We Draw Any Polygon
    // Keep If Test Fails, Keep If Test Passes But Buffer Test Fails
    // Replace If Test Passes
    glDisable(GL_DEPTH_TEST);							// Disable Depth Testing
    DrawFloor();										// Draw The Floor (Draws To The Stencil Buffer)

    /**
     * 启用深度测试和绘制颜色,并相应设置蒙板测试和函数的值,这种设置可以使我们在屏幕上绘制而不改变蒙板缓存区的值。
     */
    // We Only Want To Mark It In The Stencil Buffer
    glEnable(GL_DEPTH_TEST);							// Enable Depth Testing
    glColorMask(1,1,1,1);								// Set Color Mask to TRUE, TRUE, TRUE, TRUE
    glStencilFunc(GL_EQUAL, 1, 1);						// We Draw Only Where The Stencil Is 1
    // (I.E. Where The Floor Was Drawn)
    glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);				// Don't Change The Stencil Buffer

    /**
     * 设置并启用剪切平面,使得只能在地面的下方绘制
     */
    glEnable(GL_CLIP_PLANE0);							// Enable Clip Plane For Removing Artifacts
    // (When The Object Crosses The Floor)
    glClipPlane(GL_CLIP_PLANE0, eqr);					// Equation For Reflected Objects
    glPushMatrix();										// Push The Matrix Onto The Stack
        glScalef(1.0f, -1.0f, 1.0f);					// Mirror Y Axis
        glLightfv(GL_LIGHT0, GL_POSITION, LightPos);	// Set Up Light0
        glTranslatef(0.0f, height, 0.0f);				// Position The Object
        glRotatef(xrot, 1.0f, 0.0f, 0.0f);				// Rotate Local Coordinate System On X Axis
        glRotatef(yrot, 0.0f, 1.0f, 0.0f);				// Rotate Local Coordinate System On Y Axis
        DrawObject();									// Draw The Sphere (Reflection)
    glPopMatrix();										// Pop The Matrix Off The Stack
    glDisable(GL_CLIP_PLANE0);							// Disable Clip Plane For Drawing The Floor
    glDisable(GL_STENCIL_TEST);							// We Don't Need The Stencil Buffer Any More (Disable)

    /**
     * 绘制地面,并把地面颜色和反射的球颜色混合,使其看起来像反射的效果。
     */
    glLightfv(GL_LIGHT0, GL_POSITION, LightPos);		// Set Up Light0 Position
    glEnable(GL_BLEND);									// Enable Blending (Otherwise The Reflected Object Wont Show)
    glDisable(GL_LIGHTING);								// Since We Use Blending, We Disable Lighting
    glColor4f(1.0f, 1.0f, 1.0f, 0.5f);					// Set Color To White With 80% Alpha
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	// Blending Based On Source Alpha And 1 Minus Dest Alpha
    DrawFloor();										// Draw The Floor To The Screen

    /**
     * 在距地面高为height的地方绘制一个真正的球
     */
    glEnable(GL_LIGHTING);								// Enable Lighting
    glDisable(GL_BLEND);								// Disable Blending
    glTranslatef(0.0f, height, 0.0f);					// Position The Ball At Proper Height
    glRotatef(xrot, 1.0f, 0.0f, 0.0f);					// Rotate On The X Axis
    glRotatef(yrot, 0.0f, 1.0f, 0.0f);					// Rotate On The Y Axis
    DrawObject();										// Draw The Ball

    xrot += xrotspeed;									// Update X Rotation Angle By xrotspeed
    yrot += yrotspeed;									// Update Y Rotation Angle By yrotspeed
    glFlush();											// Flush The GL Pipeline
    return TRUE;                                //  一切 OK
}
复制代码

关注下面的标签,发现更多相似文章
评论