Shader
Shader 就是GPU执行的代码,在OpenGL中,通过glCompileShader的方法来编译shader代码,之后通过glAttachShader 和 glLinkProgram 把代码连接到GPU程序。
OpenGL一共有5种Shader,其中最常见的是vsh和fsh。
vertexShader(vsh)
vsh的作用是对定点做处理
绘制形状
public var GL_POINTS: Int32 { get }
public var GL_LINES: Int32 { get }
两个点画一个线段,比如我给定4个点,就会画两条线段,每条线段独立
public var GL_LINE_LOOP: Int32 { get }
两个点画一个线段,并且最后绘制的线会闭合,这个方式画圆环效率最高
public var GL_LINE_STRIP: Int32 { get }
用最近的点和新增的点连成一根线,比如3个点可以画两条线,不会闭合
public var GL_TRIANGLES: Int32 { get }
三个点画一个三角形,6个点可以画2个三角形
public var GL_TRIANGLE_STRIP: Int32 { get }
用最近的点和新增的点连成一个三角形,比如3个点可以画一个三角形,但是4个点,可以画2个三角形。 并且会填充颜色 其规律是:
构建当前三角形的顶点的连接顺序依赖于要和前面已经出现过的2个顶点组成三角形的当前顶点的序号的奇偶性(如果从0开始):
如果当前顶点是奇数:
组成三角形的顶点排列顺序:T = [n-1、n-2、n].
如果当前顶点是偶数:
组成三角形的顶点排列顺序:T = [n-2、n-1、n].
以上图为例,第一个三角形,顶点v2序号是2,是偶数,则顶点排列顺序是v0,v1,v2。第二个三角形,顶点v3序号是3,是奇数,则顶点排列顺序是v2,v1,v3,第三个三角形,顶点v4序号是4,是偶数,则顶点排列顺序是v2,v3,v4,以此类推。
这个顺序是为了保证所有的三角形都是按照相同的方向绘制的,使这个三角形串能够正确形成表面的一部分。对于某些操作,维持方向是很重要的,比如剔除。
注意:顶点个数n至少要大于3,否则不能绘制任何三角形。
public var GL_TRIANGLE_FAN: Int32 { get }
绘制各三角形形成一个扇形序列,以v0为起始点,(v0,v1,v2)、(v0,v2,v3)、(v0,v3,v4)。
glColorPointer
用于指定顶点颜色。用这个居然不需要再shader 配置颜色输出!
glVertexAttribPointer 参数详解
public func glVertexAttribPointer(_ indx: GLuint, _ size: GLint, _ type: GLenum, _ normalized: GLboolean, _ stride: GLsizei, _ ptr: UnsafeRawPointer!)
index:第几个属性,从0开始取,0,1,2,顺序自己定义,例如顶点位置,纹理,法线
这里只有顶点位置,也只能讨论顶点位置,所以为0
size:一个顶点所有数据的个数,这里每个顶点又两个浮点数属性值,所以是2
type:顶点描述数据的类型,这里position数组中的数据全部为float,所以是GL_FLOAT
normalized:是否需要显卡帮忙把数据归一化到-1到+1区间,这里不需要,所以设置GL_FALSE
stride:一个顶点占有的总的字节数,这里为两个float,所以是sizeof(float)*2
pointer:当前指针指向的vertex内部的偏离字节数,可以唯一的标识顶点某个属性的偏移量
离屏渲染
离开屏幕,也能渲染。如果OpenGL的窗口在后台,你去读取窗口的数据,这样做的话,你是读取不到的,所以需要pbuffer,用于离屏渲染。 vertex shader 执行的个数,是画三角形的个数(如果没三角形呢?) fragment shader 执行的次数,是光栅化像素的个数
gl_vertex 内置变量
gl_Vertex 内置变量,是通过glVertexAttribPointer 把顶点数据传递过去的
shader 相关
precision highp float; 没有在iOS上 shader 编译报错!?
attribute
varying
varying的作用是把顶点着色器的数据(变量)传递到fragment shader 中
uniform
相当于shader 中的全局变量
最近完成了两个OpenGL的demo,现在进行总结:
1.针对iOS平台,一个CALayer 只能绑定一个framebuffer,如果你需要在一个CALayer中显示多个图像,则需要创建一个新的framebuffer并绑定到另一个CALayer上。
2.需要注意的是,当前页面的layer 绑定的frameBuffer 只负责显示这个layer的内容,而默认的framebuffer仍然会被使用,用于显示整个应用程序的图像。(from gpt)
3.iOS平台至少能支持16个纹理单元,需要注意的是,纹理单元和纹理数量是不同的概念。通常情况下,现代GPU可以支持数千个甚至数万个纹理对象,调用 glTexImage2D 或 glTexSubImage2D 函数上传纹理数据到GPU的纹理对象中。
4.如果一个layer 的FBO绑定了renderbuffer,那么他的渲染结果会输出到renderbuffer。如果绑定了texture,那么渲染结果会输出到texture。两者选其一
5.渲染管道的主要流程是:顶点着色器->光栅化->片元着色器->帧缓冲
6.画板和刮刮乐的主要思路是利用FBO,把绘制内容先画到纹理上,之后再把纹理渲染出来
7.一个framebuffer 能同时绑定多个纹理,在绑定的时候,选择不同的附着点就行。同时,多纹理绑定需要调用glDrawBuffer(GL_COLOR_ATTACHMENTXXXX) 来确定渲染输出的纹理:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureID, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, textureID2, 0);
glDrawBuffer(GL_COLOR_ATTACHMENT0);
8.顶点着色器的主要作用是将输入的顶点数据转换为标准化设备坐标系中的顶点,片元着色器的主要作用是对每个像素进行光栅化和着色。因此,每个顶点都会经过一次顶点着色器处理,每个像素都会经过一次片元着色器处理,所以通常情况下,顶点着色器比片元着色器执行的次数多得多
9.Tile-Based Rendering(TBR)是一种用于3D图形渲染的技术,其主要目标是提高效率并减少能耗。在TBR中,屏幕被分成许多小的瓦片,每个瓦片都被单独地渲染,以便在必要时只更新屏幕上的一小部分,而不是整个屏幕。
TBR的优点包括:
- 提高了渲染效率:因为每个瓦片只需要渲染一次,所以TBR可以减少渲染的工作量,从而提高渲染效率。
- 减少能耗:由于TBR只需要更新屏幕上的一小部分,因此它可以减少GPU的功耗,从而减少设备的能耗。
- 更好的灵活性:由于TBR可以根据需要更新屏幕上的一小部分,因此它可以提供更好的灵活性,从而使渲染引擎更容易适应各种设备和应用场景。
TBR通常与现代GPU结合使用,包括Apple的Metal和Vulkan等现代图形API,这些API允许开发者更好地利用GPU的能力,从而实现更高效的渲染。
10.Rendering pass 是图形渲染中的一个重要概念,指的是执行一组图形操作所需的所有状态和资源,以及它们的交互方式。
11、当前屏幕渲染、离屏渲染、CPU渲染的选择
尽量使用当前屏幕渲染: 鉴于离屏渲染、CPU渲染可能带来的性能问题一般情况下,我们要尽量使用当前p屏幕渲染
离屏渲染 VS CPU渲染
由于GPU的浮点运算能力比CPU强,CPU染的效率可能不如离屏渲染,但如果仅仅是实现一人简单的效果,直接使用CPU渲染I效率又可能比离屏渲染好,毕竟离屏渲染要涉及到缓冲区创建和上下文切换等耗时操作。
12、如果你要输出到屏幕,那么就需要把FBO绑定renderbuffer,如果你需要离屏渲染,那么你就需要把FBO绑定到Texture
13、glGenFramebuffers为开辟顿缓冲区,glBindFramebuffer为切换顺缓冲区,对于离屏渲染会有更多的内存分配和切换操作。离屏消耗消耗主要在于: 1.glGenFramebuffers导致更多的内存消耗; 2.更多的gDrawArrays和gIDrawElements导致GPU有更多绘制操作;3切换缓冲区带来的消耗
14、OpenGL 中的颜色混合就是将通过各种测试准备进入帧缓冲的片元(源片元) 与缓冲中的原有片元(目标片元)按照设定的比例加权计算最终片元的颜色值。新片元不一定是直接覆盖缓冲区中的源片元。
OpenGL 渲染时会把颜色值存在颜色缓存区中,每个片段的深度值也是放在深度缓冲区。当深度缓冲区被关闭时,新的颜色将简单的覆盖原来颜色缓存区存在的颜色色值,当深度缓冲区再次打开时,新的颜色片段只是当它们比原来的值更接近邻近的裁剪平面才会替换原来的颜色片段。
目标颜色: 已经存储在颜色缓存区的颜色值;
源颜色: 作为当前渲染命令结果进入颜色缓存区的颜色值;
15、颜色缓冲区和深度缓冲区都是在渲染管道最后一个阶段,渲染到屏幕的时候起作用,但是,前面的几个阶段(顶点输入,顶点着色器,图元装配,几何着色器,裁剪,光栅化,片元着色器等)都会对颜色缓冲区和深度缓冲区进行操作
16、顶点缓冲区是一种用于存储和管理顶点数据的缓冲区,在渲染管道的顶点输入阶段使用,索引缓冲区来指定绘制的顶点顺序
17、ZFighting(Z-fighting)是计算机图形学中的一种现象,指的是两个或多个物体在同一深度位置上争夺像素渲染的问题,会导致同一区域的物体在渲染时出现抖动或闪烁的情况。
ZFighting问题通常是由于深度缓冲区的精度不足或者渲染时的浮点数精度损失导致的。当两个物体的深度值非常接近或者完全相同的时候,由于深度缓冲区精度的限制,就会出现像素渲染时两个物体抢占同一像素的情况,从而导致ZFighting现象
18、深度测试是一种在3D图形渲染中用于确定哪些片段将被渲染的技术。当多个物体的表面在同一深度位置时,深度测试可以决定哪个片段在渲染时将被绘制在屏幕上。在深度测试期间,每个片段的深度(从视角到片段的距离)将与深度缓冲区中的现有深度值进行比较。如果片段深度小于深度缓冲区中的现有深度,则该片段将被绘制,否则它将被丢弃。这确保了后面的物体不会在前面的物体之上绘制。深度测试通常在渲染管道的“光栅化”阶段执行。