重新自学学习openGL 之顶点学习

顶点数组对象: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];
}

效果图


重新自学学习openGL 之顶点学习_第1张图片

绘制两个顶点的代码

#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];
}

重新自学学习openGL 之顶点学习_第2张图片
两个顶点

三个顶点代码

#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];
}

重新自学学习openGL 之顶点学习_第3张图片
三个顶点绘制的是平面,z是0

四个顶点代码


#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];
}
重新自学学习openGL 之顶点学习_第4张图片
四个顶点值,第四个代码缩放

顶点绘制方式

多边形

我们知道,点可以连接成线,线可以连接成面. 面组合成立方体.下面我们探讨如何从点到立方体的

多变形是由线段构成的单闭合环,其中线段是由他们的顶点位置的顶点指定的。一般情况下,在绘制多变形时,有这样几种形态:

  • 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

GL_LINE_STRIP

公式
T=[n-1,n] ; n=1,2,3.....

GL_LINE_LOOP

假设顶点是v0,v1,v2,v3
他的绘制结果是v0-v1,v1-v2,v2-v3,v3-v0


GL_LINE_LOOP

公式
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


GL_TRIANGLES

公式是
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......

重新自学学习openGL 之顶点学习_第5张图片

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........

重新自学学习openGL 之顶点学习_第6张图片

代码测试

公用类代码


-(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];
}

结果


重新自学学习openGL 之顶点学习_第7张图片

点太分散了.看不出来效果

线测试代码
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_LINES startVertexIndex:0 numberOfVertices:VertexNum];
}

结果


重新自学学习openGL 之顶点学习_第8张图片
GL_LINES
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_LINE_LOOP startVertexIndex:0 numberOfVertices:VertexNum];
    
}
重新自学学习openGL 之顶点学习_第9张图片
GL_LINE_LOOP
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_LINE_STRIP startVertexIndex:0 numberOfVertices:VertexNum];
   闭合图形

重新自学学习openGL 之顶点学习_第10张图片
GL_LINE_STRIP

不闭合

面测试代码
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:VertexNum];
    
}
重新自学学习openGL 之顶点学习_第11张图片
GL_TRIANGLES
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_TRIANGLE_STRIP startVertexIndex:0 numberOfVertices:VertexNum];
    
}
重新自学学习openGL 之顶点学习_第12张图片
GL_TRIANGLE_STRIP
-(void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    [super glkView:view drawInRect:rect];
    [self.vertex drawVertexWithMode:GL_TRIANGLE_FAN startVertexIndex:0 numberOfVertices:VertexNum];
    
}
重新自学学习openGL 之顶点学习_第13张图片
GL_TRIANGLE_FAN

从上面看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). 索引就是指定四个顶点需要绘制的三角形下标

代码演示

就是在屏幕中间绘制一个绘制矩形


重新自学学习openGL 之顶点学习_第14张图片

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 获取 positioncolor的位置的时候,结果是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个属性 .默认是关闭的.结构图如下


重新自学学习openGL 之顶点学习_第15张图片
image.png

glGetUniformLocation

该函数只能在shader 编译连接之后才能获取到值,否则获取的值是无效的.

缓存使用

我们可以通过glGenBuffers 方法生成很多缓存.缓存之间的切换是通过glBindBuffer 来实现的

这可能对高手来说太简单了,但是对于新手来说可能有点用. 假设我们生成两块缓存 0,1, 0号缓存是用来存储顶点, 1号缓存是用来存储索引的. 当我们需要对缓存0号缓存进行操作的时候,我们必须调用下glBindBuffer,代表我们切换到了0号缓存来,接着我们可以glEnableVertexAttribArray ,glVertexAttribPointer等方法来操作0号缓存,要是我们想操作1号缓存,那么必须也调用下glBindBuffer.

源码地址

你可能感兴趣的:(重新自学学习openGL 之顶点学习)