美文网首页
OpenGL的简单使用

OpenGL的简单使用

作者: Johnny_Wu | 来源:发表于2018-10-16 18:03 被阅读0次

最近在研究OpenGL,所以记录一下研究的成果吧,方便日后复习。

一、概念

OpenGL(全写Open Graphics Library)是指定义了一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。它用于三维图像(二维的亦可),是一个功能强大,调用方便的底层图形库。OpenGL ES (OpenGL for Embedded Systems) 是 OpenGL 三维图形 API 的子集,针对手机、PDA和游戏主机等嵌入式设备而设计

它的渲染坐标是以中心点为原点,如下


20170312112301376.png

如何绘制:
简单来说,我这里以二维图像的渲染为例子,OpenGL是以三角形的形式进行绘制的,为什么不是四边形呢?因为四边形存在四个顶点,那么有可能存在其中一个顶点与其他三个顶点不在同一平面上,这就是3D了。这里只讨论2D,所以以三角形进行绘制,三个顶点肯定是在同一平面上的。
上面构造了外形,那么就得往里面填充像素,这里的像素可以是自己构造的某个颜色像素(这样就可以实现,按需求填充不同的颜色),也可以是某张图片的像素(这样就可以实现渲染图片)。

数据传递:
(纹理数据)CPU-->GPU-->OpenGL
比如纹理对象inputTextures,内存的纹理数据pixels(uint8 *),需通过glTexImage2D传给(GPU)纹理对象,但将内存中的数据上传到显卡的一个纹理ID之上,但是这种内存和显存之间的数据交换效率是很低的。
1、首先:(CPU-->GPU)
glBindTexture(GL_TEXTURE_2D, _inputTextures);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, (int)widths, (int)heights,
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pixels);
2、最后再调用:(GPU-->OpenGL)
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, _inputTextures); glUniform1i(filterInputTextureUniform(OpenGL), 0);

OpenGL只有拿到数据,才可以进行操作!

二、绘制一个三角形

shader,GPU的程序:
1、顶点着色器(Vertex)(负责构造外形)

// variable pass into
attribute vec4 Position;    // 接收程序传入的顶点数据
attribute vec4 SourceColor; // 接收程序传入的颜色数据
varying vec4 DestinationColor;//varying定义的,都是作为顶点着色器向片段着色器传值

void main(void) {
    
    DestinationColor = SourceColor;
    
    //gl_Position位置的输出变量,必须赋值,这里把传入来的位置顶点数据传给它,有几个数据,就调用几次。
    gl_Position = Position;
}

2、片元着色器(Fragment)(负责填充)

varying lowp vec4 DestinationColor;//接收顶点着色器传过来的值,这里把颜色值传进来。
void main(void) {
//    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // must set gl_FragColor for fragment shader
//gl_FragColor颜色输出变量,必须赋值,这里赋值外界传过来的颜色值。有几个像素就调用几次
    gl_FragColor = DestinationColor;
}

通过上面的shader小程序,似乎已完成的渲染的功能,构造外形+填充像素。那么OpenGL接口处理给shader传值和展示就可以了。

3、输入:

- (void)processShaders {
    // 编译shaders
    _glProgram = [ShaderOperations compileShaders:@"DemoTriangleVertex" shaderFragment:@"DemoTriangleFragment"];
    
    glUseProgram(_glProgram);
    // 获取指向vertex shader传入变量的指针, 然后就通过该指针来使用
    // 即将_positionSlot 与 shader中的Position参数绑定起来,作为输入
    _colorSlot = glGetAttribLocation(_glProgram, "SourceColor");
    _positionSlot = glGetAttribLocation(_glProgram, "Position");
}

上面可以看到,取到了shader的两个变量SourceColor和Position,然后给它赋值:

    //顶点数组
    const GLfloat vertices[] = {
        0.0f,  0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f,  -0.5f, 0.0f
        
    };
    // Load the vertex data,(不使用VBO)则直接从CPU中传递顶点数据到GPU中进行渲染
    // 给_positionSlot传递vertices数据
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(_positionSlot);
    
    // 颜色数组
    const GLfloat Colors[] = {
        0,0,0,1, // 左下,黑色
        1,0,0,1, // 右下,红色
        0,0,1,1, // 左上,蓝色
    };
    // 取出Colors数组中的每个坐标点的颜色值,赋给_colorSlot
    glVertexAttribPointer(_colorSlot, 4, GL_FLOAT, GL_FALSE, 0, Colors);
    glEnableVertexAttribArray(_colorSlot);

4、输出:
OpenGL是通过CALayer渲染的,准备的步骤如下:

    //配置上下文
     _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2]; //opengl es 2.0
    [EAGLContext setCurrentContext:_eaglContext]; //设置为当前上下文。
    //配置Layer,这里我新建了一个layer,也可以使用默认的view.layer
    _eaglLayer = [CAEAGLLayer layer];
    _eaglLayer.frame = CGRectMake(0, NavigationBar_HEIGHT, self.view.frame.size.width, SCREEN_HEIGHT-NavigationBar_HEIGHT);
    _eaglLayer.opaque = YES; //CALayer默认是透明的
    
    // 描绘属性:这里不维持渲染内容
    // kEAGLDrawablePropertyRetainedBacking:若为YES,则使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)计算得到的最终结果颜色的透明度会考虑目标颜色的透明度值。
    // 若为NO,则不考虑目标颜色的透明度值,将其当做1来处理。
    // 使用场景:目标颜色为非透明,源颜色有透明度,若设为YES,则使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)得到的结果颜色会有一定的透明度(与实际不符)。若未NO则不会(符合实际)。
    _eaglLayer.drawableProperties = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithBool:NO],kEAGLDrawablePropertyRetainedBacking,kEAGLColorFormatRGBA8,kEAGLDrawablePropertyColorFormat, nil];
    [self.view.layer addSublayer:_eaglLayer];

    //设置渲染区
    glGenRenderbuffers(1, &_colorRenderBuffer);
    // 设置为当前renderBuffer
    glBindRenderbuffer(GL_RENDERBUFFER, _colorRenderBuffer);
    //为color renderbuffer 分配存储空间
    [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_eaglLayer];
    
    // FBO用于管理colorRenderBuffer,离屏渲染
    glGenFramebuffers(1, &_frameBuffer);
    //设置为当前framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, _frameBuffer);
    // 将 _colorRenderBuffer 装配到 GL_COLOR_ATTACHMENT0 这个装配点上
    glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, _colorRenderBuffer);

    // Draw triangle,把内容绘制到帧缓存区
    glDrawArrays(GL_TRIANGLES, 0, 3);

    //把缓冲区的内容展示出来
    [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];

上面很多,我都是按自己的理解说的,可能有误。这就实现了绘制三角形。

三、绘制图片:

这里需要加入纹理的概念,把图片弄成纹理,然后传给shader,shader从图片纹理中取出每一个像素,进行填充。
还有一个叫纹理坐标,左下角为原点,控制图片纹理的方向(通过操作这个坐标,可以让图片倒转等等操作)。OpenGL的坐标分别对应顶点坐标的左下、右下、左上、右上,按这个顺序分别设置顶点坐标对应的纹理坐标。纹理坐标也按这个顺序调整,就可以控制图片的方向。
1、片元着色器:

uniform sampler2D Texture;  //传入图片纹理数据
varying vec2 TextureCoordsOut;  //纹理坐标

void main(void)
{
    //根据纹理坐标,取出每一个纹理像素
    vec4 mask = texture2D(Texture, TextureCoordsOut);
    gl_FragColor = vec4(mask.rgb, 1.0);
}

2、输入:

    _glProgram = [ShaderOperations compileShaders:@"DemoDrawImageTextureVertex" shaderFragment:@"DemoDrawImageTextureFragment"];
    
    glUseProgram(_glProgram);
    
    // 需要三个参数, 跟Shader中的一一对应。
    // Position: 将颜色放置在CAEAGLLayer上的哪个位置
    // Texture: 图像的纹理
    // TextureCoords: 图像的纹理坐标,即图像纹理的哪一块颜色
    _positionSlot = glGetAttribLocation(_glProgram, "Position");
    _textureSlot = glGetUniformLocation(_glProgram, "Texture");
    _textureCoordsSlot = glGetAttribLocation(_glProgram, "TextureCoords");

    //纹理输入
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    glActiveTexture(GL_TEXTURE2); // 指定纹理单元GL_TEXTURE5
    glBindTexture(GL_TEXTURE_2D, _textureID); // 绑定,即可从_textureID中取出图像数据。
    glUniform1i(_textureSlot, 2); // 与纹理单元的序号对应,这里对_textureSlot进行了赋值
  
    //纹理坐标赋值。按照OpenGL的坐标点,对应设置
    GLfloat vertices[] = {
        -1, -1, 0,   //左下
        1,  -1, 0,   //右下
        -1, 1,  0,   //左上
        1,  1,  0 }; //右上
    glVertexAttribPointer(_positionSlot, 3, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(_positionSlot);

GLfloat texCoords[] = {
        0, 0,//左下
        1, 0,//右下
        0, 1,//左上
        1, 1,//右上
    };
    glVertexAttribPointer(_textureCoordsSlot, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
    glEnableVertexAttribArray(_textureCoordsSlot);

如上,就是纹理坐标的0,0-->顶点坐标的-1,-1。就是标准的对应方式。如果纹理坐标为:
GLfloat texCoords[] = {
0, 1,//左下
1, 1,//右下
0, 0,//左上
1, 0,//右上
};
那么图片是倒转的。

对应的代码:https://github.com/wulang150/SummaryTest
其他:https://blog.csdn.net/icetime17/article/details/50436927

相关文章

网友评论

      本文标题:OpenGL的简单使用

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