最近在研究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
网友评论