在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线管理的。

图形渲染管线可以被划分为两个主要部分:第一部分把你的3D坐标转换为2D坐标,第二部分是把2D坐标转变为实际的有颜色的像素。

一、图形渲染管线(PipeLine)

渲染管线又称渲染流水线,它是图形图像从数据一步一步形成最终输出的画面所要经历的各种操作过程。

指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程。

OpenGL(开放图形库)是一种应用程序编程接口(Application Programming Interface,API)它是一种可以对图形硬件设备特征进行访问的软件库。
在OpenGL 3.0以前的版本或者使用兼容模式的OpenGL环境,OpenGL包含一个固定管线(fixed-function pipeline),它可以在不使用着色器的环境下处理几何与像素数据。我们看到的glBegin()、glRectf()以及glEnd()这些函数都是以前固定管线模式中所使用的API函数。
从3.1版本开始,固定管线从核心模式中去除,因此我们必须使用着色器来完成工作。现代OpenGL渲染管线严重依赖着色器来处理传入的数据,我们一般会使用GLSL(OpenGL Shading Language)编写着色器程序,GLSL语法类似于C语言,GLSL编译以后运行在GPU端。
现代OpenGL是用的"可编程式图形管线",之前则是用的"固定管线"。

二、流程

图形渲染管线的整个处理流程可以被划分为几个阶段,上一个阶段的输出数据作为下一个阶段的输入数据,是一个串行的,面向过程的执行过程。每一个阶段分别在GPU上运行各自的数据处理程序,这个程序就是着色器。

下边是图形渲染管线每一个阶段的抽象表示,蓝色部分代表允许自定义着色器。

1、顶点数据是一些顶点的集合,顶点一般是3维的点坐标组成。

2、基本图元(Primitives)包括点,线段,三角形等,是构成实体模型的基本单位,需要在传入顶点数据的同时通知openGL这些顶点数据要组成的基本图元类型,并将并所有的点装配成指定图元的形状。

3、顶点着色器(Vertex Shader)包含对一些顶点属性(数据)的基本处理。

4、图元装配(Primitive Assembly)把所有输入的顶点数据作为输入,并将所有的点装配成指定图元的形状。

5、几何着色器(Geometry Shader):把基本图元形式的顶点的几何作为输入,可以通过产生新顶点构造出新的基本图元来生成其它形状。

6、细分着色器(Tessellation Shader):可以把基本图元细分为更多的基本图形,创建出更加平滑的视觉效果。

7、光栅化(Rasterization):即像素化。把细分着色器输出的基本图形映射为屏幕上网格的像素点,生成供片段着色器处理的片段(Fragment),光栅化包含一个剪裁操作,会舍弃超出定义的视窗之外的像素。

8、片段着色器(Fragment Shader):主要作用是计算出每一个像素点最终的颜色,通常片段着色器会包含3D场景的一些额外的数据,如光线、阴影等。

9、测试与混合:是对每个像素点进行深度测试,Alpha测试等测试并进行颜色混合的操作,这些测试与混合操作决定了屏幕视窗上每个像素点最终的颜色以及透明度。

在整个渲染管线中需要自定义处理的主要是顶点着色器和片段着色器。

三、顶点输入:

OpenGL是一个3D图形库,所以我们在OpenGL中指定的所有坐标都是3D坐标(x、y和z)。OpenGL不是简单地把所有的3D坐标变换为屏幕上的2D像素;OpenGL仅当3D坐标在3个轴(x、y和z)上都为-1.0到1.0的范围内时才处理它。所有在所谓的标准化设备坐标(Normalized Device Coordinates)范围内的坐标才会最终呈现在屏幕上(在这个范围以外的坐标都不会显示)。

渲染一个三角形,我们一共要指定三个顶点,每个顶点都有一个3D位置。我们会将它们以标准化设备坐标的形式(OpenGL的可见区域)定义为一个float数组。

 float vertices[] = {
-0.5f, -0.5f, 0.0f,
0.5f, -0.5f, 0.0f,
0.0f, 0.5f, 0.0f
};

由于OpenGL是在3D空间中工作的,而我们渲染的是一个2D三角形,我们将它顶点的z坐标设置为0.0。这样子的话三角形每一点的深度都是一样的,从而使它看上去像是2D的。

标准化设备坐标接着会变换为屏幕坐标,这是使用glViewport函数提供的数据,进行视口变换(Viewport Transform)完成的。

示例程序:

 #include <GL/glew.h>
#include <SFML/Window.hpp> #define GLSL(src) "#version 150 core\n" #src//一个宏定义,用于将GLSL的源文件和前面的版本声明信息链接起来。 // Vertex渲染器代码片段,用于接收输入的顶点位置和颜色,并输出颜色传递给片元着色器
const GLchar* vertexSource = GLSL(
in vec2 position;
in vec3 color;
out vec3 mColor;
void main() {
mColor = color;
gl_Position = vec4(position, 0.0f, 1.0f);
}
); // Fragment渲染器片段,接受了顶点着色器的顶点颜色信息
const GLchar* fragmentSource = GLSL(
in vec3 mColor;
out vec4 oColor;
void main() {
oColor = vec4(mColor, 1.0f);
}
); // 创建指定类型的Shader
GLuint createShader(GLenum type, const GLchar* src)
{
GLuint shader = glCreateShader(type);
glShaderSource(shader, , &src, nullptr);
glCompileShader(shader);
return shader;
} int main(int argc, char* argv[])
{
// 初始化Window窗口
sf::ContextSettings settings;
settings.depthBits = ;
settings.stencilBits = ; const unsigned int WIDTH = ;
const unsigned int HEIGHT = ;
const sf::String TITLE = "Modern OpenGL";
sf::Window window(sf::VideoMode(WIDTH, HEIGHT, ), TITLE,
sf::Style::Titlebar | sf::Style::Close, settings); // 初始化GLEW
glewExperimental = GL_TRUE;
glewInit(); // 顶点数据
GLfloat vertices[] = {
0.0f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f
}; // 创建Vertex Array Object
GLuint VAO;
glGenVertexArrays(, &VAO);
glBindVertexArray(VAO); // 创建Vertex Buffer Object并装载数据
GLuint VBO;
glGenBuffers(, &VBO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); // 创建Vertex渲染器和Fragment渲染器
GLuint vertexShader = createShader(GL_VERTEX_SHADER, vertexSource);
GLuint fragmentShader = createShader(GL_FRAGMENT_SHADER, fragmentSource); // 将Vertex渲染器和Fragment渲染器关联到Shader Program
GLuint shaderProgram = glCreateProgram();
glAttachShader(shaderProgram, vertexShader);
glAttachShader(shaderProgram, fragmentShader);
glLinkProgram(shaderProgram);
glUseProgram(shaderProgram); // 设置Vertex数据的布局属性(这里包括postion和color两个属性)
GLint posAttrib = glGetAttribLocation(shaderProgram, "position");
glEnableVertexAttribArray(posAttrib);
glVertexAttribPointer(posAttrib, , GL_FLOAT, GL_FALSE, * sizeof(GLfloat), ); GLint colorAttrib = glGetAttribLocation(shaderProgram, "color");
glEnableVertexAttribArray(colorAttrib);
glVertexAttribPointer(colorAttrib, , GL_FLOAT, GL_FALSE, * sizeof(GLfloat), (void*)( * sizeof(GLfloat))); // 这个While循环是SFML的固定模式用于做事件处理
while (window.isOpen())
{
// 内层While循环用于处理事件响应
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
window.setActive();
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
// 绘制图形
glDrawArrays(GL_TRIANGLES, , );
window.display();
}
// 释放资源
glDeleteProgram(shaderProgram);
glDeleteShader(fragmentShader);
glDeleteShader(vertexShader); glDeleteBuffers(, &VBO);
glDeleteVertexArrays(, &VAO); return EXIT_SUCCESS;
}

最后的效果:

openGL图形渲染管线的更多相关文章

  1. [转]OpenGL图形渲染管线、VBO、VAO、EBO概念及用例

    直接给出原文链接吧 1.OpenGL图形渲染管线.VBO.VAO.EBO概念及用例 2.OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及 ...

  2. OpenGL图形渲染管线、VBO、VAO、EBO概念及用例

    图形渲染管线(Pipeline) 图形渲染管线指的是对一些原始数据经过一系列的处理变换并最终把这些数据输出到屏幕上的整个过程. 图形渲染管线的整个处理流程可以被划分为几个阶段,上一个阶段的输出数据作为 ...

  3. OPENGL图形渲染管线图解

    OPENGL固定图形渲染管线可以粗略地认为由下面的阶段衔接而成: 顶点颜色,光照,材质三个输入在光栅化前控制绘制管线的操作.光照和材质不能单独使用.顶点颜色,光源颜色,材质颜色都有alpha值,它们的 ...

  4. OpenGL图形管线和坐标变换[转]

    1. OpenGL 渲染管线 OpenGL渲染管线分为两大部分,模型观测变换(ModelView Transformation)和投影变换(Projection Transformation).做个比 ...

  5. 3D图形渲染管线

    3D图形渲染管线 什么是渲染(Rendering)    渲染简单的理解可能可以是这样:就是将三维物体或三维场景的描述转化为一幅二维图像,生成的二维图像能很好的反应三维物体或三维场景(如图1):    ...

  6. OpenGL图形管线和坐标变换

    转:http://blog.csdn.net/zhulinpptor/article/details/5897102 1. OpenGL 渲染管线 OpenGL渲染管线分为两大部分,模型观测变换(Mo ...

  7. [OpenGL ES 02]OpenGL ES渲染管线与着色器

    [OpenGL ES 02]OpenGL ES渲染管线与着色器 罗朝辉 (http://www.cnblogs.com/kesalin/) 本文遵循"署名-非商业用途-保持一致"创 ...

  8. OpenGL 图形管道(graphics pipeline)过程

    1.总结:Graphics pipeline 主要分为两部分工作 把3D坐标转换成2D坐标 把2D坐标转换成真实的有颜色的像素 2.下图就是一个顶点数据经过几个步骤后转化成显示在屏幕上像素的过程(一般 ...

  9. Android平台下OpenGL图形编程

    ref: Jayway Team Blog中OpenGL ES简明开发教程https://blog.jayway.com/tag/opengl-es/ OpenGL ES 开发教程http://www ...

随机推荐

  1. D. White Lines

    D. White Lines 给定一个$n\times n$的$WB$矩阵,给定一个$k*k$的能把$B$变成$W$的橡皮擦,求橡皮擦作用一次后,全为$W$的行.列总数最大值 前缀和差分 #inclu ...

  2. HDU 5073 Galaxy (数学)

    Galaxy Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Su ...

  3. xfs格式化、ext4格式化并指定inode区别

    [root@b ~]# mkfs.ext4 -N 90000000 /dev/sdb3 首先是mkfs.xfs的,重点是这几个:     -i size=512  : 默认的值是256KB,这里的设置 ...

  4. qbzt day4 下午

    有向图的强连通分量 强联通:两个点之间可以互相到达 如果某个图任意两个点都是强联通的,那么称这个图强联通 如果一个图的子图是强联通的,那么称这个图是强联通子图 一个图的极大强联通子图被称作强连通分量 ...

  5. 用Vue来实现音乐播放器(八):自动轮播图啊

    slider.vue组件的模板部分 <template> <div class="slider" ref="slider"> <d ...

  6. assert 与if

    strlen的实现用不用加断言(assert)? http://en.cppreference.com/w/cpp/error/assert 自己写strlen实现会加assert判断空指针,Debu ...

  7. 剑指offer--day08

    1.1 题目:二叉树镜像:操作给定的二叉树,将其变换为源二叉树的镜像. 1.2 思路:先交换根节点的两个子结点之后,我们注意到值为10.6的结点的子结点仍然保持不变,因此我们还需要交换这两个结点的左右 ...

  8. 一些基础的ES6 语法

    <script> window.onload = function () { //---------------------------let----------------------- ...

  9. 结合element-ui封装的一个分页函数

    第一次写博客,专门写给菜鸟看的,如果你是老鸟,你可以直接无视. 首先我们从豆瓣api获取到电影的数据列表 然后我们把他们切成一块一块的小数组 最后的数组将会是这样  原理就是以上的内容,接下来直接附上 ...

  10. centos7下安装iperf时出现 make: *** No targets specified and no makefile found. Stop.的解决方案

    我们在Linux 安装包的时候,使用make 命令出现:"make:*** No targets specified and no makefile found.Stop."这样的 ...