现代OpenGL渲染管线介绍

原文摘选自

现代OpenGL渲染管线介绍

此文对最新的OpenGL做一个简单的介绍,如有理解错误,敬请指正。英文原文:

https://glumpy.github.io/modern-gl.html

opengl已经发展了很多年,自从2003年后提出dynamic pipeline(OpenGL 2.0)后发生了重大变化,例如 shader的使用允许直接对GPU操作

在这个版本之前,OpenGL使用固定管线(fixed pipeline),现在仍旧可以找到很多使用固定管线的手册,这篇文件将介绍OpenGL编程方式上的巨变,会使得OpenGL编程更难,但是却更强大。

Shaders

[备注:Shader语言乘坐glsl,从1.0到1.5有很多版本,后来的版本就继承OpenGL的版本号,最近的版本是4.4(2014年2月)]

Shaders是一段程序(使用类C语法)在GPU上build并且在rendering pipeline的时候被执行,根据shader的特性,在rendering pipeline的不同阶段被使用,简化流程的话,我们仅仅使用vertext shader和fragment shader,如下图:

vertex shader作用于顶点,在viewport上输出顶点位置,fragment shader作用于像素级别,用来输出每个像素的颜色,因此,一个简单的vertex shader大概是这样的:

void main()
{
    gl_Position = vec4(0.0,0.0,0.0,1.0);
}

一个简单的fragment shader是这样的

void main()
{
    gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}

上面这两段shader没什么用,因为第一个将所有顶点转换为原点,第二个将屏幕所有像素输出为黑色,后面会看到如何使他们做更有用的事情。

还有一个问题:这些shader具体什么时候执行,vertex shader在每个顶点给到rendering pipeline的时候作用于每个顶点(后面会看到这是什么意思),fragment shader在vertex shader之后作用于每个像素,例如上面的图中,vertex shader将会执行3次,对每一个顶点(标注中的1,2,3顶点),fragment shader将会执行21次,每次作用于一个像素。

经过vertex shader后的顶点在primitive Generation(图元装配),OpenGL支持三种基本图元:点,线,三角形,接着对装配好的图元进行裁剪(clip),保留在视椎中的图元,丢弃不在视椎中的图元,对一半在视椎,一半不在视椎的图元进行裁剪,接着再对视椎中的图元进行剔除操作(cull)

对fragment shader的输出的每个片元进行一系列测试与处理,从而决定最终用于渲染的像素:

• Pixel ownership test:该测试决定像素在 framebuffer 中的位置是不是为当前 OpenGL

ES 所有。也就是说测试某个像素是否对用户可见或者被重叠窗口所阻挡。

• Scissor test:判断像素是否在由 glScissor 定义的剪裁矩形内,不在该剪裁区域内的像素就

会被剪裁掉

• Alpha test [DX9]在于Scissor test后面阶段,对输出的颜色进行是否透明的测试。

• Stencil Test:模版测试,将模版缓存中的值与一个参考值进行比较,从而进行相应的处理。

• Deph test:深度测试,比较下一个片段与帧缓冲区中的片段的深度,从而决定哪个像素在前,哪个像素被遮挡。在一些渲染管线中(移动平台),深度测试会提前到光栅化之前。

• Blend:将片段的颜色和帧缓冲区中已有的颜色值进行混合,并将混合所得的新值写入帧缓冲, Alpha Blend[directx9]。

• Dithering:使用有限的色彩让你看到比实际图象更多色彩的显示方式,以缓解表示颜色的值的精度不够大而导致的颜色剧变的问题。

• Framebuffer:这是流水线的最后一个阶段,Framebuffer 中存储这可以用于渲染到屏幕或纹理中的像素值,也可以从Framebuffer 中读回像素值【RenderTexture】。

Rasterization

光栅化是将vs的输出的基本图元转换为二维的片元(fragment),就是能被渲染到屏幕的像素,包含像素的位置,颜色,纹理坐标等信息,这些值是经过顶点信息插值计算得到,输出的片元(fragment)被送入下一个阶段 fragment shader中处理

在这个阶段,会进行提前进行深度测试(earyly-z),将当前片元的深度值和framebuffer中的片元的深度值比较,深度值越小,表示越靠近摄像机,会被渲染在前面(一般情况下,也可以指定深度测试的默认值,ZTest共有七种值,Less  LEqual Euqul  NotEqual Greater  GEqual  Always)

early-z是GPU硬件流水线决定,通过early-z可以提前知道深度信息,避免不必要的fragment shader计算,从而提高性能。

Buffers

前面讲了vertex shader作用每个顶点,问题是这些顶点怎么来的?时下OpenGL的思路是将其存储到GPU缓存中,在渲染之前只用传输给GPU存储一次,做法是在CPU上构建buffer,然后把他们传输到GPU,如果你的数据没有变化,就不需要更新,这和早年的fixed pipeline有很大不同,fixed piplline会在每次rendering call的时候都把顶点数据传送给GPU(只有现实列表才存储在GPU中)【可能和GPU显存比早年大幅增加有关,当然时下OpenGL的做法,会在顶点发生变化的时候,每次draw call时将最新的顶点数据送给GPU刷新缓存】

但是顶点结构是什么样的呢?关于顶点结构OpenGL没有假定任何事情,你可以自由的使用,唯一的要求是所有的同一个buffer里的顶点都应该有相同的结构(可以有不同的内容),这个和fixed pipeline有很大不同,fixed pipeline方式中OpenGL会使用隐式固定顶点结构存储有很多复杂渲染的东西(比如投影,光照,法线等),现在,全靠自己了

好消息是 现在你可以自由的做任何你想做的事情

坏消息是 你得自己编写所有,甚至连基本的投影和光照

用一个简单的例子,一个顶点结构想存储位置position和color信息,用python的话最简单的方式使用使用numpy库结构化数组

data = numpy.zeros(4, dtype = [ ("position", np.float32, 3),
                                ("color",    np.float32, 4)] )

上面在CPU中创建了四个顶点的缓存,每一个顶点有一个位置信息(三个x,y,z坐标的浮点数)和一个颜色信息(四个浮点数,分别是红绿蓝和透明通道),上面使用了三个坐标来表示一个位置信息,如果我们在二维中可以使用两个数值,同样的对于color来说,如果不想使用透明通道,也可以使用三个数值来表示,当然对于四个定点来说无关重要,但是必须意识到如果顶点数据增加到成千上万就有影响了

Uniform,attribute,varying

至此,我们已经知道shaders和buffers,但是仍然需要解释他们是如果关联起来的,那么让我们再看下我们的CPU Buffer

data = numpy.zeros(4, dtype = [ ("position", np.float32, 2),
                                ("color",    np.float32, 4)] )

我们需要告诉vertex shader它将要处理的顶点数据,位置信息是一个有3个float的元组类型,颜色是有4个float的元组类型,这就是attributes精确的要表达的意思,让我们稍微改动下前面的vertex shader

attribute vec2 position;
attribute vec4 color;
void main()
{
    gl_Position = vec4(position, 0.0, 1.0);
}

这个vertex shader现在期望一个顶点包含两个属性,一个叫做position,一个叫color,并且指定了这两个属性的类型,一个是vec2,一个是vec4(包含4个float的元组),即便我们标注了第一个属性叫position,这个属性还没有和numpy数组中真是的数据绑定,我们需要在程序中某一个时刻来做,并且不会自动绑定,需要自己来绑定。

提供给vertex shader的第二个类型信息是uniform,可以被认为是存储了一些常量数据(作用于所有的顶点),例如我们想让所有的顶点位置都缩放,就可以这样写

uniform float scale;
attribute vec2 position;
attribute vec4 color;
void main()
{
    gl_Position = vec4(position*scale, 0.0, 1.0);
}

最后的类型是varying,用来在vertex shader和fragment shader中传递信息,如果我们想传递顶点颜色给fragment shader,就可以这样写

uniform float scale;
attribute vec2 position;
attribute vec4 color;
varying vec4 v_color;

void main()
{
    gl_Position = vec4(position*scale, 0.0, 1.0);
    v_color = color;
}

然后在fragment shader中,就这样写:

varying vec4 v_color;

void main()
{
    gl_FragColor = v_color;
}

问题是fragment shader中v_color的值是多少,我们有3个顶点,21个像素,那么每个像素的颜色该是多少?

答案是三个顶点颜色的插值,插值使用每个像素到每一个顶点的距离信息来计算插值,要理解这是一个很重要的概念,任何varying数值都是顶点插值的,以此构成了基本项

如果有对应贴图,fragment shader会根据每个顶点的uv信息,从贴图中找到对应的贴图颜色。

Transformations

Projection matrix

首先我们要定义我们要看到什么,就是说我们需要顶一个viewing volum,使所有在volumn中(甚至物体的一半在voumn中)的物体都被渲染,而其外的物体不被渲染,如下图,黄色和红色的小球被渲染,而绿色的没有被渲染,也不会被看到

3D到2D的投影方式有很多,但是我们只使用透视投影(近大远小)和正交投影(平行投影,远方和近方的物体投影一样大),如上图

在老的openGL版本中,可以使用glFrustum和glOrtho来得到对应的矩阵。

根据投影方式的不同,使用下面两个投影矩阵

我们没必要在这里探究这两个矩阵是怎么构建的,只要说他们是3D世界标准矩阵就行,都是基于假定摄像机在(0,0,0)原点位置,并且朝向正前方(0,1)方向就可以。

对于透视投影,还有一个更容易操作的转换矩阵,不需要指定视椎上下左右面

fovy指定了视角,也就是视椎的夹角,aspect指定了屏幕的宽高比,这决定了x方向上的视野。

Model and view matirces

主要是下面三个转换矩阵的使用:

Model matirces: 把物体的本地坐标转换为世界坐标

View matirces:把物体从世界做标准换为摄像机坐标

Projection matrices:把摄像机坐标转换为屏幕坐标

原文地址:https://www.cnblogs.com/yizhen/p/10730500.html

时间: 2024-10-14 16:21:39

现代OpenGL渲染管线介绍的相关文章

OpenGL: 渲染管线理论

http://blog.csdn.net/augusdi/article/details/19934463 学习着色器,并理解着色器的工作机制,就要对OpenGL的固定功能管线有深入的了解. 首先要知道几个OpenGL的术语 渲染(rendering):计算机根据模型(model)创建图像的过程. 模型(model):根据几何图元创建的物体(object). 几何图元:包括点.直线和多边形等,它是通过顶点(vertex)指定的. 最终完成了渲染的图像是由在屏幕上绘制的像素组成的.在内存中,和像素

OpenGL坐标系介绍

OpenGL坐标系介绍 OpenGL可以分成四种坐标系,分别是世界坐标系,模型坐标系,眼坐标系,设备坐标系. 数学的观点:向量空间和仿射空间 仿射空间(affine space)是向量空间的扩展,除了标量和向量,它还包含另外一种对象-点. 尽管在仿射空间中对两个点以及一个点和一个标量没有定义运算,但对一个向量和一个点定义了一种运算--向量-点加法,它的结果是一个点.也可以说有一种称为点-点减法的运算,这种运算由两个点得到一个向量. 计算机科学的观点 把标量.点和向量看做是集合中的元素,并且可以按

OpenGL渲染管线概览

翻译自OpenGL Wiki:http://www.opengl.org/wiki/Rendering_Pipeline_Overview,如有错误还请赐教 管线 OpenGL渲染管线按以下方式工作: 1. 准备顶点数组数据并渲染 2. 顶点处理: 1. 每个顶点都要经过顶点着色器处理.每个顶点按所在数据流中的             顺序依次处理为输出顶点: 2. 可选的图元曲面细分阶段: 3. 可选的图元几何着色器处理,输出为一系列基本图元: 3. 顶点后处理,上一阶段的输出被调整或移位到不

OpenGL渲染管线漫谈

如果把OpenGL看做一个加工机器,它的功能就是把一系列顶点纹理数据可以在屏幕上面显示的像素.这就如同面条机器把一定比例的面和水加工成一根根面条一样.面条机器可以设置面条的宽度和厚度,这可以影响整个加工的面条,就相当于OpenGL可以设置纹理过滤模式,混合模式等全局状态,可以影响到这一帧的像素输出.从glDrawElements开始,OpenGL流水线就开始运作了,它由一系列过程串联而成,其中有些是程序可以操作的,比如顶点片段着色器:有些戏系统根据设置的全局OpenGL状态自动作用的,比如纹理采

OpenGL 开始学习指南

近期需要做一个涌潮的预报与仿真模拟,为了使模型更具有真实感,且逼真,使用起来更灵活.感觉还是得从基础的OpenGL学习.鉴于Direct3D技术存在的众多不确定性,且评论不太好的原因,决定用OpenGL来做.但是,之前接触的都是一些十分简单的OpenGL的管线变成,预计得舍弃并用现代版的OpenGL的着色语言进行实现. 下面给出一些自己在网上总结的学习OpenGL的一些建议,特别是初学者,可以拿来参考以下. 1.入门与学习资源 入门如果用NEHE,该经典教程确实众所周知,但也意味着老旧,全是固定

基于Cocos2d-x学习OpenGL ES 2.0系列——你的第一个三角形(1)

[本系列转自]http://cn.cocos2d-x.org/tutorial/lists?id=79 前言 在本系列教程中,我会以当下最流行的2D引擎Cocos2d-x为基础,介绍OpenGL ES 2.0的一些基本用法.本系列教程的宗旨是OpenGL扫盲,让大家在使用Cocos2d-x过程中,知其然,更知其所以然.本系列教程不会涉及非常底层的数学原理,同时也不会过多地提及OpenGL本身的一些细节知识.但是我会在每篇文章的最后给出一些参考链接,大家可以顺藤摸瓜,一举Get OpenGL这个新

基于Cocos2d-x学习OpenGL ES 2.0系列——编写自己的shader(2)

在上篇文章中,我给大家介绍了如何在Cocos2d-x里面绘制一个三角形,当时我们使用的是Cocos2d-x引擎自带的shader和一些辅助函数.在本文中,我将演示一下如何编写自己的shader,同时,我们还会介绍VBO(顶点缓冲区对象)和VAO(顶点数组对象)的基本用法. 在编写自己的shader之前,我觉得有必要提一下OpenGL渲染管线. 理解OpenGL渲染管线,对于学习OpenGL非常重要.下面是OpenGL渲染管线的示意图:(图中淡蓝色区域是可以编程的阶段) 此图是从wiki中拿过来的

OpenGL版本与OpenGL扩展机制

OpenGL版本与OpenGL扩展机制 1 opengl的版本区别(在opengl官方文档中有详细说明)    针对Opengl不同版本的升级是主要是扩展指令集.    现在版本是4.0啦1.1 opengl1.11995年,SGI推出了更为完善的OpenGL 1.1版本.OpenGL 1.1的性能比1.0版提高甚多.其中包括改进打印机支持,在增强元文件中包含OpenGL的调用,顶点数组的新特性,提高顶点位置.法线.颜色.色彩指数.纹理坐标.多边形边缘标识的传输速度,引入了新的纹理特性等等.1.

【Unity Shaders】学习笔记——渲染管线

[Unity Shaders]学习笔记——Shader和渲染管线 转载请注明出处:http://www.cnblogs.com/-867259206/p/5595924.html 写作本系列文章时使用的是Unity5.3. 写代码之前: 当然啦,如果Unity都没安装的话肯定不会来学Unity Shaders吧? 阅读本系列文章之前你需要有一些编程的概念. 在VS里面,Unity Shaders是没有语法高亮显示和智能提示的,VS党可以参考一下这篇文章使代码高亮显示,也可以下载shaderlab