Men的博客

欢迎光临!

0%

GLES语法

GLSL(着色器编程语言)

1、介绍
是一个和C语言语法比较类似的着色器变成语言
2、注释
和C语言一样
// this is a comment
/*

  • this is muti comment
  • /
    备注:GLSL语言必须由ASCII码字符组成,如果包括非ascii码编译会出错
    3、变量命名
    GLSL的变量名必须以字母或者下划线开头,由英文字母,数字,_组成,且不能是gl_或__开头(都是系统预留的),也不能是系统关键字
    4、预处理指令
    预处理指令以#开头,#号之前不能有除了空白字符之外的任何字符。每一个指令独占一行。内置的预处理指令如下:
    #define
    #undef
    #if
    #ifdef
    #ifndef
    #else
    #elif
    #endif
    #error
    #pragma
    #extension
    #version
    #line

#pragma
编译指示。用来控制编译器的一些行为,开发和调试时可以设置为off,默认设为on。

#pragma optimize(on)
#pragma optimize(off)

开发和调试时可以打开debug选项,以便获取更多的调试信息。默认设为off。
#pragma debug(on)
#pragma debug(off)

#extension
如果想使用GLGL默认不支持的操作,则必须启用对应的扩展,启用一个扩展可以使用下面的命令:

#extension : behavior
#extension all : behavior
其中,extension_name是扩展的名称,all是指所有的编译器支持的扩展。
behavior是指对该扩展的具体操作。比如启用、禁用等等。详情如下:
behavior 作用
require 启用该扩展。如果不支持,则报错。
enable 启用该扩展。如果不支持,则会警告。extension_name是all的时候会报错。
warn 启用该扩展。但是会检测到所有使用该扩展的地方,提出警告。
disable 禁用该扩展。如果该扩展不被支持,则提出警告。

5、预定义的变量
除此之外,还预定义了一些变量:
LINE :int类型,当前的行号,也就是在Source String中是第一行
FILE :int类型,当前Source String的唯一ID标识
VERSION :int类型,GLGL的版本
GL_ES :对于嵌入式系统(Embed System,简称 ES),它的值为1,否则为0

6、运算符及其优先级
| 1 |()
| 从右往左 |
| 3 | 乘除法 | * / % | 从左往右 |
| 4 | 加减法 | + - | 从左往右 |
| 5 | 位运算 移位 | << >> | 从左往右 |
| 6 | 大小关系 | < > <= >= | 从左往右 |
| 7 | 相等性判断 | == != | 从左往右 |
| 8 | 位运算 与 | & | 从左往右 |
| 9 | 位或算 非 | ^ | 从左往右 |
| 10 | 位或算 或 | | | 从左往右 |
| 11 | 逻辑与 | && | 从左往右 |
| 12 | 逻辑或 | || | 从左往右 |

7、关键词
列举一下GLSL中的关键词,这些全部是系统保留的,不可私自篡改。

attribute const uniform varying
break continue do for while
if else
in out inout
float int void bool true false
lowp mediump highp precision invariant
discard return
mat2 mat3 mat4
vec2 vec3 vec4 ivec2 ivec3 ivec4 bvec2 bvec3 bvec4
sampler2D samplerCube
struct

asm
class union enum typedef template this packed
goto switch default
inline noinline volatile public static extern external interface flat
long short double half fixed unsigned superp
input output
hvec2 hvec3 hvec4 dvec2 dvec3 dvec4 fvec2 fvec3 fvec4
sampler1D sampler3D
sampler1DShadow sampler2DShadow
sampler2DRect sampler3DRect sampler2DRectShadow
sizeof cast
namespace using

除此之外,所有的以”__”开头的变量全部是预留的,自定义的变量不能以“__”开头。

着色器

https://blog.csdn.net/hankern/article/details/85316476
1.1 Vertex Shader
对于发送给GPU的每一个Vertex(顶点),都要执行一次Vertex Shader。其功能是把每个顶点在虚拟空间中的三维坐标变换为可以在屏幕上显示的二维坐标,并带有用于z-buffer的深度信息。Vertex Shader可以操作的属性有:位置、颜色、纹理坐标,但是不能创建新的顶点。

vertex shader主要完成以下工作:1).基于点操作的矩阵乘法位置变换;2).根据光照公式计算每点的color值;3).生成或者转换纹理坐标。
Vertex Shader输入数据如下:
1).Attributes:由 vertext array 提供的顶点数据,如空间位置,法向量,纹理坐标以及顶点颜色,它是针对每一个顶点的数据。属性只在顶点着色器中才有,片元着色器中没有属性。属性可以理解为针对每一个顶点的输入数据。OpenGL ES 2.0 规定了所有实现应该支持的最大属性个数不能少于 8 个。
注:Vertex Attributes 是每点的属性数据。与一个index序号绑定。外部程序可通过 glBindAttribLocation将一个attribute 名与一个index绑定起来。当然,OPENGL ES 内部会自动绑定所有attributes.外部程序只需通过 glGetAttribLocation获取指定attribute名的index。 给Attribute传值可以通过 glVertexAttribPointer函数或者glVertexAttrib4fv

2).Uniforms:uniforms保存由应用程序传递给着色器的只读常量数据。在顶点着色器中,这些数据通常是变换矩阵,光照参数,颜色等。由 uniform 修饰符修饰的变量属于全局变量,该全局性对顶点着色器与片元着色器均可见,也就是说,这两个着色器如果被连接到同一 个program Object,则它们共享同一份 uniform 全局变量集。因此如果在这两个着色器中都声明了同名的 uniform 变量,要保证这对同名变量完全相同:同名+同类型,因为它们实际是同一个变量。此外,uniform 变量存储在常量存储区,因此限制了 uniform 变量的个数,OpenGL ES 2.0 也规定了所有实现应该支持的最大顶点着色器 uniform 变量个数不能少于 128 个,最大的片元着色器 uniform 变量个数不能少于 16 个。

3).Samplers:一种特殊的 uniform,在vertex shader中是可选的,用于呈现纹理。sampler 可用于顶点着色器和片元着色器。

4).Shader program:由 main 声明的一段程序源码,描述在顶点上执行的操作:如坐标变换,计算光照公式来产生 per-vertex 颜色或计算纹理坐标。

1.2 Fragment Shader
Pixel Shader(像素着色器)就是众所周知的Fragment Shader(片元着色器),它计算每个像素的颜色和其它属性。它通过应用光照值、凹凸贴图,阴影,镜面高光,半透明等处理来计算像素的颜色并输出。它也可改变像素的深度(z-buffering)或在多个渲染目标被激活的状态下输出多种颜色。一个Pixel Shader不能产生复杂的效果,因为它只在一个像素上进行操作,而不知道场景的几何形状。

Fragment Shader输入数据如下:
1).Varyings:这个在前面已经讲过了,顶点着色器阶段输出的 varying 变量在光栅化阶段被线性插值计算之后输出到片元着色器中作为它的输入,即上图中的 gl_FragCoord,gl_FrontFacing 和 gl_PointCoord。OpenGL ES 2.0 也规定了所有实现应该支持的最大 varying 变量个数不能少于 8 个。
2).Uniforms:前面也已经讲过,这里是用于片元着色器的常量,如雾化参数,纹理参数等;OpenGL ES 2.0 也规定了所有实现应该支持的最大的片元着色器 uniform 变量个数不能少于 16 个。
3).Samples:一种特殊的 uniform,用于呈现纹理。
4).Shader program:由 main 申明的一段程序源码,描述在片元上执行的操作。
FragmentShader输出为:
在顶点着色器阶段只有唯一的 varying 输出变量-即内建变量:gl_FragColor

缓存技术

https://blog.csdn.net/jxw167/article/details/55098260
在游戏中模型的渲染显示,当模型被加载后,在引擎底层需要DX库或者OpenGL库提供数据缓冲从而将数据传输到GPU中渲染绘制,模型都是由数据组成的,这些数据需要OpenGL图形库渲染它们时开辟一块缓冲内存进行存放,OpenGL为我们开发者提供了很多函数接口供我们使用,作为开发者熟悉这些关于缓冲数据操作的接口对于学习Shader编程也非常有帮助,市面上的Unity引擎和UE4虚幻引擎也是基于这些函数开发的,本片博客主要是为读者揭秘OpenGL中的数据缓冲原理。
在OpenGL中缓冲只是一块儿内存区域的对象,当把缓冲绑定到一个特定缓冲对象时,我们就给缓冲赋予了一个特殊的意义。当我们绑定到GL_ARRAY_BUFFER的时候,这个缓冲就是一个顶点数组缓冲,我们也可以简单地绑定到GL_ELEMENT_ARRAY_BUFFER。OpenGL内部为每个目标(target)储存一个缓冲,并基于目标来处理不同的缓冲。
我们使用glBufferData函数填充缓冲对象管理的内存,这个函数分配了一块内存空间,然后把数据存入其中。如果我们向它的data这个参数传递的是NULL,那么OpenGL只会帮我们分配内存,而不会填充它。如果我们先打算开辟一些内存,稍后回到这个缓冲一点一点的填充数据,有些时候会很有用。

我们还可以调用glBufferSubData函数填充特定区域的缓冲,而不是一次填充整个缓冲。这个函数需要一个缓冲目标(target),一个偏移量(offset),数据的大小以及数据本身作为参数。这个函数新的功能是我们可以给它一个偏移量(offset)来指定我们打算填充缓冲的位置与起始位置之间的偏移量。这样我们就可以插入/更新指定区域的缓冲内存空间了。一定要确保修改的缓冲要有足够的内存分配,所以在调用glBufferSubData之前,调用glBufferData是必须的。注意,glBufferData函数接口在Shader编程中使用的非常广泛。它的函数接口如下所示:

float data[] = {
0.5f, 1.0f, -0.35f

};

glBindBuffer(GL_ARRAY_BUFFER, buffer);
// 获取当前绑定缓存buffer的内存地址
void* ptr = glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
// 向缓冲中写入数据
memcpy(ptr, data, sizeof(data));
// 完成够别忘了告诉OpenGL我们不再需要它了
glUnmapBuffer(GL_ARRAY_BUFFER);

调用glUnmapBuffer函数可以告诉OpenGL我们已经用完指针了,OpenGL会知道你已经做完了。通过解映射(unmapping),指针会不再可用,如果OpenGL可以把你的数据映射到缓冲上,就会返回GL_TRUE。

把数据直接映射到缓冲上使用glMapBuffer很有用,因为不用把它储存在临时内存里。你可以从文件读取数据然后直接复制到缓冲的内存里。

使用glVertexAttribPointer函数可以指定缓冲内容的顶点数组的属性的布局(Layout)。我们已经知道,通过使用顶点属性指针我们可以交叉(Interleave)属性,也就是说我们可以把每个顶点的位置、法线、纹理坐标放在彼此挨着的地方。现在我们了解了更多的缓冲的内容,可以采取另一种方式了。我们可以做的是把每种类型的属性的所有向量数据批量保存在一个布局,而不是交叉布局。

当从文件加载顶点数据时你通常获取一个位置数组,一个法线数组和一个纹理坐标数组。需要花点力气才能把它们结合为交叉数据。使用 glBufferSubData 可以简单的实现分批处理方式:
缓冲满足的条件:

建构一个完整的帧缓冲必须满足以下条件:

我们必须往里面加入至少一个附件(颜色、深度、模板缓冲)。
其中至少有一个是颜色附件。
所有的附件都应该是已经完全做好的(已经存储在内存之中)。
每个缓冲都应该有同样数目的样本。

投影相机