渲染世界的Opengl<4>基础渲染

1.基础渲染管线
基础渲染管线分为两个部分:客户机端,以及服务器端。
服务器和客户机在功能上是异步的,也就是说他们是各自独立的软件块或硬件块。

着色器相关

着色器使用GLSL编写的程序。着色器必须从源代码中编译和链接到一起才可以使用。最终准备就绪的着色器程序随后在第一阶段构成顶点着色器,第二阶段构成片段着色器。

属性:一个队每个顶点都要做出改变的数据元素。属性可以是浮点数,整数或者布尔数据,属性总是以四维向量的形式进行内部存储的。即使我们不会使用到所有的分量。属性会从本地的客户机内存中赋值存储在图形硬件中的一个缓冲区上。这些属性只供顶点着色器使用,对于片元着色器并没有意义。这些属性对于每个顶点都要做出改变,并不意味着它们的值不能重复,而只是说明对于每个顶点都有一个实际存储值。

Uniform:属性是一种对于整个批次的属性都取统一值的单个值,是不变的。设置完Uniform变量就紧接着发出渲染一个图元批次的命令。Uniform变量实际上可以无限次的使用。

纹理:我们可以传递到着色器的第三种数据类型是纹理数据。片段着色器对一个纹理进行采样,并且在一个三角形表面上应用图形数据。纹理数据的作用不仅仅是表示图形。很多图形文件格式都是以无符号字节形式对颜色分量进行存储的。但我们仍然可以设置浮点纹理。

输出:输出数据作为一个阶段着色器的输出定义的,而在后续阶段的着色器是作为输入定义的。输出类型的数据可以简单的从一个阶段传递到下一个阶段。也可以以不同的方式插入。

2.创建坐标系

(1)正投影
通常在2D绘图中使用正投影,并且在我们的几何图形当中将Z设置为0.0。
在正投影当中,所有在这个空间范围的所有东西都会被显示在屏幕上,而不存在照相机或者视点坐标系的概念。

GLFrustum::SetOrthographic(GLfloat xMin,GLfloat xMax,GLfloat yMin,GLfloat yMax,GLfloat zMin,GLfloat zMax);

(2)透视投影
透视投影会进行透视除法对距离观察者很远的对象进行缩短和收缩。逻辑尺寸相同的对象绘制在视景体的前面比绘制在视景体背面的显得更大。

GLFrustum::SetPerspective(GLfloat fFov,GLfloat fAspect,GLfloat fNear,GLfloat fFar);

其中参数:1.垂直方向的市场角度,窗口的宽度和高度的纵横比,到近裁剪面和远裁剪面之间的距离。

3.使用存储着色器
提供的比较好的思路:由GLTools的C++类GLShaderManager进行管理,他们能够满足进行通常渲染的基本要求。其使用前必须要初始化。

(1)属性
Opengl支持16种可以为每个顶点设置的不同类型参数。可以和顶点着色器当中的任何指定变量相关联。存储着色器为每一个变量都使用一致的内部变量命名规则和相同的属性槽。
GLShaderManager预定义的标识符:

GLT_ATTRIBUTE_VERTEX:三分量顶点位置。
GLT_ATTRIBUTE_COLOR:四分量颜色。
GLT_ATTRIBUTE_NORMAL:三分量表面法线。
GLT_ATTRIBUTE_TEXTURE0:第一对2分量纹理坐标。
GLT_ATTRIBUTE_TEXTURE1:第二对2分量纹理坐标。

(2)属性
要对几何图形进行渲染,我们需要为对象递交属性矩阵,首先要绑定到我们想要使用的着色器程序上,并且提供程序的Uniform值。UseStockShader函数会选择一个存储着色器并且提供这个着色器的Uniform值。可以通过一次工作就完成。

单位着色器
所有单位着色器只是单纯的使用默认的笛卡尔坐标系。所有片段都是应用一种颜色,几何图形为实心和没有渲染的。只使用一个属性GLT_ATTRIBUTE _VERTEX。

GLShaderManager::UseStockShader(GLT_SHADER_IDENTITY, GLfloat vColor[4]);

平面着色器
允许为几何图形变换之定义个4*4的变换矩阵,通常情况下这是一种左乘模型视图矩阵和投影矩阵。使用一个属性:GLT_ATTRIBUTE _VERTEX

GLShaderManager::UseStockShader(GLT_SHADER_FLAT,GLfloat mvp[16] ,GLfloat vColor[4]);

上色着色器
-唯一的Uniform值就是在几何图形中应用的变换矩阵。设置的属性:
GLT_ATTRIBUTE _VERTEX
GLT_ATTRIBUTE _COLOR
颜色值被平滑的插入顶点之间。

GLShaderManager::UseStockShader(GLT_SHADER_SHADED,GLfloat mvp[16] );

默认光源着色器
从本质上讲,这种着色器使对象产生阴影和光照的效果。类似于由位于观察者位置的单漫射光所产生的效果。这种着色器对对象产生阴影和光照的效果。这里需要模型视图矩阵,投影矩阵和作为基本色的颜色值等的Uniform值。GLT_ ATTRIBUTE _ VERTEX、 GLT_ ATTRIBUTE _NORMAL属性需要。

GLShaderManager::UseStockShader(GLT_SHADER_DEFAULT_LIGHT, GLfloat mvMatrix[16],GLfloat pMatrix[16], GLfloat vColor[4]);

点光源着色器
点光源着色器和默认光源着色器很相似,但是光源位置可能是特定的。这种着色器接受4个Uniform值。模型矩阵、投影矩阵、视点坐标系当中的光源位置、对象基本的漫反射颜色。GLT_ ATTRIBUTE _ VERTEX、 GLT_ ATTRIBUTE _NORMAL属性需要。

GLShaderManager::UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, GLfloat mvMatrix[16],GLfloat pMatrix[16], GLfloat vLightPos[3], GLfloat vColor[4]);

纹理替换矩阵
着色器通过给定的模型视图投影矩阵,使用绑定到nTextureUnit指定的纹理单元的纹理对几何图形进行变换。片段着色器是直接从纹理样本当中直接获取的。GLT_ ATTRIBUTE _ VERTEX、 GLT_ ATTRIBUTE _NORMAL属性需要。

GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_REPLACE, GLfloat mvMatrix[16],GLint nTextureUnit);

纹理调整着色器
这种着色器将一个基本色乘一个取自纹理单元nTextureUnit的纹理。GLT_ATTRIBUTE_VERTEX、GLT_ATTRIBUTE_TEXTURE0属性必须。

GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_MODILATE, GLfloat mvMatrix[16],GLfloat vColor,GLint nTextureUnit);

纹理光源着色器
这种着色器将一个纹理通过漫反射照明计算进行调整(相乘),光线在视觉空间中的位置是给定的。这种着色器接受五个Uniform值,模型视图矩阵,投影矩阵,视觉空间中的光源位置,几何图形的基本色,即将使用的纹理单元。

GLShaderManager::UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vBaseColor[4],GLint nTextureUnit);

4.将点连接起来
在Opengl绘制当中,我们关心的不是物理屏幕坐标和像素。而是视景体当中的位置坐标。将这些点、线和三角形从创建3D空间投影到计算机屏幕上的2D图形则是着色器程序和光栅化硬件所要完成的工作。
七种基本图元:

GL_POINTS
GL_LINES
GL_LINE_STRIP
GL_LINE_LOOP:最后一个点和第一个点连接起来。
GL_TRIANGLES
GL_TRIANGLE_STRIP:一条条带上的顶点三角形。
GL_TRIANGLE_FAN:以一个圆心为中心成扇形排布,公用相邻顶点的一组三角形。

(1)点
点大小和其他几何图形不同,不会受到透视除法的影响。
相关API:

void glPointSize(GLfloat size);
//改变默认点大小

//获取点大小的范围以及最小间隔
GLfloat sizes[2];
GLfloat step;

glGetFloatv(GL_POINT_SIZE_RANGE, sizes);
glGetFloatv(GL_POINT_SIZE_GRANULARITY, &sTEP);

//使用程序点大小模式来设置点大小
glEnable(GL_PROGRAM_POINT_SIZE);

注:为了获得圆点,需要开启抗锯齿模式。

(2)线
注意一条线段是两个顶点之间绘制的,所以一批线段应该把偶偶偶数个端点,每个顶点都是线段的端点。

void glLindWidth(GLfloat width);
//控制线的宽度。

(3)线带
连续的从一个顶点到下一个顶点绘制线段,形成一个整整连接点的线条。

(4)线环
线环是线带的一种简单扩展,增加一条连接着一批次中最后一个点和第一个点的线段。

(5)三角形

注意,顺序和方向结合来指定顶点的方式称为环绕。
默认情况下Opengl认为具有逆时针方向环绕的多边形是正面的。

glFrontFace(GL_CN);
//改变默认的正面,这个命令之后,顺时针环绕的多边形被认为是正面。
//GL_CCW,是逆时针环绕的多边形被认为是正面。

(6)三角形带
绘制大量三角形的时候,这种做法可以节省大量的程序代码和数据存储空间。
其次,这种做法可以提高运算性能和节省带宽。

(7)三角形扇
围绕一个中心点相连的三角形。

(8)一个简单的批次容器
GLTools当中包含一个简单的容器类:GBatch。可以作为上述讨论的其中图元的容器。它知道在使用GLShaderManager支持的任意存储着色器的时候如何对图元进行渲染。

//首先对批次进行初始化,告诉这个类代表哪种图元。其中包括定点数以及纹理坐标。
void GLBatch::Begin(GLenum primitive, GLuint nVerts, GLuint nTextureUnits=0);

//至少要复制一个由三分量(x,y,z)顶点组成的数组。
void GLBatch::CopyVertexData3f(GLfloat* vVerts);
//还可以选择复制表面法线、颜色和纹理坐标。

void GLBatch::CopyNormalDataf(GLfloat* vNorms);
void GLBatch::CopyColorData4f(GLfloat* vColor);
void GLBatch::CopyTexCoordData2f(GLfloat* vTexCoords, GLuint uiTextureLayer);
//在完成上述工作之后可以调用end来表明已经完成相关数据复制工作。

void GLBatch::End(void);
//一旦调用end就不可以增加新的属性了。

//最后选择适当的存储着色器并且调用Draw函数。

(9)不希望出现的几何图形
这里是对于几何图形多次渲染的解决方法。

油画法:对三角形进行排序,首先渲染那些比较远的三角形,在他们上方渲染比较近的三角形。但是非常低效。

正面背面剔除:对于正面和背面进行区分的原因就是剔除。背面剔除能够极大我的提高性能。

glEnable(GL_CULL_FACE);
glDisable(GL_CULL_FACE);
void glCullFace(GLenum mode);//指明剔除背面还是正面。涉及到的参数:GL_FRONT,GL_BVACK,GL_FRONT_AND_BACK。

其一般的步骤是:设置剔除面,然后对其进行剔除。

深度测试:绘制一个像素的时候,把一个z值分配给他,表示到观察者的距离。当另外一个像素需要在屏幕上同样位置进行绘制的时候,新像素的z值会与已经存储的z值进行比较。如果新像素z值大,说明和观察者近,那么原来像素会被覆盖。

glEnable(GL_DEPTH_TEST);
//开启深度测试

以上几种思路会造成一个片段区域重复进行绘制,而且每一次绘制都会产生性能开销,如果开销过大,那么就会导致光栅化过程变慢,即“填充受限”现象。但是如果将油画法逆转而行,就会极大的提升效率。进行深度测试,然后渲染。

多边形模式:多边形不一定是实心的,在默认的情况下,多边形是作为实心图形绘制的。但是我们可以将多边性质定位显示轮廓或只有点来改变这种行为。

void glPolygonMode(GLenum face, GLenum mode);
//允许多边形渲染成试题、轮廓或者只有点。
//其中的face所需要的参数见上面的函数,有关面的枚举值。
//mode枚举值:GL_FILL、GL_LINE、GL_POINT。

(10)多边形偏移

Z-Fighting问题:如果在一个物体上绘制贴花,那么贴花图案和物体的深度值在深度缓冲区中一致。这回导致渲染失败。
解决方法:对第二次绘制时在z方向稍微做一些偏移。

void glPolygonOffset(GLfloat factor, GLfloat units);

该函数可以调节片段的深度值,这样可以使得深度值产生偏移而不至于改变3D空间中的物理位置。
总偏移量有下面的方程决定:
DepthOffset=(DZ* factor)+(r*unites)。
DZ是深度值相对于多边形屏幕区域的变化量。
r是深度缓冲区值产生变化的最小值。
注意,该函数当中使用负值z值会使得z值距离我们更近。正值会使得他们离我们更远。
在这些基础之上,我们还要启用多边形单独偏移来填充几何图形(GL_POLYGON_OFFSET_FILL)、线(GL_POLYGON_OFFSET_LINE) 、点(GL_POLYGON_OFFSET_POINT)。

(11)裁剪

另外一种提高渲染性能的方法是仅仅刷新屏幕上发生变化的部分。我们还需要将Opengl渲染限制在窗口中一个较小的矩形区域。Opengl允许我们在将要进行渲染的窗口中制定一个裁剪框。默认情况下,裁剪框和窗口一样大小,并且不会进行裁剪测试。

//开启裁剪测试。
glEnable(GL_SCISSOR_TEST);
//设置裁剪框。
void glScissor(GLint x,GLint y, GLsizei width, GLsizei height);

本章节所涉及到的示例程序,我为其做了详细的注解,记录下我的思想,以及希望能够帮助到和我一样的小白。这里基本涉及到<3><4>节所有知识。

//Opengl所需要包含的引用
#pragma comment(lib,"GLTools.lib")
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GL\freeglut.h>
#include <GLMatrixStack.h>
#include <GLFrame.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLGeometryTransform.h>
#include <math.h>
#include <GL/glut.h>


/////////////////////////////////////////////////////////////////////////////////
// An assortment of needed classes
GLShaderManager     shaderManager;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLFrame             cameraFrame;
GLFrame             objectFrame;
GLFrustum           viewFrustum;

GLBatch             pointBatch;
GLBatch             lineBatch;
GLBatch             lineStripBatch;
GLBatch             lineLoopBatch;
GLBatch             triangleBatch;
GLBatch             triangleStripBatch;
GLBatch             triangleFanBatch;

GLGeometryTransform transformPipeline;
M3DMatrix44f        shadowMatrix;


GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };


// Keep track of effects step
int nStep = 0;



///////////////////////////////////////////////////////////////////////////////
//渲染环境设置
//任务:背景颜色,渲染器管理初始化,深度测试,各种管线初始化
//对批次队列进行装填。
void SetupRC()
{
    // 设置背景颜色
    glClearColor(0.7f, 0.7f, 0.7f, 1.0f);
    //如果使用存储管理器,那么必须使用渲染器管理初始化。
    shaderManager.InitializeStockShaders();
    //开启深度测试
    glEnable(GL_DEPTH_TEST);

    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

    cameraFrame.MoveForward(-15.0f);

    //////////////////////////////////////////////////////////////////////
    // 佛罗里达州的点
    GLfloat vCoast[24][3] = { { 2.80, 1.20, 0.0 },{ 2.0,  1.20, 0.0 },
    { 2.0,  1.08, 0.0 },{ 2.0,  1.08, 0.0 },
    { 0.0,  0.80, 0.0 },{ -.32, 0.40, 0.0 },
    { -.48, 0.2, 0.0 },{ -.40, 0.0, 0.0 },
    { -.60, -.40, 0.0 },{ -.80, -.80, 0.0 },
    { -.80, -1.4, 0.0 },{ -.40, -1.60, 0.0 },
    { 0.0, -1.20, 0.0 },{ .2, -.80, 0.0 },
    { .48, -.40, 0.0 },{ .52, -.20, 0.0 },
    { .48,  .20, 0.0 },{ .80,  .40, 0.0 },
    { 1.20, .80, 0.0 },{ 1.60, .60, 0.0 },
    { 2.0, .60, 0.0 },{ 2.2, .80, 0.0 },
    { 2.40, 1.0, 0.0 },{ 2.80, 1.0, 0.0 } };

    // 加载图元
    pointBatch.Begin(GL_POINTS, 24);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();

    // 说明代表线图元
    lineBatch.Begin(GL_LINES, 24);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();

    // 线带
    lineStripBatch.Begin(GL_LINE_STRIP, 24);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();

    // 线环
    lineLoopBatch.Begin(GL_LINE_LOOP, 24);
    lineLoopBatch.CopyVertexData3f(vCoast);
    lineLoopBatch.End();

    // 多边形涉及到的点
    //产生一个金字塔形状的渲染。
    GLfloat vPyramid[12][3] = { -2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, -2.0f,
        2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, 2.0f,
        0.0f, 4.0f, 0.0f,

        -2.0f, 0.0f, 2.0f,
        -2.0f, 0.0f, -2.0f,
        0.0f, 4.0f, 0.0f };
    //进行分批处理初始化,设置先关参数。
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();


    // 使用triangle_fan,并且让中心店上升一些,产生3D效果.
    GLfloat vPoints[100][3];    //设置这个数组远远超出我们的需要。
    //因为下面对圆环进行渲染的时候仍然用到这个数组
    int nVerts = 0;
    GLfloat r = 3.0f;//设置其半径为3。
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    //注意这个循环执行七次,其中有两个重合点。
    //6即使我想渲染的扇形多边形的变数
    for (GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6) {
        nVerts++;
        //对每个点进行计算,完成其坐标的计算赋值。
        vPoints[nVerts][0] = float(cos(angle)) * r;
        vPoints[nVerts][1] = float(sin(angle)) * r;
        vPoints[nVerts][2] = -0.5f;
    }

    // 对中心点的设置。
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;

    // 对其批次队列进行装载。
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    //注意,在进行装载过后,其点数组就完全失去意义,可以对其
    //重新操作完成接下来的队列装载。

    //下面的装载就仍然使用这个数组。
    //对于其圆环点的设置。因为其点的数量过多,所以一样采用计算赋值。
    int iCounter = 0;
    GLfloat radius = 3.0f;
    for (GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
    {
        //对其x,y值进行计算。完成其坐标计算
        GLfloat x = radius * sin(angle);
        GLfloat y = radius * cos(angle);

        // 点是成对赋值的。
        //第一个点在下面
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = -0.5;
        iCounter++;
        //下一个点在上面
        vPoints[iCounter][0] = x;
        vPoints[iCounter][1] = y;
        vPoints[iCounter][2] = 0.5;
        iCounter++;
    }

    // 最后一个点和初始点一致。
    //也分为上下两个点部分。
    vPoints[iCounter][0] = vPoints[0][0];
    vPoints[iCounter][1] = vPoints[0][1];
    vPoints[iCounter][2] = -0.5;
    iCounter++;

    vPoints[iCounter][0] = vPoints[1][0];
    vPoints[iCounter][1] = vPoints[1][1];
    vPoints[iCounter][2] = 0.5;
    iCounter++;

    // 最后将上面计算完成的点赋值到对应的渲染队列当中。
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}

//自定义函数,对多边形的创建进行设置。
//任务:绘制线框和多边形渲染,注意渲染了两次。解决z冲突问题。
void DrawWireFramedBatch(GLBatch* pBatch)
{
    // 用绿色渲染多边形。
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();

    // 这里使用了多边形偏移,避免z冲突。
    glPolygonOffset(-1.0f, -1.0f);     
    glEnable(GL_POLYGON_OFFSET_LINE);//同时启用多边形单独偏移。

    // 渲染线框
    glEnable(GL_LINE_SMOOTH);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 画出线框。多边形模式设置渲染的面以及渲染的方式。
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glLineWidth(2.5f);
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    pBatch->Draw();

    // 在之前为了要求完成绘制之后,需要重新对环境进行恢复
    //使其回到默认状态。
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
}


//真正的渲染界面设置
//任务:清空背景,加载矩阵,渲染绘制图像。
void RenderScene(void)
{
    // Clear the window with current clearing color
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //加载图形矩阵
    modelViewMatrix.PushMatrix();
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    modelViewMatrix.MultMatrix(mCamera);

    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);
    modelViewMatrix.MultMatrix(mObjectFrame);
    //使用平面渲染器
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    //根据不同的nstep值来改变相关的绘制图形。
    switch (nStep) {
    case 0:
        glPointSize(4.0f);
        pointBatch.Draw();
        glPointSize(1.0f);
        break;
    case 1:
        glLineWidth(2.0f);
        lineBatch.Draw();
        glLineWidth(1.0f);
        break;
    case 2:
        glLineWidth(2.0f);
        lineStripBatch.Draw();
        glLineWidth(1.0f);
        break;
    case 3:
        glLineWidth(2.0f);
        lineLoopBatch.Draw();
        glLineWidth(1.0f);
        break;
        //以下三种情况,单独写一个函数进行处理
        //z冲突问题,多边形渲染,线框渲染等等。
    case 4:
        DrawWireFramedBatch(&triangleBatch);
        break;
    case 5:
        DrawWireFramedBatch(&triangleStripBatch);
        break;
    case 6:
        DrawWireFramedBatch(&triangleFanBatch);
        break;
    }
    //弹出堆栈
    modelViewMatrix.PopMatrix();

    // 手动重新绘制。
    glutSwapBuffers();
}

//按键函数,注意其格式。
// 任务:设置上下左右键对其绘制图形的旋转等操作。
void SpecialKeys(int key, int x, int y)
{
    //注意旋转图形的方法
    //其后面的是旋转的轴向。注意后面三个参数。
    if (key == GLUT_KEY_UP)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);

    if (key == GLUT_KEY_DOWN)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);

    if (key == GLUT_KEY_LEFT)
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);

    if (key == GLUT_KEY_RIGHT)
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);

    glutPostRedisplay();
}




///////////////////////////////////////////////////////////////////////////////
//键盘按键的函数设置
// 任务:处理当空格(ASC码为32)按下时的事件。
void KeyPressFunc(unsigned char key, int x, int y)
{
    if (key == 32)
    {
        //nstep的控制在这里完成
        nStep++;
        //每一次+1都会重新渲染。
        if (nStep > 6)
            nStep = 0;
    }
    //根据轴向对其标题进行改变
    switch (nStep)
    {
    case 0:
        glutSetWindowTitle("GL_POINTS");
        break;
    case 1:
        glutSetWindowTitle("GL_LINES");
        break;
    case 2:
        glutSetWindowTitle("GL_LINE_STRIP");
        break;
    case 3:
        glutSetWindowTitle("GL_LINE_LOOP");
        break;
    case 4:
        glutSetWindowTitle("GL_TRIANGLES");
        break;
    case 5:
        glutSetWindowTitle("GL_TRIANGLE_STRIP");
        break;
    case 6:
        glutSetWindowTitle("GL_TRIANGLE_FAN");
        break;
    }

    glutPostRedisplay();
}

///////////////////////////////////////////////////////////////////////////////
//窗口改变函数,注意其格式。
// 任务:处理窗口变化之后视口的显示。
void ChangeSize(int w, int h)
{
    //更新窗口的大小。
    glViewport(0, 0, w, h);
    //设置透视坐标系。
    viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    modelViewMatrix.LoadIdentity();
}

///////////////////////////////////////////////////////////////////////////////
// Main entry point for GLUT based programs
int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);

    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("GL_POINTS");
    //改变窗口大小的回调函数
    glutReshapeFunc(ChangeSize);
    //对按键进行相应的回调函数
    glutKeyboardFunc(KeyPressFunc);
    glutSpecialFunc(SpecialKeys);
    //对显示进行相应的回调函数
    glutDisplayFunc(RenderScene);

    //错误检查
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }

    //设置渲染环境。
    SetupRC();

    glutMainLoop();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值