OpenGL->图元解析、深度测试、正背面剔除、裁剪、混合

网格状甜甜圈

这种图形是如何画出来的呢?带着这个疑问我们来研究一下子?

下面首下看下OpenGL的渲染流程来解答这个问题?


OpenGL渲染流程图.001.jpeg

在上图中属性、uniform值、纹理、输出是这个流程的关键因素我们先简单了解下:

属性:就是对每一个顶点都要作改变的数据元素。实际上,顶点位置本身就是一个属性。属性值可以是浮点数、整数、布尔数据。
属性总是以四维向量的形式进行内部存储的,即使我们不会使用所有的4个分量。一个顶点位置可能存储(x,y,z),将占有4个分量中的3个。
实际上如果是在平面情况下:只要在xy平面上就能绘制,那么Z分量就会自动设置为0;
属性还可以是:纹理坐标、颜色值、关照计算表面法线
在顶点程序(shader渲染)可以代表你想要的任何意义。因为都是你设定的。
属性会从本地客户机内存中复制存储在图形硬件中的一个缓冲区上。这些属性只提供给顶点着色器使用,对于片元着色器木有太大意义。

注意:这些属性对每个顶点都要做改变,但并不意味着它们的值不能重复。通常情况下,它们都是不一样的,但有可能整个数组都是同一值的情况。

Uniform值

属性是一种对整个批次属性都取统一值的单一值。它是不变的。通过设置uniform变量就紧接着发送一个图元批次命令,Uniform变量实际上可以无数次限制地使用,设置一个应用于整个表面的单个颜色值,还可以设置一个时间值。在每次渲染某种类型的顶点动画时修改它。

注意:这里的uniform 变量每个批次改变一次,而不是每个顶点改变一次。
uniform变量最常见的应用是在顶点渲染中设置变换矩阵
与属性相同点:可以是浮点值、整数、布尔值
与属性不同点:顶点着色器和片元着色器都可以使用uniform变量。uniform 变量还可以是标量类型、矢量类型、uniform矩阵。
纹理

传递给着色器的第三种数据类型:纹理数据

在顶点着色器、片段着色器中都可以对纹理数据进行采样和筛选。
典型的应用场景:片段着色器对一个纹理值进行采样,然后在一个三角形表面应用渲染纹理数据。
纹理数据,不仅仅表现在图形,很多图形文件格式都是以无符号字节(每个颜色通道8位)形式对颜色分量进行存储的。
输出

在图表中第四种数据类型是输出(out);输出数据是作为一个阶段着色器的输出定义的,二后续阶段的着色器则作为输入定义。
输出数据可以简单的从一个阶段传递到下一个阶段,也可以用不同的方式插入。
客户端的代码接触不到这些内部变量我们的OpenGL开发暂时接触不到。

7种基本的图元
OpenGL-7中基本图元.001.jpeg
  • 下面直接上代码查看图元相关的内容:
#include <stdio.h>
#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLGeometryTransform.h"

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLShaderManager          shaderManager;      //存储着色器管理工具类
GLMatrixStack            projectionMatrix;   //投影矩阵
GLMatrixStack            modelViewMatrix;    //模型视图矩阵
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.0,0.0f,1.0f};
GLfloat vBlack[] = {0.0f,0.0f,0.0f,1.0f};

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


void ChangeSize(int w,int h){
    glViewport(0, 0, w,h);
    //创建投影矩阵,并将它载入投影矩阵堆栈中
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0, 500.f);
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //调用顶部载入单元矩阵
    modelViewMatrix.LoadIdentity();//单元矩阵
    
    
}
void KeyPressFunc(unsigned char key,int x,int y){
    if (key == 32) {
        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_LIINE_LOOP");
            break;
        case 4:
            glutSetWindowTitle("GL_TRIANGLES");
            break;
        case 5:
            glutSetWindowTitle("GL_TRIANGLE_STRIP");
            break;
        case 6:
            glutSetWindowTitle("GL_TRIANGLE_FAN");
            break;
        default:
            break;
    }
    glutPostRedisplay();//重新展示窗口大小
}
void  SetupRC(){
    //灰色背景
    glClearColor(0.7f, 0.8f,0.9f, 1.0f);//存储着色器的初始化
    shaderManager.InitializeStockShaders();
    glEnable(GL_DEPTH_TEST);//开启深度测试
    //设置变换管线 以使用两个矩阵堆栈
    transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    cameraFrame.MoveForward(-15.0f);
    
    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.0
    };
    //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) {
        //数组下标自增,每自增一次就是表示一个顶点
        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个顶点(包括圆心)
    //添加闭合的终点
    
    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;
    //从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++;
    }
    
    // 关闭循环
    //结束循环,在循环位置生成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();
    
}
void DrawWireFramedBatch(GLBatch *pBatch){
   /*
    GLShaderManager 中的Uniform 值---平面着色器
    参数1:平面着色器
    参数2:运行为几何图形变换指定 一个 4 * 4 变换矩阵
            ---transformPipeline 变换管线(指定连个矩阵堆栈)
    */ 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 将多边形正面或者背面设为线框模式 实现线框渲染
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    //设置线条宽度
    glLineWidth(5.5f);
    
   /*
    GLShaderManager 中的Uniform值--平面着色器
    参数1:平面着色器
    参数2:运行为几何图形变换指定一个 4 * 4 变换矩阵
    
    */
 shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
    pBatch->Draw();
    
    //复原原本的设置
    //通过调用glPolygonMode将多边形正面或者背面设置为全部填充模式
    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_POLYGON_OFFSET_LINE);
    glLineWidth(1.0f);
    glDisable(GL_BLEND);
    glDisable(GL_LINE_SMOOTH);
    
}

//召唤场景
void RenderScene(){
    glClear(GL_COLOR_BUFFER_BIT |
            GL_DEPTH_BUFFER_BIT |
            GL_STENCIL_BUFFER_BIT
            );
    modelViewMatrix.PushMatrix();//压栈
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    //矩阵乘以矩阵顶部的矩阵,相乘的结果随后存储在堆栈的顶部
    modelViewMatrix.MultMatrix(mCamera);
    
    //只要使用GetMatrix函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载
    //使用GLShaderManager 或者获取顶部矩阵的顶点副本数据
    M3DMatrix44f mObjectFrame;
    objectFrame.GetMatrix(mObjectFrame);
    
    //矩阵乘以矩阵堆栈的顶部矩阵 想乘的结果随后简存在堆栈的顶部
    modelViewMatrix.MultMatrix(mObjectFrame);
    
 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(&triangleFanBatch);
            break;
        case 6:
            DrawWireFramedBatch(&triangleStripBatch);
            break;
    }
    //还原到以前的模型视图矩阵(单位矩阵) 绘制完毕 还原矩阵
    modelViewMatrix.PopMatrix();
    //进行缓冲区的交换
    glutSwapBuffers();
}


//特殊键位处理
void SpecialKeys(int key, int x ,int y){
    if (key == GLUT_KEY_UP) {
        //围绕一个指定的X Y Z轴旋转 弧度->度数的方法
        objectFrame.RotateWorld(m3dDegToRad(-0.5f), 1.0f, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_DOWN) {
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_LEFT) {
        objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0, 1.0f, 0.0);
    }
    if (key == GLUT_KEY_RIGHT) {
        objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
    }
    glutPostRedisplay();
}
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);
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW ERROR:%s\n",glewGetString(err));
        return 1;
    }
    //绘制
    SetupRC();
    glutMainLoop();
    return 0;
}

下面是甜甜圈的设置:

  • 刚开始设置的时候,可以看到甜甜圈是凌乱的,有黑色的还有切割状态的?因为使用点光源着色器看到的3D场景就会有阴影的出现,观察一个物体那些事可见的?那些是不可见的??
解决方法?
  • 油画算法:先绘制场景中离观察者比较远的物体,在绘制比较近的物体,弊端:如果几个图像相互交叉有该怎么办?
方法二:
  • 正背面剔除:OpenGL可以做到检查所有正面朝向观察者的面,并渲染他们,从而丢弃背面朝向他们的面,可以节约片元着色器的性能
深度
  • 深度其实就是该像素点在3D世界中距离摄像机的距离,Z值
深度缓冲区
  • 深度缓冲区就是一块内存区域,专门存储每个像素点(绘制在屏幕上的深度值)深度值越大,则离摄像机越远 在不使用深度测试的时候,如果我们先绘制一个距离比较近的物体,在绘制距离比较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉,有了深度缓冲区后,绘制物体的顺序就不那么重要了,实际上,只要存在深度缓冲区,OpenGL都会把像素的深度值写入到缓冲区中,除非调用glDepthMask(GL_FALSE)来禁止写入
深度测试
  • 深度缓冲区(DeptBuffer)和颜色缓冲区是对应的。颜色缓冲区存储像素的颜色信号,深度缓冲区存储像素的深度信息。在决定是否绘制一个物体表面时,首先要将表面对应的像素的深度值与当前缓冲区中的值进行比较。如果大于深度缓冲区中的值,则丢弃这部分,否则利用这个像素值对应的深度值和颜色值。分别更新深度缓冲区和颜色缓存区。这个过程称为"深度测试"
ZFighting闪烁问题
  • 因为开启深度测试后,OpenGL就不会再去绘制模式被遮挡的部分,这样实现的显示更加真实,但是由于深度缓冲区精度限制对于相差非常小的情况下,OpenGL可能出现判断不准确的,导致深度测试的结果不可预测,出现交错闪烁
    怎么办?
    解决方法:多边形偏移
    启用Polygon Offset 让深度之间产生间隔。如果两个图形之间有间隔,是不是意味着就不会产生干涉,可以理解为在执行深度测试前将立方体的深度值做一些细微的增加,于是就能将重叠的两个图形深度值之前有所区分

#include <stdio.h>
#include "GLTools.h"        //包含了大部分GLTool中类似C语言的独立函数
#include "GLMatrixStack.h"  //矩阵工具类,可以利用GLMarixStack加载单元矩阵
#include "GLFrame.h"        //矩阵工具类,表示位置,通过设置
#include "GLFrustum.h"      //矩阵工具类,用来快速设置正、透视投影矩阵,完成坐标从3D->2D映射过程
#include "GLGeometryTransform.h"//变换管道,用来快速在代码中传输视图矩阵、投影矩阵、视图投影变换矩阵等
#include "GLBatch.h"        //三角形批次类,利用它可以传输顶点、光照、纹理、颜色数据到存储着色器中

#include <math.h>
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

//设置角色帧 作为相机
GLFrame         viewFrame;
//使用GLFrustum类来设置投影视图
GLFrustum       viewFrustum;//设置图元绘制时的投影方式
GLTriangleBatch torusBatch;//容器类
GLMatrixStack   modelViewMatrix;//模型视图矩阵
GLMatrixStack   projectionMatrix;//投影矩阵
GLGeometryTransform  transformPipeLine;//变换管道 存储模型视图 投影 模型视图投影矩阵
GLShaderManager  shaderManager;//存储着色器管理工具类

//标记:背面剔除、深度测试
int iCull = 0;
int iDepth = 0;
//窗口改变
void ChangeSize(int w,int h){
    if (h == 0) {
        h = 1;
    }
    glViewport(0, 0, w, h);
    //设置透视模式,初始化其透视矩阵
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0, 100.f);
    //把透视矩阵加载到透视矩阵堆栈中
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    //初始化渲染管道
    transformPipeLine.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void SpecialKeys(int key ,int x,int y){
    if (key == GLUT_KEY_UP) {
        //m3dDegToRad 度数 -> 弧度  以x轴渲染,负值向上
        viewFrame.RotateWorld(m3dDegToRad(-5.f), 1.0, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_DOWN) {
        //m3dDegToRad 度数 -> 弧度
        viewFrame.RotateWorld(m3dDegToRad(5.f), 1.0, 0.0f, 0.0f);
    }
    if (key == GLUT_KEY_LEFT) {
        //m3dDegToRad 度数 -> 弧度
        viewFrame.RotateWorld(m3dDegToRad(-5.f), 0.0, 1.0f, 0.0f);
    }
    if (key == GLUT_KEY_RIGHT) {
        //m3dDegToRad 度数 -> 弧度
        viewFrame.RotateWorld(m3dDegToRad(5.f), 0.0, 1.0f, 0.0f);
    }
    //重新刷新
    glutPostRedisplay();
}
void ProcessMenu(int value){
    switch (value) {
        case 1:
            iDepth = !iDepth;
            break;
        case 2:
            iCull = !iCull;
            break;
        case 3:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
            break;
        case 4:
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            break;
        case 5:
            glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
            break;
    }
    glutPostRedisplay();
}
//渲染场景
void RenderScene(){
    //清楚窗口和深度缓冲区
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    //开启、关闭正背面剔除功能
    if (iCull) {
        glEnable(GL_CULL_FACE);
        glFrontFace(GL_CCW);
        glCullFace(GL_BACK);
    }else{
        glDisable(GL_CULL_FACE);
    }
    
    
    //根据设置的iDepth标记判断是否开启深度测试
    if (iDepth) {
        glEnable(GL_DEPTH_TEST);
        
    }else{
        glDisable(GL_DEPTH_TEST);
    }
    //把摄像机矩阵压入模型矩阵中
    modelViewMatrix.PushMatrix(viewFrame);
    //设置颜色
    GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};
    //使用默认光源着色器
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),vRed);
    torusBatch.Draw();
    modelViewMatrix.PopMatrix();
    glutSwapBuffers();
}
void SetupRC(){
    glClearColor(0.3, 0.3f, 0.3f, 1.0f);
    shaderManager.InitializeStockShaders();
    viewFrame.MoveForward(7.0);//将相机向后移动7个单元:肉眼到物体的距离
    
    
    gltMakeTorus(torusBatch, 1.0, 0.3, 52, 26);
    glPointSize(4.0f);
}
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("Geometry Test Program");
    glutReshapeFunc(ChangeSize);
    
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);
    
    //添加右击菜单栏
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Toggle dept test", 1);
    glutAddMenuEntry("Toggle cull backface", 2);
    glutAddMenuEntry("set Fill Mode", 3);
    glutAddMenuEntry("set Line Mode", 4);
    glutAddMenuEntry("set Point Mode", 5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);
    
    GLenum err= glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error:%s \n",glewGetErrorString(err));
    }
    SetupRC();
    glutMainLoop();
    return 0;
}

裁剪

//
//  main.cpp
//  OpenGL03-裁剪
//
//  Created by apple on 2019/11/7.
//  Copyright © 2019年 apple. All rights reserved.
//
#include <stdio.h>
#include "GLTools.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include<GL/glut.h>
#endif
void ChangeSize(int w,int h){
    if (h == 0) {
        h = 0;
    }
    glViewport(0, 0, w, h);
}
void RenderScene(){
    glClearColor(0.0, 0.0, 1.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    
    //设置彩铅区域为红色
    glClearColor(1.0, 0.0f, 0.0f, 0.0f);
    //设置裁剪尺寸
    glScissor(100, 100, 600, 400);
    //开启裁剪测试
    glEnable(GL_SCISSOR_TEST);
    //开启清屏,执行裁剪
    glClear(GL_COLOR_BUFFER_BIT);
    
    
    //裁剪一个绿色小矩形
    glClearColor(0.0f, 1.0f, 0.0f, 0.0f);
    //设置裁剪尺寸
    glScissor(200, 200, 400, 200);
    //开始清屏执行裁剪
    glClear(GL_COLOR_BUFFER_BIT);
    
    //关闭裁剪测试
    glDisable(GL_SCISSOR_TEST);
    
    //强制执行缓冲区
    glutSwapBuffers();
}
int main(int argc,char *argv[]){
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(800, 600);
    glutCreateWindow("OpenGL Scissor");
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutMainLoop();
    
    return 0;
}
混合
  • 混合:OpenGL渲染时会把颜色值存在颜色缓冲区,每个片段的深度值也是放在深度缓冲区。当深度缓冲区关闭时,新的颜色将简单的覆盖原来颜色缓冲区的颜色值,当深度缓冲区再次打开时,新的颜色片段只是当他们比原来的值更接近临近的裁剪平面才会替换原来的颜色片段
  • 目标颜色:已经存储在颜色缓存区的颜色值
  • 源颜色:作为当前渲染命令结果进入颜色缓存区的颜色值

当混合功能被开启时,源颜色和目标颜色的组合方式是混合方程式控制的。在默认情况下,混合方程式如下所示:
Cf = (Cs * S) + (Cd * D)

Cf: 最终计算参数的颜色
Cs: 源颜色
Cd: 目标颜色
S: 源混合因子
D: 目标混合因子


//
//  main.cpp
//  OpenGL03-混合
//
//  Created by apple on 2019/11/7.
//  Copyright © 2019年 apple. All rights reserved.
//

#include <stdio.h>

#include "GLTools.h"
#include "GLShaderManager.h"

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif

GLShaderManager shaderManager;

GLBatch         squareBatch;
GLBatch         greenBatch;
GLBatch         redBatch;
GLBatch         blueBatch;
GLBatch         blackBatch;

GLfloat blockSize = 0.2f;
GLfloat vVerts[] = {
    -blockSize,-blockSize,0.0f,
    blockSize,-blockSize,0.0f,
    blockSize,blockSize,0.0f,
    -blockSize,blockSize,0.0f
};

//初始化操作
void SetupRC(){
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    shaderManager.InitializeStockShaders();
    
    //绘制一个移动矩形
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
    
    //绘制4个固定矩形
    GLfloat vBlock[] = {
        0.25,0.25,0.0f,
        0.75f, 0.25f, 0.0f,
        0.75f, 0.75f, 0.0f,
        0.25f, 0.75f, 0.0f
    };
    greenBatch.Begin(GL_TRIANGLE_FAN, 4);
    greenBatch.CopyVertexData3f(vBlock);
    greenBatch.End();
    
    
    GLfloat vBlock2[] = { -0.75f, 0.25f, 0.0f,
        -0.25f, 0.25f, 0.0f,
        -0.25f, 0.75f, 0.0f,
        -0.75f, 0.75f, 0.0f};
    
    redBatch.Begin(GL_TRIANGLE_FAN, 4);
    redBatch.CopyVertexData3f(vBlock2);
    redBatch.End();
    
    
    GLfloat vBlock3[] = { -0.75f, -0.75f, 0.0f,
        -0.25f, -0.75f, 0.0f,
        -0.25f, -0.25f, 0.0f,
        -0.75f, -0.25f, 0.0f};
    
    blueBatch.Begin(GL_TRIANGLE_FAN, 4);
    blueBatch.CopyVertexData3f(vBlock3);
    blueBatch.End();
    
    
    GLfloat vBlock4[] = { 0.25f, -0.75f, 0.0f,
        0.75f, -0.75f, 0.0f,
        0.75f, -0.25f, 0.0f,
        0.25f, -0.25f, 0.0f};
    
    blackBatch.Begin(GL_TRIANGLE_FAN, 4);
    blackBatch.CopyVertexData3f(vBlock4);
    blackBatch.End();
    
}
void ChangeSize(int w,int h){
    glViewport(0, 0, w, h);
}
//上下左右键位控制移动
void SpecialKeys(int key, int x, int y)
{
    GLfloat stepSize = 0.025f;
    
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[7]; //以左上角的为顶点计算 其偏移量到底多少
    
    
    if (key == GLUT_KEY_UP) {
        blockY += stepSize;
    }
    if(key == GLUT_KEY_DOWN)
        blockY -= stepSize;
    
    if(key == GLUT_KEY_LEFT)
        blockX -= stepSize;
    
    if(key == GLUT_KEY_RIGHT)
        blockX += stepSize;
    
    if(blockX < -1.0f) blockX = -1.0f;
    if(blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;;
    if(blockY < -1.0f + blockSize * 2)  blockY = -1.0f + blockSize * 2;
    if(blockY > 1.0f) blockY = 1.0f;
    
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize*2;
    
    vVerts[3] = blockX + blockSize*2;
    vVerts[4] = blockY - blockSize*2;
    
    vVerts[6] = blockX + blockSize*2;
    vVerts[7] = blockY;
    
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    squareBatch.CopyVertexData3f(vVerts);
    glutPostRedisplay();
    
}
//召唤场景
void RenderScene(void)
{
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    //定义4种颜色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 0.5f };
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    GLfloat vBlue[] = { 0.0f, 0.0f, 1.0f, 1.0f };
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    
    //召唤场景的时候,将4个固定矩形绘制好
    /*
     使用单位着色器
     参数1:简单的使用默认笛卡尔坐标系(-1,1),所有的片段都应用一种颜色
     参数2:着色器颜色
     */
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vGreen);
    greenBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    redBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    blueBatch.Draw();
    
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
    blackBatch.Draw();
    
    //组件核心代码
    //1.开启混合
    glEnable(GL_BLEND);
    //开启组合函数 计算混合颜色因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //使用着色器管理器
    /*
     使用单位着色器
     参数1:简单的使用默认笛卡尔坐标系(-1,1) 所有片段都应用一种颜色,GLT_SHADER_IDENTITY
     参数2:着色器颜色
     */
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);
    //容器类开始绘制
    squareBatch.Draw();
    
    //关闭混合功能
    glDisable(GL_BLEND);
    //同步绘制命令
    glutSwapBuffers();
}


int main(int argc, char * argv[]){
    
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("移动矩形,观察颜色");
    
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    SetupRC();
    
    glutMainLoop();
    return 0;
    
    
    return 0;
}