【OpenGL】理解一些基本问题

写在前面

啦啦啦,搞了很久的Unity Shaders,越学越觉得基础知识很重要。学Unity Shader的时候,总会想,shader到底是什么呢?shader的pipeline是什么呢?它们是怎么工作的?有哪些限制?等等问题。但这些问题,Unity是不负责告诉你的。它专注于how,而不是what和why。想要深入理解一些问题,感觉还是要从GL或者DX学起。后面会学习GL龙书第八版~当然Unity我也不会放弃的。

这篇文章旨在回答一些基本问题。We always rant about them...

什么是OpenGL

这个问题很简单,它就是应用程序接口,也就是API,用于访问图形硬件中的可编程特性。OpenGL和DX相比有一个很大的特点就是跨平台的特性。换句话说,它是不依赖硬件的接口,可以运行在各种不同类型的图形硬件系统上,甚至完全是一个软件(而没有图形硬件)。

OpenGL是一种客户端-服务器(client-server)类型的系统。我们编写的程序就是一个客户端,而我们的计算机图形硬件制造商提供的OpenG的实现就是服务器。在一些OpenGL的实现里(例如一些和X Window System相关的应用),客户端和服务器可能会在不同的机器上运行,中间用网络连接。在这种情况下,客户端可以发起OpenGL命令,然后转换成窗口系统特定的协议,再发送给服务器,最终在服务器上执行OpenGL进行图像显示。

为什么OpenGL不提供窗口操作

这个问题我经常会问。。。为什么写个GL还要用这么多第三方库!连个窗口都不能自己画吗!这其实不能怪OpenGL,这正是它的优点——跨平台的特点造成的。因为它可以不依赖硬件和系统,因此就不会包含执行窗口任务的函数,或者处理用户输入等。这些函数是由我们使用的应用或系统来提供。

为什么OpenGL没有读取三维模型或者图片的函数

我以前经常抱怨,发展这么多年的OpenGL,怎么连读取三维模型这么简单的画图需求都不提供呢!!!好吧,这也是它的跨平台特性造成的。和上一点一样,这些操作是和系统存储格式密切相关的,OpenGL不管的~我们必须从点、线、三角形和patches这样的几何图元集合中自己构建三维对象。

什么是Shader

这是一类在图形硬件上执行的特殊函数。我们可以理解成,Shader是一些为图形处理单元(GPU)编译的小程序。OpenGL包含了编译工具来把我们编写的Shader源代码编译成可以在GPU上运行的代码。在OpenGL中,我们可以使用四种shader阶段。最常见的就是vertex shaders——它们可以处理顶点数据;以及fragment shaders,它们处理光栅化后生成的fragments。

vertex shaders和fragment shaders是每个OpenGL程序必不可少的部分。

Shader有什么用

请直接看下一节~

OpenGL的渲染流水线(Rendering Pipeline)

渲染流水线,就是一系列有序的处理阶段的序列,用于把我们应用中的数据转化到OpenGL生成一个最终的图像。下图是OpenGL4.3使用的流水线。(跟早期的相差很大)(来源:OpenGL Programming Guide 8th Edition)

OpenGL从我们提供的几何数据(顶点和几何图元)出发,首先使用了一系列shader阶段来处理它:vertex shading,tessellation shading(它本身就包含了两种shaders),最后是geometry shading,然后再传递给光栅化程序(rasterizer)。光栅化程序将会为每个在裁剪区域(clipping region)内部的图元生成fragments,然后再为每个fragment执行一个fragment shader。

如你所见,shaders真是无处不在啊!不是所有的阶段都是需要的。如上面所说,只有vertex和fragment shaders是我们必须实现的。Tessellation和geometry shaders都是可选的。

下面,我们对每个阶段进行更深入地解释。下面的内容难度系数五颗星(对新手。)!但是,请坚持看下去!它们很重要!

准备向OpenGL发送数据

OpenGL要求所有的数据都必须存储在缓存对象中(buffer objects)。缓存对象是OpenGL管理的一些内存空间。“想要让我办事,请先把你的东西放到我的地盘!”把数据放到这些缓存里有很多方法,但最常见的是使用glBufferData()来实现。当然这里面还有一些其他步骤,我们后面会讲到。

向OpenGL发送数据

在我们初始化了缓存以后,我们可以通过调用OpenGL的绘图操作来要求把几何图元绘制到屏幕上。这些操作例如有glDrawArrays()。我们后面会讲到。这个绘制的过程意味着我们把顶点数据传递给OpenGL服务器。

Vertex Shading

对于通过绘制命令绘制的每一个顶点,OpenGL将调用一个vertex shader来处理关于这个顶点的相关信息。Vertex shaders的复杂性可以变化非常大,有的很简单,就是仅仅复制数据然后传递给下一个流水线阶段,我们称这种为pass-through shader;有的很复杂,会执行很多操作来计算顶点的屏幕位置(通常使用变换矩阵来完成,后面会讲到),还可能会进行光照计算来计算顶点的颜色,或者其他技术。

通常,一个应用会包含多个vertex shader,但同一时间只有一个会被激活(active)。

Tessellation Shading

Tessellation,读 泰斯类什,可以翻译成曲面细分。在vertex shader处理了每一个顶点的相关信息后,如果tessellation shader阶段被激活的话,它就会继续处理这些数据。在后面我们会看到tessellation使用patchs来描述一个对象的形状,并且允许细化(tessellate)相对简单的patch集合,来增加几何图元的数量,提高模型的平滑度和真实度。Tessellation Shading阶段可以使用两个shaders来控制patch数据,生成最终的形状。

更多内容可以参见这篇文章(虽然是DirectX的。。。)

Geometry Shading

这一阶段允许在光栅化之前处理单独的几何图元,包括创建新的图元。这一阶段同样是可选的,但是会很有用!后面会讲到。

Primitive Assembly

之前的流水线阶段都是在顶点上进行操作的,最终它们携带着处理这些顶点是如何组织构建成一个个几何图元的相关信息来到了这一阶段。primitive assembly阶段负责把顶点组织成它们相关的几何图元,为裁剪和光栅化做准备。

裁剪(Clipping)

有时,一些顶点会在视野(viewport)的外部,这时和这些顶点相关的图元就需要被裁剪。这个操作是OpenGL自动完成的。

光栅化(Rasterization)

在裁剪完成后,更新后的图元就会被发送给光栅化程序去生成fragments。那么什么是fragment呢?一个fragment可以看成是一个“候选像素”,这类像素在帧缓存中的一块区域中。一个fragment仍可以被拒绝(reject),并且永远不会更新它的相关像素位置。处理fragments是后面两个阶段的任务——fragment shading和per-fragment操作。

Fragment Shading

最后一个我们可以编程控制颜色的阶段就是fragment shading。在这个阶段,我们使用一个shader来决定该fragment的最终颜色(其实下个阶段,per-fragment操作仍可以进行最后的颜色修改)和它的深度值(depth value)。在fragment shaders里我们可以进行非常强大的纹理映射的工作。如果一个fragment shader认为某个fragment不应该绘制出来,它还可以终结一个fragment的处理过程。这个过程称为fragment discard。

我们可以想来区分vertex shading(包括tessellation和geometry shading)和fragment shading:vertex shading决定了一个图元在屏幕上的位置,而fragment shading使用这些信息来决定该fragment的颜色。

Per-Fragment操作

除了fragment shader,我们还可以执行其他的fragment处理操作,也是对单个fragment的最后的处理。在这个阶段,一个fragment的可见性(visibility)已经通过depth testing(也就是z-buffering)和stencil testing决定了。

如果一个fragment成功通过了所有的检测,它就会直接写入帧缓存中,更新它的像素颜色(也可能是深度值)。如果blending被开启了,该fragment的颜色会和当前的像素颜色进行混合去产生一个新的颜色,再写入帧缓存中。

时间: 2024-11-04 20:07:18

【OpenGL】理解一些基本问题的相关文章

OpenGL理解

说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640*480分辨率.16色来做吗?显然是不行的. 本帖的目的是让大家放弃TC的老旧图形接口,让大家接触一些新事物. OpenGL作为当前主流的图形API之一,它在一些场合具有比DirectX更优越的特性. 1.与C语言紧密结合. OpenGL命令最初就是用C语言函数来进行描述的,对于学习过C语言的人来讲,OpenGL是容易理解和学习的

通过OpenGL理解前端渲染原理(1)

一.OpenGL OpenGL,是一套绘制3D图形的API,当然它也可以用来绘制2D的物体.OpenGL有一大套可以用来操作模型和图片的函数,通常编写OpenGL库的人是显卡的制造者.我们买的显卡都支持特定版本的OpenGL. 下图是用OpenGL做的旋转的立方体. 二.渲染原理 2.1 渲染管道 在OpenGL中,所有东西都在一个3D的空间里,而我们的屏幕和窗口都是2D的,所以OpenGL需要将3D的坐标转换成2D的坐标,做这件事的是OpenGL中的渲染管道(graphics pipeline

OpenGL在什么样的领域才是主角?

从OpenGL入门到现在掌握OpenGL开发(仅仅是掌握而已).随着对OpenGL理解的加深,也一点点的了解OpenGL所涉及的行业,有些行业OpenGL是主角,有些行业OpenGL是配角,之所以自己一直对OpenGL感兴趣是因为它是世界的图形API,就像C/C++一样.可能说到底,自己还是对计算机图形学技术感兴趣,就跟有人对互联网开发技术感兴趣,有人对智能手机APP感兴趣,有人对嵌入式技术感兴趣一样,有些人甚至对木马病毒技术感兴趣...这些对技术感兴趣的人都相信它们对将来有用.但有些场景Ope

【机器学习基础】理解为什么机器可以学习1——PAC学习模型

引言 自从下定决心认真学习机器学习理论开始,接触到很多基本问题,但其实都不是很理解,比如损失函数.风险函数.经验结构最小化.结构风险最小化.学习方法的泛化能力.VC维等,这些概念在学习中都纯属空泛的概念存在,我都不理解这些概念存在的意义. 为什么会存在这样的问题呢?我自己想了一下,有几个原因:首先,很多相关的书籍在讲授这些概念的时候,很少说这些为什么会有这样的概念问题,为解决什么问题引入的这些概念:然后,还有一些书,在简单表述了这些概念之后就立马挨个介绍算法了,遇到这样的书也会忽视这些基础问题的

glBindFramebuffer() 离屏渲染+双缓存+读取opengl像素 glReadPixels()

Opengl4.0中可以进行离屏渲染,即创造一个帧缓存对象(FBO),绑定一个帧缓存对象后,所有对Op--engl的操作都会针对这个帧缓存对象执行.而最近做项目时,在做一个拍照功能--读取Opengl渲染出的像素,并存入到BMP位图中.项目采用的是Opengl1.0和Opengl4.3结合的方法,并且两者的使用相对独立.使用旧的Opengl方法运行程序时,通过 glReadBuffer(GL_FRONT);//指定要读取的缓存 glReadPixels(0, 0, width, height,

缩进SSD,全称Single So Mu8名女性指控著名演员弗

导读:谭铁牛院士在第十九次中科院院士大会上发表了<人工智能:天使还是魔鬼>的主题报告,深度解读了60多年来人工智能发展历史,人工智能的七大现状,发s:blogcsdnneforezaricledeails76408139 文章标签: sring idea 实例博客应用 个人分类: sringcloud 所属专栏: 史上最简单的 Sr由于第一次接触WDF驱动开发,因此底层驱动基于微软提供的PCI9056驱动例子(因PCIe和PCI配置空间基本一致,故对9056例子做适当修改便可直接安装使用).

IntelliJ IDEA 创建Web项目(全教程)

说明:IntelliJ IDEA 版本为14.JDK 版本为1.7tomcat 版本为apache-tomcat-7.0.70 注:在创建过程中注意相关软件版本位数的问题.32位,64位的软件混搭会导致访问不成功的问题!!! 首先要理解一个基本问题:对比eclipse ,在IntelliJ IDEA中“new Project”相当于eclipse中的工作空间(Workspace),而“new Module”相当于eclipse中的工程(Project).以下均采用Intellij的说法,请自行对

说说我对OpenGL坐标变换几个关键点的理解

刚接触OpenGL的朋友们,可能对坐标变换不太理解. 本人不才, 接触了三维一段时间后,冒昧说说我的理解, 如有偏差, 请指正. 一:  首先说说什么是世界坐标. 每个三维模型都有自己的局部坐标, 这个大家都好理解,  这个称作模型坐标, 坐标原点可以是模型的中心.   但是一个场景中如果有许多个三维模型,  那要想标准其每个位置, 就需要一个统一的坐标来标定,  那么这个坐标就叫世界坐标.  这都好理解对吧,  下面说点难的. 二:  再说说世界坐标怎么转换到摄像机坐标. 我们知道,  观察三

OpenGL的状态机理解

OpenGL是一种状态机模式,比如你用glEnable打开一个状态,在以后的绘图中将一直保留并应用这个状态,除非你调用glDisable及同类函数来改变该状态或程序退出.例如当前颜色是一个状态变量,可以把当前颜色设置为白色.红色或其他任何颜色,在此之后绘制的所有物体都将使用这种颜色,直到把当前颜色设置为其他颜色. 除当前颜色之外,OpenGL绝大多数函数都是一种状态机,如控制当前视图和投影变换.直线和多边形点画模式.多边形绘图模式.像素包装约定.光照的位置和特征以及被绘制物体的材料属性等. 前一