顶点数组对象:Vertex Array Object,VAO
顶点缓冲对象:Vertex Buffer Object,VBO
索引缓冲对象:Element Buffer Object,EBO或Index Buffer Object,IBO
本章我们主要介绍顶点的相关知识.并且根据顶点的相关知识做些小的demo
顶点是啥?
顶点就是坐标位置,不管你是画直线,三角形,正方体,球体,以及3D游戏人物等,都需要顶点来确定其形状。
顶点坐标创建
- 顶点的坐标数据类型是为GLfloat 类型,这个是OpenGL 要求的,4个字节长度
- 顶点可可以被定为为二维或者三维,这个看你的实际情况!
但是你要注意,所有的内部计算都是建立在三维数据的基础之上
,比如:你定义一个点(x,y) 是二维形式,OpenGL默认把它的z设置为0,看到这里你以为三维就是(x,y,z)的形式吗?不是的,OpenGL 是根据三维投影几何的齐次方程坐标进行操作的,因此在内部计算是都是用4个浮点坐标值表示(x,y,z,w) 如果w不等于0 那么这些坐标值就对应于与欧几里德三维点(x/w,y/w,z/w)。一般情况下w默认为1.0.
根据三维投影几何的齐次方程,我们知道,描述一个点需要四个值,因此顶点的坐标数据类型是用4个字节长度来表示,每一个字节长度代表齐次方程的一个参数
假设齐次方程的参数表示方式是(x,y.z,w) .但是有时候我们传入的坐标数据可能是少于4个长度的.因此 系统会给我们添加默认参数来补齐.
x,y,z 的默认值是0 ,w的默认值是1
不考虑点在shader中的加工处理的情况下
假设我们传入的数据宽度是 一个字节,那么代表的是只能绘制直线
假如我们传入的数据宽度是两个字节,那么可以绘制平面图形
假如我们传入的数据宽度是三个字节,那么可以绘制立体图形
假如我们传入的数据参数是四个字节,第四个字节就是代表这给绘制的三维图形进行缩放操作.
代码测试
封装库代码没有贴出来,可以到源码地址去下载或者这篇博客查找
公用代码
#import
#import "OpenGLUtilsHeader.h"
NS_ASSUME_NONNULL_BEGIN
@interface VertexDimViewController : GLKViewController
@property (nonatomic ,strong) EAGLContext * eagcontext;
@property (nonatomic ,assign) GLuint program;
@property (nonatomic ,strong) Shader * shader ;
@property (nonatomic ,assign) GLint vertexcolor;
@property (nonatomic ,strong) Vertex * vertex ;
@end
NS_ASSUME_NONNULL_END
#import "VertexDimViewController.h"
@interface VertexDimViewController ()
@end
@implementation VertexDimViewController
-(void)createEagContext{
self.eagcontext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
[EAGLContext setCurrentContext:self.eagcontext];
}
-(void)configure{
GLKView *view = (GLKView*)self.view;
view.context = self.eagcontext;
view.drawableDepthFormat = GLKViewDrawableDepthFormat24;
}
-(void)createShader{
self.shader = [Shader new];
[self.shader compileLinkSuccessShaderName:@"Shader" completeBlock:^(GLuint program) {
glBindAttribLocation(program, GLKVertexAttribPosition, "position"); // 0代表枚举位置
_vertexcolor = glGetUniformLocation(program, "color");
}];
}
-(void)loadVertex{
}
-(void)viewDidLoad{
[super viewDidLoad];
[self createEagContext];
[self configure];
[self createShader];
[self loadVertex];
}
@end
绘制一个顶点代码
#define VertexNum 3
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:1];
GLfloat vertex[1];
vertex[0]=1.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=0;
[self.vertex setVertex:vertex index:1];
vertex[0]=-1.0;
[self.vertex setVertex:vertex index:2];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:1 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.vertex drawVertexWithMode:GL_LINE_LOOP startVertexIndex:0 numberOfVertices:VertexNum];
}
效果图
绘制两个顶点的代码
#define VertexNum 3
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:2];
GLfloat vertex[2];
vertex[0]=1.0;
vertex[1]=1.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=0;
vertex[1]=0;
[self.vertex setVertex:vertex index:1];
vertex[0]=-1.0;
vertex[1]=0.0;
[self.vertex setVertex:vertex index:2];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:2 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
}
三个顶点代码
#define VertexNum 3
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:3];
GLfloat vertex[3];
vertex[0]=1.0;
vertex[1]=1.0;
vertex[2]=0;
[self.vertex setVertex:vertex index:0];
vertex[0]=0;
vertex[1]=0;
[self.vertex setVertex:vertex index:1];
vertex[0]=-1.0;
vertex[1]=0.0;
[self.vertex setVertex:vertex index:2];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:3 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
}
四个顶点代码
#define VertexNum 3
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:4];
GLfloat vertex[4];
vertex[0]=1.0;
vertex[1]=1.0;
vertex[2]=0;
vertex[3]=2.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=0;
vertex[1]=0;
[self.vertex setVertex:vertex index:1];
vertex[0]=-1.0;
vertex[1]=0.0;
[self.vertex setVertex:vertex index:2];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:4 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
}
顶点绘制方式
多边形
我们知道,点可以连接成线,线可以连接成面. 面组合成立方体.下面我们探讨如何从点到立方体的
多变形是由线段构成的单闭合环,其中线段是由他们的顶点位置的顶点指定的。一般情况下,在绘制多变形时,有这样几种形态:
- 1.内部的像素将被填充
- 2.绘制外边的边框
- 3.只绘制点
其实可以这么理解,我们可以绘制最小的单位 点 . 我们也可以绘制点点连线,或者点点连线组合出来的封闭图形.
绘制面
我们知道点可以成线,线能成面,面才能成万物. 因此面的使用更广些,因此这里需要研究下面.
不管绘制平面和绘制立体图形,我们都是在面上进行操作的,任何屏幕都可以用三角形组成.因为三个点才能成面嘛
.
绘制模式
多边形的几种形态就对应下面的绘制模式
- GL_POINTS
- GL_LINES
- GL_LINE_LOOP
- GL_LINE_STRIP
- GL_TRIANGLES
- GL_TRIANGLE_STRIP
- GL_TRIANGLE_FAN
下面介绍具体介绍每个模式绘制的具体形式
GL_POINTS
给n个顶点的每一个都绘制一个点
GL_LINES
绘制一系列的非连接直线段
它的绘制过程是这样的:先绘制V0,V1一条直线, 然后绘制V2,V3,又是一条直线。如下图:
问:如果顶点的数据为奇数怎么办?
答: 最后一个顶点被忽略,就这么任性!
公式
T=[2n-1,2n] n=1,2,3.... ;
GL_LINE_STRIP
假设顶点v0,v1,v2,v3
他的绘制顺序是v0-v1,v1-v2,v2-v3
公式
T=[n-1,n] ; n=1,2,3.....
GL_LINE_LOOP
假设顶点是v0,v1,v2,v3
他的绘制结果是v0-v1,v1-v2,v2-v3,v3-v0
公式
T1= [n-1,n]
T2=[n,0];
T=T1+T2;n=1,2,3....
两部分组成,闭合
绘制方式
openGL 有两种绘制方式
voidglDrawArrays(GLenum mode, GLint first, GLsizei count);
voidglDrawElements(GLenum mode, GLsizei count, GLenum type,constGLvoid *indices);
GL_TRIANGLES
假设顶点v0,v1,v2,v3,v4,v5
绘制顺序是v0-v1-v2 ,v3-v4-v5
公式是
T=[3n-2,3n-1,3n]; n=1,2,3....
GL_TRIANGLE_STRIP
假设顶点v0,v1,v2,v3,v4,v5,v6
排列顺序v0-v1-v2 v2-v1-v3,v2-v3-v4,v4-v3-v5,v4-v5-v6
保证所有的三角形都是逆时针
公式
如果当前顶点是奇数:
组成三角形的顶点排列顺序:T = [n-1 n-2 n]. n=3,5.....
如果当前顶点是偶数:
组成三角形的顶点排列顺序:T = [n-2 n-1 n]. n=2.4......
GL_TRIANGLE_FAN
假设顶点v0,v1,v2,v3,v4,v5,v6
排列顺序v0-v1-v2 v0-v2-v3,v0-v4-v5,v0-v5-v6
所有的三角形都是从小到大排列
公式
是T = [0,n-1,n-2]. n=2,3,4........
代码测试
公用类代码
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:4];
GLfloat vertex[4];
vertex[0]=0;
vertex[1]=1.0;
vertex[2]=0;
vertex[3]=2.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=-1;
vertex[1]=0.3;
[self.vertex setVertex:vertex index:1];
vertex[0]=-1.0;
vertex[1]=-0.3;
[self.vertex setVertex:vertex index:2];
vertex[0]=0;
vertex[1]=-1.0;
[self.vertex setVertex:vertex index:3];
vertex[0]=1;
vertex[1]=-0.3;
[self.vertex setVertex:vertex index:4];
vertex[0]=1;
vertex[1]=0.3;
[self.vertex setVertex:vertex index:5];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:4 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
}
我们一共设置了六个点
点测试代码
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_POINTS startVertexIndex:0 numberOfVertices:VertexNum];
}
结果
点太分散了.看不出来效果
线测试代码
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_LINES startVertexIndex:0 numberOfVertices:VertexNum];
}
结果
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_LINE_LOOP startVertexIndex:0 numberOfVertices:VertexNum];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_LINE_STRIP startVertexIndex:0 numberOfVertices:VertexNum];
闭合图形
不闭合
面测试代码
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_TRIANGLE_STRIP startVertexIndex:0 numberOfVertices:VertexNum];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
[super glkView:view drawInRect:rect];
[self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:VertexNum];
}
从上面看GL_TRIANGLE_STRIP
是比较特殊的形式了.这里需要注意使用这种模式的规则
顶点绘制API
glDrawArrays (GLenum mode, GLint first, GLsizei count)
glDrawElements (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)
这是两种绘制方式,第一种没有索引,第二种是带有索引进行绘制.
带有索引绘制会减少内存中顶点的个数.
假设矩形的顶点个数有4个 v0 ,v1,v2,v3 那么我们以GL_TRIANGLES 绘制矩形.
使用glDrawArrays所需要的顶点个数是 v0,v1,v2, v0,v2,v3.六个.
使用glDrawElements 就需要四个 ,不过我们需要加入四个顶点的索引(0,1,2,0,2,3). 索引就是指定四个顶点需要绘制的三角形下标
代码演示
就是在屏幕中间绘制一个绘制矩形
glDrawArrays 方式
#define VertexNum 6
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:4];
GLfloat vertex[4];
vertex[0]=-1.0;
vertex[1]=-1.0;
vertex[2]=0;
vertex[3]=2.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=-1;
vertex[1]=1;
[self.vertex setVertex:vertex index:1];
vertex[0]=1;
vertex[1]=1;
[self.vertex setVertex:vertex index:2];
vertex[0]=-1.0;
vertex[1]=-1.0;
[self.vertex setVertex:vertex index:3];
vertex[0]=1;
vertex[1]=1;
[self.vertex setVertex:vertex index:4];
vertex[0]=1;
vertex[1]=-1;
[self.vertex setVertex:vertex index:5];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:4 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
}
glDrawElements 使用VAO 缓存
#define VertexNum 4
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:4];
GLfloat vertex[4];
vertex[0]=-1.0;
vertex[1]=-1.0;
vertex[2]=0;
vertex[3]=2.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=-1;
vertex[1]=1;
[self.vertex setVertex:vertex index:1];
vertex[0]=1;
vertex[1]=1;
[self.vertex setVertex:vertex index:2];
vertex[0]=1;
vertex[1]=-1;
[self.vertex setVertex:vertex index:3];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:4 attribOffset:0];
static const GLuint tfan1[2 * 3] =
{
0,1,2,
0,2,3,
};
self.element = [VertexElement new];
[self.element allocWithIndexNum:6 indexArr:tfan1];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
[self.element drawElementIndexWithMode:GL_TRIANGLES];
}
element对象实现
#import "VertexElement.h"
@interface VertexElement()
@property (nonatomic, assign) GLuint indexBuffer;
@property (nonatomic ,assign) GLuint *indexs;
@property (nonatomic ,assign) GLsizei count ;
@end
@implementation VertexElement
- (instancetype)init
{
self = [super init];
if (self) {
[self _customInit];
}
return self;
}
-(void)_customInit{
glGenBuffers(1, &_indexBuffer);
}
-(void)allocWithIndexNum:(GLsizei)count indexArr:(GLuint*)indexArr{
if (self.indexs) {
[self releaseIndexs];
}
self.count = count;
self.indexs =(GLuint*)malloc(count *sizeof(GLuint));
memset( self.indexs, 0, count *sizeof(GLuint));
for (int i =0; i
glDrawElements 不使用VAO 缓存
#define VertexNum 4
-(void)loadVertex{
self.vertex = [Vertex new];
[self.vertex allocVertexNum:VertexNum andEachVertexNum:4];
GLfloat vertex[4];
vertex[0]=-1.0;
vertex[1]=-1.0;
vertex[2]=0;
vertex[3]=2.0;
[self.vertex setVertex:vertex index:0];
vertex[0]=-1;
vertex[1]=1;
[self.vertex setVertex:vertex index:1];
vertex[0]=1;
vertex[1]=1;
[self.vertex setVertex:vertex index:2];
vertex[0]=1;
vertex[1]=-1;
[self.vertex setVertex:vertex index:3];
[self.vertex bindBufferWithUsage:GL_STATIC_DRAW];
[self.vertex enableVertexInVertexAttrib:GLKVertexAttribPosition numberOfCoordinates:4 attribOffset:0];
}
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
static NSInteger count = 0;
// 清除颜色缓冲区
glClearColor(1,1, 1, 1);
glClear(GL_COLOR_BUFFER_BIT);
count ++;
if (count > 50 ) {
count = 0;
// 根据颜色索引值,设置颜色数据,就是刚才我们从着色器程序中获取的颜色索引值
glUniform4f(self.vertexcolor, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, arc4random_uniform(255)/225.0, 1);
}
// 使用着色器程序
glUseProgram(self.shader.program);
// 绘制
static const GLuint tfan1[2 * 3] =
{
0,1,2,
0,2,3,
};
[VertexElement drawElementIndexWithMode:GL_TRIANGLES indexNum:6 indexArr:tfan1];
}
顶点属性
GLKVertexAttribPosition,
GLKVertexAttribNormal,
GLKVertexAttribColor,
GLKVertexAttribTexCoord0,
GLKVertexAttribTexCoord1
当我们使用系统定义的shader的时候,也就是苹果给我们封装的类GLKBaseEffect 的时候.系统已经给我们绑定好了属性的位置. (自定义的shader可以随意指定. 与上面的属性没啥关系)�
学习过程中出现的问题
glBindAttribLocation 和 glGetAttribLocation 获取的位置不一致
当我绑定att位置的时候
glBindAttribLocation(program, 0, "position"); // 0代表枚举位置
glBindAttribLocation(program, 1, "color"); // 0代表枚举位置
再用 glGetAttribLocation 获取 position
和 color
的位置的时候,结果是position
= 1 ,color
=0. 的错误
这是因为在编译shader的时候, 我是link shader 之后再绑定的 属性位置.这个时候glBindAttribLocation
就失效了.我们需要在link之前进行
绑定属性
原始代码
-(BOOL)compileLinkSuccessShaderName:(NSString *)shader completeBlock:(void(^)(GLuint program))completeBlock
{
NSURL *vertShaderURL, *fragShaderURL;
GLuint vertShader, fragShader;
GLuint luint =self.program;
vertShaderURL = [[NSBundle mainBundle] URLForResource:shader withExtension:@"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER URL:vertShaderURL]) {
NSLog(@"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderURL = [[NSBundle mainBundle] URLForResource:shader withExtension:@"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER URL:fragShaderURL]) {
NSLog(@"Failed to compile fragment shader");
return NO;
}
// Attach vertex shader to program.
glAttachShader(luint, vertShader);
// Attach fragment shader to program.
glAttachShader(luint, fragShader);
if (![self linkProgram:luint]) {
NSLog(@"Failed to link program: %d", luint);
if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (luint) {
glDeleteProgram(luint);
luint = 0;
}
return NO;
}
completeBlock(luint);
glUseProgram(luint);
return YES;
}
改正代码
-(BOOL)compileLinkSuccessShaderName:(NSString *)shader completeBlock:(void(^)(GLuint program))completeBlock
{
NSURL *vertShaderURL, *fragShaderURL;
GLuint vertShader, fragShader;
GLuint luint =self.program;
vertShaderURL = [[NSBundle mainBundle] URLForResource:shader withExtension:@"vsh"];
if (![self compileShader:&vertShader type:GL_VERTEX_SHADER URL:vertShaderURL]) {
NSLog(@"Failed to compile vertex shader");
return NO;
}
// Create and compile fragment shader.
fragShaderURL = [[NSBundle mainBundle] URLForResource:shader withExtension:@"fsh"];
if (![self compileShader:&fragShader type:GL_FRAGMENT_SHADER URL:fragShaderURL]) {
NSLog(@"Failed to compile fragment shader");
return NO;
}
// Attach vertex shader to program.
glAttachShader(luint, vertShader);
// Attach fragment shader to program.
glAttachShader(luint, fragShader);
completeBlock(luint);
if (![self linkProgram:luint]) {
NSLog(@"Failed to link program: %d", luint);
if (vertShader) {
glDeleteShader(vertShader);
vertShader = 0;
}
if (fragShader) {
glDeleteShader(fragShader);
fragShader = 0;
}
if (luint) {
glDeleteProgram(luint);
luint = 0;
}
return NO;
}
glUseProgram(luint);
return YES;
}
glEnableVertexAttribArray 和 glVertexAttribPointer
这两个函数是经常同时使用.本项目代码如下
glEnableVertexAttribArray(index);
glVertexAttribPointer(index, // Identifies the attribute to use
count, // number of coordinates for attribute
GL_FLOAT, // data is floating point
GL_FALSE, // no fixed point scaling
self.getVertexWidth , // total num bytes stored per vertex
NULL + offset);
这里容易进入的误区是glEnableVertexAttribArray 和 glVertexAttribPointer 的第一个参数含义相同.
这是打错特错的.我就因为这个理解导致我找bug 找了一下午.
知识补充:每个vbo (顶点缓存对象)可以有16个属性 .默认是关闭的.结构图如下
glGetUniformLocation
该函数只能在shader 编译连接之后才能获取到值,否则获取的值是无效的.
缓存使用
我们可以通过glGenBuffers 方法生成很多缓存.缓存之间的切换是通过glBindBuffer 来实现的
这可能对高手来说太简单了,但是对于新手来说可能有点用. 假设我们生成两块缓存 0,1, 0号缓存是用来存储顶点, 1号缓存是用来存储索引的. 当我们需要对缓存0号缓存进行操作的时候,我们必须调用下
glBindBuffer
,代表我们切换到了0号缓存来,接着我们可以glEnableVertexAttribArray
,glVertexAttribPointer
等方法来操作0号缓存,要是我们想操作1号缓存,那么必须也调用下glBindBuffer
.
源码地址