OpenGL ES 入门指南

一、OpenGL ES 简介

OpenGL ES (OpenGL for Embedded Systems) 是OpenGL的子集,专为移动设备和嵌入式系统设计。它是智能手机、平板电脑、游戏主机等设备上2D/3D图形渲染的标准API。

二、OpenGL ES版本

  • OpenGL ES 1.x: 固定功能管线

  • OpenGL ES 2.0: 可编程着色器管线,移除了固定功能

  • OpenGL ES 3.0/3.1/3.2: 扩展功能,更多特性

三、基础概念

1. 渲染管线

OpenGL ES 2.0+的可编程管线主要包含:

  1. 顶点着色器 - 处理每个顶点

  2. 图元装配 - 组成点、线、三角形

  3. 光栅化 - 将图元转换为片段

  4. 片段着色器 - 处理每个片段(像素)

  5. 逐片段操作 - 深度测试、混合等

2. 着色器(Shaders)

(1) 着色器类型
着色器类型 作用
顶点着色器 处理每个顶点的位置、法线、UV坐标等属性
片段着色器 计算每个像素(片段)的最终颜色
计算着色器 (ES 3.1+)通用计算,用于非图形任务(如物理模拟、粒子系统)
(2) 着色器编写流程
  1. 编写 GLSL ES 代码(顶点/片段着色器)。

  2. 编译着色器glCreateShader + glCompileShader)。

  3. 链接着色器程序glCreateProgram + glLinkProgram)。

  4. 在渲染时使用该程序glUseProgram)。

(3) 语法
(3.1) 语法版本声明

OpenGL ES 着色器需指定版本:

glsl

// OpenGL ES 2.0(WebGL 1.0)
#version 100

// OpenGL ES 3.0(WebGL 2.0)
#version 300 es
  • ES 2.0 使用较旧的语法(如 attribute / varying)。

  • ES 3.0+ 使用现代语法(如 in / out / layout)。

(3.2) 变量限定符
顶点着色器输入(Vertex Shader Input)
限定符 ES 2.0 ES 3.0+ 说明
顶点属性 attribute in + layout 每个顶点独有的数据(如位置)
统一变量 uniform uniform 全局常量(如变换矩阵)
顶点→片段着色器传递数据
限定符 ES 2.0 ES 3.0+ 说明
插值变量 varying out (VS) → in (FS) 光栅化阶段插值(如UV坐标)
片段着色器输出
限定符 ES 2.0 ES 3.0+ 说明
最终颜色 gl_FragColor out vec4 fragColor 片段着色器的输出颜色
(3.3) 精度限定符(Precision Qualifiers)

OpenGL ES 要求显式指定浮点数/整数精度(尤其是片段着色器):

glsl

// 设置默认浮点精度(ES 2.0 片段着色器必须声明)
precision mediump float;

// 声明变量时指定精度
highp vec3 position;   // 高精度(32-bit,适合位置)
mediump vec2 uv;       // 中等精度(16-bit,适合UV坐标)
lowp vec4 color;       // 低精度(10-bit,适合颜色)
 (4) OpenGL ES 2.0 着色器示例
(4.1) 顶点着色器(Vertex Shader)

glsl

#version 100
attribute vec3 aPosition;    // 顶点位置
attribute vec2 aTexCoord;    // 纹理坐标
varying vec2 vTexCoord;      // 传递给片段着色器的UV

uniform mat4 uMVPMatrix;     // 模型-视图-投影矩阵

void main() {
    gl_Position = uMVPMatrix * vec4(aPosition, 1.0);
    vTexCoord = aTexCoord;   // 传递UV坐标
}
(4.2) 片段着色器(Fragment Shader)

glsl

#version 100
precision mediump float;     // 必须声明默认精度

varying vec2 vTexCoord;      // 来自顶点着色器的UV
uniform sampler2D uTexture;  // 纹理采样器

void main() {
    gl_FragColor = texture2D(uTexture, vTexCoord);
}
 (5) OpenGL ES 3.0 着色器示例
(5.1) 顶点着色器(Vertex Shader)

glsl

#version 300 es
layout(location = 0) in vec3 aPosition;  // 使用 layout 指定属性位置
layout(location = 1) in vec2 aTexCoord;
out vec2 vTexCoord;                      // 输出到片段着色器

uniform mat4 uMVPMatrix;

void main() {
    gl_Position = uMVPMatrix * vec4(aPosition, 1.0);
    vTexCoord = aTexCoord;
}
(5.2) 片段着色器(Fragment Shader)

glsl

#version 300 es
precision mediump float;
in vec2 vTexCoord;           // 来自顶点着色器的输入
uniform sampler2D uTexture;
out vec4 fragColor;          // 输出颜色(替代 gl_FragColor)

void main() {
    fragColor = texture(uTexture, vTexCoord);
}

3. 坐标系统

  • 模型坐标 → 世界坐标 → 视图坐标 → 裁剪坐标 → 屏幕坐标

  • 通过模型(Model)、视图(View)、投影(Projection)矩阵变换

四、环境搭建

4.1 Android 开发环境

  1. 安装 Android Studio

  2. 配置 NDK (Native Development Kit)

  3. 在 CMakeLists.txt 中添加 OpenGL ES 依赖:

cmake

find_library( # Sets the name of the path variable.
              log-lib
              log )

find_library( gles-lib
              GLESv2 )

target_link_libraries( # Specifies the target library.
                      native-lib
                      ${log-lib}
                      ${gles-lib} )

4.2 iOS 开发环境

  1. 在 Xcode 项目中添加 OpenGLES.framework

  2. 包含头文件:

#import <OpenGLES/ES2/gl.h>
#import <OpenGLES/ES2/glext.h>

五、OpenGL ES 程序示例

初始化 OpenGL ES 上下文

// Android 示例
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
eglInitialize(display, nullptr, nullptr);

const EGLint attribs[] = {
    EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
    EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
    EGL_BLUE_SIZE, 8,
    EGL_GREEN_SIZE, 8,
    EGL_RED_SIZE, 8,
    EGL_DEPTH_SIZE, 16,
    EGL_NONE
};

EGLConfig config;
EGLint numConfigs;
eglChooseConfig(display, attribs, &config, 1, &numConfigs);

EGLSurface surface = eglCreateWindowSurface(display, config, window, nullptr);

const EGLint contextAttribs[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE
};

EGLContext context = eglCreateContext(display, config, nullptr, contextAttribs);
eglMakeCurrent(display, surface, surface, context);

简单的三角形绘制

// 顶点着色器
const char* vertexShaderSource = 
    "attribute vec4 vPosition;"
    "void main() {"
    "   gl_Position = vPosition;"
    "}";

// 片段着色器
const char* fragmentShaderSource = 
    "precision mediump float;"
    "void main() {"
    "   gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);"
    "}";

// 编译着色器
GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
glCompileShader(vertexShader);

GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
glCompileShader(fragmentShader);

// 创建程序
GLuint program = glCreateProgram();
glAttachShader(program, vertexShader);
glAttachShader(program, fragmentShader);
glLinkProgram(program);
glUseProgram(program);

// 顶点数据
GLfloat vertices[] = {
    0.0f,  0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f
};

// 创建VBO
GLuint vbo;
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

// 获取顶点属性位置
GLint positionLoc = glGetAttribLocation(program, "vPosition");
glEnableVertexAttribArray(positionLoc);
glVertexAttribPointer(positionLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);

// 绘制
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);

六、纹理映射

// 加载纹理
GLuint loadTexture(const char* data, int width, int height) {
    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    
    // 设置纹理参数
    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);
    
    // 加载纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 
                 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    
    return textureId;
}

// 在着色器中使用纹理
const char* fragmentShaderSource = 
    "precision mediump float;"
    "uniform sampler2D uTexture;"
    "varying vec2 vTexCoord;"
    "void main() {"
    "   gl_FragColor = texture2D(uTexture, vTexCoord);"
    "}";

七、3D 变换

// 在顶点着色器中添加变换矩阵
const char* vertexShaderSource = 
    "uniform mat4 uMVPMatrix;"
    "attribute vec4 vPosition;"
    "attribute vec2 aTexCoord;"
    "varying vec2 vTexCoord;"
    "void main() {"
    "   gl_Position = uMVPMatrix * vPosition;"
    "   vTexCoord = aTexCoord;"
    "}";

// C++ 代码中设置矩阵
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

glm::mat4 model = glm::mat4(1.0f);
glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f), 
                            glm::vec3(0.0f, 0.0f, 0.0f), 
                            glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 projection = glm::perspective(glm::radians(45.0f), 
                                      (float)width/(float)height, 
                                      0.1f, 100.0f);
glm::mat4 mvp = projection * view * model;

GLint mvpLoc = glGetUniformLocation(program, "uMVPMatrix");
glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, glm::value_ptr(mvp));

八、最佳实践

  1. 避免频繁的状态切换: 尽量一次性设置好所有状态

  2. 使用顶点缓冲对象(VBO): 减少CPU-GPU数据传输

  3. 批处理绘制调用: 合并多个小绘制调用

  4. 纹理压缩: 使用ETC/PVRTC等压缩格式

  5. 着色器优化: 减少分支和复杂计算

  6. 帧率控制: 使用垂直同步(VSync)避免过度绘制

九、调试技巧

  1. 使用 glGetError() 检查错误

  2. 在着色器编译后检查日志:

    GLint success;
    glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
    if (!success) {
        GLchar infoLog[512];
        glGetShaderInfoLog(shader, 512, nullptr, infoLog);
        // 输出错误信息
    }
  3. 使用图形调试工具如 RenderDoc 或 Xcode OpenGL ES Debugger

十、高级示例代码

  1. 帧缓冲对象(FBO)离屏渲染

    // OpenGL ES 2.0 FBO 离屏渲染完整示例
    #include <GLES2/gl2.h>
    #include <EGL/egl.h>
    #include <iostream>
    
    // 全局变量
    GLuint fbo;                     // 帧缓冲对象
    GLuint colorTexture;            // 颜色附件纹理
    GLuint depthStencilRBO;         // 深度和模板渲染缓冲对象
    GLuint quadVAO;                 // 全屏四边形VAO
    GLuint quadVBO;                 // 全屏四边形VBO
    GLuint sceneProgram;            // 场景着色器程序
    GLuint postProcessProgram;      // 后期处理着色器程序
    int width = 800, height
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

byxdaz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值