当前位置: 移动技术网 > IT编程>开发语言>Java > 8、OpenGL - 渲染框架

8、OpenGL - 渲染框架

2020年07月13日  | 移动技术网IT编程  | 我要评论

OpenGL 渲染架构

 

1、渲染架构

顶点着色器、片元着色器,可以自定义编程

图元装配、渲染,是我们不可以控制的

 

1.1、渲染流程

1、设置顶点数据和其他参数
2、在定点着色器中进行运算的到裁剪的坐标
3、细分着色器、几何着色器,不可自定义。
4、图元设置,根据设置构成点、线、三角形。
5、裁剪,裁剪掉超出显示区域的部分。
6、光栅化,将图源栅格化为一个个的像素点。
7、片元着色器,将应对的栅格(像素)填充为具体的颜色
8、渲染图像。


1.2、参数类型

将数据传给顶点着色器有三种方式

(1)、Attributes(ins) 属性:
顶点数据,X/Y/Z
投影矩阵、模型矩阵
纹理坐标(图片映射关系坐标)

Attributes(ins) 属性 只能传到顶点着色器,然后间接的传到片元着色器中去。
传入的数据例如
顶点数据,X/Y/Z
投影矩阵、模型矩阵
纹理坐标(图片映射关系坐标)
传进去的纹理坐标,其实对顶点着色器并没有用,目的是为了通过顶点着色器将纹理坐标传递给片元着色器

Attributes 属性只能传入顶点着色器,不能直接传递到片元着色器,只能通过GLSL代码间间接传递。

顶点数据
纹理坐标
光照法线
颜色数据

 

(2)、Uniform:
传输统一批次,不会改来改去的数据

例如:时间戳,变换矩阵,颜色值等
可以传给顶点着色器 和 片元着色器中

Uniforms 值可以传入顶点着色器、片元着色器。

 

(3)、Texture Data

纹理Data

position Vertex 顶点位置

 

由于OpenGL 是基于C的API,因此它非常便捷且受到广泛支持。作为C API,他与基于Objective-C 的 Cocoa 应用程序无缝集成。OpenGL提供应用程序用于生产2D 或3D图像的函数。您的应用程序将渲染的图像呈现给屏幕或将他们复制回自己的内存。

OpenGL规范没有提供自己的窗口层。它依赖于OS X 定义的功能来将 OpenGL 绘图与窗口系统集成。您的应用程序创建OS X OpenGL 渲染上下文并将渲染的目标附加到其上(称为可绘制对象)。渲染上下文管理 OpenGL 状态更改和通过调用 OpenGL API 创建的对象

练习题:
下面说明正确的是:
A、顶点着色器只能接受 Attribute 值
B、片元着色器可以间接获取Attribute 值
C、片元着色器可以接受 Attribute 值
D、纹理单元数据给顶点着色器实际上是没有必要的。

答案:B D
A、顶点着色器可以接受  Uniforms 、Texture Data 、 Attribute 这三种值
C、片元着色器不能直接接受Attribute 值,需要间接的通过顶点着色器得到


2、投影

2.1、透视投影

GLFrustum类通过 setPerspective 方法为我们构建一个平截头体。

GLFrustum::SetPerpective(float fFov, float fAspect, float fNear, float fFar);

正投影:显示2D 图形(一样大)
透视投影:3D图形(远小近大更加逼真)

参数:
fFov: 垂直方向上的视场角度
fAspect:窗口的宽度与高度的纵横比
fNear:进裁剪面距离
fFar:远裁剪面距离

纵横比 = 宽(w)/ 高(h)

练习题:
下面说明正确的是:
A、正投影不能使用在3D 图形上
B、透视投影不能正确渲染屏幕图形
C、正投影和透视投影的目的是为了让3D图形映射成二维屏幕坐标
D、正投影只能渲染屏幕图形
答案:C
无论值正投影还是透视投影都可以 渲染2D 和 3D 图形。区别在于正投影渲染出来的效果是一样的,渲染的3D图形没有远小近大的效果,看不出来是3D 图形还是2D 图形。透视投影在渲染3D 图形的时候是有远小近大的效果的。


3、参数的使用

3.1、顶点着色器

我们可以通过 attrubutes 获取的顶点数据,通过 uniforms 获取的 MVP 矩阵,在进行运算得到最终的顶点坐标。

gl_position = M_pro * M_view * M_model * V_local

 

3.2、片元着色器

比如在处理视频帧时,一般会使用 YUV 格式。要渲染到屏幕上需要 YUV 格式转换为 RGB 格式。在片元着色器中,通过 YUV 数据进行矩阵运算就可以得到具体的 RGB 颜色值了。 Alpha 通道直接赋值为 1.0。
RGB = YUV * 转换矩阵

纹理
像素数据。片元着色器中,可以通过纹理坐标获取的到对应的颜色值。如果对得到颜色值进行处理,就可以得到“滤镜”效果。

 

4、存储着色器

4.1、初始化

GLShaderManager 的初始化

GLShaderManager shaderManager;
shaderManager.InitializeStockShaders();

 

4.2、不同的存储着色器

4.2.1、单元着色器
参数1:存储着色器种类 - 单元着色器
参数2:颜色值

GLShaderManager::UserStockSHader(GLT_SHADER_IDENTITY,GLgloat vColor[4]);

使用场景:
绘制默认OpenGL 坐标系(-1, 1)下图形。图形所有片段都会以一种颜色填充。
例如:三角形,正方形的显示可以,但是如果想实现变化如旋转、缩放等用单元着色器是实现不了的。

 

4.2.2、平面着色器
参数1:存储着色器种类 - 平面着色器
参数2:允许变化的 4*4 矩阵
参数3:颜色色值

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

使用场景:在绘制图形时,可以应用变换(模型/投影变化)。
使用功能最多的着色器


4.2.3、上色着色器
参数1:存储着色器种类 - 上色着色器
参数2:允许变化的 4 * 4矩阵

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

使用场景:在绘制图形时,可以应用变换(模型 / 投影变化)。颜色将会平滑地插入到顶点之间,称为平滑着色
使用的非常少

 

4.2.4、默认光源着色器
参数1:存储着色器种类-默认光源着色器
参数2:模型4*4矩阵
参数3:投影4*4矩阵
参数4:颜色值

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

使用场景:在绘制图形时,可以应用变换(模型/ 投影变化)。这种着色器会使绘制的图形产生阴影和光照的效果
逼真,例如游戏中的光照等,默认光源模拟的是自然光


4.2.5、点光源着色器
参数1:存储着色器种类-点光源着色器
参数2:模型4*4矩阵
参数3:投影4*4矩阵
参数4:点光源的位置
参数5:漫反射颜色值

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

使用场景:在绘制图形时,可以应用变换(模型/投影变化)。这种着色器会绘制的图形产生阴影和光照的效果。它与默认光源着色器非常类似,区别只是光源位置可能是特定的。
点光源实现的例如一个台灯的光源等


4.2.6、纹理替换矩阵着色器
参数1:存储着色器种类 - 纹理替换矩阵着色器
参数2:模型 4* 4矩阵
参数3:纹理单元

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

使用场景:在绘制图形时,可以应用变换(模型 / 投影变化)。这种着色器通过给定的模型视图投影矩阵,使用纹理单元来进行颜色填充。其中每个像素点的颜色是从纹理中获取。
相当于上面又盖了一个图片的效果


4.2.7、纹理调整着色器
参数1:存储着色器种类 - 纹理调整着色器
参数2:模型4* 4矩阵
参数3:颜色值
参数4:纹理单元

GLShaderManager::UserStockShader(GLT_SHADER_TEXTURE_MODULATE,
                                 GLfloat mvMatrix[16],
                                 GLfloat vColor[4],
                                 GLint nTextureUnit);

使用场景:在绘制图形时,可以应用变换(模型/投影变换)。这种着色器通过给定的模型视图投影矩阵。着色器将一个基本色乘以一个取自纹理单元nTextureUnit 的纹理,将颜色与纹理进行颜色混合才填充到片段中。
例如上面想加一个蓝色,将颜色和像素的颜色进行混合


4.2.7、纹理光源着色器
参数1:存储着色器种类 - 纹理光源着色器
参数2:模型4 *4矩阵
参数3:投影4*4矩阵
参数4:点光源位置
参数5:颜色值
参数6:纹理单元

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

使用场景:在绘制图形时,可以应用变换(模型/投影变化)。这种着色器通过给定的模型视图投影矩阵,着色器将一个纹理通过漫反射照明计算进行调整(相乘)。
是一个综合的着色器,有光源还有纹理,还有混合颜色
使用的也比较少

缓存和缓冲是一个概念(也就是说在一些文章中有写到缓冲区有些写的是缓存区,其实都是一样的。)
 

练习题:

下面的归类合理的是 :
A、顶点着色器,片元着色器,细分着色器,纹理替换矩阵着色器
B、平面着色器,单元着色器,顶点着色器,点光源着色器
C、顶点着色器,片元着色器,细分着色器
D、单元着色器,默认光源着色器,平面着色器,纹理光源着色器

答案:C D
A、纹理替换矩阵着色器 属于固定管线下的着色器,其他都是可编程管线下的着色器
B、顶点着色器 属于可编程管线下的着色器,其他都是固定管线下的着色器
C、属于可编程管线下的着色器
D、属于固定管线下的着色器


5、OpenGL 的基本连接方式

5.1、OpenGL 节本图元连接方式

      图元                                                  描述
GL_POINTS                          每个顶点在屏幕上都是单独点
GL_LINES                              每一对顶点定义一个线段
GL_LINE_STRIP                   一个从第一个顶点依次经过每一个后续顶点而绘制的线条
GL_LINE_LOOP                    和GL_LINE_STRIP相同,但是最后一个顶点和第一个顶点连接起来了
GL_TRIANGLES                   每3个顶点定义一个新的三角形
GL_TRIANGLE_STRIP        共用一个条带(strip)上的顶点的一组三角形
GL_TRIANGLE_FAN            以一个圆点为中心呈扇形排序,共用相邻顶点的一组三角形

 

5.1.1、GL_TRIANGLE_STRIP(三角形带) 优点
用前 3 个顶点指定第 1 个三角形之后,接下来的每一个三角形,只需要再指定 1 个顶点。需要绘制大量的三角形时,采用这种方法可以节省大量的程序代码和数据存储空间。
提供运算行能喝节省带宽。更少的顶点意味着数据从内存传输到图形卡的速度更快,并且顶点着色器需要处理的次数也更少。

5.1.2、OpenGL 三角形环绕方式
在默认情况下,OpenGL 认为具有逆时针方向环绕的多边形为正面
GL_CW :顺时针环绕的多边形为正面
GL_CCW:逆时针环绕的多边形为正面
glFrontFace(GL_CW);


5.1.3、OpenGL 点 / 线
1、最简单也是最常用的 4.0f,表示点的大小

glPointerSize(4.0f);

2、设置点的大小范围和点与点之间的间隔

GLfloat size[2] = {2.0f, 4.0f};
GLfloat step = 1.0f;

3、获取点大小范围和最小步长

glGetFloatv(GL_POINT_SIZE_RANGE, size);
glGetFloatv(GL_POINT_GRAULARITY, &step);

4、通过使用程序点大小模式来设置点大小

glEnable(GL_PROGRAM_POINT_SIZE);

5、这种模式下允许我们通过编程在顶点着色器或几何着色器中设置点大小。着色器内建变量:gl_PointSize(内建变量),并且可以在着色器源码直接写

gl_PointSize = 5.0;

6、设置线段宽度

glLineWidth(2.5f);

 

5.2、三角形

5.2.1、OpenGL 三角形
对于OpenGL 光栅化最欢迎的是三角形。3个顶点就能构成一个三角形。三角形类型中来自于顶点。并不是所有的三角形都是正三角形等。

5.2.2、OPenGL 三角形环绕方式
在绘制第一个三角形时,线条是按照从V0 - V1,再到V2。最后再回到V0 的一个闭合三角形。这个是沿着顶点顺时针方法向。这种顺序与方向结合来指定顶点的方式成为环绕


5.2.3、OpenGL 三角形环绕方式
在默认情况下,OpenGL 认为具有逆时针方向环绕的多边形为正面。这就意味着上图左边是正面,右边是反面。

glFrontFace(GL_CW)
GL_CW:告诉OpenGL 顺时针环绕的多边形为正面
GL_CCW:告诉OpenGL 逆时针多边形为正面


OpenGL 三角形带

对于很多表明或者形状而言,我们会需要绘制几个相连的三角形。这是我们使用GL_TRIANGLE_STRIP 图元绘制一串相连三角形,从而节省大量的时间。

优点:
1、用前3个顶点指定第一个三角形之后,对于接下来的每一个三角形,只需要指定1个顶点。需要绘制大量的三角形时,采用这种方法可以节省大量的程序代理和数据存储空间
2、提供运算性能和节省带宽。更少的顶点意味着数据从内存传输到图形卡的速度更快,并且顶点着色器需要处理的次数也更少了。


OpenGL 三角形扇

对于很多表面或者形状而言,我们会需要绘制几个相连的三角形。这是我们可以使用功能GL——TRIANGLE_FAN 图元绘制一组围绕一个中心点相连的三角形


6、OpenGL 工具类

GLBatch,是在GLTools中包含的一个简单容器类。

void GLBatch::Begain(GLeunm primitive,GLuint nVerts, Gluint nTexttureunints = 0);

参数1:图元
参数2:顶点数
参数3:一组或者2组纹理坐标(可选)

//复制顶点数据(一个由3分量x,y,z顶点组成的数组)

void GLBatch::CopyVerterxData3f(GLfloat * vNorms);

//复制颜色数据

void GLBatch::CopyColorData4f(GLFloat * vColors);

//复制纹理坐标数据

void GLBatch::CopyTextCoordData2f(GLFloat *vTextCoords, GLuint uiTextureLayer);

//结束数据复制

Void GLBatch::End(void);

//绘制图形

Void GLBatch::Draw(void);

 

 

7、图元组合实现

使用固定存储着色器 OpenGL 下多种图元组合实现

使用OpenGL 的环境配置参阅:3、OpenGL - Mac 上的环境配置

详细代码参见Demo 

Demo地址 ->  OpenGLDemos -> 1、OpenGL图元

//点击空格,将固定位置上的顶点数据以6种不同形态展示!
#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

/*
 GLMatrixStack 变化管线使用矩阵堆栈
 
 GLMatrixStack 构造函数允许指定堆栈的最大深度、默认的堆栈深度为64.这个矩阵堆在初始化时已经在堆栈中包含了单位矩阵。
 GLMatrixStack::GLMatrixStack(int iStackDepth = 64);
 
 //通过调用顶部载入这个单位矩阵
 void GLMatrixStack::LoadIndentiy(void);
 
 //在堆栈顶部载入任何矩阵
 void GLMatrixStack::LoadMatrix(const M3DMatrix44f m);
 */
// 各种需要的类
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;

// 此函数在呈现上下文中进行任何必要的初始化。.
// 这是第一次做任何与opengl相关的任务。
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
        
    };

//    GLfloat vPyramid[12][3] = {
//        -4.0f, 0.0f, -4.0f,
//        4.0f, 0.0f, -4.0f,
//        0.0f, 8.0f, 0.0f,
//
//        4.0f, 0.0f, -4.0f,
//        4.0f, 0.0f, 4.0f,
//        0.0f, 8.0f, 0.0f,
//
//        4.0f, 0.0f, 4.0f,
//        -4.0f, 0.0f, 4.0f,
//        0.0f, 8.0f, 0.0f,
//
//        -4.0f, 0.0f, 4.0f,
//        -4.0f, 0.0f, -4.0f,
//        0.0f, 8.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();
}
void DrawWireFramedBatch(GLBatch* pBatch)
{
    /*------------画绿色部分----------------*/
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
          --transformPipeline 变换管线(指定了2个矩阵堆栈)
     参数3:颜色值
    */
    shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
    pBatch->Draw();
    
    /*-----------边框部分-------------------*/
    /*
        glEnable(GLenum mode); 用于启用各种功能。功能由参数决定
        参数列表:http://blog.csdn.net/augusdi/article/details/23747081
        注意:glEnable() 不能写在glBegin() 和 glEnd()中间
        GL_POLYGON_OFFSET_LINE  根据函数glPolygonOffset的设置,启用线的深度偏移
        GL_LINE_SMOOTH          执行后,过虑线点的锯齿
        GL_BLEND                启用颜色混合。例如实现半透明效果
        GL_DEPTH_TEST           启用深度测试 根据坐标的远近自动隐藏被遮住的图形(材料
     
     
        glDisable(GLenum mode); 用于关闭指定的功能 功能由参数决定
     
     */
    
    //画黑色边框
    glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
    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(2.5f);
    
    /* GLShaderManager 中的Uniform 值——平面着色器
     参数1:平面着色器
     参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
         --transformPipeline.GetModelViewProjectionMatrix() 获取的
          GetMatrix函数就可以获得矩阵堆栈顶部的值
     参数3:颜色值(黑色)
     */
    
    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(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();
}
//特殊键位处理(上、下、左、右移动)
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();
}
//根据空格次数。切换不同的“窗口名称”
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_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();
}
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;
}

 

 

 

 

 

 

 

 

本文地址:https://blog.csdn.net/shengdaVolleyball/article/details/107300756

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网