OpenGL基础知识

本文详细介绍了OpenGL的图形渲染管线,包括顶点着色器、图元装配、几何着色器、光栅化、片段着色器和测试与混合等步骤。同时,讲解了着色器的编写、加载和使用,以及如何向顶点着色器传递数据。此外,还提到了EGL环境配置、纹理处理、帧缓冲区和FBO的概念,以及YUV视频转换到RGBA的过程。
摘要由CSDN通过智能技术生成

最近两个月开始在做图像渲染的项目,做了一些OpenGL ES的笔记,都是一些比较入门级别的知识,有大佬发现哪里有理解错误的地方,欢迎批评指正。

若编译工程出现"未定义的引用"error时,也许是因为没有链接这些库 -lGLU -lglut -lglfw3 -lGL -lX11 -lpthread -lXrandr -lXi -ldl

OpenGL图形渲染管线:

顶点着色器->图元装配->几何着色器->光栅化->片段着色器->测试与混合

着色器:一段运行在GPU上的程序,由开发者编写(glsl),顶点着色器 和 片段着色器 开发者必须自己写,几何着色器可选

顶点着色器
功能:确定绘制图形的形状
输入顶点数据、颜色值、纹理采样器,对顶点数据进行变换处理后,输出至图元装配
每个顶点着色器 只处理一个顶点坐标,有多少个顶点就执行多少次
图元装配
接收顶点着色器的输出作为输入,将顶点数据组装为图元
所谓图元,指的就是点、线、三角形等最基本的几何图形,再复杂的图形也是由这些基本图形组成
还会将超出屏幕的顶点坐标进行裁剪,裁剪之后,顶点坐标被映射为屏幕坐标
几何着色器
非必须
光栅化
接收图元数据作为输入,将一个图元转化为一张二维的图片,这张图片由若干个片段组成,输出这些片段
片段近似看作像素(不完全相同),一个片段包含渲染该片段所需的位置(屏幕坐标)、颜色、深度、纹理坐标等全部信息
片段着色器
计算片段的颜色。
每个片段着色器接收一个片段数据的输入,所以有几个片段就会执行所少次
测试与混合(帧缓冲区)
丢弃一些不需要显示的片段,测试主要包括 深度测试 和 模板测试
深度测试 基于深度缓冲区,只显示深度最小的片段(即 最外表面的图形)
模板测试 基于模板缓冲区,控制屏幕需要显示的内容
混合 与显示在它背后的片段的颜色按照透明度进行叠加形成新的颜色,通俗讲就是透过有色玻璃看事物

OpenGL是一个状态机(State Machine):一系列的变量描述OpenGL此刻应当如何运行。
OpenGL的状态通常被称为OpenGL上下文(Context)
我们通常使用如下途径去更改OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。
假设当我们想告诉OpenGL去画线段而不是三角形的时候,我们通过改变一些上下文变量来改变OpenGL状态,从而告诉OpenGL如何去绘图。一旦我们改变了OpenGL的状态为绘制线段,下一个绘制命令就会画出线段而不是三角形。

当使用OpenGL的时候,我们会遇到一些状态设置函数(State-changing Function),这类函数将会改变上下文。以及状态使用函数(State-using Function),这类函数会根据当前OpenGL的状态执行一些操作。只要你记住OpenGL本质上是个大状态机,就能更容易理解它的大部分特性。

glBindBuffer()用状态机的方式去理解就是进入了某个状态

一.配置EGL环境

EGL 是 OpenGLES 与屏幕之间的桥梁,让OpenGLES绘制的内容能够呈现在屏幕上
OpenGLES只负责处理数据,但是并不知道如何渲染到屏幕上,必须把处理好的数据 交给EGL,才能呈现在屏幕上
Display(EGLDisplay) 显示设备的抽象
surface 存放颜色缓冲、深度缓冲、模板缓冲等的容器
顶点着色器的核心功能 就是完成将3维坐标中的点,通过变换和投影,转换为2维的屏幕上
配置好EGL环境之后,就要加载着色器(编译、链接着色器)

EGL是在Android上使用OpenGL时经常使用的,当然有其他的与窗口系统的桥梁可以使用,例如QT对OpenGL的封装

二.着色器的编写

precision precision-qualifier type; 精度修饰符,表示数据的精度,精度越高,渲染质量越高,不过性能开销就越大
precision mediump float; //例如这个

attribute 只能在顶点着色器中使用的变量
一般用来表示:顶点坐标、纹理坐标、颜色、法线等。
uniform 统一变量,只能用,不能改,通过函数 glUniform*()函数赋值
可以在顶点着色器和片段着色器中共享使用(相当于一个被两个着色器共享的全局变量)
一般用来表示:变换矩阵,材质,光照参数等。
varying 是顶点着色器和片段着色器之间做数据传递用的,相当于顶点着色器和片段着色器的桥梁
一般在顶点着色器修改varying变量的值,然后在片段着色器使用该varying变量的值
因此varying变量在顶点着色器和片段着色器中的声明必须是一致的。用户的程序不能使用此变量

vec4 四维向量
mat4 四维矩阵
sampler2D 2D纹理采样器,与纹理单元关联,纹理单元与纹理ID绑定在一起

gl_Position glsl 1.0 内置的顶点着色器输出变量
gl_FragColor glsl 1.0 内置的片段着色器输出变量,它的类型是vec4,每个分量分别表示RGBA,并且颜色每个分量的强度设置在0.0到1.0之间

clamp(x, max, min); 返回中间值
mix(x, y, a); 线性混合,返回 x*(1-a)+y*a

三.加载着色器

//创建一个空的着色器对象
void glCreateShader(GLenum shaderType);1:着色器类型(顶点着色器或片元着色器)
//着色器对象 加载 着色器代码(glsl),即把着色器代码注入着色器对象
void glShaderSource(GLuint shader, GLsizei count, const GLchar* const * string, const GLint *length);1:着色器对象
参2:元素数
参3:要加载到着色器的源代码的指针数组
参4:长度数组
//编译着色器对象
void glCompileShader(GLuint shader);1:要编译的着色器对象
//打印编译的异常信息
void glGetShaderiv(GLuint shader, GLenum pname, GLint *params);1:要查询的着色器对象
参2:GL_SHADER_TYPE:着色器类型
 		用来判断并返回着色器类型,若是顶点着色器返回GL_VERTEX_SHADER,若为片元着色器返回GL_FRAGMENT_SHADER
     GL_DELETE_STATUS:删除状态
        判断着色器是否被删除,是返回GL_TRUE,否则返回GL_FALSE
     GL_COMPILE_STATUS:编译状态
 		用于检测编译是否成功,成功为GL_TRUE,否则为GL_FALSE.
     GL_INFO_LOG_LENGTH:返回着色器的信息日志的长度
     GL_SHADER_SOURCE_LENGTH:着色器源码长度
参3:返回的结果
//获取着色器对象的日志
glGetShaderInfoLog(GLuint shader, GLsizei maxLength, GLsizei *length, GLchar *infoLog); 
//创建一个空的着色器程序的对象
GLuint glCreateProgram(void);
//将前面创建的着色器对象 关联给 着色器程序的对象
void glAttachShader(GLuint program, GLuint shader);1:程序对象
参2:着色器对象
//将 着色器程序 与 OpenGL程序 链接
void glLinkProgram(GLuint program);1:程序对象
//打印链接时出现的异常信息  
void glGetProgramiv(GLuint program, GLenum pname, GLint *params);1:要查询的程序对象
参2:GL_DELETE_STATUS
     GL_LINK_STATUS
     GL_VALIDATE_STATUS
     GL_INFO_LOG_LENGTH
     GL_ATTACHED_SHADERS
     GL_ACTIVE_ATTRIBUTES
     GL_ACTIVE_UNIFORMS
     GL_ACTIVE_ATTRIBUTE_MAX_LENGTH
     GL_ACTIVE_UNIFORM_MAX_LENGTH
参3:返回的结果
//获取着色器程序对象的日志
glGetProgramInfoLog();
//宣告 着色器程序将被当前OpenGL程序使用
void glUseProgram(GLuint program);1:程序对象

至此,OpenGL程序 已经处于 激活着色器程序 的状态,后面就可以向着色器传递数据

四.向顶点着色器传递“顶点属性数组”

向顶点着色器传递的数据,叫做“顶点属性数组”(注意,该数组包含除了顶点坐标以外,还有其他顶点相关的属性)

//指定着色器中接收数据的变量名
GLint glGetAttribLocation(GLuint program, const GLchar* name);1:程序对象
参2:顶点着色器中attribute变量的名称
返回:参2(属性变量)的位置(顶点的索引)
//生成缓冲区对象
void glGenBuffers(GLsizei n, GLuint* buffers);1:要生成缓冲区对象的数量
参2:要存储缓冲区对象名称的数组
//将缓冲对象绑定到OpenGL上下文环境中以便使用
void glBindBuffer(GLenum target,GLuint buffer);1:指定缓冲区对象的类型
 GL_ARRAY_BUFFER         VBO
 GL_ELEMENT_ARRAY_BUFFER EBO
参2:缓冲区对象的名称
//将顶点数据传入缓冲区中
void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage);1:指定缓冲区对象的类型
参2:指明缓冲区对象的存储大小
参3:指向数据的指针,将数据复制到缓冲区中
参4:指明数据在缓冲区中的存储方式
 GL_STREAM_DRAW 数据每次绘制时都会改变
 GL_STREAM_READ
 GL_STREAM_COPY
 GL_STATIC_DRAW 数据不会改变
 GL_STATIC_READ
 GL_STATIC_COPY
 GL_DYNAMIC_DRAW 数据会被改变很多
 GL_DYNAMIC_READ
 GL_DYNAMIC_COPY
//更新缓冲区对象中的一部分数据
void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid * data);1:指定缓冲区对象的类型
参2:指定从什么地方开始更新原来的数据(以字节为单位)3:需要更新的数据量的大小
参4:指向新数据的指针,将新数据复制到缓冲区中指定的位置
//指明顶点属性数组要传入哪个attribute变量中,并指明如何取用顶点属性数组的数据
void glVertexAttribPointer(GLuint index, GLint size, GLenum type,
                           GLboolean normalized, GLsizei stride, const void * pointer)1:顶点着色器中的attribute变量
参2:一个顶点属性需要多少个数组元素表示,如3维坐标,size就是33:每一个数组元素的格式是什么,比如GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE
参4:是否需要归一化,即是否需要将数据范围映射到-11的区间内
参5:步长,表示前一个顶点属性的起始位置到下一个顶点属性的起始位置间隔多少字节
     如果传0,则说明顶点属性数据是紧密挨着的
参6:当前这个顶点属性在数组中是从第几个元素开始的
//使能顶点着色器中的attribute变量
void glEnableVertexAttribArray(GLuint index);1:顶点着色器中的attribute变量

默认情况下,出于性能考虑,所有顶点着色器的属性(Attribute)变量都是关闭的,意味着数据在着色器端是不可见的,哪怕数据已经上传到GPU,由glEnableVertexAttribArray启用指定属性,才可在顶点着色器中访问逐顶点的属性数据。glVertexAttribPointer或VBO只是建立CPU和GPU之间的逻辑连接,从而实现了CPU数据上传至GPU,但是,数据在GPU端是否可见,即,着色器能否读取到数据,由是否启用了对应的属性决定,这就是glEnableVertexAttribArray的功能,允许顶点着色器读取GPU(服务器端)数据。
至此,顶点着色器中的 attribute变量 已经接收到了顶点属性数组。

五.图元装配

//绘制,从顶点属性数组中提取数据,渲染基本图元
void glDrawArrays(GLenum mode, GLint first, GLsizei count);1:目标图元类型
 GL_POINTS  每个顶点作为一个点处理,n个顶点即绘制n个点
 GL_LINE_STRIP  绘制从第一个顶点到最后一个顶点依次连接的一组线段
 GL_LINE_LOOP  绘制从第一个顶点到最后一个顶点依次连接的一组线段,并且最后一个顶点与第一个顶点相连
 GL_LINES  连接每两个顶点作为一条线段,n个顶点即绘制n/2条线段
 GL_TRIANGLE_STRIP  绘制一组相连的三角形,[012][123][234][345]
 GL_TRIANGLE_FAN    绘制一组相连的三角形,[012][023][034][045]
 GL_TRIANGLES       绘制独立的三角形,[012][345]2:指定从顶点属性数组中的哪一位开始绘制,一般为03:绘制顶点的数量

glDrawArrays(GL_TRIANGLE_STRIP, 0, 3);
是OpenGL的绘制指令,在它前面的一系列指令只是配置,而glDrawArrays就像一个流水线开关,一踩下去,整个流水线才真正动起来,传入OpenGL的数据才像河流一样在一个个阶段流动起来,直到形成的图像数据被绘制到对应的帧缓冲
该方法参1指定上面说到的图元类型,这里传的是三角形条带GL_TRIANGLE_STRIP,参2表示从传入的顶点属性数组的第几个元素开始绘制,参3表示绘制多少个顶点属性数组元素

//若代码中需要索引来渲染,采用了EBO时,则需要用glDrawElements来代替glDrawArrays,来指明我们从索引缓冲渲染
glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid *indices);1:目标图元类型
 GL_POINTS
 GL_LINE_STRIP
 GL_LINE_LOOP
 GL_LINES
 GL_TRIANGLE_STRIP
 GL_TRIANGLE_FAN
 GL_TRIANGLES
参2:绘制顶点的个数
参3:索引中,值的类型。
 GL_UNSIGNED_BYTE
 GL_UNSIGNED_SHORT
 GL_UNSIGNED_INT
参4:指向索引数组的指针

六.片段着色器、纹理采样、YUV视频转换

我们要将图片纹理渲染到屏幕上,就要拿到图片的像素数据数组,然后将像素数据数组通过纹理单元传到片段着色器中,
再通过纹理采样函数将纹理中对应坐标的颜色值采样出来,最后赋值一个最终的片段颜色值。

//指定要将颜色缓冲区清空成什么颜色
void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha);
//用glClearColor或glClearDepth等函数指定的值,清空缓冲区
void glClear(GLbitfield mask);1:GL_COLOR_BUFFER_BIT   颜色缓冲
     GL_DEPTH_BUFFER_BIT   深度缓冲
     GL_STENCIL_BUFFER_BIT 模板缓冲
     GL_ACCUM_BUFFER_BIT   累积缓冲
//返回统一变量的位置
GLint glGetUniformLocation(GLuint program, const GLchar *name);1:程序对象
参2:统一变量的名称
//加载数据到统一变量中
void glUniform*(GLint location, ...);1:要被加载数据的统一变量的位置
参2:要加载的数据。
注意:若统一变量是纹理采样器,参2012等,这些012代表的是纹理单元,即第几层纹理。
glUniform1i()可以给片段着色器中的纹理采样器分配一个纹理单元,这样就可以在一个片段着色器中使用多个纹理
纹理单元的主要目的是让着色器可以使用多个纹理,通过把纹理单元赋值给采样器,我们可以一次绑定多个纹理
//生成纹理对象
void glGenTextures(GLsizei n, GLuint* texture);1:生成纹理对象的数量
参2:存储纹理对象名称的数组
//绑定纹理对象到纹理目标
void glBindTexture(GLenum target, GLuint texture);1:纹理目标
 GL_TEXTURE_2D
 GL_TEXTURE_3D
 GL_TEXTURE_2D_ARRAY
 GL_TEXTURE_2D_CUBE_MAP
参2:纹理对象名称
//配置纹理
void glTexParameteri(GLenum target, GLenum pname, GLint param);1:纹理目标,必须是GL_TEXTURE_1D或GL_TEXTURE_2D
参2:   GL_TEXTURE_WRAP_S
 GL_TEXTURE_WRAP_T
 GL_TEXTURE_MIN_FILTER
 GL_TEXTURE_MAG_FILTER
参3:指定参2的值
 GL_CLAMP
 GL_LINEAR
 GL_LINEAR_MIPMAP_NEAREST
 GL_NEAREST


//纹理环绕配置
//横坐标环绕配置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
//纵坐标环绕配置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 

//纹理过滤配置
//纹理分辨率大于图元分辨率,即纹理需要被缩小的过滤配置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//纹理分辨率小于图元分辨率,即纹理需要被放大的过滤配置
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//加载RGBA格式的图像数据
void glTexImage2D(
         GLenum  target,
         GLint   level,
         GLint   internalformat,
         GLsizei width,
         GLsizei height,
         GLint   border,
         GLint   format,
         GLenum  type,
   const GLvoid  *pixels);1:纹理目标,必须是 GL_TEXTURE_2D
参2:级别编号,级别0是基础映像级别。级别n是第n个 mipmap 缩减图像
参3:纹理中的颜色组件数,必须为1234或以下之一:
 GL_ALPHA, GL_ALPHA4, GL_ALPHA8, GL_ALPHA12, GL_ALPHA16, 
 GL_LUMINANCE, GL_LUMINANCE4, GL_LUMINANCE8, GL_LUMINANCE12, GL_LUMINANCE16, 
 GL_LUMINANCE_ALPHA, GL_LUMINANCE4_ALPHA2, GL_LUMINANCE6_ALPHA4, GL_LUMINANCE8_ALPHA8, 
 GL_LUMINANCE12_ALPHA4, GL_LUMINANCE12_ALPHA12, GL_LUMINANCE16_ALPHA16, 
 GL_INTENSITY, GL_INTENSITY4, GL_INTENSITY8, GL_INTENSITY12, GL_INTENSITY16, 
 GL_R3_G3_B2, GL_RGB, GL_RGB4, GL_RGB5, GL_RGB8, GL_RGB10, GL_RGB12, GL_RGB16, 
 GL_RGBA, GL_RGBA2, GL_RGBA4, GL_RGB5_A1, GL_RGBA8, GL_RGB10_A2, GL_RGBA12, GL_RGBA16
参4:纹理的宽度
参5:纹理的高度
参6:边框的宽度,必须是017:像素数据的格式
 GL_COLOR_INDEX
 GL_RED
 GL_GREEN
 GL_BLUE
 GL_ALPHA
 GL_RGB
 GL_RGBA  每个元素都是完整的RGBA元素
 GL_BGR_EXT
 GL_BGRA_EXT
 GL_LUMINANCE  每个元素都是单个亮度值
 GL_LUMINANCE_ALPHA
参8:像素数据的数据类型
 GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, 
 GL_SHORT, GL_UNSIGNED_INT, GL_INT, GL_FLOAT
参9:指向纹理的指针
//激活指定的纹理单元
glActiveTexture(GLenum texture);1:指定激活的纹理单元号,例如GL_TEXTURE0、GL_TEXTURE1、...

一个纹理单元可以有多个类型,1D、2D等,一个纹理对象能够绑定到多个目标类型上,
一个纹理对象可以绑定到多个纹理单元上,一个纹理单元上只能有一个同种纹理类型

例程:

//激活 GL_TEXTURE0纹理单元
glActiveTexture(GL_TEXTURE0);
//绑定 texture1纹理 到 GL_TEXTURE0纹理单元
glBindTexture(GL_TEXTURE_2D, texture1);
//将着色器中的ourTexture1采样器 与 GL_TEXTURE0纹理单元对应
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0);
//激活 GL_TEXTURE1纹理单元
glActiveTexture(GL_TEXTURE1);
//绑定 texture2纹理 到 GL_TEXTURE1纹理单元
glBindTexture(GL_TEXTURE_2D, texture2);
//将着色器中的ourTexture2采样器 与 GL_TEXTURE1纹理单元对应
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
//此时,采样器 与 纹理 就通过纹理单元关联了起来

七.帧缓冲区

默认使用一个帧缓冲和应用程序窗口关联。默认帧缓冲总会包含一个双重缓冲机制的颜色缓冲。
所谓双重缓冲,即一个直接在窗口显示的前置缓冲,以及一个用来渲染新图像的后备缓冲。
渲染图像是一个按照某个规律(一般是从左到右、从上到下)渲染每个像素。如果一个缓冲既要显示又在渲染,会发生什么呢?
闪烁感是难以幸免了,如果屏幕刷新率和渲染频率不一致的话,可能还会出现画面撕裂感,所以双缓冲的引入是非常必要的。
在这里eglSwapBuffers方法是在绘制指令处理完成,即图像已经渲染到后备缓冲之后,交换前后两个缓冲,将最新图像显示在屏幕上。

帧缓冲区对象(FBO),用于"离屏渲染"
就是暂时将未处理完的帧 不直接渲染到屏幕上,而是渲染到离屏Buffer中缓存起来,在恰当的时机取出来渲染到屏幕
FBO(Frame Buffer Object)提供了:
颜色缓冲区(color buffer)
深度缓冲区(depth buffer)
模版缓冲区(stencil buffer)
但不会直接为这些缓冲区分配空间,而是为这些缓冲区提供一个或多个挂接点。
我们需要为各个缓冲区分别创建对象,申请空间,然后挂接到相应的挂接点上。

FBO提供:
多个颜色附着点(GL_COLOR_ATTACHMENT0、GL_COLOR_ATTACHMENT1、...)
一个深度附着点(GL_DEPTH_ATTACHMENT)
一个模板附着点(GL_STENCIL_ATTACHMENT)
//生成一个FBO对象,保存对象ID
void glGenFramebuffers(GLsizei n, GLuint* framebuffers);1:要生成的FBO的数量
参2:存储要生成FBO的名称的数组
//绑定FBO
void glBindFramebuffer(GLenum target, GLuint framebuffer);1:FBO绑定的目标。必须是GL_FRAMEBUFFER
参2:FBO的名称
//关联纹理和FBO,并执行渲染步骤。后续若需要使用FBO的效果时,只操作与FBO绑定的纹理即可
glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);1:FBO绑定的目标。必须是GL_FRAMEBUFFER
参2:附加纹理的附着点。GL_COLOR_ATTACHMENT0、GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT
参3:纹理的目标。GL_TEXTURE_2D等
参4:要附加图像的纹理对象
参5:要附加的纹理的mipmap等级,必须为0
//解绑FBO,参1填0就是解绑
glBindFramebuffer();
//删除FBO
glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers);1:要删除的FBO的数量
参2:存储要删除FBO的名称的数组

默认情况下,OpenGL ES使用的帧缓冲区是窗口系统提供的,即屏幕本身就是一个默认的FBO。
使用FBO进行纹理贴图时,需要以真正的纹理坐标(原点在图片的左下角)为基准进行贴图。
若使用屏幕默认的FBO进行纹理贴图,应先以左下角为纹理坐标原点进行贴图,然后将贴图后的屏幕默认FBO绕x轴旋转180度,
即与屏幕坐标(原点在左上角)重合。
单独使用FBO时,也遵循以真正的纹理坐标(原点在图片的左下角)为基准进行贴图,再旋转。

自己创建的FBO与窗口系统提供的FBO有几个重要的不同:
1.自建FBO具有用于颜色缓冲器、深度缓冲器、模板缓冲器的可修改的附着点,图像可以附着和分离。
2.附加图像的大小和格式完全在GL内控制,不受窗口系统事件的影响,例如像素格式选择,窗口大小调整和显示模式改变。
3.当渲染或从自建FBO中读取时,像素所有权测试总是成功(即它们拥有所有像素)
4.没有可见的颜色缓冲位平面,只有一个“屏幕外”的彩色图像附件,因此没有前后缓冲区或swap。
5.没有多重采样缓冲区,因此依赖于实现的状态变量GL_SAMPLES和GL_SAMPLE_BUFFERS的值对于自建FBO都是零。

渲染缓冲区对象(RBO)
在创建完FBO之后,我们需要将一些东西放到FBO中来使用,RBO是一种图像表面,专为绑定到FBO而设计的
RBO是一种图像表面,可以是颜色表面、模板表面或深度模板组合等,可以为给定的FBO挑选任意的RBO组合

//生成RBO
void glGenRenderbuffers(GLsizei n,GLuint* renderbuffers);1:要生成的RBO的数量
参2:要存储生成RBO的名称的数组
//绑定RBO
void glBindRenderbuffer(GLenum target,GLuint renderbuffer);1:RBO绑定的目标,必须为GL_RENDERBUFFER
参2:RBO的名称
//创建并初始化RBO的数据存储、格式、尺寸
void glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);1:RBO绑定的目标,必须为GL_RENDERBUFFER
参2:RBO的颜色、深度、模板可渲染格式,GL_RGBA4,GL_RGB565,GL_RGB5_A1,GL_DEPTH_COMPONENT16等
参3:RBO的宽度(以像素为单位)4:RBO的高度(以像素为单位)
//将RBO附加到FBO上
void glFramebufferRenderbuffer(GLenum target, GLenum attachment,
          GLenum renderbuffertarget, GLuint renderbuffer);1:FBO绑定的目标。必须是GL_FRAMEBUFFER
参2:RBO应附加到的附加点,GL_COLOR_ATTACHMENT0,GL_DEPTH_ATTACHMENT、GL_STENCIL_ATTACHMENT
参3:RBO绑定的目标,必须为GL_RENDERBUFFER
参4:要附加的RBO

纹理缓冲区对象(TBO)??

//开一个视图的窗口
void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);12:指定了视图左下角位置
参34:指定了视图的宽度和高度
//启用或禁止写入深度缓冲区
void glDepthMask(GLboolean flag);1:指定是否器用深度缓冲区
 GL_FALSE
 GL_TRUE

因为最终的片段颜色值是RGBA格式的,所以我们要采样到纹理对应坐标位置的RGBA颜色值,但是视频是YUV格式的,这里就需要一个转化过程,即将YUV转为RGBA。OpenGL又给我们提供了GL_LUMINANCE这种格式,它表示只取一个颜色通道。因为通过GL_LUMINANCE这种格式,我们可以对YUV图像拆分为3个通道来读取。我们可以用3个纹理单元,分别将每个通道数据传入着色器中,最后在着色器中将3个通道数据又合在一起。
首先要做的一点就是,读取YUV视频,将其分解为3个颜色通道,并且可以一帧帧读取。


glm使用方法:

//位移
trans = glm::translate(trans, glm::vec3(1.0f, 0.0f, 0.0f));  //x轴方向位移1.0
//旋转
trans = glm::rotate(trans, glm::radians(45.0f), glm::vec3(0.0f, 0.0f, 1.0f));//沿z轴逆时针旋转45度
//缩放
trans = glm::scale(trans, glm::vec3(1.30, 1.30, 1.30));  //三个方向均缩放1.3倍
//将矩阵传递给着色器(glsl)
unsigned int transformLoc = glGetUniformLocation(myShader->ID, "transform");
glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans));

//在顶点着色器中定义mat4类型的变量transform,在main中与向量相乘后,赋值给gl_Position,这样才会改变
uniform mat4 transform;
 
void main()
{
   gl_Position = transform * vec4(aPos.x, aPos.y, aPos.z, 1.0);
   vertexColor = vec4(aColor.x, aColor.y, aColor.z, 1.0);
   TexCoord = aTexCoord;
}
glm::ortho(float left, float right, float bottom, float top, float zNear, float zFar);12:指定了平截头体的左右坐标。
参34:指定了平截头体的底部和顶部。
通过这四个参数我们定义了近平面和远平面的大小。
参56:定义了近平面和远平面的距离。
glm::perspective(float fovy, float aspect, float zNear, float zFar);1:视锥上下面之间的夹角
参2:宽高比,即视窗的宽/高
参34:分别为近截面和远截面的深度

有新的理解还会更新

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值