深入探讨OpenGLES:自定义顶点与片元着色器的编译与连接技巧(第二部分)

更新:11-12 名人轶事 我要投稿 纠错 投诉

今天给各位分享深入探讨OpenGLES:自定义顶点与片元着色器的编译与连接技巧(第二部分)的知识,其中也会对进行解释,如果能碰巧解决你现在面临的问题,别忘了关注本站,现在开始吧!

具体思路

二.编译链接前,我们先来了解一些基本的OpenGLES的函数参数的说明

2.1编译/链接流程说明

1) Shader和程序对象

2)创建并编译着色器

3)创建并链接程序

4) 获取和设置统一变量

5) 获取和设置属性

6)Shader编译器和程序二进制代码

使用着色器进行渲染需要创建2 个基本对象: 着色器对象和程序对象。获取链接的着色器对象的一般过程包括6个步骤:

1.创建顶点着色器对象和段着色器对象

2. 将源代码链接到每个着色器对象

3.编译shader对象

4. 创建程序对象

5. 将编译好的着色器对象连接到程序对象

6. 链接器对象

2.2函数及参数说明

创建着色器:GLuint glCreateShader(GLenum类型)参数样式一般使用GL_VERTEX_SHADER(顶点着色器)或GL_FRAGMENT_SHADER(片段着色器)返回着色器句柄

删除着色器:glDeleteShader(GLuint着色器)参数:设置刚刚创建的顶点/片段句柄

void glShaderSource(GLuint 着色器,GLSizei 计数,const GLChar * const *string,const GLint

*length) 将shader源码附加到shader对象上(上一篇文章我写了两个顶点/片段着色器.vsh和.fsh文件,这里需要将写好的源码附加到shader上(设备上)参数说明:

1)参数1:shader,要编译的shader对象*shader

2)参数2:numOfStrings,传递的源代码字符串数量为1。特别说明:该参数大多数情况下为1。

3)参数3:字符串,着色器程序的源代码(真正的着色器程序源代码)

4) lenOfStrings, length, 一个数组,其中包含每个字符串的长度,或者NULL,表示该字符串以NULL 结尾

对应的程序代码:

-(void)compileShader:(GLuint*)shadertype:(GLenum)typefile:(NSString*)file{

//1.读取文件路径字符串,并根据传入的顶点/片段着色器源代码地址将其转换为C语言字符串。

NSString* 内容=[NSString stringWithContentsOfFile:文件编码:NSUTF8StringEncoding error:nil];

const GLchar* 源=(GLchar*)[内容UTF8String];

//2.创建着色器(基于类型)

*着色器=glCreateShader(类型);

glShaderSource(*着色器,1,源,NULL);

//4.将shader源代码编译为目标代码

glCompileShader(*着色器);

}

OpenGLES无法像iOS那样设置断电并输出信息。如果想查看刚刚创建编译的着色器,可以通过:void glGetShaderiv(GLuint Shader, GLenum pname, GLint *params) 查看链接状态。我将在下面详细解释如何执行此操作。使用void glGetShaderInfolog(GLuint Shader, GLSizei maxLength, GLSizei *length, GLChar *infoLog) 获取输出日志。具体参数如下:

(1) void glGetShaderiv(GLuint 着色器, GLenum pname, GLint *params);

着色器——需要编译的着色器对象的句柄

pname—获取的信息参数可以是GL_COMPILE_STATUS/GL_DELETE_STATUS/GL_INFO_LOG_LENGTH/GL_SHADER_SOURCE_LENGTH/GL_SHADER_TYPE

params——指向查询结果的整数存储位置的指针

(2) void glGetShaderInfolog(GLuint 着色器, GLSizei maxLength, GLSizei *length, GLChar *infoLog);

Shader—需要获取信息日志的Shader对象的句柄

maxLength——保存信息日志的缓存区大小

length——写入的信息日志的长度(减去空终止符);如果不需要知道长度,该参数可以为Null

infoLog——指向保存信息日志的字符缓冲区的指针

2.3创建与链接程序

(1) 创建程序

GLUint glCreateProgram() 描述: 创建程序对象并返回值: 返回执行新程序对象的句柄。同样,只要创建了,就会为创建的程序删除void glDeleteProgram(GLuint program)参数。

void glAttachShader( GLuint program , GLuint Shader ) 描述: 将刚刚创建的顶点/片段着色器附加到创建的程序中。当然,如果连接了,就会断开连接。 void glDetachShader(GLuint 程序);向创建的程序发送指令,断开附件

(2) 链接器

glLinkProgram(GLuint program) 参数是刚刚创建的程序

检查链接状态:void glGetProgramiv (GLuint program,GLenum pname, GLint *params)

参数说明:program:需要获取该信息的程序对象句柄

pname:参数获取信息,可以是:

GL_ACTIVE_ATTRIBUTES

GL_ACTIVE_ATTRIBUTES_MAX_LENGTH

GL_ACTIVE_UNIFORM_BLOCK

GL_ACTIVE_UNIFORM_BLOCK_MAX_LENGTH

GL_ACTIVE_UNIFROMS

GL_ACTIVE_UNIFORM_MAX_LENGTH

GL_ATTACHED_SHADERS

GL_DELETE_STATUS

GL_INFO_LOG_LENGTH

GL_LINK_STATUS

GL_PROGRAM_BINARY_RETRIEVABLE_HINT

GL_TRANSFORM_FEEDBACK_BUFFER_MODE

GL_TRANSFORM_FEEDBACK_VARYINGS

GL_TRANSFORM_FEEDBACK_VARYING_MAX_LENGTH

GL_VALIDATE_STATUS

params: 指向查询结果整数存储位置的指针

对应程序中的代码为:

glLinkProgram(self.myPrograme);

GLintlink 状态;

//获取链接状态

glGetProgramiv(self.myPrograme,GL_LINK_STATUS, linkStatus);

if(linkStatus==GL_FALSE) {

GLchar 消息[512];注意:因为我们不知道错误信息的长度,所以在设置数组时可以尽量让它尽可能大。第512章可以修改。

glGetProgramInfoLog(self.myPrograme,sizeof(消息),0,消息[0]);

NSString*messageString=[NSStringstringWithUTF8String:message];

NSLog(@"程序链接错误:%@",messageString);

返回;

}

(3)使用程序

void glUseProgram(GLuint program) 说明:创建并链接后,使用程序

三.FrameBuffffer (帧缓冲区)和RenderBuffffer(渲染缓冲区)说明以及他两的关系

首先我们看一张图:

FrameBuffffer Objects、RenderBuffffer Objects和Textures的解释来自苹果官网:

renderbuffffer 对象是应用程序分配的2D 图像缓冲区。 renderbuffffer 可用于分配和存储颜色、深度或模板值。它还可以用作帧缓冲中的颜色、深度或模板附件。渲染缓冲区是由类似屏幕的窗口系统提供的可绘制表面。例如,pBuffer。一个renderbuffffer,那么它就不能像GL纹理那样直接使用。

一个frameBuffffer对象(通常称为FBO)。是收集颜色、深度和模板缓冲区的连接点。描述属性的状态,例如颜色、深度以及模板缓冲区的大小和格式,都与FBO(帧缓冲区对象)相关联。并且纹理名称和renderBuffffer对象也与FBO相关联。各种2D 图形可以附加到帧缓冲对象的颜色附加点。它们包含存储在渲染缓冲区对象、2D 纹理或立方体贴图中的颜色值。或者3D 纹理中的mip 级2D 切片。同样,包含当前深度值的各种2D 图形可以附加到FBO 的深度附加点。唯一可以附加到FBO 模板附加点的二维图像是存储模板值的渲染缓冲对象。

四.设置RenderBufferFrameBuffer

(一)设置RenderBuffer

1. 定义缓存ID

GLuint 缓冲区;

2、申请缓存区标志

glGenRenderbuffers(1, 缓冲区);

3. 使用属性保存

self.myColorRenderBuffer=缓冲区;

4.将标识符绑定到GL_RENDERBUFFER

glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);

5.Context将drawable对象的CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象

[来自Drawable:self.myEagLayer的self.myContext renderbufferStorage:GL_RENDERBUFFER];

(二)设置FrameBuffer

1.定义缓存ID

GLuint 缓冲区;

2、申请缓存区标志

//glGenRenderbuffers(1, 缓冲区);

//glGenFramebuffers(1, 缓冲区);

glGenBuffers(1, buffer);//也可以指定对应的缓冲区标志,如上面两行

3.

self.myColorFrameBuffer=缓冲区;

4.

glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);

/*生成帧缓冲区后,需要将renderbuffer绑定到帧缓冲区。

调用glFramebufferRenderbuffer函数绑定对应的附着点,以便后续的绘制工作。

*/

5、通过glFramebufferRenderbuffer函数将渲染缓冲区myColorRenderBuffer绑定到GL_COLOR_ATTACHMENT0。

glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);

注意:这里不会将Context与帧缓冲区联系起来,因为帧缓冲区的控制是由GPU控制,当我们用于图像图形显示的时候,是从渲染缓冲区拿的,我们只需要开辟帧缓冲区,把它和渲染缓冲区一起绑定到GL_COLOR_ATTACHMENT0就行了,表示帧缓冲区和渲染缓冲区对应的关系,可以看上面苹果爸爸的解释,你会看到最后一行代码和设置渲染缓冲区的是不一样的,渲染缓冲区是把Context将可绘制对象drawable object"s CAEAGLLayer的存储绑定到OpenGL ES renderBuffer对象

五.使用固GLKView定着色器和自定义着色器的函数使用不同点(开辟和绑定我们就不说了,代码一样,直接讲不通的地方)加载纹理的不同点

1.设置顶点坐标的不同点

在GLKView固定着色器里面,我们设置顶点坐标数据:

glEnableVertexAttribArray(GLKVertexAttribPosition);

glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 0);

在自定义顶点着色器中,我们将顶点坐标数据设置为:

将顶点数据从CPU 内存复制到GPU

glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);

通过myPrograme将顶点数据传递到顶点着色器程序的位置

1.glGetAttribLocation,用于获取顶点属性的入口。

2.告诉OpenGL ES,通过glEnableVertexAttribArray,

3.最终的数据是通过glVertexAttribPointer传递的。

(1)注意:第二个参数字符串必须与输入变量:sharv.vsh中的位置一致

GLuint 位置=glGetAttribLocation(self.myPrograme, "位置");

(2).设置适当的格式从缓冲区读取数据

glEnableVertexAttribArray(位置);

(3).设置读取方式

参数1:index,顶点数据的索引

参数2:size,每个顶点属性的分量个数,1、2、3、4。默认初始值为4。

参数3:type,数据中各个组成部分的类型。常用的有GL_FLOAT、GL_BYTE、GL_SHORT。默认初始值为GL_FLOAT

参数4:归一化,定点数据值是归一化还是直接转换为固定值。 (GL_FALSE)

参数5:stride,连续顶点属性之间的偏移量,默认为0;

参数6:指定一个指向数组中第一个顶点属性的第一个分量的指针。默认值为0

glVertexAttribPointer(位置,3,GL_FLOAT,GL_FALSE,sizeof(GLfloat) *5,NULL);

2.设置纹理坐标的不同点

在GLKView固定着色器中,我们设置纹理坐标数据:

纹理坐标数据

glEnableVertexAttribArray(GLKVertexAttribTexCoord0);

glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, (GLfloat *)NULL + 3);

在自定义顶点着色器中,我们将纹理坐标数据设置为:

(1).glGetAttribLocation,用于获取顶点属性的入口。

//注意:第二个参数字符串必须与sharv.vsh中的输入变量:textCooperative一致

GLuint textCoor=glGetAttribLocation(self.myPrograme, "textCooperative");

(2).设置适当的格式从缓冲区读取数据

glEnableVertexAttribArray(textCoor);

(3).设置读取方式(见参数说明)

glVertexAttribPointer(textCoor,2,GL_FLOAT,GL_FALSE,sizeof(GLfloat)*5, (float*)NULL+3);

3.加载纹理的不同点

在GLKView固定着色器中,我们设置加载纹理:

1.获取纹理图像路径

NSString *filePath=[[NSBundle mainBundle]pathForResource:@"timg" ofType:@"jpg"];

2.设置纹理参数

纹理坐标原点是左下角,但图片显示原点应该是左上角。

NSDictionary *选项=[NSDictionary DictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];

GLKTextureInfo *textureInfo=[GLKTextureLoader textureWithContentsOfFile:filePath options:options error:nil];

3.使用Apple的GLKit提供的GLKBaseEffect来完成shader工作(顶点/片段)

cEffect=[[GLKBaseEffect alloc]init];

cEffect.texture2d0.enabled=GL_TRUE;

cEffect.texture2d0.name=textureInfo.name;

在自定义着色器里面,我们设置加载纹理:

//从图像加载纹理

- (GLuint)setupTexture:(NSString*)文件名{

//1.将UIImage 转换为CGImageRef

计算机图形图像处理

mageRef spriteImage = [UIImage imageNamed:fileName].CGImage;     //判断图片是否获取成功     if(!spriteImage) {         NSLog(@"Failed to load image %@", fileName);         exit(1);     }     //2、读取图片的大小,宽和高     size_twidth =CGImageGetWidth(spriteImage);     size_theight =CGImageGetHeight(spriteImage);     //3.获取图片字节数 宽*高*4(RGBA)     GLubyte* spriteData = (GLubyte*)calloc(width * height *4,sizeof(GLubyte));     //4.创建上下文     /*      参数1:data,指向要渲染的绘制图像的内存地址      参数2:width,bitmap的宽度,单位为像素      参数3:height,bitmap的高度,单位为像素      参数4:bitPerComponent,内存中像素的每个组件的位数,比如32位RGBA,就设置为8      参数5:bytesPerRow,bitmap的没一行的内存所占的比特数      参数6:colorSpace,bitmap上使用的颜色空间  kCGImageAlphaPremultipliedLast:RGBA      */     CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);     //5、在CGContextRef上-->将图片绘制出来     /*      CGContextDrawImage 使用的是Core Graphics框架,坐标系与UIKit 不一样。UIKit框架的原点在屏幕的左上角,Core Graphics框架的原点在屏幕的左下角。      CGContextDrawImage       参数1:绘图上下文      参数2:rect坐标      参数3:绘制的图片      */     CGRectrect =CGRectMake(0,0, width, height);     //6.使用默认方式绘制     CGContextDrawImage(spriteContext, rect, spriteImage);     //7、画图完毕就释放上下文     CGContextRelease(spriteContext);     //8、绑定纹理到默认的纹理ID(     glBindTexture(GL_TEXTURE_2D, 0);     //9.设置纹理属性     /*      参数1:纹理维度      参数2:线性过滤、为s,t坐标设置模式      参数3:wrapMode,环绕模式      */     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);     glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);     floatfw = width, fh = height;     //10.载入纹理2D数据     /*      参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D      参数2:加载的层次,一般设置为0      参数3:纹理的颜色值GL_RGBA      参数4:宽      参数5:高      参数6:border,边界宽度      参数7:format      参数8:type      参数9:纹理数据      */     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData);     //11.释放spriteData     free(spriteData);        return0; } 效果下一篇,我们讲纹理的翻转 最后附上demo:链接: https://pan.baidu.com/s/1-6RJ0toD6WUkoozX0Fu1Cw 提取码: fweb

关于深入探讨OpenGLES:自定义顶点与片元着色器的编译与连接技巧(第二部分)到此分享完毕,希望能帮助到您。

用户评论

素衣青丝

终于到了深度学习部分了!一直在想怎么应用于游戏开发。

    有8位网友表示赞同!

龙吟凤

这段时间接触OpenGL就卡在了着色器这块,希望能学到一些实际的例子。

    有11位网友表示赞同!

久爱不厌

之前做过简单点的着色器代码,但是没深入了解过编译和链接的过程,这个应该很有帮助。

    有5位网友表示赞同!

岁岁年年

想学习自定义顶点/片元着色器主要是为了做一些特殊的渲染效果。

    有5位网友表示赞同!

命里缺他

感觉这篇文章终于揭开了OpenGL的奥秘!可以让我更好地理解游戏开发了。

    有15位网友表示赞同!

青山暮雪

最近在学3D建模,不知道如何应用着色器,看来这个文章正好能解决我的疑惑。

    有10位网友表示赞同!

﹏櫻之舞﹏

自定义着色器才能实现更加个性化的渲染效果,期待学习。

    有18位网友表示赞同!

执笔画眉

OpenGLES的文章讲解得非常详细,应该能一步步学会编写自定义着色器。

    有13位网友表示赞同!

请在乎我1秒

想做一些手机游戏,需要了解OpenGL和着色器的知识。

    有14位网友表示赞同!

傲世九天

这篇教程看起来很有用,可以让我更好地理解OpenGL的原理。

    有9位网友表示赞同!

凉话刺骨

学习OpenGL的难度其实挺大的,希望这篇文章能帮我入门!

    有20位网友表示赞同!

oО清风挽发oО

之前看过一些OPENGL的入门教程,但感觉还不够深入,这个文章希望能解答我的问题。

    有7位网友表示赞同!

我绝版了i

OpenGLES的应用场景很多,掌握着色器的知识很重要。

    有12位网友表示赞同!

疲倦了

我想要学习OpenGL开发移动游戏,这篇文章应该很有用!

    有6位网友表示赞同!

为爱放弃

期待看到一些实际代码示例,更好地理解如何编译和连接着色器。

    有19位网友表示赞同!

病房

我一直对GPU渲染感兴趣,这个文章能帮助我更深入地了解相关知识!

    有6位网友表示赞同!

枫无痕

希望学习完这篇教程能够自己写出一些有趣的着色器效果。

    有10位网友表示赞同!

揉乱头发

想制作3D动画,需要对OpenGL和着色器有一定的理解。

    有13位网友表示赞同!

tina

学习OpenGLES可以让游戏开发更加灵活多彩,期待学习。

    有7位网友表示赞同!

【深入探讨OpenGLES:自定义顶点与片元着色器的编译与连接技巧(第二部分)】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:农村招赘女婿:揭秘那些姑娘们的未来生活 下一篇:深入了解VIPJR牛津课程:真实评价与体验分享