一、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+的可编程管线主要包含:
-
顶点着色器 - 处理每个顶点
-
图元装配 - 组成点、线、三角形
-
光栅化 - 将图元转换为片段
-
片段着色器 - 处理每个片段(像素)
-
逐片段操作 - 深度测试、混合等
2. 着色器(Shaders)
(1) 着色器类型
着色器类型 | 作用 |
---|---|
顶点着色器 | 处理每个顶点的位置、法线、UV坐标等属性 |
片段着色器 | 计算每个像素(片段)的最终颜色 |
计算着色器 | (ES 3.1+)通用计算,用于非图形任务(如物理模拟、粒子系统) |
(2) 着色器编写流程
-
编写 GLSL ES 代码(顶点/片段着色器)。
-
编译着色器(
glCreateShader
+glCompileShader
)。 -
链接着色器程序(
glCreateProgram
+glLinkProgram
)。 -
在渲染时使用该程序(
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; // 传递给片段着色器的UVuniform 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 开发环境
-
安装 Android Studio
-
配置 NDK (Native Development Kit)
-
在 CMakeLists.txt 中添加 OpenGL ES 依赖:
cmake
find_library( # Sets the name of the path variable.log-liblog )find_library( gles-libGLESv2 )target_link_libraries( # Specifies the target library.native-lib${log-lib}${gles-lib} )
4.2 iOS 开发环境
-
在 Xcode 项目中添加 OpenGLES.framework
-
包含头文件:
#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));
八、最佳实践
-
避免频繁的状态切换: 尽量一次性设置好所有状态
-
使用顶点缓冲对象(VBO): 减少CPU-GPU数据传输
-
批处理绘制调用: 合并多个小绘制调用
-
纹理压缩: 使用ETC/PVRTC等压缩格式
-
着色器优化: 减少分支和复杂计算
-
帧率控制: 使用垂直同步(VSync)避免过度绘制
九、调试技巧
-
使用
glGetError()
检查错误 -
在着色器编译后检查日志:
GLint success; glGetShaderiv(shader, GL_COMPILE_STATUS, &success); if (!success) {GLchar infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);// 输出错误信息 }
-
使用图形调试工具如 RenderDoc 或 Xcode OpenGL ES Debugger
十、高级示例代码
-
帧缓冲对象(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 = 600; // 渲染尺寸// 编译着色器工具函数 GLuint compileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, NULL);glCompileShader(shader);// 检查编译错误GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, NULL, infoLog);std::cerr << "Shader compilation failed:\n" << infoLog << std::endl;}return shader; }// 创建着色器程序工具函数 GLuint createShaderProgram(const char* vertSource, const char* fragSource) {GLuint vertShader = compileShader(GL_VERTEX_SHADER, vertSource);GLuint fragShader = compileShader(GL_FRAGMENT_SHADER, fragSource);GLuint program = glCreateProgram();glAttachShader(program, vertShader);glAttachShader(program, fragShader);glLinkProgram(program);// 检查链接错误GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, NULL, infoLog);std::cerr << "Program linking failed:\n" << infoLog << std::endl;}glDeleteShader(vertShader);glDeleteShader(fragShader);return program; }// 初始化FBO和渲染资源 void initResources() {// ============================================// 1. 创建帧缓冲对象(FBO)// ============================================glGenFramebuffers(1, &fbo);glBindFramebuffer(GL_FRAMEBUFFER, fbo);// ============================================// 2. 创建颜色附件纹理// ============================================glGenTextures(1, &colorTexture);glBindTexture(GL_TEXTURE_2D, colorTexture);// 创建空的纹理(数据传NULL,因为我们要渲染到它)glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);// 设置纹理参数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);// 将纹理附加到FBO的颜色附件glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTexture, 0);// ============================================// 3. 创建深度和模板渲染缓冲对象(RBO)// ============================================glGenRenderbuffers(1, &depthStencilRBO);glBindRenderbuffer(GL_RENDERBUFFER, depthStencilRBO);// 分配存储空间(24位深度 + 8位模板)glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);// 将RBO附加到FBO的深度和模板附件glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthStencilRBO);glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthStencilRBO);// ============================================// 4. 检查FBO完整性// ============================================if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {std::cerr << "Framebuffer is not complete!" << std::endl;}// 解绑FBO,回到默认帧缓冲glBindFramebuffer(GL_FRAMEBUFFER, 0);// ============================================// 5. 设置全屏四边形(用于后期处理)// ============================================float quadVertices[] = {// 位置(x,y) // 纹理坐标(s,t)-1.0f, 1.0f, 0.0f, 1.0f, // 左上-1.0f, -1.0f, 0.0f, 0.0f, // 左下1.0f, -1.0f, 1.0f, 0.0f, // 右下-1.0f, 1.0f, 0.0f, 1.0f, // 左上1.0f, -1.0f, 1.0f, 0.0f, // 右下1.0f, 1.0f, 1.0f, 1.0f // 右上};glGenVertexArrays(1, &quadVAO);glGenBuffers(1, &quadVBO);glBindVertexArray(quadVAO);glBindBuffer(GL_ARRAY_BUFFER, quadVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);// 位置属性glEnableVertexAttribArray(0);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);// 纹理坐标属性glEnableVertexAttribArray(1);glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));// ============================================// 6. 创建场景着色器程序(渲染一个旋转的彩色三角形)// ============================================const char* sceneVertexShader = "attribute vec4 vPosition;""uniform mat4 uMVPMatrix;""varying vec4 vColor;""void main() {"" gl_Position = uMVPMatrix * vPosition;"// 根据位置生成颜色(简单示例)" vColor = vec4(vPosition.x + 0.5, vPosition.y + 0.5, 0.5, 1.0);""}";const char* sceneFragmentShader = "precision mediump float;""varying vec4 vColor;""void main() {"" gl_FragColor = vColor;""}";sceneProgram = createShaderProgram(sceneVertexShader, sceneFragmentShader);// ============================================// 7. 创建后期处理着色器程序(边缘检测效果)// ============================================const char* postProcessVertexShader = "attribute vec2 aPos;""attribute vec2 aTexCoord;""varying vec2 vTexCoord;""void main() {"" gl_Position = vec4(aPos, 0.0, 1.0);"" vTexCoord = aTexCoord;""}";const char* postProcessFragmentShader = "precision mediump float;""varying vec2 vTexCoord;""uniform sampler2D uTexture;""uniform vec2 uTexelSize;""void main() {"// Sobel边缘检测算子" vec3 topLeft = texture2D(uTexture, vTexCoord + vec2(-uTexelSize.x, uTexelSize.y)).rgb;"" vec3 top = texture2D(uTexture, vTexCoord + vec2(0.0, uTexelSize.y)).rgb;"" vec3 topRight = texture2D(uTexture, vTexCoord + vec2(uTexelSize.x, uTexelSize.y)).rgb;"" vec3 left = texture2D(uTexture, vTexCoord + vec2(-uTexelSize.x, 0.0)).rgb;"" vec3 center = texture2D(uTexture, vTexCoord).rgb;"" vec3 right = texture2D(uTexture, vTexCoord + vec2(uTexelSize.x, 0.0)).rgb;"" vec3 bottomLeft = texture2D(uTexture, vTexCoord + vec2(-uTexelSize.x, -uTexelSize.y)).rgb;"" vec3 bottom = texture2D(uTexture, vTexCoord + vec2(0.0, -uTexelSize.y)).rgb;"" vec3 bottomRight = texture2D(uTexture, vTexCoord + vec2(uTexelSize.x, -uTexelSize.y)).rgb;"// 计算水平和垂直梯度" vec3 gx = -topLeft - 2.0 * left - bottomLeft + topRight + 2.0 * right + bottomRight;"" vec3 gy = -topLeft - 2.0 * top - topRight + bottomLeft + 2.0 * bottom + bottomRight;"// 计算梯度幅度" vec3 edge = sqrt(gx * gx + gy * gy);"" float edgeIntensity = length(edge);"// 输出边缘检测结果(黑色背景+白色边缘)" gl_FragColor = vec4(vec3(1.0 - edgeIntensity), 1.0);""}";postProcessProgram = createShaderProgram(postProcessVertexShader, postProcessFragmentShader); }// 渲染场景到FBO void renderSceneToFBO(float angle) {// ============================================// 1. 绑定自定义FBO// ============================================glBindFramebuffer(GL_FRAMEBUFFER, fbo);glViewport(0, 0, width, height);// ============================================// 2. 清除颜色和深度缓冲// ============================================glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// ============================================// 3. 绘制场景(这里是一个旋转的彩色三角形)// ============================================glUseProgram(sceneProgram);// 定义三角形顶点float vertices[] = {0.0f, 0.5f, 0.0f, // 上顶点-0.5f, -0.5f, 0.0f, // 左下0.5f, -0.5f, 0.0f // 右下};// 创建旋转矩阵float s = sin(angle), c = cos(angle);float mvpMatrix[] = {c, -s, 0.0f, 0.0f,s, c, 0.0f, 0.0f,0.0f, 0.0f, 1.0f, 0.0f,0.0f, 0.0f, 0.0f, 1.0f};// 传递矩阵到着色器GLint mvpLoc = glGetUniformLocation(sceneProgram, "uMVPMatrix");glUniformMatrix4fv(mvpLoc, 1, GL_FALSE, mvpMatrix);// 绘制三角形GLuint vbo;glGenBuffers(1, &vbo);glBindBuffer(GL_ARRAY_BUFFER, vbo);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);GLint posLoc = glGetUniformLocation(sceneProgram, "vPosition");glEnableVertexAttribArray(posLoc);glVertexAttribPointer(posLoc, 3, GL_FLOAT, GL_FALSE, 0, 0);glDrawArrays(GL_TRIANGLES, 0, 3);// 清理glDeleteBuffers(1, &vbo); }// 渲染后期处理效果 void renderPostProcessing() {// ============================================// 1. 绑定回默认帧缓冲(屏幕)// ============================================glBindFramebuffer(GL_FRAMEBUFFER, 0);glViewport(0, 0, width, height);glClear(GL_COLOR_BUFFER_BIT);// ============================================// 2. 使用后期处理着色器渲染全屏四边形// ============================================glUseProgram(postProcessProgram);// 绑定FBO的颜色纹理glActiveTexture(GL_TEXTURE0);glBindTexture(GL_TEXTURE_2D, colorTexture);glUniform1i(glGetUniformLocation(postProcessProgram, "uTexture"), 0);// 设置纹理像素大小(用于边缘检测)glUniform2f(glGetUniformLocation(postProcessProgram, "uTexelSize"), 1.0f/width, 1.0f/height);// 绘制全屏四边形glBindVertexArray(quadVAO);glDrawArrays(GL_TRIANGLES, 0, 6); }// 主渲染函数 void renderFrame(float angle) {// 1. 渲染场景到FBOrenderSceneToFBO(angle);// 2. 渲染后期处理效果到屏幕renderPostProcessing(); }// 清理资源 void cleanup() {glDeleteFramebuffers(1, &fbo);glDeleteTextures(1, &colorTexture);glDeleteRenderbuffers(1, &depthStencilRBO);glDeleteVertexArrays(1, &quadVAO);glDeleteBuffers(1, &quadVBO);glDeleteProgram(sceneProgram);glDeleteProgram(postProcessProgram); }// 示例主循环(伪代码) int main() {// 初始化OpenGL ES和EGL(平台相关代码略)initEGLAndOpenGLES();// 初始化资源initResources();// 主循环float angle = 0.0f;while (!shouldClose) {angle += 0.01f; // 更新旋转角度// 渲染帧renderFrame(angle);// 交换缓冲eglSwapBuffers(eglDisplay, eglSurface);}// 清理资源cleanup();return 0; }
-
多重采样抗锯齿(MSAA)
EGL配置设置 (启用MSAA) // 选择EGL配置时请求MSAA const EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,EGL_SURFACE_TYPE, EGL_WINDOW_BIT,EGL_BLUE_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_RED_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_DEPTH_SIZE, 24,EGL_STENCIL_SIZE, 8,EGL_SAMPLE_BUFFERS, 1, // 启用多重采样EGL_SAMPLES, 4, // 4x MSAAEGL_NONE };完整代码 #include <GLES3/gl3.h> #include <EGL/egl.h> #include <vector> #include <cmath> #include <iostream> #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp>// 顶点着色器 const char* vertexShaderSource = R"( #version 300 es precision mediump float; layout(location = 0) in vec3 aPos; layout(location = 1) in vec3 aColor;out vec3 ourColor;uniform mat4 model; uniform mat4 view; uniform mat4 projection;void main() {gl_Position = projection * view * model * vec4(aPos, 1.0);ourColor = aColor; } )";// 片段着色器 const char* fragmentShaderSource = R"( #version 300 es precision mediump float;in vec3 ourColor; out vec4 FragColor;void main() {FragColor = vec4(ourColor, 1.0); } )";// 立方体顶点数据 float vertices[] = {// 位置 // 颜色-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,// 其他面数据...// 为简洁起见省略,实际使用时需要完整立方体数据 };GLuint CompileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, nullptr);glCompileShader(shader);GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);std::cerr << "Shader compilation failed:\n" << infoLog << std::endl;}return shader; }GLuint CreateShaderProgram(const char* vertexSource, const char* fragmentSource) {GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexSource);GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentSource);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Program linking failed:\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);return program; }int main() {// 初始化EGLEGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);eglInitialize(eglDisplay, nullptr, nullptr);// 配置MSAAconst EGLint attribs[] = {EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,EGL_SURFACE_TYPE, EGL_WINDOW_BIT,EGL_BLUE_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_RED_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_DEPTH_SIZE, 24,EGL_STENCIL_SIZE, 8,EGL_SAMPLE_BUFFERS, 1, // 启用多重采样EGL_SAMPLES, 4, // 4x MSAAEGL_NONE};EGLint numConfigs;EGLConfig eglConfig;eglChooseConfig(eglDisplay, attribs, &eglConfig, 1, &numConfigs);// 创建窗口表面 (具体平台相关代码省略)EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, nativeWindow, nullptr);// 创建上下文const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 3,EGL_NONE};EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, contextAttribs);eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext);// 加载着色器GLuint shaderProgram = CreateShaderProgram(vertexShaderSource, fragmentShaderSource);// 设置顶点缓冲区和属性GLuint VBO, VAO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);// 位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 颜色属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// 启用多重采样 (通常默认启用)glEnable(GL_MULTISAMPLE);// 渲染循环while (!shouldClose) {// 清除颜色和深度缓冲glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 启用深度测试glEnable(GL_DEPTH_TEST);// 使用着色器程序glUseProgram(shaderProgram);// 创建变换矩阵glm::mat4 model = glm::rotate(glm::mat4(1.0f), (float)glfwGetTime(), glm::vec3(0.5f, 1.0f, 0.0f));glm::mat4 view = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -3.0f));glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);// 传递矩阵到着色器GLint modelLoc = glGetUniformLocation(shaderProgram, "model");GLint viewLoc = glGetUniformLocation(shaderProgram, "view");GLint projLoc = glGetUniformLocation(shaderProgram, "projection");glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(model));glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(view));glUniformMatrix4fv(projLoc, 1, GL_FALSE, glm::value_ptr(projection));// 渲染立方体glBindVertexArray(VAO);glDrawArrays(GL_TRIANGLES, 0, 36);// 交换缓冲区eglSwapBuffers(eglDisplay, eglSurface);}// 清理资源glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);// 清理EGLeglDestroyContext(eglDisplay, eglContext);eglDestroySurface(eglDisplay, eglSurface);eglTerminate(eglDisplay);return 0; }
-
实例化渲染
顶点着色器 (instancing.vert) #version 300 es precision mediump float;// 标准顶点属性 layout(location = 0) in vec3 aPosition; layout(location = 1) in vec3 aNormal; layout(location = 2) in vec2 aTexCoord;// 实例化属性 - 每个实例不同 layout(location = 3) in vec3 aInstancePosition; layout(location = 4) in vec3 aInstanceColor; layout(location = 5) in float aInstanceScale;// 输出到片段着色器 out vec3 vNormal; out vec2 vTexCoord; out vec3 vColor;// 统一变量 uniform mat4 uViewProjection;void main() {// 应用实例缩放和平移vec3 position = aPosition * aInstanceScale + aInstancePosition;gl_Position = uViewProjection * vec4(position, 1.0);vNormal = aNormal;vTexCoord = aTexCoord;vColor = aInstanceColor; }片段着色器 (instancing.frag) #version 300 es precision mediump float;// 从顶点着色器输入 in vec3 vNormal; in vec2 vTexCoord; in vec3 vColor;// 输出颜色 out vec4 fragColor;void main() {// 简单的光照计算vec3 lightDir = normalize(vec3(0.5, 1.0, 0.7));float diff = max(dot(normalize(vNormal), lightDir), 0.2);vec3 lighting = vColor * (diff + 0.2); // 环境光+漫反射fragColor = vec4(lighting, 1.0); }C++ 主程序 (main.cpp) #include <GLES3/gl3.h> #include <EGL/egl.h> #include <vector> #include <cmath> #include <iostream>// 简单的立方体顶点数据 const float cubeVertices[] = {// 位置 // 法线 // 纹理坐标-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f,-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f,-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f,// 其他面的数据...// 为简洁起见省略,实际使用时需要完整立方体数据 };// 编译着色器 GLuint CompileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, nullptr);glCompileShader(shader);// 检查编译错误GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);std::cerr << "Shader compilation failed:\n" << infoLog << std::endl;}return shader; }// 创建着色器程序 GLuint CreateShaderProgram(const char* vertexSource, const char* fragmentSource) {GLuint vertexShader = CompileShader(GL_VERTEX_SHADER, vertexSource);GLuint fragmentShader = CompileShader(GL_FRAGMENT_SHADER, fragmentSource);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);// 检查链接错误GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Program linking failed:\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);return program; }int main() {// 初始化EGL和OpenGL ES (平台相关代码省略)// 假设已经创建了有效的EGL上下文// 加载着色器const char* vertexShaderSource = "..."; // 上面顶点着色器代码const char* fragmentShaderSource = "..."; // 上面片段着色器代码GLuint shaderProgram = CreateShaderProgram(vertexShaderSource, fragmentShaderSource);// 设置立方体VAOGLuint cubeVAO, cubeVBO;glGenVertexArrays(1, &cubeVAO);glGenBuffers(1, &cubeVBO);glBindVertexArray(cubeVAO);glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);// 顶点属性// 位置glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);// 法线glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));// 纹理坐标glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));// 实例化数据const int instanceCount = 1000;std::vector<glm::vec3> instancePositions;std::vector<glm::vec3> instanceColors;std::vector<float> instanceScales;// 生成随机实例数据for (int i = 0; i < instanceCount; ++i) {// 在球面上随机分布float radius = 5.0f;float theta = static_cast<float>(rand()) / RAND_MAX * 2.0f * M_PI;float phi = static_cast<float>(rand()) / RAND_MAX * M_PI;instancePositions.emplace_back(radius * sin(phi) * cos(theta),radius * sin(phi) * sin(theta),radius * cos(phi));instanceColors.emplace_back(static_cast<float>(rand()) / RAND_MAX,static_cast<float>(rand()) / RAND_MAX,static_cast<float>(rand()) / RAND_MAX);instanceScales.push_back(0.1f + static_cast<float>(rand()) / RAND_MAX * 0.2f);}// 实例化VBOGLuint instanceVBOs[3];glGenBuffers(3, instanceVBOs);// 位置glBindBuffer(GL_ARRAY_BUFFER, instanceVBOs[0]);glBufferData(GL_ARRAY_BUFFER, instanceCount * sizeof(glm::vec3), instancePositions.data(), GL_STATIC_DRAW);glEnableVertexAttribArray(3);glVertexAttribPointer(3, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);glVertexAttribDivisor(3, 1); // 告诉OpenGL这是每实例数据// 颜色glBindBuffer(GL_ARRAY_BUFFER, instanceVBOs[1]);glBufferData(GL_ARRAY_BUFFER, instanceCount * sizeof(glm::vec3), instanceColors.data(), GL_STATIC_DRAW);glEnableVertexAttribArray(4);glVertexAttribPointer(4, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (void*)0);glVertexAttribDivisor(4, 1);// 缩放glBindBuffer(GL_ARRAY_BUFFER, instanceVBOs[2]);glBufferData(GL_ARRAY_BUFFER, instanceCount * sizeof(float), instanceScales.data(), GL_STATIC_DRAW);glEnableVertexAttribArray(5);glVertexAttribPointer(5, 1, GL_FLOAT, GL_FALSE, sizeof(float), (void*)0);glVertexAttribDivisor(5, 1);glBindVertexArray(0);// 设置视口glViewport(0, 0, 800, 600);// 简单的视图投影矩阵glm::mat4 view = glm::lookAt(glm::vec3(0.0f, 0.0f, 10.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), 800.0f / 600.0f, 0.1f, 100.0f);glm::mat4 viewProjection = projection * view;// 渲染循环while (!shouldClose) { // shouldClose由平台代码控制glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 启用深度测试glEnable(GL_DEPTH_TEST);// 使用着色器glUseProgram(shaderProgram);// 上传视图投影矩阵GLuint viewProjLoc = glGetUniformLocation(shaderProgram, "uViewProjection");glUniformMatrix4fv(viewProjLoc, 1, GL_FALSE, glm::value_ptr(viewProjection));// 绘制实例化立方体glBindVertexArray(cubeVAO);glDrawArraysInstanced(GL_TRIANGLES, 0, 36, instanceCount); // 假设完整立方体有36个顶点glBindVertexArray(0);// 交换缓冲区eglSwapBuffers(eglDisplay, eglSurface);}// 清理glDeleteVertexArrays(1, &cubeVAO);glDeleteBuffers(1, &cubeVBO);glDeleteBuffers(3, instanceVBOs);glDeleteProgram(shaderProgram);return 0; }
-
计算着色器(OpenGL ES 3.1+)
#include <GLES3/gl31.h> #include <EGL/egl.h> #include <iostream> #include <vector>// 计算着色器源码 - 简单数组相加 const char* computeShaderSource = R"( #version 310 es layout(local_size_x = 1) in; // 定义工作组大小layout(binding = 0) buffer InputA {float a[]; };layout(binding = 1) buffer InputB {float b[]; };layout(binding = 2) buffer Output {float result[]; };void main() {uint index = gl_GlobalInvocationID.x;result[index] = a[index] + b[index]; } )";// 顶点着色器源码 (用于渲染结果) const char* vertexShaderSource = R"( #version 310 es precision highp float; layout (location = 0) in vec2 aPos; layout (location = 1) in float aValue;out float vValue;void main() {gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0);vValue = aValue; } )";// 片段着色器源码 (用于渲染结果) const char* fragmentShaderSource = R"( #version 310 es precision highp float; in float vValue; out vec4 FragColor;void main() {// 根据值的大小着色 (0-1映射到蓝-红)FragColor = vec4(vValue, 0.0, 1.0 - vValue, 1.0); } )";GLuint compileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, nullptr);glCompileShader(shader);GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);std::cerr << "Shader compilation error:\n" << infoLog << std::endl;}return shader; }GLuint createComputeProgram() {GLuint computeShader = compileShader(GL_COMPUTE_SHADER, computeShaderSource);GLuint program = glCreateProgram();glAttachShader(program, computeShader);glLinkProgram(program);GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Compute program linking error:\n" << infoLog << std::endl;}glDeleteShader(computeShader);return program; }GLuint createRenderProgram() {GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Render program linking error:\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);return program; }int main() {// 初始化EGL和OpenGL ES上下文 (这里简化了)// 实际应用中需要根据平台正确初始化const int dataSize = 64; // 数据元素数量// 准备输入数据std::vector<float> inputA(dataSize);std::vector<float> inputB(dataSize);std::vector<float> output(dataSize);for (int i = 0; i < dataSize; ++i) {inputA[i] = (float)i / dataSize;inputB[i] = (float)(dataSize - i) / dataSize;}// 创建SSBO (着色器存储缓冲对象)GLuint ssboA, ssboB, ssboOutput;glGenBuffers(1, &ssboA);glGenBuffers(1, &ssboB);glGenBuffers(1, &ssboOutput);// 绑定并填充SSBOglBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboA);glBufferData(GL_SHADER_STORAGE_BUFFER, dataSize * sizeof(float), inputA.data(), GL_STATIC_DRAW);glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboB);glBufferData(GL_SHADER_STORAGE_BUFFER, dataSize * sizeof(float), inputB.data(), GL_STATIC_DRAW);glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboOutput);glBufferData(GL_SHADER_STORAGE_BUFFER, dataSize * sizeof(float), nullptr, GL_STATIC_DRAW);// 创建计算着色器程序GLuint computeProgram = createComputeProgram();// 运行计算着色器glUseProgram(computeProgram);// 绑定SSBO到指定绑定点glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, ssboA);glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 1, ssboB);glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 2, ssboOutput);// 分派计算着色器glDispatchCompute(dataSize, 1, 1); // 每个工作组处理一个元素// 确保计算完成glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);// 读取结果glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssboOutput);float* result = (float*)glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, dataSize * sizeof(float), GL_MAP_READ_BIT);// 打印部分结果std::cout << "Compute shader results (first 10 elements):\n";for (int i = 0; i < 10; ++i) {std::cout << inputA[i] << " + " << inputB[i] << " = " << result[i] << "\n";}glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);// 创建渲染数据 (可视化结果)std::vector<float> positions(dataSize * 2);for (int i = 0; i < dataSize; ++i) {positions[i*2] = -1.0f + 2.0f * i / dataSize;positions[i*2+1] = 0.0f;}// 创建渲染VAO和VBOGLuint VAO, posVBO, valueVBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &posVBO);glGenBuffers(1, &valueVBO);glBindVertexArray(VAO);// 位置数据glBindBuffer(GL_ARRAY_BUFFER, posVBO);glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(float), positions.data(), GL_STATIC_DRAW);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 值数据 (来自计算结果)glBindBuffer(GL_ARRAY_BUFFER, valueVBO);glBufferData(GL_ARRAY_BUFFER, output.size() * sizeof(float), result, GL_STATIC_DRAW);glVertexAttribPointer(1, 1, GL_FLOAT, GL_FALSE, sizeof(float), (void*)0);glEnableVertexAttribArray(1);// 创建渲染程序GLuint renderProgram = createRenderProgram();// 渲染循环while (true) { // 实际应用中应该有终止条件glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(renderProgram);glBindVertexArray(VAO);glDrawArrays(GL_POINTS, 0, dataSize);// 实际应用中应该交换缓冲区// eglSwapBuffers(...);}// 清理资源glDeleteBuffers(1, &ssboA);glDeleteBuffers(1, &ssboB);glDeleteBuffers(1, &ssboOutput);glDeleteBuffers(1, &posVBO);glDeleteBuffers(1, &valueVBO);glDeleteVertexArrays(1, &VAO);glDeleteProgram(computeProgram);glDeleteProgram(renderProgram);return 0; }
-
几何着色器(OpenGL ES 3.2+)。几何着色器可以用于:动态生成几何图形、点精灵扩展、几何细分、法线可视化等。
#include <GLES3/gl32.h> #include <EGL/egl.h> #include <iostream> #include <vector> #include <cmath>// 顶点着色器源码 const char* vertexShaderSource = R"( #version 320 es precision highp float; layout (location = 0) in vec2 aPos;void main() {gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); } )";// 几何着色器源码 - 将点扩展为三角形 const char* geometryShaderSource = R"( #version 320 es precision highp float; layout (points) in; layout (triangle_strip, max_vertices = 3) out;void main() {// 第一个顶点gl_Position = gl_in[0].gl_Position + vec4(-0.1, -0.1, 0.0, 0.0);EmitVertex();// 第二个顶点gl_Position = gl_in[0].gl_Position + vec4(0.1, -0.1, 0.0, 0.0);EmitVertex();// 第三个顶点gl_Position = gl_in[0].gl_Position + vec4(0.0, 0.1, 0.0, 0.0);EmitVertex();EndPrimitive(); } )";// 片段着色器源码 const char* fragmentShaderSource = R"( #version 320 es precision highp float; out vec4 FragColor;void main() {FragColor = vec4(1.0, 0.5, 0.2, 1.0); } )";// 编译着色器 GLuint compileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, nullptr);glCompileShader(shader);// 检查编译错误GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);std::cerr << "Shader compilation error:\n" << infoLog << std::endl;}return shader; }// 创建着色器程序 GLuint createShaderProgram() {GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vertexShaderSource);GLuint geometryShader = compileShader(GL_GEOMETRY_SHADER, geometryShaderSource);GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fragmentShaderSource);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, geometryShader);glAttachShader(program, fragmentShader);glLinkProgram(program);// 检查链接错误GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Program linking error:\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(geometryShader);glDeleteShader(fragmentShader);return program; }int main() {// 初始化EGL和OpenGL ES (这里简化了EGL初始化过程)// 实际应用中需要根据平台正确初始化EGL// 顶点数据 - 一些点std::vector<float> vertices = {-0.5f, 0.5f,0.0f, 0.0f,0.5f, -0.5f};// 创建VAO和VBOGLuint VAO, VBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 创建着色器程序GLuint shaderProgram = createShaderProgram();// 渲染循环while (true) { // 实际应用中应该有终止条件glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_POINTS, 0, 3); // 绘制3个点// 实际应用中应该交换缓冲区// eglSwapBuffers(...);}// 清理资源glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteProgram(shaderProgram);return 0; }
-
基于物理的渲染(PBR)
#include <GLES3/gl3.h> #include <EGL/egl.h> #include <vector> #include <cmath> #include <string>// PBR 顶点着色器 const char* pbrVertexShader = R"( #version 300 es precision highp float; layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords;out vec3 WorldPos; out vec3 Normal; out vec2 TexCoords;uniform mat4 projection; uniform mat4 view; uniform mat4 model;void main() {WorldPos = vec3(model * vec4(aPos, 1.0));Normal = mat3(transpose(inverse(model))) * aNormal;TexCoords = aTexCoords;gl_Position = projection * view * vec4(WorldPos, 1.0); } )";// PBR 片段着色器 const char* pbrFragmentShader = R"( #version 300 es precision highp float;in vec3 WorldPos; in vec3 Normal; in vec2 TexCoords;uniform vec3 albedo; uniform float metallic; uniform float roughness; uniform float ao;uniform vec3 lightPositions[4]; uniform vec3 lightColors[4];uniform vec3 camPos;const float PI = 3.14159265359;out vec4 FragColor;// 法线分布函数 (Trowbridge-Reitz GGX) float DistributionGGX(vec3 N, vec3 H, float roughness) {float a = roughness * roughness;float a2 = a * a;float NdotH = max(dot(N, H), 0.0);float NdotH2 = NdotH * NdotH;float nom = a2;float denom = (NdotH2 * (a2 - 1.0) + 1.0);denom = PI * denom * denom;return nom / denom; }// 几何函数 (Schlick GGX) float GeometrySchlickGGX(float NdotV, float roughness) {float r = (roughness + 1.0);float k = (r * r) / 8.0;float nom = NdotV;float denom = NdotV * (1.0 - k) + k;return nom / denom; }// 几何函数 (Smith) float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {float NdotV = max(dot(N, V), 0.0);float NdotL = max(dot(N, L), 0.0);float ggx2 = GeometrySchlickGGX(NdotV, roughness);float ggx1 = GeometrySchlickGGX(NdotL, roughness);return ggx1 * ggx2; }// Fresnel-Schlick近似 vec3 fresnelSchlick(float cosTheta, vec3 F0) {return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); }void main() {vec3 N = normalize(Normal);vec3 V = normalize(camPos - WorldPos);// 计算反射率vec3 F0 = vec3(0.04); F0 = mix(F0, albedo, metallic);// 反射方程vec3 Lo = vec3(0.0);for(int i = 0; i < 4; ++i){// 计算每个光源的辐射vec3 L = normalize(lightPositions[i] - WorldPos);vec3 H = normalize(V + L);float distance = length(lightPositions[i] - WorldPos);float attenuation = 1.0 / (distance * distance);vec3 radiance = lightColors[i] * attenuation;// Cook-Torrance BRDFfloat NDF = DistributionGGX(N, H, roughness); float G = GeometrySmith(N, V, L, roughness); vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); vec3 nominator = NDF * G * F;float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;vec3 specular = nominator / denominator;vec3 kS = F;vec3 kD = vec3(1.0) - kS;kD *= 1.0 - metallic; float NdotL = max(dot(N, L), 0.0); Lo += (kD * albedo / PI + specular) * radiance * NdotL;} // 环境光照 (简化版)vec3 ambient = vec3(0.03) * albedo * ao;vec3 color = ambient + Lo;// HDR色调映射color = color / (color + vec3(1.0));// gamma校正color = pow(color, vec3(1.0/2.2)); FragColor = vec4(color, 1.0); } )";// 球体网格生成 void createSphere(std::vector<float>& vertices, std::vector<unsigned int>& indices, unsigned int X_SEGMENTS = 64, unsigned int Y_SEGMENTS = 64) {const float PI = 3.14159265359f;for (unsigned int y = 0; y <= Y_SEGMENTS; ++y){for (unsigned int x = 0; x <= X_SEGMENTS; ++x){float xSegment = (float)x / (float)X_SEGMENTS;float ySegment = (float)y / (float)Y_SEGMENTS;float xPos = std::cos(xSegment * 2.0f * PI) * std::sin(ySegment * PI);float yPos = std::cos(ySegment * PI);float zPos = std::sin(xSegment * 2.0f * PI) * std::sin(ySegment * PI);vertices.push_back(xPos);vertices.push_back(yPos);vertices.push_back(zPos);// 法线就是位置向量vertices.push_back(xPos);vertices.push_back(yPos);vertices.push_back(zPos);// 纹理坐标vertices.push_back(xSegment);vertices.push_back(ySegment);}}for (unsigned int y = 0; y < Y_SEGMENTS; ++y){for (unsigned int x = 0; x < X_SEGMENTS; ++x){indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);indices.push_back(y * (X_SEGMENTS + 1) + x);indices.push_back(y * (X_SEGMENTS + 1) + x + 1);indices.push_back((y + 1) * (X_SEGMENTS + 1) + x);indices.push_back(y * (X_SEGMENTS + 1) + x + 1);indices.push_back((y + 1) * (X_SEGMENTS + 1) + x + 1);}} }GLuint compileShader(GLenum type, const char* source) {GLuint shader = glCreateShader(type);glShaderSource(shader, 1, &source, nullptr);glCompileShader(shader);GLint success;glGetShaderiv(shader, GL_COMPILE_STATUS, &success);if (!success) {char infoLog[512];glGetShaderInfoLog(shader, 512, nullptr, infoLog);std::cerr << "Shader compilation error:\n" << infoLog << std::endl;}return shader; }GLuint createShaderProgram(const char* vsSource, const char* fsSource) {GLuint vertexShader = compileShader(GL_VERTEX_SHADER, vsSource);GLuint fragmentShader = compileShader(GL_FRAGMENT_SHADER, fsSource);GLuint program = glCreateProgram();glAttachShader(program, vertexShader);glAttachShader(program, fragmentShader);glLinkProgram(program);GLint success;glGetProgramiv(program, GL_LINK_STATUS, &success);if (!success) {char infoLog[512];glGetProgramInfoLog(program, 512, nullptr, infoLog);std::cerr << "Program linking error:\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);return program; }int main() {// 初始化EGL和OpenGL ES上下文 (这里简化了)// 创建球体网格std::vector<float> sphereVertices;std::vector<unsigned int> sphereIndices;createSphere(sphereVertices, sphereIndices);// 创建VAO和VBOGLuint sphereVAO, sphereVBO, sphereEBO;glGenVertexArrays(1, &sphereVAO);glGenBuffers(1, &sphereVBO);glGenBuffers(1, &sphereEBO);glBindVertexArray(sphereVAO);glBindBuffer(GL_ARRAY_BUFFER, sphereVBO);glBufferData(GL_ARRAY_BUFFER, sphereVertices.size() * sizeof(float), sphereVertices.data(), GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, sphereEBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sphereIndices.size() * sizeof(unsigned int), sphereIndices.data(), GL_STATIC_DRAW);// 位置属性glEnableVertexAttribArray(0);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);// 法线属性glEnableVertexAttribArray(1);glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));// 纹理坐标属性glEnableVertexAttribArray(2);glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));// 创建PBR着色器程序GLuint pbrShader = createShaderProgram(pbrVertexShader, pbrFragmentShader);// 光源设置glm::vec3 lightPositions[] = {glm::vec3(-10.0f, 10.0f, 10.0f),glm::vec3( 10.0f, 10.0f, 10.0f),glm::vec3(-10.0f, -10.0f, 10.0f),glm::vec3( 10.0f, -10.0f, 10.0f)};glm::vec3 lightColors[] = {glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f),glm::vec3(300.0f, 300.0f, 300.0f)};// 相机位置glm::vec3 cameraPos(0.0f, 0.0f, 5.0f);// 投影矩阵glm::mat4 projection = glm::perspective(glm::radians(45.0f), 800.0f / 600.0f, 0.1f, 100.0f);// 渲染循环while (true) {// 清除颜色和深度缓冲glClearColor(0.1f, 0.1f, 0.1f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 视图矩阵glm::mat4 view = glm::lookAt(cameraPos, glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f));// 激活PBR着色器glUseProgram(pbrShader);// 传递矩阵glUniformMatrix4fv(glGetUniformLocation(pbrShader, "projection"), 1, GL_FALSE, &projection[0][0]);glUniformMatrix4fv(glGetUniformLocation(pbrShader, "view"), 1, GL_FALSE, &view[0][0]);// 传递光源信息for (int i = 0; i < 4; ++i) {glUniform3fv(glGetUniformLocation(pbrShader, ("lightPositions[" + std::to_string(i) + "]").c_str()), 1, &lightPositions[i][0]);glUniform3fv(glGetUniformLocation(pbrShader, ("lightColors[" + std::to_string(i) + "]").c_str()), 1, &lightColors[i][0]);}// 传递相机位置glUniform3fv(glGetUniformLocation(pbrShader, "camPos"), 1, &cameraPos[0]);// 渲染多个球体,每个球体有不同的金属度和粗糙度int nrRows = 7;int nrColumns = 7;float spacing = 2.5;glm::mat4 model = glm::mat4(1.0f);for (int row = 0; row < nrRows; ++row) {for (int col = 0; col < nrColumns; ++col) {model = glm::mat4(1.0f);model = glm::translate(model, glm::vec3((col - (nrColumns / 2)) * spacing,(row - (nrRows / 2)) * spacing,0.0f));glUniformMatrix4fv(glGetUniformLocation(pbrShader, "model"), 1, GL_FALSE, &model[0][0]);// 设置材质属性glm::vec3 albedo = glm::vec3(0.5f, 0.0f, 0.0f);float metallic = (float)row / (float)nrRows;float roughness = glm::clamp((float)col / (float)nrColumns, 0.05f, 1.0f);float ao = 1.0f;glUniform3fv(glGetUniformLocation(pbrShader, "albedo"), 1, &albedo[0]);glUniform1f(glGetUniformLocation(pbrShader, "metallic"), metallic);glUniform1f(glGetUniformLocation(pbrShader, "roughness"), roughness);glUniform1f(glGetUniformLocation(pbrShader, "ao"), ao);// 渲染球体glBindVertexArray(sphereVAO);glDrawElements(GL_TRIANGLES, sphereIndices.size(), GL_UNSIGNED_INT, 0);}}// 实际应用中应该交换缓冲区// eglSwapBuffers(...);}// 清理资源glDeleteVertexArrays(1, &sphereVAO);glDeleteBuffers(1, &sphereVBO);glDeleteBuffers(1, &sphereEBO);glDeleteProgram(pbrShader);return 0; }
十一、 学习资源
-
OpenGL ES 官方参考
-
LearnOpenGL ES
-
Google Android OpenGL ES 教程
-
Apple OpenGL ES 指南