绘图基础图形

002– 3D图形 和 OpenGL 简介
http://www.jianshu.com/p/ff26de4eee71
001–OpenGL在Mac
上的条件搭建:http://www.jianshu.com/p/f3b21fe058e3

护眼图,文字太多啦!哈哈哈。。。

OpenGL 简介

OpenGL发展至今,已经20余年。作为一个成熟而久负出名的跨平台的处理器图形应用程序接口规范,它曾经被普遍应在游戏、影视、军事、航空航天、地理、艺术学、机械设计,以及各个科学数据可视化的世界。

再者随着互联网和活动平台的火速发展,异步突起的OpenGL ES 和 WebGL
标准也吸引了大批量开发者的眼珠子。而那六头与OpenGL
本身一样有千头万绪的联络。

OpenGL 大概协助所有现有的主流操作系统平台,包蕴Windows、Mac OS
X以及各样UNIX平台。它同时也足以用来大概拥有主流的编程语言环境中,例如C/C++、Java、C#、Visual
Basic、Python等。因而,OpenGL应当是大地最为常见学习和动用的图片开发API接口。

Open GL 概述

什么是OpenGL

OpenGL 是一种应用程序编程接口(Application Programming
Interface,API)
,它是一种能够对图片硬件设备特性开展走访的软件库。包罗了500个不等的命令,可以用于安装所需的靶子、图像和操作,以便开发交互式的3维总计机图形应用程序。

OpenGL
被设计为一个现代化的,硬件无关的接口。因而不须求考虑统计机操作系统和窗口系统的前提下,在各个不一样的图片硬件系统上,或者完全通过软件的章程贯彻OpenGL
接口。

OpenGL
自身是不分包其余履行的窗扣任何或者处理用户输入输出的函数。事实上大家须求经过应用程序所运行的窗口系统提供的接口来举行这类操作。

OpenGL也不曾提供任何用户表明3维实体模型,或者读取图像文件的操作。我们必要通过一层层几何图元(包括点、线、三角形以及patch)来创设三维空间的物体。

OpenGL 程序

  • 渲染:表示总括机从模型创设最终图像的经过。OpenGL
    只是一种基于光栅化的种类。
  • 模型(场景对象):通过几何图元(点、线、三角形)来打造的。
  • 着色器,它是图形硬件设计所执行的一类特殊的函数。可以驾驭为图像处理单元(GPU)编译的一种小型程序。
  • 多种差距的着色阶段(shander
    stage),其中最常用的牢笼终端着色器(vertex
    shader)以及片元着色器,前者用于拍卖顶点数据,后者用于拍卖光栅化后的片元数据。所有OpenGL程序都须求用到那两类着色器
  • 帧缓存(framebuffer),像素(pixel),是屏幕上不大的可知单元。总计机种类将持有的像素保存到帧缓存当中,后者是有图片硬件装备管理的一块独立内存区域,可以一贯照射到最后的来得设备上

什么叫 光栅化?
将输入图元的数学描述转为显示屏地方对应的像素片元,称为关栅化

OpenGL 渲染图像的OpenGL 程序需求执行的操作:

  • 从OpenGL的几何图元中设置数据,用于营造形状。
  • 使用区其余着色器(shader)对输入的图元数据举行总括操作,判断它们的职责、颜色,以及其余渲染属性。
  • 将输入图元的数学描述转化为与屏幕位置对应的像素片元(fragment)。这一步也号称光栅化(rasterization)。
  • 最终,针对光栅化进度爆发的各样片元,执行片元着色器(fragment
    shader),从而控制以此片元的末段颜色和职位。
  • 一经有必不可少,还亟需对每个片元执行一些外加的操作,例如判断片元对应的目标是否可知,或者将片元的水彩与当前屏幕地方的水彩举办融合。

案例1 绘制一个正方形

#include <iostream>
#include <GLUT/GLUT.h>

void draw() {

    //设置清屏色
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    //设置颜色,红色
    glColor3f(1.0f, 0.0f, 0.0f);
    //设置绘图时的坐标系统
    glOrtho(0.0f, 1.0f, 0.0f, 1.0f, -1.0f, 1.0f);
    //开始渲染
    glBegin(GL_POLYGON);
    //设置多边形的4个顶点
    glVertex3f(0.25f, 0.25f, 0.0f);
    glVertex3f(0.75f, 0.25f, 0.0f);
    glVertex3f(0.75f, 0.75f, 0.0f);
    glVertex3f(0.25f, 0.75f, 0.0f);
    //结束渲染
    glEnd();
    //强制刷新缓冲区,保证绘制命令被执行
    glFlush();

}

int main(int argc, const char* argv[]) {
    //初始化GLUT库
    glutInit(&argc, (char**)argv);
    //创建一个窗口并制定窗口名
    glutCreateWindow("HelloWorld");
    //注册一个绘图函数,操作系统在必要时刻就会对窗体进行重新绘制操作
    glutDisplayFunc(draw);
    //进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环(永生循环)
    glutMainLoop();
    return 0;
}

OpenGL语法

运用GLUT那些作为函数名的都是选择了GLUT库的

OpenGL 渲染管线

什么样是渲染管线?
它是一各类数据处理进程,并且将应用程序的数额转换来终极的渲染图像。

OpenGl
首先接受用户提供的几何数据(顶点和几何图元),并且将它输入到一多级着色器阶段中开展拍卖,包涵:顶点着色、细分着色、以及最后的几何着色,然后它将进入光删化单元。光栅化单元负责对持有剪切区域内的图元生成片元数据,然后对各类生成的片元都执行一个片元着色器。

实际,唯有极端着色器 和
片元着色器是必须的。细分和几何着色器是可选的步调。

案例2 不难绘制一个三角

2.1要导入什么框架

  • #include<GLTools.h>
    GLTool.h头文件包括了多数GLTool中类似C语言的单身函数
  • #include<GLShaderManager.h> 移入了GLTool 着色器管理器(shader
    Mananger)类。没有着色器,大家就不能在OpenGL(主题框架)进行着色。着色器管理器不仅允许我们成立并管理着色器,还提供一组“存储着色器”,他们力所能及举办部分起先䄦基本的渲染操作。
  • 在Mac 系统下,#include<glut/glut.h>
  • 在Windows 和
    Linux上,大家运用freeglut的静态库版本并且须求添加一个宏。
    #define FREEGLUT_STATIC
    #include<GL/glut.h>

2.2 启动GLUT

1.主次的延续“main”函数伊始拍卖
GLTools函数glSetWorkingDrectory用来安装当前工作目录。实际上在Windows中是不需要的,因为做事目录默许就是与程序可举办实施顺序一样的目录。不过在Mac
OS
X中,那么些程序将当前工作文件夹改为应用程序捆绑包中的/Resource文件夹。GLUT的先期设定自动举行了那一个中设置,不过如此中艺术尤其安全。

2.创制窗口并设置展现格局
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
GLUT_DOUBLE:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后快速转换成窗口视图,那种方法,日常用来变化动画效果;
GLUT_DEPTH:标志将一个纵深缓存区分配为突显的一片段,因而大家可以履行深度测试;
GLUT_STENCIL:确保大家也会有一个可用的模版缓存区。
纵深、模板测试前边会仔细讲到。

3.初始化GLEW库
双重调用GLEW库开始化OpenGL 驱动程序中具备丢失的入口点,以确保OpenGL
API对开发者完全可用。
调用glewInit()函数三遍就能成功这一步。在准备做别的渲染此前,还要检查确定驱动程序的初阶化进程中没有出现其余难点。

4.SetupRC()
实则那些函数对GLUT
没有怎么影响,可是在实际初阶渲染此前,咱们那边展开任何OpenGL
起先化都极度有益。那里的RC代表渲染环境,那是一个运作中的OpenGL状态机的句柄。在其余OpenGL
函数起功用之前务必创设一个渲染环境。而GLUT在大家首先次创立窗口时就完毕了这项工作。

5.先导化设置
void glClearColor(GLclampf red,GLclampf green,GLclampf blue,GLclampf alpha);

在windows 颜色成分取值范围:0-255以内
在iOS、OS 颜色成分取值范围:0-1时期浮点值
![科普颜色值表](/Users/liuyi/Documents/潭州教育/VIP Open GL/第一天/01
备课/01–OpenGL初览下/课件/颜色值表.png
)

#include "GLShaderManager.h"

#include "GLTools.h"

#include <glut/glut.h>

//简单的批次容器,是GLTools的一个简单的容器类。
GLBatch triangleBatch;

GLShaderManager shaderManager;


//窗口大小改变时接受新的宽度和高度,其中0,0代表窗口中视口的左下角坐标,w,h代表像素

void ChangeSize(int w,int h)

{

    glViewport(0,0, w, h);

}

//为程序作一次性的设置

void SetupRC()

{

    //设置背影颜色

    glClearColor(0.0f,0.0f,1.0f,1.0f);

    //初始化着色管理器

    shaderManager.InitializeStockShaders();

    //设置三角形,其中数组vVert包含所有3个顶点的x,y,笛卡尔坐标对。

      GLfloat vVerts[] = {

        -0.5f,0.0f,0.0f,

        0.5f,0.0f,0.0f,

        0.0f,0.5f,0.0f,

    };

    //批次处理

    triangleBatch.Begin(GL_TRIANGLES,3);

    triangleBatch.CopyVertexData3f(vVerts);

    triangleBatch.End();

}

//开始渲染

void RenderScene(void)

{

    //清除一个或一组特定的缓冲区
    //缓冲区是一块存在图像信息的储存空间,红色、绿色、蓝色和alpha分量通常一起分量通常一起作为颜色缓存区或像素缓存区引用。
    //OpenGL 中不止一种缓冲区(颜色缓存区、深度缓存区和模板缓存区)。

    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);

    //设置一组浮点数来表示红色

    GLfloat vRed[] = {1.0f,0.0f,0.0f,1.0f};

    //传递到存储着色器,即GLT_SHADER_IDENTITY着色器,这个着色器只是使用指定颜色以默认笛卡尔坐标第在屏幕上渲染几何图形
    //没有着色器,在OpenGL 核心框架中就无法进行任何渲染。在后面的课程中我们讲到不用固定渲染管线,当然在前期会先学习如果使用存储着色器。

    shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed);

    //提交着色器

    triangleBatch.Draw();

    //将在后台缓冲区进行渲染,然后在结束时交换到前台

    glutSwapBuffers();

}

int main(int argc,char* argv[])

{

    //设置当前工作目录,针对MAC OS X
    /*
    `GLTools`函数`glSetWorkingDrectory`用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的`/Resource`文件夹。`GLUT`的优先设定自动进行了这个中设置,但是这样中方法更加安全。
    */
    gltSetWorkingDirectory(argv[0]);

    //初始化GLUT库

    glutInit(&argc, argv);

    /*初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指

     双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区
  2.创建窗口并设置显示模式
`glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);`
`GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;
`GLUT_DEPTH`:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
`GLUT_STENCIL`:确保我们也会有一个可用的模板缓存区。
深度、模板测试后面会细致讲到
     */

    glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);

    //GLUT窗口大小,标题窗口

    glutInitWindowSize(800,600);

    glutCreateWindow("Triangle");

    //注册回调函数

    glutReshapeFunc(ChangeSize);

    glutDisplayFunc(RenderScene);

    //驱动程序的初始化中没有出现任何问题。

    GLenum err = glewInit();

    if(GLEW_OK != err) {

        fprintf(stderr,"glew error:%s\n",glewGetErrorString(err));

        return 1;

    }

    //调用SetupRC

    SetupRC();

    glutMainLoop();

    return 0;

}

OpenGL 常见流程

01 OpenGL 渲染流程.png

  • 顶点着色器 和 片元着色器是必需的。细分和几何着色器是可选的捕捉。
  • OpenGL 要求将有着的数据都保留到缓存对象中(buffer object)

将数据输出到OpenGL

当将缓存数据先导化完成后,大家得以经过调用OpenGL
的一个绘制命令来呼吁渲染几何图元。使用的是glDrawArrays()就是一个常用的绘图命令

OpenGL 的绘图平常都是讲顶点数据传输到OpenGL
服务端。大家得以将一个巅峰视为一个亟需统一处理的数据包。那个包裹的数码足以是我们须要的此外数据(也就课),通常其中差不多一向会蕴藏地方数据。其余数据或者用来觉得一个像素的尾声颜色。

简单易行介绍关键概念

  • 终端着色:对于绘制命令传输的种种终端,OpenGL都会调用一个巅峰着色器来处理顶点相关的数码。只是将数据复制并传递到下一个着色阶段,叫做传递着色器(pass-through
    shader)。寻常来说,一个犬牙相制的应用程序可能含有众多顶点着色器,但在同等时刻只好有一个巅峰着色器起功效
  • 分开着色:顶点着色器处理每个终端的涉嫌数据之后,倘诺还要激活了分割着色器,那么它将更为处理那几个数据。(第9章介绍)
    细分着色器阶段会用到多个着色器来分别管理Patch数据并发生最后的形制。
  • 几何着色:允许在光栅化从前对每一个几何图元做更进一步的拍卖,例如创立新的图元。这几个着色阶段是可选的。大家在背后的学科详解
  • 图元装配:图元装配将顶点及连锁的集合图元之间协会起来,准备下一步剪切和光栅化操作
  • 剪切:顶点可能落在视口(viewport)之外,此时与终极相关的图元会做出改变,以确保相关的像素不会在视口外绘制。剪切(clipping)由OpenGL自动已毕。
  • 光栅化:将革新后的图元(primitive)数据传递到光栅化单元,身材对应的片元(fragment).大家将一个片元是为一个“候选的像素”。也就是可以放置在帧缓存(framebuffer)中的像素,不过它也说不定被最后剔除,不再更新对应的像素地方。之后七个级次将会履行片元的拍卖。
  • 片元着色:最终一个足以透过编程控制显示屏上出示颜色的级差。在Fragment
    Shader阶段中,大家利用着色器计算片元的末尾颜色和它的吃水值。

终端着色和片元着色器的里边的界别?
极端着色器(包含细分和几何着色)决定了一个图元应该置身显示器的哪些地方,而片元着色使用那些音讯来控制某一个片元的颜料应该是何等?

  • 渐渐元的操作:这么些等级里会使用深度测试(depth test
    )和模板测试(stencil test)的章程来控制一个片元是还是不是可知的。

main函数里的常用函数

glutInit() 负责初始化GLUT库。它会处理向程序输入的命令行参数,并且移除其中与控制GLUT如何操作相关的部分。它必须是应用程序第一个GLUT函数,负责设置其他GLUT例程必需的数据结构。

glutInitDisplayMode() 设置了程序所使用的窗口类型。窗口设置更多的OpenGL 特性,例如RAGA颜色空间,使用深度缓存或动画效果。

glutInitWindowsSize() 设置所需的窗口大小,如果不想在这个设置一个固定值,也可以先查询显示设备的尺寸,然后根据计算机的屏幕动态设置窗口的大小。

glutCreateWindow(),它的功能和它的名字一样,如果当前的系统环境可以满足glutInitDisplayMode()的显示模式要求,这里就会创建一个窗口(此时会调用计算机窗口系统的接口)。只有GLUT创建了一个窗口之后(其中包含创建创建OpenGL环境的过程),我们才可以使用OpenGL相关的函数

glewInit()函数,属于另一个辅助库GLEW(OpenGL Extention Wrangler)。GLEW可以简化获取函数地址的过程,并且包含了可以跨平台使用的其他一些OpenGL编程方法。

glutDisplayFunc(),它设置了一个显示回调(diplay callback),即GLUT在每次更新窗口内容的时候回自动调用该例程

glutMainLoop(),这是一个无限执行的循环,它会负责一直处理窗口和操作系统的用户输入等操作。(注意:不会执行在glutMainLoop()之后的所有命令。)

起初化顶点数组对象

void glGenVertexArray(GLsizei n ,GLUINT * arrays)
分配顶点Arr对象
返回n个未使用的对象名到数组arrays中,用作顶点数组对象。返回的名字可以用来分配的缓存对象,并且它们已经使用未初始化的顶点数组集合的默认状态进行了数值的初始化。

void glBindVertexArray(GLuint array);
绑定激活对象数组
glBindVertexArray(),如果输入的变量array非0,并且是glGenVertexArrays()所返回的,那么它将创建一个新的顶点数组对象并且与其名称关联起来。如果绑定到的是一个已经创建的顶点数组对象中,那么会激活这个顶点数组对象,并且直接影响对象找那个所保存的顶点数组对象,并且将渲染状态重设为顶多数组的默认状态。
如果array不是glGenVertexArrays(),所返回的数值,或者它已经被glDeleteVertexArray()函数释放,那么这里将产生一个GL_INVALID_OPERATION错误。

在2种情况下,我们需要绑定一个对象:
    1.创建一个对象并初始化它所对应的数据时;
    2.每次准备使用这个对象,而不是当前绑定的对象时。

当我们完成对顶点数组对象的操作之后,是可以调用glDeleteVertexArrays()将它释放的。

void glDeleteVertexArrays(GLsizei n,GLuint *arrays);
删除n个在arrays中定义的顶点数组对象,这样所有的名称可以再次用作顶点数据。如果绑定顶点数组已经被删除,那么当前绑定的顶点数组对象被重设为0(类似执行了glBindBuffer()函数,并且输入参数为0)。默认的顶点数组会变成当前对象。在arrays当中未使用的名称都会被释放,但是当前顶点数组的状态不会发生任何变化


为了保证程序的完整性,可以调用gllsVertexArray()检查某个名称释放已经被保留为一个顶点数组对象了。
GLboolean gllsVertexArray(GLuint array);
如果array是一个已经用GLgenVertexArrays()创建且没有被删除的顶点数组对象的名称,那么返回GL_TRUE,如果array为0或者不是任何顶点数组对象的名称,那么返回GL_FALSE;

分红顶点缓存对象

极端数组对象承担保存一密密麻麻的数量,这一个数量保存到缓冲对象中,并且由近日绑定的极限数组对象管理。缓存对象就是OpenGL
服务端分配和治本的一个块内存区域,并且大概拥有传入的OpenGL的多少都存储在缓存对象当中。
终点缓冲对象的开头化进程与极端数组对象的创导进度看似,不过必要有向缓存添加多少的一个经过。

1.创建顶点缓存对象的名称
void glGenBuffers(GLsizei n,GLuint *buffers);

返回n个当前未使用的缓存对象名,并保存到buffers数组中。返回到buffers中的名称不一定是连续的整型数据。
这里返回的名称只用于分配其他缓存对象,它们在绑定之后只会记录一个可用状态。
0是一个保留的缓存对象名称,glGenBuffers()永远都不会返回这个值的缓存对象。

2.绑定缓存对象
出于OpenGL
中有成百上千不一样体系的缓存对象,因而绑定一个缓存时,须求指定相应的品种。

1.指定当前激活/绑定的对象 
void glBindBuffer(GLenum target,GLunit buffer);

参数target的类型:GL_ARRAY_BUFFER、GL_ELEMENT_ARRAY_BUFFER、GL_PIXEL_PACK_BUFFER、GL_PIXEL_UNPACK_BUFFER、GL_COPY_READ_BUFFER、GL_COPY_WRITE_BUFFER、GL_TRANSFORM_FEEDBACK_BUFFER、GL_UNIFORM_BUFFER。
顶点数据缓存使用GL_ARRAY_BUFFER

参数buffer:绑定的缓存对象名称

glBindBuffer完成3项工作:1.如果是第一次绑定buffer,且它是一个非零无符号整型,那么将创建一个与该名称相对应的新缓存对象。2.如果绑定到是一个已经创建的缓存对象,那么它将成为当前被激活的缓存对象。3.如果绑定的buffer值为0,那么OpenGL将不在对当前target应用任何缓存对象。

2.释放缓存
void glDeleteBuffers(GLSizei n,const GLuint *buffers);

删除n个保存在buffer数组中的缓存对象。被释放的缓存对象可以重用。
如果删除的缓存已经绑定,那么该对象所以绑定将会重置为默认缓存对象,即相当于用0作为参数执行glBindBuffer(),如果试图删除不存在的缓存对象,或者缓存对象为0,那么将忽略该操作(不会产生错误)

3.判断一个整数值是否是一个缓存对象的名称。
GLboolean gllsBuffer(GLuint buffer);
如果buffer是一个已经分配并且没有释放的缓存对象名称,则返回GL_TRUE。如果buffer为0或者不是一个缓存对象的名称,则返回GL_FALSE。

将数据载入缓存对象

伊始化顶点缓存对象之后,大家需要把顶点数据从目的传输到缓存对象中。这一步步是因而glBufferData()来落到实处的。它首要有2个职责:分红顶点数据所需的蕴藏空间,然后将数据从应用程序的数组拷贝到OpenGL
服务端的内存中

glBufferData(GLenum target,GLsizeiptr size,const GLVoid *data,Glenum usage);

当OpenGL 服务端内存中分配size个存储单元(通常都是byte),用于存储数据或者索引。如果当前绑定的对象已经存在了关联数据,那么首先会删除这些数据。

参数target:
顶点属性数据,GL_ARRAY_BUFFER;
索引数据,索引数据,GL_ELEMENT_ARRAY_BUFFER;
从OpenGL 中获取的像素数据,GL_PIXEL_PACK_BUFFER;
OpenGL 的像素数据GL_PIXEL_UNPACK_BUFFER;
对于缓存直接的复制数据,GL_COPY_READ_BUFFER、GL_COPY_WRITE_BUFFER;
对于通过transform feedback 着色器获得结果,GL_TRANSFORM_FEEDBACK_BUFFER、
一致变量,GL_UNIFORM_BUFFER。
对于纹理缓存中存储的纹理数据,GL_TEXTURE_BUFFER

参数size:存储数据的总数量,data存储元素的总数*单位元素存储空间
参数data: 客户端内存的指针,以便初始化缓存对象,要么是NULL,如果传入的指针合法,那么将会有size个大小的数据从客户端拷贝到服务端。如果传入的是NULL,那么将保留size大小的未初始化的数据,以备后用。

如果所需的size大小超过了服务端能够分配的额度,那么glBufferData()将产生一个GL_OUT_OF_MEMORY错误。如果usage设置的不是可用的模式值,那么会产生GL_INVALID_VALUE错误。

须臾间领略这么多内容,可能会略带不方便。可是那个函数在后头的学科学习中会重复多次面世。

初阶化顶点与片元着色器

对此每一个OpenGL 程序,当使用的OpenGL
版本高于或者等于3.1时,都亟待指定至少2个着色器:顶点着色器 和
片元着色器。

对于OpenGL程序员而言,着色器使用OpenGL着色语言(OpenGL shading
Language,GLSL)编写的袖珍函数。GLSL是组成具有OpenGL着色器的语言。它与C++语言万分接近。用GLSL中的所有特性并无法用于OpenGL每一个着色阶段。当我们得以以字符串的款型传输GLSL着色器到OpenGL。

为了让学员更便于选拔着色器开发,我们选用将着色器字符串的内存保存到文件,并且选用LoadShaders()读取文件和创造OpenGL着色程序。

发表评论

电子邮件地址不会被公开。 必填项已用*标注