在OpenGL ES 3程序中,Shader和Program是两个重要的概念,至少需要创建一个顶点Shader对象、一个片段Shader对象和一个Program对象,才能用着色器进行渲染,理解Shader对象和Program对象的最佳方式是将它们比作C语言的编译器和链接程序,从Shader的创建到Program的链接共六个基本步骤,创建Shader、加载Shader源码、编译Shader、创建Program、绑定Program与Shader、链接Program,下面介绍Shader和Program的创建方法及相关概念。

1、创建Shader

GLuint glCreateShader(GLenum shaderType);

创建着色器使用glCreateShader,成功时返回一个空的着色器对象句柄,一个正整数,失败时返回0,shaderType有误时产生错误GL_INVALID_ENUM,shaderType可以是:

GL_VERTEX_SHADER // vertex
GL_FRAGMENT_SHADER // fragment
GL_COMPUTE_SHADER // from GL ES 3.1
GL_TESS_CONTROL_SHADER // from GL ES 3.2
GL_TESS_EVALUATION_SHADER // from GL ES 3.2
GL_GEOMETRY_SHADER // from GL ES 3.2

与glCreateShader对应的有个glDeleteShader,用于删除着色器对象,包括内存释放和shader句柄释放,如果这个着色器对象attach到了一个程序对象,那么这个着色器对象将作个删除标记而不会立即删除,等到不再attach任何程序对象时再删除,参数shader无效时产生错误GL_INVALID_VALUE。

void glDeleteShader(GLuint shader);

对一个着色器对象来说,判断其是否有删除标记,可以调用如下函数:

glGetShaderiv(shader, GL_DELETE_STATUS, params);

2、加载Shader源码

void glShaderSource(GLuint shader,GLsizei count,const GLchar **string,const GLint *length);

创建了着色器对象之后,通过glShaderSource加载着色器源码,替换原有的着色器源码,shader为着色器对象句柄,count为string数组和length数组的大小,string为指针数组,保存了count个着色器源码,length为整型数组,指定了每个着色器源码的长度,如果length为NULL,则认为着色器源码字符串以null结尾,length为正数时只加载指定长度的着色器源码字符串,如果lengh不合适,可能导致错误产生,length为负数时也认为着色器源码字符串以null结尾。加载失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。glShaderSource拷贝了对应的着色器源码,所以之后就可以立即释放相关的内存了。

3、编译Shader

void glCompileShader(GLuint shader);

成功加载着色器源码之后,通过glCompileShader编译着色器对象,处理上一步加载的着色器源码字符串,shader为前面创建的着色器对象句柄,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

当OpenGL ES编译和绑定着色器时,着色器代码通常解析为某种中间表现形式,这和大部分编译语言相同,如抽象语法树,编译器必须将抽象形式转换为硬件的机器指令,理想状态下,这个编译器还应该进行大量的优化,如无用代码删除、常量传播等,进行这些工作需要付出代价,主要是CPU时间和内存。OpenGL ES 3.0实现必须支持在线着色器编译,用glGetBooleanv检索的GL_SHADER_COMPILER值必须是GL_TRUE,可以指定着色器使用glShaderSource,还可以尝试缓解着色器编译对资源的影响,也就是说,一旦完成了应用程序中着色器的编译,就可以调用如下glReleaseShaderCompiler,这个函数提示OpenGL ES实现已经完成了着色器编译的工作,可以释放它的资源了。需要注意的是,这个函数只是一个提示,如果决定用glCompileShader编译更多的着色器,那么OpenGL ES实现需要重新为编译器分配资源。

void glReleaseShaderCompiler(void);

4、检查Shader状态

void glGetShaderiv(GLuint shader,GLenum pname,GLint *params);
void glGetShaderInfoLog(GLuint shader,GLsizei maxLength,GLsizei *length,GLchar *infoLog);

glGetShaderiv用于检查Shader状态,shader为着色器对象句柄,pname为待检查的Shader状态,params返回检查的Shader状态,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION、GL_INVALID_ENUM,Shader状态即函数参数pname取值如下:

GL_SHADER_TYPE:着色器类型,如GL_VERTEX_SHADER等。
GL_DELETE_STATUS:着色器是否有删除标记。
GL_COMPILE_STATUS:着色器是否编译成功。
GL_INFO_LOG_LENGTH:着色器通知日志长度,包括null结尾的字符。
GL_SHADER_SOURCE_LENGTH:着色器源码字符串长度,包括null结尾的字符。

glGetShaderInfoLog用于获取Shader通知日志的字符串表示,shader为着色器对象句柄,maxLength为获取通知日志的buffer的最大长度,length返回获取的通知日志的长度,不包括null结尾的字符,可以为NULL,infoLog返回通知日志,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

另外,还有几个有用的函数,glIsShader判断是否为着色器对象,glGetShaderSource获取着色器源码的字符串表示。

GLboolean glIsShader(GLuint shader);
void glGetShaderSource(GLuint shader,GLsizei bufSize,GLsizei *length,GLchar *source);

5、创建Program

GLuint glCreateProgram(void);

创建程序对象使用glCreateProgram,glCreateProgram创建一个空的程序对象,一个非零值,失败时返回0。与glCreateProgram对应的有个glDeleteProgram,用于删除程序对象,包括内存释放和program句柄释放,如果这个程序对象在使用中,那么将作个删除标记而不会立即删除,等到不再使用时再删除,其中的着色器对象将自动detach,着色器对象有删除标记就删除,否则不删除。参数program无效时产生错误GL_INVALID_VALUE。

void glDeleteProgram(GLuint program);

对一个程序对象来说,判断其是否有删除标记,可以调用如下函数:

void glGetProgramiv(program, GL_DELETE_STATUS, params);

6、绑定Program与Shader

void glAttachShader(GLuint program,GLuint shader);

创建了程序对象之后,通过glAttachShader将其绑定到一个着色器对象,program和shader为对应的程序对象和着色器对象,同一种类型的着色器对象只能绑定一次,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。与glAttachShader对应的有个glDetachShader,用于解除绑定,program和shader为对应的程序对象和着色器对象,如果着色器对象有删除标记且没有被其它地方使用将删除,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

void glDetachShader(GLuint program,GLuint shader);

7、链接Program

void glLinkProgram(GLuint program);

程序对象与着色器对象attach之后,使用glLinkProgram链接程序对象,这是使用程序对象前的最后一步,参数program为需要链接的程序对象句柄,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。程序对象链接成功之后,所有活动的用户定义的uniform变量都被初始化为0而且被设置一个位置,这个位置使用glGetUniformLocation获取,所有活动的命名uniform块中的uniform变量都被设置一个偏移量,包括数组和矩阵,所有活动的用户定义的attribute变量没有绑定到一般的顶点属性索引时将在此时绑定一个。链接操作负责生成最终的可执行程序,链接阶段是生成在硬件上运行的最终指令的时候,链接程序将检查各种对象的数量,确保成功链接,例如,链接程序将确保顶点着色器写入片段着色器使用的所有顶点着色器输出变量并用相同的类型声明,确保任何在顶点和片段着色器中都声明的统一变量和统一变量缓冲区的类型相符,确保最终的程序符合具体实现的限制,如属性、统一变量、输入输出着色器变量的数量等。链接程序对象可能会失败,常见原因可参照https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glLinkProgram.xhtml。

8、检查Program状态

void glGetProgramiv(GLuint program,GLenum pname,GLint *params);
void glGetShaderInfoLog(GLuint shader,GLsizei maxLength,GLsizei *length,GLchar *infoLog);

检查Program状态的方法类似于上面介绍的检查Shader状态的方法,只是使用的函数不同,分别为glGetProgramiv和glGetShaderInfoLog,glGetProgramiv的参数pname支持的状态包括(详细用法可参照https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGetProgramiv.xhtml):

GL_ACTIVE_ATOMIC_COUNTER_BUFFERS
GL_ACTIVE_ATTRIBUTES
GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
GL_ACTIVE_UNIFORM_BLOCKS
GL_ACTIVE_UNIFORM_BLOCK_MAX_NAME_LENGTH
GL_ACTIVE_UNIFORMS
GL_ACTIVE_UNIFORM_MAX_LENGTH
GL_ATTACHED_SHADERS
GL_COMPUTE_WORK_GROUP_SIZE
GL_DELETE_STATUS
GL_GEOMETRY_LINKED_INPUT_TYPE
GL_GEOMETRY_LINKED_OUTPUT_TYPE
GL_GEOMETRY_LINKED_VERTICES_OUT
GL_GEOMETRY_SHADER_INVOCATIONS
GL_INFO_LOG_LENGTH
GL_LINK_STATUS
GL_PROGRAM_BINARY_RETRIEVABLE_HINT
GL_PROGRAM_SEPARABLE
GL_TESS_CONTROL_OUTPUT_VERTICES
GL_TESS_GEN_MODE
GL_TESS_GEN_POINT_MODE
GL_TESS_GEN_SPACING
GL_TESS_GEN_VERTEX_ORDER
GL_TRANSFORM_FEEDBACK_BUFFER_MODE
GL_TRANSFORM_FEEDBACK_VARYINGS
GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH
GL_VALIDATE_STATUS

下面是一个创建Shader和Program的代码例子:

// esShader.c
GLuint esLoadShader ( GLenum type, const char *shaderSrc )
{GLuint shader;GLint compiled;// Create the shader objectshader = glCreateShader ( type );if ( shader == 0 ){return 0;}// Load the shader sourceglShaderSource ( shader, 1, &shaderSrc, NULL );// Compile the shaderglCompileShader ( shader );// Check the compile statusglGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );if ( !compiled ){GLint infoLen = 0;glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );if ( infoLen > 1 ){char *infoLog = malloc ( sizeof ( char ) * infoLen );glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );esLogMessage ( "Error compiling shader:\n%s\n", infoLog );free ( infoLog );}glDeleteShader ( shader );return 0;}return shader;
}GLuint ESUTIL_API esLoadProgram ( const char *vertShaderSrc, const char *fragShaderSrc )
{GLuint vertexShader;GLuint fragmentShader;GLuint programObject;GLint linked;// Load the vertex/fragment shadersvertexShader = esLoadShader ( GL_VERTEX_SHADER, vertShaderSrc );if ( vertexShader == 0 ){return 0;}fragmentShader = esLoadShader ( GL_FRAGMENT_SHADER, fragShaderSrc );if ( fragmentShader == 0 ){glDeleteShader ( vertexShader );return 0;}// Create the program objectprogramObject = glCreateProgram ( );if ( programObject == 0 ){return 0;}glAttachShader ( programObject, vertexShader );glAttachShader ( programObject, fragmentShader );// Link the programglLinkProgram ( programObject );// Check the link statusglGetProgramiv ( programObject, GL_LINK_STATUS, &linked );if ( !linked ){GLint infoLen = 0;glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );if ( infoLen > 1 ){char *infoLog = malloc ( sizeof ( char ) * infoLen );glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );esLogMessage ( "Error linking program:\n%s\n", infoLog );free ( infoLog );}glDeleteProgram ( programObject );return 0;}// Free up no longer needed shader resourcesglDeleteShader ( vertexShader );glDeleteShader ( fragmentShader );return programObject;
}

9、使用着色器程序

void glUseProgram(GLuint program);

最后,成功链接程序对象之后,就可以使用这个着色器程序了,后面在添加一些顶点数据就可以进行图元绘制了。glUseProgram把这个program程序对象安装到当前渲染状态的一部分,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

另外,还有几个有用的函数,glIsProgram判断是否为程序对象,glGetIntegerv(GL_CURRENT_PAROGRAM, data)获取当前活动的程序对象,glGetAttachedShaders获取attach到一个程序对象上的着色器对象,glValidateProgram用于验证程序。

GLboolean glIsProgram(  GLuint program);
void glGetIntegerv(GLenum pname, GLint * data); // glGetXxx
void glGetAttachedShaders(GLuint program,GLsizei maxCount,GLsizei *count,GLuint *shaders);
void glValidateProgram(GLuint program);

10、uniform变量查询与设置

程序对象链接成功之后,就可以在对象上进行许多查询,如活动的uniform变量,是着色语言中的只读变量,uniform变量分为两种类型,默认uniform变量和uniform变量块,如下所示:

// default
uniform mat4 matViewProj;
uniform mat3 matNormal;
uniform mat3 matTexGen;// block
uniform TransformBlock
{mat4 matViewProj;mat3 matNormal;mat3 matTexGen;
};

如果uniform变量在顶点着色器和片段着色器中均有声明,则声明的类型必须相同,且在两个着色器中的值也需相同。查找活动uniform变量之前,可以使用GL_ACTIVE_UNIFORMS参数调用glGetProgramiv函数,获得活动uniform变量的个数,包括前面提到的两种类型的uniform变量和内建uniform变量,还可以使用GL_ACTIVE_UNIFORM_MAX_LENGTH获得uniform变量名的最大长度,然后就可以使用如下几个函数查询活动uniform变量了。

void glGetActiveUniform(GLuint program,GLuint index,GLsizei bufSize,GLsizei *length,GLint *size,GLenum *type,GLchar *name);

glGetActiveUniform获取程序对象program中位置index的活动uniform变量的信息,bufSize指定name的最大长度,length返回name的实际长度,可以为NULL,uniform变量非数组时size为1,uniform变量是数组时size为使用的最大数组元素,type返回uniform变量的类型,name返回uniform变量名,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。type返回的uniform变量类型与着色语言中的变量类型有个对应关系,如GL_FLOAT对应于float,详细对应关系可参照https://www.khronos.org/registry/OpenGL-Refpages/es3/html/glGetActiveUniform.xhtml。

void glGetActiveUniformsiv(GLuint program,GLsizei uniformCount,const GLuint *uniformIndices,GLenum pname,GLint *params);

glGetActiveUniformsiv获取程序对象program中uniformCount个uniform活动变量的类型为pname的信息,uniformIndices是个长度为uniformCount的保存了uniform变量位置的数组,params返回对应的结果,也是个长度为uniformCount的数组,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION、GL_INVALID_ENUM。pname类型如下:

GL_UNIFORM_TYPE
GL_UNIFORM_SIZE
GL_UNIFORM_NAME_LENGTH
GL_UNIFORM_BLOCK_INDEX
GL_UNIFORM_OFFSET
GL_UNIFORM_ARRAY_STRIDE
GL_UNIFORM_MATRIX_STRIDE
GL_UNIFORM_IS_ROW_MAJOR

知道了活动uniform变量名之后,通过glGetUniformLocation可以获取其位置,失败时返回-1,可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION。

GLint glGetUniformLocation(GLuint program,const GLchar *name);

然后,通过uniform变量的位置就可以使用glGetUniformXxx查询其值或者使用glUniformXxx改变其值了,失败时可能的错误为GL_INVALID_VALUE、GL_INVALID_OPERATION,下面是这两个函数的某一个版本。

void glGetUniformfv(GLuint program,GLint location,GLfloat *params);
void glUniform1f(GLint location,GLfloat v0);

下面是示例代码:

GLint maxUniformLen;
GLint numUniforms;
char *uniformName;
GLint index;glGetProgramiv(progObj, GL_ACTIVE_UNIFORMS, &numUniforms);
glGetProgramiv(progObj, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxUniformLen);
uniformName = malloc(sizeof(char) * maxUniformLen);for (index = 0; index < numUniforms; ++i) {Glint len;GLint size;GLenum type;GLint location;// get the uniform infoglGetAcitveUniform(progObj, index, maxUniformLen, &len, &size, &type, uniformName);// get the uniform locationlocation = glGetUniformLocation(progObj, uniformName);
}

11、uniform变量缓冲区对象

可以使用缓冲区对象存储uniform变量数据,从而在程序中的着色器之间甚至程序之间共享uniform变量,这种缓冲区对象称作uniform变量缓冲区对象,使用uniform变量缓冲区对象,可以在更新大的uniform变量块时降低API开销,而且增加了uniform变量的可用存储,可以不受默认uniform变量块大小的限制,更新uniform变量缓冲区对象时可以使用glBufferData创建和初始化一个缓冲区对象、glBufferSubData更新缓冲区对象、glMapBufferRange映射缓冲区对象的一个区域、glUnmapBuffer使用缓冲区对象前解除映射的数据等。

与统一变量位置值用于引用统一变量类似,统一变量块索引用于引用统一变量块,可以用glGetUniformBlockIndex获取命名统一变量块的索引。从统一变量块索引,可以用glGetActiveBlockName和glGetActiveUniformBlockiv确定活动统一变量块的细节,获取统一变量块的许多属性,命名统一变量块的索引必须小于GL_ACTIVE_UNIFORM_BLOCKS的值。

GLuint glGetUniformBlockIndex(GLuint program,const GLchar *uniformBlockName);
void glGetActiveUniformBlockName(GLuint program,GLuint uniformBlockIndex,GLsizei bufSize,GLsizei *length,GLchar *uniformBlockName);
void glGetActiveUniformBlockiv(GLuint program,GLuint uniformBlockIndex,GLenum pname,GLint *params);

glGetActiveUniformBlockiv的pname可以是:

GL_UNIFORM_BLOCK_BINDING
GL_UNIFORM_BLOCK_DATA_SIZE
GL_UNIFORM_BLOCK_NAME_LENGTH
GL_UNIFORM_BLOCK_ACTIVE_UNIFORMS
GL_UNIFORM_BLOCK_ACTIVE_UNIFORM_INDICES
GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER
GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER

一旦有了统一变量块索引,就可以调用glUniformBlockBinding,将该索引和程序中的统一变量块绑定点关联,索引不能超过GL_MAX_UNIFORM_BUFFER_BINDINGS的值。最后,可以用glBindBufferRange或者glBindBufferBase将统一变量缓冲区对象绑定到GL_UNIFORM_BUFFER目标和程序中的统一变量块绑定点。

void glUniformBlockBinding(GLuint program,GLuint uniformBlockIndex,GLuint uniformBlockBinding);
void glBindBufferRange(GLenum target,GLuint index,GLuint buffer,GLintptr offset,GLsizeipt rsize);
void glBindBufferBase(GLenum target,GLuint index,GLuint buffer);

上面函数的target如下,需要注意的是不同类型的target其offset是不同的,可以用glGetXxx查询其限制。

GL_ATOMIC_COUNTER_BUFFER // from GL ES 3.1 (4)
GL_SHADER_STORAGE_BUFFER // from GL ES 3.1 (GL_SHADER_STORAGE_BUFFER_OFFSET_ALIGNMENT)
GL_TRANSFORM_FEEDBACK_BUFFER // (4)
GL_UNIFORM_BUFFER // (GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT)

下面是示例代码:

GLuint blockId, bufferId;
GLint blockSize;
GLuint bindingPoint = 1;
GLfloat lightData[] =
{// lightDirection (padded to vec4 based on std140 rule)1.0f, 0.0f, 0.0f, 0.0f,// lightPosition0.0f, 0.0f, 0.0f, 1.0f
}// retrive the uniform block index
blockId = glGetUniformBlockIndex(program, "LightBlock");
// associate the uniform block index with a binding point
glUniformBlockBinding(program, blockId, bindingPoint);
// get the size of lightData
// alternatively we can calculate it using sizeof(lightData) in this example
glGetAcitveUniformBlockiv(program, blockId, GL_UNIFORM_BLOCK_DATA_SIZE, &blockSize);
// create and fill a buffer object
glGenBuffers(1, &bufferId);
glBindBuffer(GL_UNIFORM_BUFFER, bufferId);
glBufferData(GL_UNIFORM_BUFFER, blockSize, lightData, GL_DYNAMIC_DRAW);
// bind the buffer object to the uniform block bindint point
glBindBufferBase(GL_UNIFORM_BUFFER, bindingPoint, buffer);

12、属性查询

上面介绍了uniform变量的查询和设置方法,我们还需要使用程序对象设置顶点属性,对顶点属性的查询和统一变量查询非常相似,可以用GL_ACTIVE_ATTRIBUTES查询找到活动属性列表,GL_ACTIVE_ATTRIBUTE_MAX_LENGTH查询活动属性名字的最大长度,可以用如下glGetActiveAttrib等函数找到某个属性的特性、设置顶点数组以加载顶点属性值等。glGetXxx(GL_MAX_VERTEX_ATTRIBS)还可以查询最大的顶点属性。

void glGetActiveAttrib(GLuint program,GLuint index,GLsizei bufSize,GLsizei *length,GLint *size,GLenum *type,GLchar *name);

13、着色程序二进制码

着色程序二进制码是完全编译和链接的着色程序的二进制表示,可以保存到文件供以后使用,避免了在线编译的代价,也不用在实现中分发着色器源代码,涉及如下两个函数:

void glGetProgramBinary(GLuint program,GLsizei bufsize,GLsizei *length,GLenum *binaryFormat,void *binary);
void glProgramBinary(GLuint program,GLenum binaryFormat,const void *binary,GLsizei length);

glGetProgramBinary用于获取程序二进制码,program为程序对象句柄,bufsize为可以写入binary的最大字节数,不能小于GL_PROGRAM_BINARY_LENGTH(用glGetProgramiv获取),否则产生错误GL_INVALID_OPERATION,length为二进制数据的字节数,可以为NULL,binaryFormat为供应商专用二进制格式标志,binary为着色器编译器生成的二进制数据指针。glProgramBinary用于将程序二进码读回OpenGL ES实现,program为程序对象句柄,binaryFormat为供应商专用二进制格式标志,binary为着色器编译器生成的二进制数据指针,length为二进制数据的字节数,失败时可能的错误为GL_INVALID_OPERATION、GL_INVALID_ENUM,相关函数涉及glGetXxx的参数GL_NUM_PROGRAM_BINARY_FORMATS和GL_PROGRAM_BINARY_FORMATS。

【OpenGL ES】着色器Shader与程序Program相关推荐

  1. OpenGL ES着色器语言之语句和结构体(官方文档第六章)内建变量(官方文档第七、八章)...

    OpenGL ES着色器语言之语句和结构体(官方文档第六章) OpenGL ES着色器语言的程序块基本构成如下: 语句和声明 函数定义 选择(if-else) 迭代(for, while, do-wh ...

  2. OpenGL ES着色器语言之变量和数据类型

    所有变量和函数在使用前必须声明.变量和函数名是标识符. 没有默认类型,所有变量和函数声明必须包含一个声明类型以及可选的修饰符.变量在声明的时候首先要标明类型,后边可以跟多个变量,之间用逗号隔开.很多情 ...

  3. OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章)

    OpenGL ES着色器语言之变量和数据类型(二)(官方文档第四章) 4.5精度和精度修饰符 4.5.1范围和精度 用于存储和展示浮点数.整数变量的范围和精度依赖于数值的源(varying,unifo ...

  4. OpenGL ES着色器语言(GLSL ES)规范 ——下篇

    文章目录 前言 分支和循环 if.if-else for continue.break.discard 着色器内置变量 函数 函数定义 规范声明 webgl内置函数 存储限定字 const attri ...

  5. OpenGL:着色器shader

    参考资料:https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/ 着色器基本描述 首先看一个着色器 #version ...

  6. iOS OpenGl ES着色器

    首先创建两个文件分别为simple.frag和simple.vert simple.frag内容 const char* SimpleFragmentShader = STRINGIFY(varyin ...

  7. opengl着色器shader介绍

    1. Shader  Shader其实就是一段执行在GPU上的程序,此程序使用OpenGL ES SL语言来编写.它是一个描述顶点或像素特性的简单程序.在opengles中常用的shader有两种:v ...

  8. OpenGL播放yuv数据流(着色器SHADER)-IOS(一)

    OpenGL播放yuv数据流(着色器SHADER)-IOS(一) 和windows平台的类似,只是用object-c编写的,着色器语言shader,rgb转yuv有些不同,具体看代码注释. //.h ...

  9. 【我的OpenGL学习进阶之旅】着色器编译器和程序二进制码

    目录 一.着色器编译器 二.程序二进制码 2.1 glGetProgramBinary 2.2 glProgramBinary 一.着色器编译器 当你要求OpenGL ES 编译和链接着色器的时候,光 ...

最新文章

  1. web前端培训:CSS中单行文本溢出显示省略号的方法
  2. http请求响应的组成部分的介绍 用cherome查看请求响应内容 curl命令行的使用
  3. 【StatLearn】统计学习中knn算法实验(2)
  4. 智联招聘爬虫源码分析(一)
  5. USB自定义HID设备实现-STM32
  6. 树莓派 mysql集群_多树莓派集群服务器
  7. sqlite3_colum
  8. 这 3 种 DDD 分层架构的模式,你掌握了么?
  9. Redis 优势以及性能问题
  10. 让你的博文自动带上缩址,方便发到微博客上
  11. Java基础:Util包下常用的数据结构介绍
  12. 为什么深度学习有效?(why deep learning works)
  13. java爬虫 教程_Java爬虫其实也很简单,教你实用的入门级爬虫
  14. 企业大数据规划建设方案(PPT)
  15. 汉字、图形,Zebra打印机完全解决方案
  16. Idea标记(或书签)功能
  17. c语言随机数 抛硬币,C语言 抛硬币的问题
  18. 中学计算机课都学什么时候开始,初一的学习课程都有哪些 都学什么科目
  19. 记录--嵌入式设备生成二维码
  20. TIME_WAIT和CLOSE_WAIT区别

热门文章

  1. 京东撸货轻松月入十万,做到这几点,你也可以!
  2. CS224d lecture 14札记
  3. LeetCode日拱一卒
  4. Java开发面试题目,熬夜整理Java面试笔试题
  5. effective C++条款四十三解读
  6. 旋转体表面积公式推导及证明错误
  7. HEVC代码记录(删除)
  8. 计算机初级培训教学大纲,计算机初级培训教学大纲(范文).doc
  9. 运维告诉我CPU飙升300%,为什么我的程序上线就奔溃了
  10. 7-3 学习打卡(11.28)