美文网首页
OpenGL ES 金字塔纹理、颜色混合

OpenGL ES 金字塔纹理、颜色混合

作者: Maji1 | 来源:发表于2020-08-04 00:28 被阅读0次
我们还是来先看一下效果图:

了解OpenGL的同学应该知道,使用固定着色器可以绘制这样一个金字塔。今天我们来讲解一下如何使用GLSL绘制这样一个金字塔。
学习这片文章需要先了解一下上一篇文章 OpenGL ES 纹理绘制

我们先来准备顶点着色程序和片元着色程序的代码。

顶点着色程序shaderv.glsl文件:

attribute vec4 position;
attribute vec4 positionColor;
attribute vec2 textureCoordinate;

uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;

varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
void main()
{
    varyColor = positionColor;
    varyTextCoord = textureCoordinate;
    
    vec4 transPos;
    transPos = projectionMatrix * modelViewMatrix * position;
    gl_Position = transPos;
}

position:顶点坐标。
positionColor:顶点颜色坐标。
textureCoordinate:纹理坐标。
projectionMatrix:投影矩阵。
modelViewMatrix: 模型视图矩阵。
varyColor:用于将传positionColor递到片元着色器。varyColor名字和类型需要跟片元着色程序保持一致。
varyTextCoord:用于将传textureCoordinate递到片元着色器。varyTextCoord名字和类型需要跟片元着色程序保持一致。

片元着色程序shaderf.glsl文件:

precision highp float;
varying lowp vec4 varyColor;
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;

void main()
{
    vec4 weakMask = texture2D(colorMap, varyTextCoord);
    vec4 mask = varyColor;
    float alpha = 0.3; 
    vec4 tempColor = mask * (1.0 - alpha) + weakMask * alpha;
    gl_FragColor = tempColor; 
}

precision highp float:默认精度。
colorMap:纹理采样器。
varyTextCoord:从顶点着色器传递过来的纹理坐标。
varyColor:从顶点着色器传递过来的颜色坐标。
tempColor:纹素和颜色转换后的的颜色值。我们本案例绘制的是颜色和纹理混合的效果,所以需要转换。

下面我们来看下代码的核心流程。
我们在上一篇文章已 OpenGL ES 纹理绘制 中经介绍过纹理填充的流程。本案例主流程其实是一样的。

    //1.设置layer
    [self setupPyramidLayer];
    //2.设置上下文
    [self setupPyramidContext];
    //3.清空renderBuffer和frameBuffer
    [self deleteBuffer];
    //4.设置renderBuffer
    [self setupRenderBuffer];
    //5.设置frameBuffer
    [self setupFrameBuffer];
    //6.绘制
    [self draw];

前五步准备工作的代码,跟上一篇文章 OpenGL ES 纹理绘制 中是一样的,这里就不多介绍了。

我们重点介绍一下核心代码第六步绘制:
6.1 基本操作

    glClear(GL_COLOR_BUFFER_BIT);
    glClearColor(0.0, 0.0, 0.0, 1.0);
    CGFloat scale = [[UIScreen mainScreen] scale];
    glViewport(self.frame.origin.x * scale, self.frame.origin.y * scale, self.frame.size.width * scale, self.frame.size.height * scale);

6.2 加载shader程序

    NSString *vShaderPath = [[NSBundle mainBundle] pathForResource:@"shaderv" ofType:@"glsl"];
    NSString *fShaderPath = [[NSBundle mainBundle] pathForResource:@"shaderf" ofType:@"glsl"];
    
    if (self.program) {
        glDeleteProgram(self.program);
        self.program = 0;
    }
    //加载程序program
    self.program = [self loadProgramWithShadervPath:vShaderPath shaderfPath:fShaderPath];

6.3 链接

    glLinkProgram(self.program);
    GLint linkSuccess;
    glGetProgramiv(self.program, GL_LINK_STATUS, &linkSuccess);
    if (linkSuccess == GL_FALSE) {
        GLchar message[512];
        glGetProgramInfoLog(self.program, sizeof(message), 0, &message[0]);
        NSString *messageString = [NSString stringWithUTF8String:message];
        NSLog(@"Link program failed: %@", messageString);
        return;
    }
    NSLog(@"Link program success!");

6.4 使用

glUseProgram(self.program);

到此前面的代码都是我们上篇文章 OpenGL ES 纹理绘制 介绍过的,一样的代码。



6.5 处理顶点数据

我们这里绘制的金字塔,既有颜色也有图片,所以我们的顶点坐标需要包含金字塔的 顶点坐标、颜色坐标、纹理坐标

     //顶点坐标
    GLfloat attrArr[] =
    {
        -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f,   0.0f, 1.0f, //左上0
        0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f,   1.0f, 1.0f, //右上1
        -0.5f, -0.5f, 0.0f,     1.0f, 1.0f, 1.0f,   0.0f, 0.0f, //左下2
        
        0.5f, -0.5f, 0.0f,      1.0f, 1.0f, 1.0f,   1.0f, 0.0f, //右下3
        0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f,   0.5f, 0.5f, //顶点4
    };
    
    //索引数组
    GLuint indices[] =
    {
        0, 3, 2,
        0, 1, 3,
        0, 2, 4,
        0, 4, 1,
        2, 3, 4,
        1, 4, 3,
    };
    if (self.vertices == 0) {
        glGenBuffers(1, &_vertices);
    }
    glBindBuffer(GL_ARRAY_BUFFER, self.vertices);
    glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), &attrArr, GL_DYNAMIC_DRAW);

这里引入了索引数组 indices[],用于制定顶点的连接方式。我们用一张图来标注一下金字塔的5个顶点:


索引数组 indices[]中代表了哪三个点连接成一个三角形。

6.6 设置读取方式

    GLsizei s = (GLsizei)sizeof(GLfloat)*8;
    GLuint position = glGetAttribLocation(self.program, "position");
    glEnableVertexAttribArray(position);
    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, s, NULL);
    
    GLuint positionColor = glGetAttribLocation(self.program, "positionColor");
    glEnableVertexAttribArray(positionColor);
    glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, s, (float *)NULL + 3);
    
   
    GLuint textureCoordinate = glGetAttribLocation(self.program, "textureCoordinate");
    glEnableVertexAttribArray(textureCoordinate);
    glVertexAttribPointer(textureCoordinate, 2, GL_FLOAT, GL_FALSE, s, (float *)NULL + 6);
  • glGetAttribLocation(self.program, "position");
    program读取顶点数据position,注意这里的"position"一定要跟shaderv.vsh文件中的position保持一致。
  • glEnableVertexAttribArray(position);
    打开顶点数据读取通道,默认是关闭的。
  • glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 5, NULL);设置读取方式:

6.7 设置投影矩阵、模型视图矩阵

    GLint projectionMatrixSlot = glGetUniformLocation(self.program, "projectionMatrix");
    KSMatrix4 _projectionMatrix;
    ksMatrixLoadIdentity(&_projectionMatrix);
    float width = self.frame.size.width;
    float height = self.frame.size.height;
    float aspect = width / height;
    ksPerspective(&_projectionMatrix, 30.0, aspect, 5.0f, 20.0f);
    
    glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
    
    KSMatrix4 _modelViewMatrix;
    ksMatrixLoadIdentity(&_modelViewMatrix);
    ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
    
    KSMatrix4 _rotationMatrix;
    ksMatrixLoadIdentity(&_rotationMatrix);
    ksRotate(&_rotationMatrix, xDegree, 1.0, 0.0, 0.0);
    ksRotate(&_rotationMatrix, yDegree, 0.0, 1.0, 0.0);
    ksRotate(&_rotationMatrix, zDegree, 0.0, 0.0, 1.0);
    
    ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_modelViewMatrix);
    GLuint modelViewMatrixSlot = glGetUniformLocation(self.program, "modelViewMatrix");
    glUniformMatrix4fv(modelViewMatrixSlot, 1, GL_FALSE, (GLfloat *)&_modelViewMatrix.m[0][0]);
  • glGetUniformLocation(self.program, "projectionMatrix"):从program读取投影矩阵projectionMatrix,注意这里的"projectionMatrix"一定要跟shaderv.glsl文件中的projectionMatrix保持一致。

  • glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
    location:该变量在shader中的位置。
    count:被赋值的矩阵的数目(因为uniform变量可以是一个数组)。
    transpose:表明在向uniform变量赋值时该矩阵是否需要转置。
    value:传递给uniform变量的数据的指针。

  • xDegree, yDegree, zDegree 是我们在旋转中修改的角度。

这段代码中KS开头的使用到了三方库中的API。

6.8 加载纹理

[self setupTexture:@"scence"];

该段代码的内容在上一篇文章 OpenGL ES 纹理绘制 中有详解。

6.9 设置纹理采样器 sampler2D

    GLint colorMap = glGetUniformLocation(self.program, "colorMap");
    glUniform1i(colorMap, 0);
    //开启剔除模式
    glEnable(GL_CULL_FACE);

colorMap需要跟shaderf.fsh文件中保持一致。

6.10 绘制

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

绘制方法跟上篇文章的有所不同。

  • 参数一:绘制图元装配方式。
  • 参数二:索引数组的个数。
  • 参数三:索引数组中的类型。
  • 参数四:索引数组。

6.11 从渲染缓存区显示到屏幕上

    [self.currentContext presentRenderbuffer:GL_RENDERBUFFER];

最后实现转转我们还需要创建个定时器修改角度 xDegree, yDegree, zDegree , 然后重新绘制。

相关文章

网友评论

      本文标题:OpenGL ES 金字塔纹理、颜色混合

      本文链接:https://www.haomeiwen.com/subject/skzbrktx.html