索引绘图案例:GLSL&GLKit

361 阅读3分钟

GLSL

着色器源码

// shader.vsh 
attribute vec4 position;		// 顶点坐标
attribute vec4 positionColor;	// 顶点颜色
uniform mat4 projectionMatrix;	// 投影矩阵
uniform mat4 modelViewMatrix;	// 模型视图矩阵
varying lowp vec4 varyColor;	// 桥接的顶点颜色

void main()
{
    varyColor = positionColor;
    vec4 vPos;
    vPos = projectionMatrix * modelViewMatrix * position;
    gl_Position = vPos;
}

// shader.fsh
varying lowp vec4 varyColor;

void main()
{
    gl_FragColor = varyColor;
}

这里我们只会把顶点颜色传递给片元着色器,系统则会自动将顶点之间的颜色进行计算和渲染.

GLSL索引绘图的案例,与EGL&OpenGL着色语言及案例中前五步骤一模一样,不同的在于第六步中的6.3 顶点、纹理数据6.4 开始绘制、显示,

顶点、纹理数据

// 顶点坐标、顶点颜色
GLfloat attrArr[] =
{
    -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, //左上0
    0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, //右上1
    -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f, //左下2
    
    0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f, //右下3
    0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, //顶点4
};

// 索引数组
GLuint indices[] =
{
    0, 3, 2,
    0, 1, 3,
    0, 2, 4,
    0, 4, 1,
    2, 3, 4,
    1, 4, 3,
};


GLuint vBuffer;
glGenBuffers(1, &vBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
// 传递顶点数据
GLuint position = glGetAttribLocation(self.myProgram, "position");
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);

// 传递顶点颜色
GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
glEnableVertexAttribArray(positionColor);
glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (GLfloat *)NULL+3);

GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");


// 投影矩阵
float width = self.frame.size.width;
float height = self.frame.size.height;
KSMatrix4 _projectionMatrix;
ksMatrixLoadIdentity(&_projectionMatrix);
float aspect = width / height;
ksPerspective(&_projectionMatrix, 30.0f, aspect, 5.0f, 20.0f);
// 传递投影矩阵数据
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);

// 模型视图矩阵
KSMatrix4 _modelViewMatrix;
ksMatrixLoadIdentity(&_modelViewMatrix);
ksTranslate(&_modelViewMatrix, 0, 0, -10.0f);

KSMatrix4 _rotateMatrix;
ksMatrixLoadIdentity(&_rotateMatrix);

//XYZ
ksRotate(&_rotateMatrix, xDegree, 1.0, 0.0, 0.0);
ksRotate(&_rotateMatrix, yDegree, 0.0, 1.0, 0.0);
ksRotate(&_rotateMatrix, zDegree, 0.0, 0.0, 1.0);
// 矩阵相乘
ksMatrixMultiply(&_modelViewMatrix, &_rotateMatrix, &_modelViewMatrix);
// 传递模型矩阵数据
glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat*)&_modelViewMatrix.m[0][0]);

// 正背面剔除
glEnable(GL_CULL_FACE);

开始绘制、显示

glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices);

GLKit

因为GLKit已经帮我们省略了很多工作,这里只需要两个步骤

  • 初始化上下文、GLKview等准备工作
  • 渲染

初始化

// 初始化设置
- (void)setupContext{
    self.mContext = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
    GLKView *glkView = (GLKView *)self.view;
    glkView.context = self.mContext;
    // 设置颜色格式
    glkView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    // 设置深度测试精度
    glkView.drawableDepthFormat = GLKViewDrawableDepthFormat24;
    [EAGLContext setCurrentContext:self.mContext];
    glEnable(GL_DEPTH_TEST);
}

渲染


- (void)render{
    
    GLfloat attrArr[] =
    {
        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, //左上
        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, //右上
        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f, //左下
        
        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f, //右下
        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, //顶点
    };
    
    //2.绘图索引
    GLuint indices[] =
    {
        0, 3, 2,
        0, 1, 3,
        0, 2, 4,
        0, 4, 1,
        2, 3, 4,
        1, 4, 3,
    };
    
    // 将顶点数据放入数组缓冲区
    GLuint vBuffer;
    glGenBuffers(1, &vBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, vBuffer);
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_STATIC_DRAW);
    
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
    
    glEnableVertexAttribArray(GLKVertexAttribColor);
    glVertexAttribPointer(GLKVertexAttribColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (float *)NULL+3);
    
    // 将索引数组存储到索引缓冲区
    GLuint iBuffer;
    glGenBuffers(1, &iBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
    
    // 基础着色器
    self.mEffect = [[GLKBaseEffect alloc]init];
    // 设置投影矩阵
    CGSize size = self.view.bounds.size;
    float aspect = fabs(size.width / size.height);
    GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(90.0), aspect, 0.1f, 100.0f);
    projectionMatrix = GLKMatrix4Scale(projectionMatrix, 1.0, 1.0, 1.0);
    self.mEffect.transform.projectionMatrix = projectionMatrix;
    // 设置模型视图矩阵
    GLKMatrix4 modelViewMatrix = GLKMatrix4Translate(GLKMatrix4Identity, 0.0f, 0.0f, -2.0f);
    self.mEffect.transform.modelviewMatrix = modelViewMatrix;
    
    // 索引数据个数
    self.count = sizeof(indices)/sizeof(indices[0]);
    
    // 定时器
    double seconds = 0.1;
    timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
    dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, seconds * NSEC_PER_SEC, 0.0);
    __weak typeof(self) weakSelf = self;
    dispatch_source_set_event_handler(timer, ^{
        weakSelf.xDegree += 0.1f * weakSelf.XB;
        weakSelf.yDegree += 0.1f * weakSelf.YB;
        weakSelf.zDegree += 0.1f * weakSelf.ZB;
    });
    dispatch_resume(timer);
    
    // 设置颜色
    glClearColor(0.3, 0.3, 0.3, 1.0);
    
}

GLKit代理方法

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect{
    
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    [self.mEffect prepareToDraw];
    
    glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);
}

以上两个案例的不同

  • attrib属性数据传输
// GLSL
GLuint position = glGetAttribLocation(self.myProgram, "position");
glEnableVertexAttribArray(position);
glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);

// GLKit
glEnableVertexAttribArray(GLKVertexAttribPosition);
glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);

GLKit封装了着色器源码,使用GLKVertexAttribPosition通道用于顶点数据的传输,而在GLSL使用中,使用自己编写的着色器源码中的position作为顶点数据传输通道,所以首先需要glGetAttribLocation进行获取后将通道开启进行传输。

  • 渲染方法
// GLSL
glDrawElements(GL_TRIANGLES, sizeof(indices)/sizeof(indices[0]), GL_UNSIGNED_INT, indices); 
// 最后的参数传递索引数组

// GLKit
GLuint iBuffer;
glGenBuffers(1, &iBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, iBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glDrawElements(GL_TRIANGLES, self.count, GL_UNSIGNED_INT, 0);  
// 最后参数传递0

GLKit利用索引缓冲区保存索引数组进行渲染,GLSL是直接将索引数组通过传递给glDrawElements函数来渲染。

  • 矩阵数据
// GLSL
glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat*)&_projectionMatrix.m[0][0]);

// GLKit
self.mEffect.transform.projectionMatrix = projectionMatrix;

GLSL顶点着色器源码中uniform修饰投影矩阵参数,所以使用glUniformMatrix4fv来进行传递,uniform修饰的参数不用开启开关的操作。GLKit因为已经过封装的缘故,可以直接传递给基础着色器。