美文网首页OpenGL初探
第三节—绘制一个完整的图形

第三节—绘制一个完整的图形

作者: L_Ares | 来源:发表于2020-04-24 11:24 被阅读0次

本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

在前面的文章中学习了OpenGL的一些基础内容,本节则是第一次开始写代码,从一些简单的图形开始绘制,一步一步的深入学习。

首先是要根据OpenGL的环境构建那一节的内容,将OpenGL的环境搭建完成。然后整理绘制图形的思路。

首先,找出需要的库,三方类。

其次,定义需要的变量。

最后,涉及到渲染,如何去做理解并熟悉这套固定的流程。

一、相关引用类的介绍

  1. 着色器管理器

(1)、GLTools的着色器管理器类

(2)、提供了创建和管理着色器的管理者,而且还提供了一组存储着色器,可以进行一些初级操作

#include "GLShaderManager.h"

  1. GL工具包

这里面包含了GLTool大多数的类似C语言的独立函数。包括这一节使用的GLBatch批次类

和gltSetWorkingDirectory设置工作区间

#include "GLTools.h"

3.GLUT


在MacOS下面需要GLUT。在Windows和Linux下面有FreeGLUT的静态版本库,并且需要添加一个宏

#include <GLUT/GLUT.h>

1.1

二、定义需要用到的变量

画的就是图1.1显示的正方形


1. 定义一个着色器管理器
GLShaderManager shaderManager

2. 定义一个批次类容器
GLBatch triangleBatch

3. 定义图形的边长
GLfloat blockSize = 0.1f

4. 定义正方形四个顶点的坐标(根据情况自行调整)
顶点坐标顺序D->C->B->A
GLfloat vVerts[] = {
    -blockSize,-blockSize,0,
    blockSize,-blockSize,0,
    blockSize,blockSize,0,
    -blockSize,blockSize,0
}

三、自定义函数

这里面的自定义的意思是把需要用到的操作归类,形成一个思维逻辑,这样更好的理解绘制的流程。

先说下流程:

创建并设置窗口--->设置渲染环境--->渲染的详细操作

然后可以按照顺序来写,就很容易理解。

  1. 创建并设置窗口:
触发条件:
(1). 新建窗口
(2). 窗口尺寸大小发生改变
处理业务:
(1). 设置OpenGL的视口
(2). 设置OpenGL的投影方式等
参数说明:
(1). 参数中的x,y为0是代表了窗口中视图左下角的坐标,通常情况下x,y都是0
(2). w和h的单位是像素
void changeSize(int w, int h)
{
    glViewport(0,0,w,h);
}

  1. 渲染环境设置:

触发条件:
(1). 手动触发
处理业务:
(1). 设置窗口清屏颜色
(2). 初始化存储着色器管理器
(3). 设置顶点信息和绘制方式
(4). 利用三角形批次类将数据拷贝到着色器
void setUpRC ()
{
    //设置清屏颜色
    glClearColor(0.98f, 0.4f, 0.7f, 1.f);
    //初始化着色器管理器
    shaderManager.InitializeStockShaders();
    //设置要绘制的图形的绘制方式和顶点数量
    triangleBatch.Begin(GL_TRIANGLE_FAN,4);
    //拷贝顶点和绘制信息到存储着色器
    triangleBatch.CopyVertexData3f(vVerts);
    //告知完成了渲染环境的设置,可以进行渲染
    triangleBatch.End();
    
}

  1. 渲染详情:

触发条件:
(1). 系统自动触发
(2). 手动触发
处理业务:
(1). 清除缓冲区
(2). 调用存储着色器
(3). 绘制图形
void RenderScence(void)
{
    //清除缓冲区,需要重新对缓冲区的数值进行预置
    //OpenGL不止一种缓冲区
    //可以清除一个或者一组特定的缓冲区
    //参数是想要气你感触的缓冲区的类型,都是宏,下方会有介绍这些宏都是什么
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    //设置要绘制的图形的颜色,用数组来保存RGBa的值
    GLfloat vRed[] = {1.f,0.f,0.f,1.f};
    
    //选定着色器种类和指定渲染颜色
    //第一个参数是指定使用的着色器种类
    //第二个参数用于指定颜色和以默认的笛卡尔坐标系在屏幕上渲染图形
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);

    //利用三角形批次类GLBatch提交着色器收到的信息
    triangleBatch.Draw();

    //交付后台缓冲区进行渲染,并且在渲染完成后交换缓冲区
    glutSwapBuffers();

}

四、main函数的操作


int main(int argc,char *argv[])
{
    //设置工作目录和程序目录一致。
    //MacOS和Windows不同,工作目录和程序的可执行程序可能不在同一个文件夹下
    //需要将工作目录设置到程序的 /Resource 文件夹下
    //GLUT的优先设定已经设置过了工作目录,这里的手动设定是为了保证100%相同
    gltSetWorkingDirectory(argv[0]);

    //初始化GLUT库
    //GLUT是一个程序内部运行的本地消息循环,会不断的拦截适当的消息,
    //调用我们在不同的时间注册的回调函数。在这里我们会注册两个回调函数,下面会有说明
    glutInit(&argc,argv);    

    //初始化显示模式。一般的,都是设置这四个。参数信息在下方备注。
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

    //初始化窗口大小
    glutInitWindowSize(800,600);

    //创建窗口名称
    glutCreateWindow("Triangle");

    //注册窗口大小改变后会调用的回调函数
    //呼应上面GLUT的2个回调函数之1
    glutReshapeFunc(changeSize);

    //注册发生渲染后调用的回调函数
    //GLUT在每次发生重新绘制的时候都会自动调用,执行这个被注册的函数回调
    //呼应上面的GLUT的2个回调函数之1
    glutDisplayFunc(RenderScence);

    //初始化一个Glew库,确保OpenGL的API完全的可用。
    //在试图尝试做任何渲染之前,都要确定驱动程序的初始化过程中间不能出现问题。
    GLenum status = glewInit();
    if(GLEW_OK != status) {
        printf("glew Error : %s, \n",glewGetErrorString(status));
        return 1;
    }

    //设置渲染环境
    setUpRC();

    //这是一个无限执行的循环
    //在运行的过程中接收并执行窗口上或者系统传来的用户指令
    //它只执行在它之前键入的指令,在它之后的指令不执行。
    glutMainLoop();

    return 0;

}

五、图形移动等特殊设置

根据上述的代码,已经完全的完成了一个简单的图形绘制。在这里,还可以注册一个特殊的Func,用来完成一些特别的操作,比如点击键盘的上下左右移动绘制好的图形等。

下面是添加可移动的方法。

要注意,因为OpenGL的视图范围我们在MacOS中都认为是(-1,1)的坐标区间,所以不要超过(-1,1)的大小范围

  1. 首先还是抽出来一个回调函数。

void SpecialKeys(int key, int x, int y)
{
    //设置每次点击按键可以移动的距离
    GLfloat stepSize = 0.025f

    //移动图形,我们先找出一个相对的顶点来做移动,然后根据这个相对顶点
    //移动其他的顶点,不需要四个顶点全部做一遍设置,我们以图1.1中的A点作为相对点,A的x坐标与D相同,直接取vVerts[0]即可。
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[10];

    //设置键位和键位对应的操作
    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.f)
    {
        blockX = -1.f;
    }

    if (blockX > (1.f - 2 * blockSize))
    {
        blockX = 1.f - 2 * blockSize;
    }

    if (blockY <(-1.f + 2 * blockSize))
    {
        blockY = -1.f + 2 * blockSize;
    }

    if (blockY > 1.f)
    {
        blockY = 1.f;
    }

    //然后将每一个点的x,y坐标都重新计算后设置
    //A点
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    //B点
    vVerts[6] = blockX + 2 * blockSize;
    vVerts[7] = blockY;

    //C点
    vVerts[3] = blockX + 2 * blockSize;
    vVerts[4] = blockY - 2 * blockSize;

    //D点
    vVerts[0] = blockX;
    vVerts[1] = blockY - 2 * blockSize;

    //利用GLBatch三角形批次类将顶点数据重新拷贝进入着色器,并手动触发渲染函数
    triangleBatch.CopyVertexData3f(vVerts);
    
    //重新发送渲染的信号,这也就是手动触发渲染回调函数RenderScence
    glutPostRedisplay();

}

  1. 然后在main函数里面注册特殊函数回调即可。

glutSpecialFunc(SpecialKeys);

六、上述参数的简单解释和拓展

详细的拓展会在以后的延展文章中学习写作。本文只介绍有哪些常用参数和大概的意思。

1.在3.2里面的setUpRC函数

设置要绘制的图形的绘制方式和顶点数量

    triangleBatch.Begin(GL_TRIANGLE_FAN,4)

第一个参数是选择顶点绘制的方式,这里默认大家都知道在OpenGL中绘制图形依靠的基础元素是点,线,三角形。下面先介绍三角形的绘制方式,因为本文用的就是三角形的绘制。如图6.1。

图6.1

图中都是可选的参数,按照需要的方式设置即可。

2.在3.3中,渲染RenderScence中

(1) 清除缓冲区


    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

GL_COLOR_BUFFER_BIT : 颜色缓冲区
GL_DEPTH_BUFFER_BIT : 深度缓冲区
GL_STENCIL_BUFFER_BIT : 模板缓冲区
GL_ACCUM_BUFFER_BIT : 累积缓冲区

(2)指定着色器种类和渲染颜色


    shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);

GLT_SHADER_IDENTITY : 单位着色器/单元着色器
GLT_SHADER_FLAT : 平面着色器
GLT_SHADER_SHADED : 上色着色器
GLT_SHADER_DEFAULT_LIGHT : 默认光源着色器
GLT_SHADER_POINT_LIGHT_DIFF : 点光源着色器
GLT_SHADER_TEXTURE_REPLACE : 纹理替换矩阵着色器
GLT_SHADER_TEXTURE_MODULATE : 纹理调整着色器
GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF : 纹理光源着色器

3.在main函数中

    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);

如图6.3

图6.3

OpenGL一般都用双缓冲窗口,所以一般也都是初始化这4个显示模式。

总结

到此,绘制一个完整的图形也就完成了。

总结一下:

  1. main函数里面:
  • gltSetWorkingDirectory(argv[0])设置工作目录和程序的可执行程序在相同目录,这个是固定写法,就是要写。原因上面有,记住就行了。

  • glutInit(&argc,argv)一样,就是要这么写,固定写法。因为你不写,也就没有glut循环,也就不能调用那几个注册函数,也就不能实现每次出现变化的时候系统自动调用回调函数。

  • glutInitDisplayMode: 初始化显示模式

  • glutInitWindowSize : 初始化窗口大小

  • glutCreateWindow : 创建窗口并且命名窗口

  • 利用GLUT,注册各种回调函数,例如:glutReshapeFunc,glutDisplayFunc,glutSpecialFunc

  • glewInit : 初始化一个glew,确保API都可以正常使用。

  • 调用渲染环境设置函数:setUpRC

  • 设置GLUT的主Loop : glutMainLoop

  1. changSize里面:
  • 设置视口大小:glViewPort
  • 设置投影方式。
  1. RenderScence里面:
  • 清除缓冲区 : glClear
  • 调用着色器 : shaderManager.UseStockShader
  • 利用GLBatch提交着色器 : triangleBatch.Draw
  • 利用GLUT交换缓冲区 : glutSwapBuffer

相关文章

网友评论

    本文标题:第三节—绘制一个完整的图形

    本文链接:https://www.haomeiwen.com/subject/fhuaihtx.html