美文网首页
OpenGL ES GLSL绘制金字塔

OpenGL ES GLSL绘制金字塔

作者: 大橘猪猪侠 | 来源:发表于2020-08-07 22:40 被阅读0次

    这个金字塔的外部效果,由顶点颜色和纹理颜色混合成,我们先实现用顶点颜色实现这个金字塔的样子,最终效果如下图所示:


    QQ20200802-122907-HD.gif

    绘制这个金字塔,我们分6部去实现这个效果:

    第一步:绘制图层:

    self.mEagLayer = (CAEAGLLayer *)self.layer;
        [self setContentScaleFactor:[[UIScreen mainScreen]scale]];
        self.mEagLayer.opaque = YES;
        self.mEagLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
    

    第二步设置上下文:

    EAGLContext *context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES3];
        if(!context){
            NSLog(@"创建失败");
            return;
        }
        if(![EAGLContext setCurrentContext:context]){
            NSLog(@"设置失败");
            return;
        }
        self.mContext = context;
    

    第三步:删除缓冲区

    glDeleteBuffers(1, &_myColorRenderBuffer);
        _myColorRenderBuffer = 0;
        glDeleteBuffers(1, &_myColorFrameBuffer);
        _myColorFrameBuffer = 0;
    

    第四步、第五步:设置RenderBuffer和FrameBuffer

    -(void)setupFrameBuffer{
        //1、定义一个缓存区
        GLuint buffer;
        //2、申请缓冲区标志
        glGenFramebuffers(1, &buffer);
        //3、赋值
        self.myColorFrameBuffer = buffer;
        //4、设置当前的frameBuffer
        glBindFramebuffer(GL_FRAMEBUFFER, self.myColorFrameBuffer);
        //5、将myColorFrameBuffer配置到GL_COLOR_ATTACHMENT0附着点上
        glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, self.myColorRenderBuffer);
    }
    
    //4、设置renderbuffer
    -(void)setupRenderBuffer{
        //1、定义一个缓冲区
        GLuint buffer;
        //2、申请一个缓冲区标志
        glGenRenderbuffers(1, &buffer);
        //3、赋值
        self.myColorRenderBuffer = buffer;
        //4、将标示符绑定
        glBindRenderbuffer(GL_RENDERBUFFER, self.myColorRenderBuffer);
        [self.mContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:self.mEagLayer];
    }
    

    在第六步之前,我们还需要编写GLSL着色器:

    新建两个空文件,shaderf.glsl和shaderv.glsl
    在shaderf.glsl中的代码:

    precision highp float;
    varying lowp vec4 varyColor;
    void main(){
        gl_FragColor = varyColor;
    }
    

    在shaderv.glsl中的代码:

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

    然后我们将加载GLSL文件的代码写成两个函数来实现:

    -(GLuint)loadShader:(NSString *)vert frag:(NSString *)frag{
        //创建两个临时变量
        GLuint verShader,fragShader;
        //创建一个program
        GLuint program = glCreateProgram();
        
        //编译文件
        [self compileShader:&verShader type:GL_VERTEX_SHADER file:vert];
        [self compileShader:&fragShader type:GL_FRAGMENT_SHADER file:frag];
        
        //创建最终的程序
        glAttachShader(program, verShader);
        glAttachShader(program, fragShader);
        
        //释放不需要的shader
        glDeleteShader(verShader);
        glDeleteShader(fragShader);
        
        return program;
    
    }
    
    //链接shader
    -(void)compileShader:(GLuint *)shader type:(GLenum)type file:(NSString *)file{
        //读取文件路径字符串
        NSString *content = [NSString stringWithContentsOfFile:file encoding:NSUTF8StringEncoding error:nil];
        //获取文件路径字符串
        const GLchar *source = (GLchar *)[content UTF8String];
        
        //创建一个shader
        *shader = glCreateShader(type);
        
        //将顶点着色器源码附加到着色器对象上
        glShaderSource(*shader, 1, &source, NULL);
        
        //将着色器源代码编译成目标代码
        glCompileShader(*shader);
    }
    

    第六步:在写完着色器之后,开始绘制金字塔,这一步是最重要的。

     //1、清屏颜色
        glClearColor(0.5, 0.7, 0.9, 1.0);
        glClear(GL_COLOR_BUFFER_BIT);
        
        CGFloat scale = [[UIScreen mainScreen]scale];
        
        //2、设置视口
        glViewport(self.frame.origin.x*scale, self.frame.origin.y, self.frame.size.width*scale, self.frame.size.height*scale);
        
        //3、获取顶点着色器、片源着色器
        NSString *vertFile = [[NSBundle mainBundle]pathForResource:@"shaderv" ofType:@"glsl"];
        NSString *fragFile = [[NSBundle mainBundle]pathForResource:@"shaderf" ofType:@"glsl"];
        
        //4、判断self.myProgram是否存在,
        if(self.myProgram){
            glDeleteProgram(self.myProgram);
            self.myProgram = 0;
        }
        //5、加载程序到myProgram中来
        self.myProgram = [self loadShader:vertFile frag:fragFile];
        
        //6、链接
        glLinkProgram(self.myProgram);
        GLint linkSuccess;
        
        //7、获取链接状态
        glGetProgramiv(self.myProgram, GL_LINK_STATUS, &linkSuccess);
        if (linkSuccess == GL_FALSE) {
            GLchar messages[256];
            glGetProgramInfoLog(self.myProgram, sizeof(messages), 0, &messages[0]);
            NSString *messageString = [NSString stringWithUTF8String:messages];
            NSLog(@"error:%@", messageString);
            
            return ;
        }else {
            glUseProgram(self.myProgram);
        }
        
        //8、创建顶点数组&索引数组
        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
        };
        
        //(2).索引数组
        GLuint indices[] =
        {
            0, 3, 2,
            0, 1, 3,
            0, 2, 4,
            0, 4, 1,
            2, 3, 4,
            1, 4, 3,
        };
        
        //(3)判断顶点缓冲区是否为空,如果为空则申请一个缓冲区标识符
        if(self.myVertices == 0){
            glGenBuffers(1, &_myVertices);
        }
        
        //9、处理顶点数据
        
        glBindBuffer(GL_ARRAY_BUFFER, _myVertices);
        glBufferData(GL_ARRAY_BUFFER, sizeof(attrArr), attrArr, GL_DYNAMIC_DRAW);
        
        GLuint position = glGetAttribLocation(self.myProgram, "position");
        
        //打开position
        glEnableVertexAttribArray(position);
        
        glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, NULL);
        
        
        //10、处理顶点颜色值
        GLuint positionColor = glGetAttribLocation(self.myProgram, "positionColor");
        
        glEnableVertexAttribArray(positionColor);
        
        glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*6, (float *)NULL +3);
            
        //11、找到myProgram中的projectionMatrix、modelViewMatrix 2个矩阵的地址。如果找到则返回地址,否则返回-1,表示没有找到2个对象。
        GLuint projectionMatrixSlot = glGetUniformLocation(self.myProgram, "projectionMatrix");
        GLuint modelViewMatrixSlot = glGetUniformLocation(self.myProgram, "modelViewMatrix");
        
        float width = self.frame.size.width;
        float height = self.frame.size.height;
        
        
        //12、创建4*4投影矩阵
        KSMatrix4 _projectionMatrix;
        
        ksMatrixLoadIdentity(&_projectionMatrix);
        float aspect = width/height;
        
        ksPerspective(&_projectionMatrix, 30, aspect, 5.0, 20.0);
        
        glUniformMatrix4fv(projectionMatrixSlot, 1, GL_FALSE, (GLfloat *)&_projectionMatrix.m[0][0]);
        
        //13、创建一个4*4矩阵
        KSMatrix4 _modelViewMatrix;
        //获取单元矩阵
        ksMatrixLoadIdentity(&_modelViewMatrix);
        //平移z轴平移-10
        ksTranslate(&_modelViewMatrix, 0.0, 0.0, -10.0);
        //创建4*4矩阵,旋转矩阵
        KSMatrix4 _rotationMatrix;
        //设置为单元矩阵
        ksMatrixLoadIdentity(&_rotationMatrix);
        //旋转
        ksRotate(&_rotationMatrix, xAngle, 1.0, 0.0, 0.0);
        ksRotate(&_rotationMatrix, yAngle, 0.0, 1.0, 0.0);
        ksRotate(&_rotationMatrix, zAngle, 0.0, 0.0, 1.0);
        //矩阵相乘
        ksMatrixMultiply(&_modelViewMatrix, &_rotationMatrix, &_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);
        
        //要求本地窗口系统显示OpenGL ES渲染<目标>
        [self.mContext presentRenderbuffer:GL_RENDERBUFFER];
    

    以上的代码就是绘制顶点颜色金字塔的代码。

    下面我们开始用纹理颜色和顶点颜色混合的方法去实现金字塔

    首先,将两个GLSL文件代码进行修改:

    precision highp float;
    varying lowp vec4 varyColor;
    varying lowp vec2 varyTextCoord;
    uniform sampler2D colorMap;
    void main(){
        lowp vec4 temp = texture2D(colorMap,varyTextCoord);
        vec4 mask = varyColor;
        float alpha = 0.3;
        
        vec4 tempColor = mask * (1.0-alpha) + temp * alpha;
        
        gl_FragColor = tempColor;
    }
    
    
    
    
    attribute vec4 position;
    attribute vec4 positionColor;
    attribute vec2 textCoordinate;
    
    uniform mat4 projectionMatrix;
    uniform mat4 modelViewMatrix;
    
    varying lowp vec4 varyColor;
    
    varying lowp vec2 varyTextCoord;
    
    void main(){
        varyColor = positionColor;
        varyTextCoord = textCoordinate;
        
        vec4 vPos;
        
        vPos = projectionMatrix * modelViewMatrix * position;
        gl_Position = vPos;
    }
    
    }
    

    接下来在绘制的方法中的金字塔坐标去添加纹理坐标:

    GLfloat attrArr[] =
        {
            -0.5f, 0.5f, 0.0f,      1.0f, 0.0f, 1.0f, 1.0f,1.0f,//左上0
            0.5f, 0.5f, 0.0f,       1.0f, 0.0f, 1.0f, 0.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,1.0f,//右下3
            0.0f, 0.0f, 1.0f,       0.0f, 1.0f, 0.0f, 0.0f,1.0f//顶点4
        };
    

    在坐标设置完之后,我们需要处理纹理数据

     //处理纹理数据
        GLuint textCoor = glGetAttribLocation(self.myProgram, "textCoordinate");
        
        glEnableVertexAttribArray(textCoor);
        
        
        glVertexAttribPointer(textCoor, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, (float *)NULL+6);
        
        [self setupTexture:@"fengjing.jpg"];
        
        //设置纹理采样器
        glUniform1i(glGetUniformLocation(self.myProgram, "textCoordinate"), 0);
        
    

    //从图片中加载纹理的函数

    -(GLuint)setupTexture:(NSString *)fileName{
        //1、将UIImage转换成CGImageRef
        CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
    
        //判断图片是否获取成功
        if(!spriteImage){
            NSLog(@"图片加载失败");
            exit(1);
        }
    
        //2、读取图片大小,宽和高
        size_t width = CGImageGetWidth(spriteImage);
        size_t height = CGImageGetHeight(spriteImage);
    
        //3、获取图片字节数 宽*高*4(RGBA)
    
        GLubyte *spriteDate = (GLubyte *)calloc(width*height*4, sizeof(GLubyte));
    
    
        //4、创建上下文
     
        CGContextRef spriteContext = CGBitmapContextCreate(spriteDate, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
    
    
        //5、在CGContextRef上--> 将图片绘制出来
    
        CGRect rect = CGRectMake(0, 0, width, height);
    
        //6、使用默认方式绘制
        CGContextDrawImage(spriteContext, rect, spriteImage);
        
        //翻转
        CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
        CGContextTranslateCTM(spriteContext, 0, rect.size.height);
        CGContextScaleCTM(spriteContext, 1.0, -1.0);
        CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
        CGContextDrawImage(spriteContext, rect, spriteImage);
        
        //7、画完图就释放上下文
        CGContextRelease(spriteContext);
    
        //8、绑定纹理到默认的纹理ID
        glBindTexture(GL_TEXTURE_2D, 0);
    
        //9.设置纹理属性
        
    
        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);
    
        float fw = width,fh = height;
        //10.载入纹理2D数据
    
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteDate);
    
        //11、释放spriteDate
        free(spriteDate);
    
        return 0;
    }
    

    其中在处理顶点数据和顶点颜色时,我们需要修改数组长度为8

    glVertexAttribPointer(position, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, NULL);
    
    glVertexAttribPointer(positionColor, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*8, (float *)NULL +3);
    

    当修改完之后,最终效果如下图所示:


    QQ20200802-124515-HD.gif

    完整demo

    相关文章

      网友评论

          本文标题:OpenGL ES GLSL绘制金字塔

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