OpenGL概述
OpenGL(Open Graphics Library)是一个跨平台的、语言无关的应用程序编程接口(API),用于开发生成二维和三维图像的应用程序。这个API广泛用于计算机图形渲染,包括视频游戏、CAD(计算机辅助设计)、虚拟现实等领域。
- 特点和功能
OpenGL 的核心特点和功能包括:
- 跨平台:OpenGL 可以在多种操作系统上运行,包括 Windows、macOS、Linux、iOS 和 Android。这使得开发者可以编写一次代码,多平台部署。
- 底层硬件加速:OpenGL API 允许直接利用底层硬件(如图形处理单元或 GPU)加速图形渲染过程,从而实现高效率的图形输出。
- 广泛的应用:由于其高效和灵活的特性,OpenGL 被广泛应用于游戏开发、专业图形设计和科学可视化等多个领域。
- 版本和扩展:OpenGL 随着时间发展,推出了多个版本,每个版本都增加了新的特性和改进。此外,它支持扩展机制,允许硬件制造商实现并支持超出核心规范的新功能。
- 主要组件
- 渲染管线:OpenGL 定义了一个复杂的渲染管线,该管线描述了从原始顶点数据到最终图像的转换过程。这包括变换、光照、着色和光栅化等步骤。
- 着色器:从 OpenGL 2.0 开始,引入了着色器(Shader)程序,允许开发者通过编写顶点着色器和片元着色器来自定义渲染效果。着色器是用 GLSL(OpenGL Shading Language)编写的。
- 纹理:OpenGL 支持多种纹理技术,可以将图像或其他模式应用于三维物体表面,以增强视觉效果。
OpenGL ES在Android上进行视频帧渲染
-
总体流程
image.png
- 初始化 OpenGL ES 环境:
创建一个 GLSurfaceView 并将其添加到布局中。
创建一个自定义的 GLSurfaceView.Renderer 类,并实现其接口方法(如 onSurfaceCreated、onSurfaceChanged 和 onDrawFrame)。
- 加载和编译着色器:
编写顶点着色器和片段着色器,用于处理和显示视频帧数据。
使用 OpenGL ES 的 API 加载、编译并链接这些着色器。
- 创建纹理:
创建一个或多个纹理对象,用于存储视频帧数据。
使用 glGenTextures、glBindTexture 和 glTexImage2D 等函数初始化纹理。
- 设置纹理参数:
配置纹理的过滤和包装参数,例如 GL_TEXTURE_MIN_FILTER、GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S 和 GL_TEXTURE_WRAP_T。
- 上传视频帧数据到纹理:
在每次需要渲染新的视频帧时,将解码后的 YUV 数据上传到纹理中。通常 Y、U、V 分量分别上传到不同的纹理单元中。
- 绘制视频帧:
在 onDrawFrame 方法中,将纹理绑定到片段着色器中,并绘制一个矩形(通常是两个三角形构成)来显示视频帧。
- 释放资源:
在适当的时候释放 OpenGL ES 资源(如纹理和着色器)。
下面是Demo 用OpenGL ES 渲染视频帧:
public class VideoRenderer implements GLSurfaceView.Renderer {
private int[] textures = new int[3]; // YUV 分量的纹理
private int program;
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
// 加载和编译着色器
String vertexShaderCode = "uniform mat4 uMVPMatrix; attribute vec4 vPosition; attribute vec2 aTexCoord; varying vec2 vTexCoord; void main() { gl_Position = uMVPMatrix * vPosition; vTexCoord = aTexCoord; }";
String fragmentShaderCode = "precision mediump float; uniform sampler2D Ytex; uniform sampler2D Utex; uniform sampler2D Vtex; varying vec2 vTexCoord; void main() { vec3 yuv; yuv.x = texture2D(Ytex, vTexCoord).r; yuv.y = texture2D(Utex, vTexCoord).r - 0.5; yuv.z = texture2D(Vtex, vTexCoord).r - 0.5; gl_FragColor = vec4(mat3(1.0, 1.0, 1.0, 0.0, -0.39465, 2.03211, 1.13983, -0.58060, 0.0) * yuv, 1.0); }";
int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
program = GLES20.glCreateProgram();
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
GLES20.glLinkProgram(program);
GLES20.glUseProgram(program);
// 创建纹理
GLES20.glGenTextures(3, textures, 0);
for (int i = 0; i < 3; i++) {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[i]);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
}
}
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
}
@Override
public void onDrawFrame(GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
// 上传 YUV 数据到纹理
updateTextures();
// 绘制视频帧
GLES20.glUseProgram(program);
// 绑定纹理
for (int i = 0; i < 3; i++) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0 + i);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[i]);
}
// 绘制矩形
drawRectangle();
}
private void updateTextures() {
// 在这里上传解码后的 YUV 数据到纹理
}
private void drawRectangle() {
// 在这里绘制矩形,使用顶点和纹理坐标
}
private int loadShader(int type, String shaderCode) {
int shader = GLES20.glCreateShader(type);
GLES20.glShaderSource(shader, shaderCode);
GLES20.glCompileShader(shader);
return shader;
}
}