先以一对Shader的代码为例子开始:
private final String mVertexShader =
"uniform mat4 uMVPMatrix;\n" +
"uniform mat4 uSTMatrix;\n" +
"attribute vec4 aPosition;\n" +
"attribute vec4 aTextureCoord;\n" +
"varying vec2 vTextureCoord;\n" +
"void main() {\n" +
" gl_Position = uMVPMatrix * aPosition;\n" +
" vTextureCoord = (uSTMatrix * aTextureCoord).xy;\n" +
"}\n";
private final String mFragmentShader =
"#extension GL_OES_EGL_image_external : require\n" +
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform samplerExternalOES sTexture;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture, vTextureCoord);\n" +
"}\n";
一点一点摘出来其中的意思。
1. Shader的几种变量:
form:输入,在vertex和fragment两者之间声明方式完全一样,则它可以在vertex和fragment共享使用。(相当于一个被vertex和fragment shader共享的全局变量)
uniform: 统一值,常用于设置各种变换矩阵。限定了表示一个变量的值将由应用程序在着色器执行之前指定,并且在图元的处理过程中不会发生变化。在Shader被绑定后,应用程序可以通过API来修改uniform变量:glGetUniformLocation。
attribute:只能在vertex shader中使用的变量。(不能在fragmentshader中声明attribute变量,也不能被fragmentshader中使用)一般用attribute变量来表示一些顶点的数据,如:顶点坐标position,法线,纹理坐标TexCoordIn,顶点颜色等。
在application中,一般用函数glBindAttribLocation()来绑定每个attribute变量的位置,然后用函数glVertexAttribPointer()为每个attribute变量赋值。
Varying: 输出将会被提供给 片段着色器-fragmentshader 使用是vertex和fragment shader之间做数据传递用的。一般vertexshader修改varying变量的值,然后fragmentshader使用该varying变量的值,比如TexCoordOut。因此varying变量在vertex和fragment shader二者之间的声明必须是一致的。application不能使用此变量。
sampler2D:它就是和texture绑定的一个数据容器接口,用于处理2D类型贴图。其值于调用时赋值i,对应于glActiveTexture(GL_TEXTURE0+i); 例如:
// fragment shader
private final String mFragmentShader =
"precision mediump float;\n" +
"varying vec2 vTextureCoord;\n" +
"uniform sampler2D sTexture2;\n" +
"uniform sampler2D sTexture1;\n" +
"void main() {\n" +
" gl_FragColor = texture2D(sTexture1, vTextureCoord);\n" +
"}\n";
// using shader
// get postion
GLuint tex1 = glGetUniformLocation(pp->po, "sTexture1");
GLuint tex2 = glGetUniformLocation(pp->po, "sTexture2");
// activate multi texture unit and bind textures
glActiveTexture(GL_TEXTURE0);
glBindTexture(t0);
glActiveTexture(GL_TEXTURE1);
glBindTexture(t1);
// set sTexture1 to GL_TEXTURE0
glUniform1i(texl, 0);
// set sTexture2 to GL_TEXTURE0
glUniform1i(tex2, 1);
精度Precision:精度限定。 在Vertex Shader中,如果没有默认的精度,则float和int精度都为highp;在Fragment Shader中,float没有默认的精度,所以必须在Fragment Shader中为float指定一个默认精度或为每个float变量指定精度。
有三个选项,highp, mediump, lowp
vec2 = Codea 中的 vec2,如 vec2(3,4),还有 vec3 和 vec4
bool = boolean(true 或 false)布尔型,真值或假值
int = integer 整型
float = 带小数的数 浮点型
sampler2D = 一个 2D 图像
mat2 = 2*2 的表(mat3 和 mat4 分别是 3*3 和 4*4 的表)
2. vertexShader,
" gl_Position = Position;\n" \
" oTexCoord = TexCoord;\n" \
Position是顶点位置坐标, TexCoord是纹理映射坐标,赋值号左侧的部分就是准备由这个处理阶段传递给下一道工序的输出变量
gl_Position: 用于vertex shader, 写顶点位置;被图元收集、裁剪等固定操作功能所使用;它是内部变量,表示变换后点的对应位置,其内部声明是:highp vec4 gl_Position;
vertex里的x,y,z是相对值,只要保证比例就可以。而u,v只能是0~1的范围
vertex shader工作是顶点坐标坐标变换、光照、材质等顶点的相关操作,每顶点执行一次,处理的是外面传给shader的顶点数组。传入相应的Attribute变量、Uniforms变量、采样器以及临时变量,经过顶点着色器后生成作为输出的Varying变量。在vertex shader和fragment shader之间会完成光栅化。所以如果希望对所有像素进行操作,需要在fragment shader里做处理。
3. fragmentShader,
lowp vec4 col =texture2D(texture, vTexCoord) * vColor ;
gl_FragColor = col;
texture2D, 取得纹理贴图上面一个像素点的颜色,这个像素点位于x,y,由 vTexCoord 指定,最后把这个像素点的颜色放到一个名为 col 的变量中。
这里的vTexCoord坐标是光栅化之后的点对应输入图像上点的坐标,相当于从输出图像逐个像素取出来的点,对应输入位置的坐标。
fragment shader处理的是rasterize之后的片元数据,输入varying vec2 vTextureCoord是片元像素对应输入纹理上的位置,输入uniform samplerExternalOESsTexture是输入纹理。通过gl_FragColor = texture2D(sTexture, vTextureCoord)对纹理对应位置进行采样
光栅化(Rasterize/rasteriztion)。
Adobe官方翻译成栅格化或者像素化,就是把矢量图形转化成像素点的过程。在vertex shader和fragment shader之间完成。屏幕上显示的画面都是由像素组成,而三维物体都是点线面构成的。要让点线面,变成能在屏幕上显示的像素,就需要Rasterize这个过程。就是从矢量的点线面的描述,变成像素的描述。
其他例子:
attribute vec2 texCoord; 变量 texCoord 是一个 vec2 以及一个 attribute(因此它对于每个顶点都不同),我们可以根据它的名字来猜测:它保留了应用于这个点的纹理贴图的坐标位置。
varying highp vec2 vTexCoord; 变量 vTexCoord 是一个高精度的 vec2,它还是一个 varying,这意味着它是一个输出,因此它将会被插值到每个像素点,并且发送给 片段着色器-fragment shader。你可以从 main 函数中的代码看到,vTexCoord =texCoord,因此所有这些代码所做的就是传递贴图的位置坐标给 片段着色器-fragmentshader。因此我们回到所有这个着色器所做的事实,它取得位置坐标,纹理和颜色信息(来自attribute 输入变量),然后把它们未做改动地赋值给输出(varying)变量.