三:OpenGL渲染基础

上一节为大家介绍了OpenGL的基本术语还编写了一个最基础的“HelloWorld”的Demo,相信大家肯定目标不止于此,那么接下来进入到今天的知识讲解
本节主要需要掌握的知识点有:

1.OpenGL渲染的流程

2.解OpenGL 数据传递的3种⽅式

3.OpenGL 提供的存储着⾊器

4. 正投影/透视投影 API的使⽤

5. OpenGL 上常⻅图元

6.代码Demo: 在存储着⾊器的情况下渲染图形并能通过键盘控制.


OpenGL渲染的流程和数据方式:

**在渲染过程中,必须存储2种着⾊器,分别是顶点着⾊器、⽚元着⾊器。顶点着⾊器是第⼀个着⾊器、⽚元着⾊器是最后⼀个。顶点着⾊器中处理顶点、⽚元着⾊器处理像素点颜⾊。
**顶点着色器是基于顶点的;
**片元着色器是基于像素的;

1.由顶点数组将顶点数据和属性(Attributes)传送到顶点着色器;
2.Uniforms数据传送到顶点着色器;
3.传送纹理数据Texture到片元着色器


OpenGL 提供的存储着⾊器:

单元着⾊器

GLShaderManager::UserStockShader(GLT_SHADER_IDENTITY,GLfloat vColor[4]);
参数1: 存储着⾊器种类-单元着⾊器
参数2: 颜⾊
使⽤场景: 绘制默认OpenGL 坐标系(-1,1)下图形. 图形所有⽚段都会以⼀种颜⾊填充

平⾯着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_FLAT,GLfloat mvp[16],GLfloat vColor[4]);
参数1: 存储着⾊器种类-平⾯着⾊器
参数2: 允许变化的4*4矩阵
参数3: 颜⾊
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化)

上⾊着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_SHADED,GLfloat mvp[16]);
参数1: 存储着⾊器种类-上⾊着⾊器
参数2: 允许变化的44矩阵
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化) 颜⾊将会平滑地插⼊到顶点之间,称为
平滑着⾊.

默认光源着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_DEFAULT_LIGHT,GLfloatmvMatrix[16],GLfloat pMatrix[16],GLfloat vColor[4]);
参数1: 存储着⾊器种类-默认光源着⾊器
参数2: 模型44矩阵
参数3: 投影4
4矩阵
参数4: 颜⾊值
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化) 这种着⾊器会使绘制的图形产⽣阴影和光照的效果

点光源着⾊器:
GLShaderManager::UserStockShader(GLT_SHADER_POINT_LIGHT_DIEF,GLfloatmvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vColor[4]);
参数1: 存储着⾊器种类-点光源着⾊器
参数2: 模型44矩阵
参数3: 投影4
4矩阵
参数4: 点光源的位置
参数5: 漫反射颜⾊值
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化) 这种着⾊器会使绘制的图形产⽣阴影和光照的效果.它与默认光源着⾊器⾮常类似,区别只是光源位置可能是特定的.

纹理替换矩阵着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_REPLACE,GLfloatmvMatrix[16],GLint nTextureUnit);
参数1: 存储着⾊器种类-纹理替换矩阵着⾊器
参数2: 模型4*4矩阵
参数3: 纹理单元
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化)这种着⾊器通过给定的模型视图投影矩阵.使⽤纹理单元来进⾏颜⾊填充.其中每个像素点的颜⾊是从纹理中.

纹理调整着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_MODULATE,GLfloatmvMatrix[16],GLfloat vColor[4],GLint nTextureUnit);
参数1: 存储着⾊器种类-纹理调整着⾊器
参数2: 模型4*4矩阵
参数3: 颜⾊值
参数4: 纹理单元
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化)这种着⾊器通过给定的模型视图投影矩阵. 着⾊器将⼀个基本⾊乘以⼀个取⾃纹理单元nTextureUnit 的纹理.将颜⾊与纹理进⾏颜⾊混合后才填充到⽚段中.

纹理光源着⾊器
GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIEF,GLfloat mvMatrix[16],GLfloat pMatrix[16],GLfloat vLightPos[3],GLfloat vBaseColor[4],GLint nTextureUnit);
参数1: 存储着⾊器种类-纹理光源着⾊器
参数2: 模型44矩阵
参数3: 投影4
4矩阵
参数4: 点光源位置
参数5: 颜⾊值(⼏何图形的基本⾊)
参数6: 纹理单元
使⽤场景: 在绘制图形时, 可以应⽤变换(模型/投影变化)这种着⾊器通过给定的模型视图投影矩阵. 着⾊器将⼀个纹理通过漫反射照明计算进⾏调整(相乘).


OpenGL 上常⻅图元:

GL_POINTS : 每个顶点在屏幕上都是单独点
GL_LINES 每⼀对顶点定义⼀个线段
GL_LINE_STRIP ⼀个从第⼀个顶点依次经过每⼀个后续顶点⽽绘制的线条
GL_LINE_LOOP 和GL_LINE_STRIP相同,但是最后⼀个顶点和第⼀个顶点连接起来了.
GL_TRIANGLES 每3个顶点定义⼀个新的三⻆形
GL_TRIANGLE_STRIP 共⽤⼀个条带(strip)上的顶点的⼀组三⻆形
GL_TRIANGLE_FAN 以⼀个圆点为中⼼呈扇形排列,共⽤相邻顶点的⼀组三⻆形

image.png

//1.最简单也是最常⽤的 4.0f,表示点的⼤⼩ glPointSize(4.0f);
//2.设置点的⼤⼩范围和点与点之间的间隔 GLfloat sizes[2] = {2.0f,4.0f}; GLfloat step = 1.0f;
//3.获取点⼤⼩范围和最⼩步⻓ glGetFloatv(GL_POINT_SIZE_RANGE ,sizes); glGetFloatv(GL_POINT_GRAULARITY ,&step);
//4.通过使⽤程序点⼤⼩模式来设置点⼤⼩ glEnable(GL_PROGRAM_POINT_SIZE);
//5.这种模式下允许我们通过编程在顶点着⾊器或⼏何着⾊器中设置点⼤⼩。着⾊器内建变量: gl_PointSize,并且可以在着⾊器源码直接写 gl_PointSize = 5.0
//6. 设置线段宽度 glLineWidth(2.5f);

GLBatch批次类:实现图元的绘制
void GLBatch::Begain(GLeunm primitive,GLuint nVerts,GLuint nTexttureUnints = 0); 
参数1:图元
参数2:顶点数
参数3:⼀组或者2组纹理坐标(可选)
//复制顶点数据(⼀个由3分量x,y,z顶点组成的数组) 
void GLBatch::CopyVerterxData3f(GLfloat *vVerts); 
//复制表⾯法线数据
void GLBatch::CopyNormalDataf(GLfloat *vNorms); 
//复制颜⾊数据
void GLBatch::CopyColorData4f(GLfloat *vColors); 
//复制纹理坐标数据
void GLBatch::CopyTexCoordData2f(GLFloat *vTextCoords, GLuint uiTextureLayer); 
//结束数据复制
void GLBatch::End(void); 
//绘制图形
void GLBatch::Draw(void);


Demo实现

头文件引入


#include "GLTools.h"    
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"

#include 
#ifdef __APPLE__
#include 
#else
#define FREEGLUT_STATIC
#include 
#endif

参数定义

// 各种需要的类
GLShaderManager     shaderManager;  //着色器管理类
GLMatrixStack       modelViewMatrix;//模型视图堆栈
GLMatrixStack       projectionMatrix;//投影视图堆栈
GLFrame             cameraFrame; //观察者坐标
GLFrame             objectFrame; //物体世界坐标
//投影矩阵
GLFrustum           viewFrustum;

//容器类(7种不同的图元对应7种容器对象)
GLBatch             pointBatch;
GLBatch             lineBatch;
GLBatch             lineStripBatch;
GLBatch             lineLoopBatch;
GLBatch             triangleBatch;
GLBatch             triangleStripBatch;
GLBatch             triangleFanBatch;

//几何变换的管道
GLGeometryTransform transformPipeline;

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


// 跟踪效果步骤
int nStep = 0;

main函数实现部分


int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    //申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    //设置window 的尺寸
    glutInitWindowSize(800, 600);
    //创建window的名称
    glutCreateWindow("GL_POINTS");
    //注册回调函数(改变尺寸)
    glutReshapeFunc(ChangeSize);
    //点击空格时,调用的函数
    glutKeyboardFunc(KeyPressFunc);
    //特殊键位函数(上下左右)
    glutSpecialFunc(SpecialKeys);
    //显示函数
    glutDisplayFunc(RenderScene);
    
    //判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    //绘制
    SetupRC();
    
    //runloop运行循环
    glutMainLoop();
    return 0;
}

SetupRC部分:初始化工作

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);
    
    /*
     常见函数:
     void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
      参数1:表示使用的图元
      参数2:顶点数
      参数3:纹理坐标(可选)
     
     //负责顶点坐标
     void GLBatch::CopyVertexData3f(GLFloat *vNorms);
     
     //结束,表示已经完成数据复制工作
     void GLBatch::End(void);
     
     
     */
    //定义一些点,三角形形状。
   
    GLfloat vCoast[9] = {
        3,3,0,0,3,0,3,0,0
        
    };
    
    //用点的形式
    pointBatch.Begin(GL_POINTS, 3);
    pointBatch.CopyVertexData3f(vCoast);
    pointBatch.End();
    
    //通过线的形式
    lineBatch.Begin(GL_LINES, 3);
    lineBatch.CopyVertexData3f(vCoast);
    lineBatch.End();
    
    //通过线段的形式
    lineStripBatch.Begin(GL_LINE_STRIP, 3);
    lineStripBatch.CopyVertexData3f(vCoast);
    lineStripBatch.End();
    
    //通过线环的形式
    lineLoopBatch.Begin(GL_LINE_LOOP, 3);
    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};
    
    
    //GL_TRIANGLES 每3个顶点定义一个新的三角形
    triangleBatch.Begin(GL_TRIANGLES, 12);
    triangleBatch.CopyVertexData3f(vPyramid);
    triangleBatch.End();
    
    
    // 三角形扇形--六边形
    GLfloat vPoints[100][3];    
    int nVerts = 0;
    //半径
    GLfloat r = 3.0f;
    //原点(x,y,z) = (0,0,0);
    vPoints[nVerts][0] = 0.0f;
    vPoints[nVerts][1] = 0.0f;
    vPoints[nVerts][2] = 0.0f;
    
    //M3D_2PI 就是2Pi 的意思,就一个圆的意思。 绘制圆形
    for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
        
        //数组下标自增(每自增1次就表示一个顶点)
        nVerts++;
        /*
         弧长=半径*角度,这里的角度是弧度制,不是平时的角度制
         既然知道了cos值,那么角度=arccos,求一个反三角函数就行了
         */
        //x点坐标 cos(angle) * 半径
        vPoints[nVerts][0] = float(cos(angle)) * r;
        //y点坐标 sin(angle) * 半径
        vPoints[nVerts][1] = float(sin(angle)) * r;
        //z点的坐标
        vPoints[nVerts][2] = -0.5f;
    }
    
    // 结束扇形 前面一共绘制7个顶点(包括圆心)
    //添加闭合的终点
    //课程添加演示:屏蔽177-180行代码,并把绘制节点改为7.则三角形扇形是无法闭合的。
    nVerts++;
    vPoints[nVerts][0] = r;
    vPoints[nVerts][1] = 0;
    vPoints[nVerts][2] = 0.0f;
    
    // 加载!
    //GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列,共用相邻顶点的一组三角形
    triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
    triangleFanBatch.CopyVertexData3f(vPoints);
    triangleFanBatch.End();
    
    //三角形条带,一个小环或圆柱段
    //顶点下标
    int iCounter = 0;
    //半径
    GLfloat radius = 3.0f;
    //从0度~360度,以0.3弧度为步长
    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);
        
        //绘制2个三角形(他们的x,y顶点一样,只是z点不一样)
        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++;
    }
    
    // 关闭循环
    printf("三角形带的顶点数:%d\n",iCounter);
    //结束循环,在循环位置生成2个三角形
    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++;
    
    // GL_TRIANGLE_STRIP 共用一个条带(strip)上的顶点的一组三角形
    triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
    triangleStripBatch.CopyVertexData3f(vPoints);
    triangleStripBatch.End();
}

changeSize部分:当窗口发生改变时进行调用

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();
}

SepcialKey部分:方向键按下时的处理

//特殊键位处理(上、下、左、右移动)
void SpecialKeys(int key, int x, int y)
{
    
    if(key == GLUT_KEY_UP)
        //围绕一个指定的X,Y,Z轴旋转。
        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();
}

KeyPressFunc部分:空格键按下时的处理

void KeyPressFunc(unsigned char key, int x, int y)
{
    if(key == 32)//ASCII码对应的“空格键”
    {
        nStep++;
        
        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();
}

RenderScene部分:场景渲染部分

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;
    //只要使用 GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。用来使用GLShaderManager 的使用。或者是获取顶部矩阵的顶点副本数据
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
     --transformPipeline.GetModelViewProjectionMatrix() 获取的
     GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
    
    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;
        case 4:
            DrawWireFramedBatch(&triangleBatch);
            break;
        case 5:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleFanBatch);
            break;
    }
    
    //还原到以前的模型视图矩阵(单位矩阵)
    modelViewMatrix.PopMatrix();
    
    // 进行缓冲区交换
    glutSwapBuffers();
}

总结:

ps:Demo部分大致了解就行,里面所涉及到的模型堆栈,后面小节会为大家讲解,大家主要了解渲染流程即可.

顶点->顶点缓冲区->顶点着色器->图元装配->片元着色器->渲染->显示屏幕

下一节:四:OpenGL渲染技巧

你可能感兴趣的:(三:OpenGL渲染基础)