OpenGL ES简单示例

OpenGLES简单示例

#include <EGL/egl.h>
#include <GLES3/gl3.h>
#include <iostream>

#define WINDOW_WIDTH 800
#define WINDOW_HEIGHT 600

int main() {
    // 初始化 EGL
    EGLDisplay display;
    EGLConfig config;
    EGLContext context;
    EGLSurface surface;
    EGLint numConfigs;
    EGLint majorVersion;
    EGLint minorVersion;

    // 初始化 EGL Display
    display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, &majorVersion, &minorVersion);

    // 配置 EGL
    EGLint attribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,
        EGL_BLUE_SIZE, 6,
        EGL_GREEN_SIZE, 8,
        EGL_RED_SIZE, 8,
        EGL_NONE
    };
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);

    // 创建 EGL Context
    EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL_NONE
    };

    // 创建 EGL Surface
    surface = eglCreateWindowSurface(display, config, nativeWindow, NULL);

    context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);

    // 绑定 EGL Context 和 Surface
    eglMakeCurrent(display, surface, surface, context);

    // 设置视口大小
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

    // 清除颜色缓冲
    glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    // 定义顶点数据
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.0f,  // 左下角
         0.5f, -0.5f, 0.0f,  // 右下角
         0.0f,  0.5f, 0.0f   // 顶部
    };

    // 创建并绑定顶点缓冲对象
    GLuint VAO[0];
    GLuint VBO;
    glGenVertexArrays(1, VAO);
	glBindVertexArray(VAO[0]);
    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    // 创建着色器程序
    const char* vertexShaderSource = R"(
        #version 300 es
        precision mediump float;
        layout (location = 0) in vec3 aPos;
        void main() {
            gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
        }
    )";

    const char* fragmentShaderSource = R"(
        #version 300 es
        precision mediump float;
        out vec4 FragColor;
        void main() {
            FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
        }
    )";

    GLuint vertexShader, fragmentShader, shaderProgram;
    // 创建顶点着色器
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 创建片段着色器
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 创建着色器程序
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // 使用着色器程序
    glUseProgram(shaderProgram);

    // 绑定顶点数据
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
    glEnableVertexAttribArray(0);

    // 绘制三角形
    glDrawArrays(GL_TRIANGLES, 0, 3);

    // 交换缓冲区
    eglSwapBuffers(display, surface);

    // 清理
    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    glDeleteBuffers(1, &VBO);

    // 等待退出
    std::cout << "Press Enter to exit..." << std::endl;
    std::cin.get();

    // 清理 EGL
    eglDestroyContext(display, context);
    eglDestroySurface(display, surface);
    eglTerminate(display);

    return 0;
}

该示例首先使用了EGL创建了渲染表面,让我们详细介绍下。EGL作为Khronos 组织为开发内容所提供的API家族的一员, (大部分)平台无关的。
EGL可以用于管理绘图表面(window surface只是一种类型,还有pbuffer,pixmap)。首先该示例使用eglGetDisplay:

eglGetDisplay

EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id);
eglGetDisplay 是 EGL 库中的一个函数,函数返回EGLDisplay对象,它代表了与渲染目标设备的连接,
如果显示连接不可用,则 eglGetDisplay 将返回 EGL_NO_DISPLAY,这个错误表示显示连接不可用。
display_id 参数通常是一个表示显示设备的本地显示类型,EGLNativeDisplayType是为了匹配原生窗口显示类型,在各个平台有不同的定义。
如果您只是希望使用默认的显示设备,那么您可以直接使用 EGL_DEFAULT_DISPLAY,而不需要显式地指定 display_id

eglInitialize

当成功打开链接之后需要初始化EGL 需要调用eglInitialize
EGLBoolean eglInitialize(EGLDisplay display, EGLint *majorVersion, EGLint *minorVersion);
           display指定 EGL 显示连接
           majorVersion 指定 EGL 实现返回的主版本号,可能为 NULL
           minorVersion,指定 EGL 实现返回的次版本号,可能为 NULL
这个函数用于初始化EGL内部数据结构,并将返回EGL的版本号保存在majorVersion,minorVersion。
如果初始化成功,则返回 EGL_TRUE,否则返回 EGL_FALSE。另外还可以通过 EGLint eglGetError();查询EGL的错误状态。
EGL_BAD_DISPLAY--如果 display 没有指定有效的 EGLDisplay。
EGL_NOT_INITIALIZED--如果 EGL 不能初始化。

eglChooseConfig

如果一切进行的顺利的话,现在我们初始化了EGL,现在是确定可用渲染表面的类型和配置的时候了,这里有2种方法:
1.查询支持的所有配置,找出最好的选择(使用eglGetConfigs,eglGetConfigAttrib)
2.指定一组需要的配置,让EGL推荐最佳配置(使用eglChooseConfig)
一般情况下我们使用第二种方法,因为这样更容易获得想要获得的最佳配置
EGLBoolean eglChooseConfig(EGLDisplay dpy,   //EGL 显示连接句柄,标识了要进行配置选择的显示连接。
                           const EGLint *attrib_list, //一个以 EGL_NONE 结尾的整数数组,用于指定所需配置的属性。属性列表中的每个元素都由属性名称(如 EGL_RED_SIZE)和相应的属性值组成。如({EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_NONE})
                           EGLConfig *configs, //一个用于存储选择的配置的指针数组。eglChooseConfig 函数将从可用配置中选择适合条件的配置,并将其存储在此数组中。
                           EGLint config_size,//configs数组的大小
                           EGLint *num_config); //存储满足attrib_list需求,得到的满足需求的实际配置数量。
    //如以上代码所示这里指定所需配置的属性为
    EGLint attribs[] = {
        EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT,  //指定了渲染类型为 OpenGL ES 3
        EGL_BLUE_SIZE, 6,   //指定蓝色缓冲区的位数是6位
        EGL_GREEN_SIZE, 8,  //指定绿色缓冲区的位数是8位
        EGL_RED_SIZE, 8,    //指定红色缓冲区的位数是8位
        EGL_NONE
    };
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
以上示例指定了蓝色缓冲区的位数为6位,表示如果8位RGB 0-256 如要表示蓝色200 使用6位表示 2^6=64
64*200/256
使用了eglChooseConfig后根据指定的配置属性attribs 将返回满足需求的配置,存放在config中,
这里config只能保存一组可用配置,但那也是足够的,而numconfigs 保存满足指定配置的所有配置数量。
这样我们得到了满足我们需求的config

如果我们希望使用第一种方法得到满足我们需求的config:(1.查询支持的所有配置,找出最好的选择(使用eglGetConfigs,eglGetConfigAttrib))
以下是一个简单的示例:
#include <EGL/egl.h>
#include <iostream>
#include <vector>

int main() {
    // 初始化 EGL
    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    eglInitialize(display, nullptr, nullptr);

    // 获取所有配置
    EGLint numConfigs;
    eglGetConfigs(display, nullptr, 0, &numConfigs);
    std::vector<EGLConfig> configs(numConfigs);
    eglGetConfigs(display, configs.data(), numConfigs, &numConfigs);

    // 选择合适的配置
    EGLConfig chosenConfig = nullptr;
    for (const auto& config : configs) {
        EGLint redSize, greenSize, blueSize;
        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
        if (redSize == 8 && greenSize == 8 && blueSize == 6) {
            chosenConfig = config;
            break;
        }
    }

    // 如果未选择配置,则打印错误信息并退出
    if (!chosenConfig) {
        std::cerr << "Failed to find a suitable EGL configuration." << std::endl;
        return 1;
    }
    return 0;
}
EGLBoolean eglGetConfigs(EGLDisplay display, //EGL 显示连接句柄,标识了要进行配置选择的显示连接。
                         EGLConfig *configs, //用于保存得到配置的数组
                         EGLint config_size, //configs的数组大小
                         EGLint *num_config);//得到的EGL所有可用配置数量

eglGetConfigs有2种用法,第一种用法:当我们传递configs为nullptr时,如上代码,这时会返回EGL_TRUE,并将得到的EGL所有可用配置数量保存在num_config,这时我们就可以根据得到的数量初始化configs来保存这些配置了,如上代码。

第二种用法:传递configs数组接受所有配置,将得到所有配置保存在configs,如上代码所示,这样我们就得到了所有的可用配置,接下来我们就可以根据我们的需求筛选一组config保存下来。

   // 选择合适的配置
    EGLConfig chosenConfig = nullptr;
    for (const auto& config : configs) {
        EGLint redSize, greenSize, blueSize;
        eglGetConfigAttrib(display, config, EGL_RED_SIZE, &redSize);
        eglGetConfigAttrib(display, config, EGL_GREEN_SIZE, &greenSize);
        eglGetConfigAttrib(display, config, EGL_BLUE_SIZE, &blueSize);
        if (redSize == 8 && greenSize == 8 && blueSize == 6) {
            chosenConfig = config;
            break;
        }
    }
如上所示遍历configs每个配置 ,并使用eglGetConfigAttrib 查询该配置下特定属性的值,保存并将该值保存到下来,并判断值是否是自己需要的。
调用成功则返回EGL_TRUE 调用失败则返回EGL_FALSE 可使用 可以使用eglGetError 查询失败的原因,
如果返回EGL_BAD ATTRIBUTE则attribute不是有效的属性。使用eglGetConfigAttrib 检查该配置特定属性的值,是不是满足我们的需求,当同时满足,则保存该配置,待使用。
EGLBoolean eglGetConfigAttrib(EGLDisplay display, //EGL 显示连接句柄,标识了要进行配置选择的显示连接
                              EGLConfig config,  //EGLConfig 对象,表示要查询的 EGL 配置
                              EGLint attribute,  //EGLint 类型的属性标识符,表示要查询的属性
                              EGLint *value);    //指向 EGLint 类型变量的指针,用于存储查询到的属性值。

当我们有了符合渲染需求的EGLConfig 就为创建窗口表面做好了准备:可以使用eglCreateWindowSurface
创建窗口表面:
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, //EGLDisplay 对象,表示与窗口表面关联的显示连接。
                                  EGLConfig config, //EGLConfig 对象,表示要创建窗口表面的 EGL 配置。
                                  EGLNativeWindowType win, //EGLNativeWindowType 类型的参数,表示原生窗口的句柄或标识符,用于与 EGL 表面关联。
                                  const EGLint *attrib_list);//指向 EGL 属性列表的指针,用于指定窗口表面的属性。这是一个以 EGL_NONE 结尾的整数数组
eglCreateWindowSurface接受的属性是 attrib_list:
EGL_RENDER_BUFFER EGL_SINGLE_BUFFER或EGL_BACK_BUFFER
EGL_SINGLE_BUFFER 表示渲染表面将只有一个渲染缓冲区。在绘制完成后,渲染缓冲区中的内容将直接显示到屏幕上,不会进行双缓冲,可能会出现闪烁或撕裂的现象。
EGL_BACK_BUFFER 表示渲染表面将具有双缓冲区,即前缓冲区和后缓冲区。在绘制完成后,渲染缓冲区中的内容首先会绘制到后缓冲区,
然后通过交换缓冲区的操作将后缓冲区的内容显示到屏幕上,这样可以避免闪烁和撕裂现象。
默认情况下是EGL_BACK_BUFFER 当设置为null则为默认属性

eglCreateWindowSurface创建窗口表面失败的可能原因:

EGL_BAD_MATCH:原生窗口属性与提供的 EGLConfig 不匹配。这可能是因为 EGLConfig 不支持渲染到窗口(即 EGL_SURFACE_TYPE 属性没有设置为 EGL_WINDOW_BIT)。
EGL_BAD_CONFIG:如果提供的 EGLConfig 没有得到系统的支持,则会标记此错误。
EGL_BAD_NATIVE_WINDOW:如果提供的原生窗口句柄无效,则会指定此错误。
EGL_BAD_ALLOC:如果 eglCreateWindowSurface 无法为新的 EGL 窗口分配资源,或者已经有与提供的原生窗口关联的 EGLConfig,则会发生这种错误。
可以用于错误检查:
EGLint attribList[] = { EGL_RENDER_BUFFER, EGL_BACK_BUFFER, EGL_NONE };
EGLSurface surface = eglCreateWindowSurface(display, config, nativeWindow, attribList);
if (surface == EGL_NO_SURFACE) {
    switch (eglGetError()) {
        case EGL_BAD_MATCH:
            // Check window and EGLConfig attributes to determine compatibility, or verify that the EGLConfig supports rendering to a window
            break;
        case EGL_BAD_CONFIG:
            // Verify that provided EGLConfig is valid
            break;
        case EGL_BAD_NATIVE_WINDOW:
            // Verify that provided EGLNativeWindow is valid
            break;
        case EGL_BAD_ALLOC:
            // Not enough resources available; handle and recover
            break;
        default:
            // Handle any other errors
            break;
    }
}

创建渲染上下文 eglCreateContext

eglCreateContext 函数用于创建一个新的 EGL 上下文,并将其与特定的显示设备(display)和配置(config)关联起来。它允许您指定共享上下文(shareContext),以便与已经存在的 OpenGL 上下文共享状态信息。该函数的参数说明如下:
EGLContext eglCreateContext(EGLDisplay display, //EGLDisplay类型,表示要创建上下文的EGL显示连接。
                            EGLConfig config,   //EGLConfig类型,表示与上下文关联的EGL配置。
                            EGLContext shareContext, //EGLContext类型,表示要与新创建的上下文共享状态信息的现有上下文。如果不想共享状态信息,可以传递EGL_NO_CONTEXT。
                            const EGLint *attribList); //指向属性列表的指针,用于指定上下文的属性。属性列表是以EGL_NONE结尾的一系列属性值对。
eglCreateContext 的attribList属性列表:
   EGLint contextAttribs[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3, //指定使用的openglES版本3相关的上下文类型
    };
eglCreateContext 创建渲染上下文,失败的可能原因:
EGL_BAD_CONFIG   提供的EGLconfig无效

eglMakeCurrent 将EGL上下文与绘图表面进行关联

EGLBoolean eglMakeCurrent(EGLDisplay display, //EGL 显示连接的句柄,用于标识渲染设备。
                          EGLSurface draw,    //EGL 绘图表面的句柄,指定要渲染到的目标表面。
                          EGLSurface read,    //EGL 读取表面的句柄,用于像素读取等操作。通常情况下,可以将其设为与 draw 相同的值。
                          EGLContext context);//要与指定表面关联的 EGL 上下文的句柄。

glViewport 设置视口大小

void glViewport(GLint x, GLint y, GLsizei width, GLsizei height);
这个函数设置了视口,指定opengles渲染区域在窗口的位置和大小,x,y指定了视口的左下角在窗口中的坐标
width 和 height 参数指定了视口的宽度和高度。

glClearColor 设置清除颜色缓冲区使用的颜色

void glClearColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha);
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);这行代码设置了清除颜色缓冲区时使用的颜色为 (0.2, 0.3, 0.3),
即红色分量为 0.2,绿色分量为 0.3,蓝色分量为 0.3,透明度为 1.0(不透明)。

glClear 执行清除操作

void glClear(GLbitfield mask);
这个函数用于清除指定的缓冲区。参数 mask 指定了需要清除的缓冲区,可以是以下值的组合:
GL_COLOR_BUFFER_BIT:清除颜色缓冲区
GL_DEPTH_BUFFER_BIT:清除深度缓冲区
GL_STENCIL_BUFFER_BIT:清除模板缓冲区
当调用 glClear(GL_COLOR_BUFFER_BIT);清除了颜色缓冲区,并用之前设置的颜色填充整个缓冲区。
为什么要清除颜色缓冲区?
清除颜色缓冲是在开始绘制新帧之前的一个常见操作,它确保屏幕上的每个像素都被初始化为指定的颜色值,
以便绘制新的图像。这是绘制新帧的准备工作,类似于在画布上涂上底色,以便开始新的绘画。

定义顶点数据

  // 定义顶点数据
    GLfloat vertices[] = {
        -0.5f, -0.5f, 0.0f,  // 左下角
         0.5f, -0.5f, 0.0f,  // 右下角
         0.0f,  0.5f, 0.0f   // 顶部
    };

​​ 在OpenGL中,通常会使用标准化设备坐标(Normalized Device Coordinates, NDC)来表示顶点的位置。NDC是一个以屏幕为单位的坐标空间,在这个空间中,左下角是(-1, -1),右上角是(1, 1)。这种坐标范围使得顶点的位置不依赖于屏幕的大小和比例。

管理顶点数据,将顶点数据保存在GPU,减少CPU与GPU数据传输次数

GLuint VAO[0];
GLuint VBO;
glGenVertexArrays(1, VAO); //要生成1个顶点数组对象(VAO) VAO 用于存储生成的顶点数组对象名称的数组
glBindVertexArray(VAO[0]); //用于绑定顶点数组对象(VAO)到当前OpenGL上下文中。
glGenBuffers(1, &VBO); //用于生成顶点缓冲对象, 1:要生成的顶点缓冲对象的数量这里设置1个 ,&VBO 指向存储生成的顶点缓冲对象名称的数组的指针。
glBindBuffer(GL_ARRAY_BUFFER, VBO); //void glBindBuffer(GLenum target, GLuint buffer);target:指定要绑定的缓冲目标,可以是以下值之一 GL_ARRAY_BUFFER:用于存储顶点属性数据。GL_ELEMENT_ARRAY_BUFFER:用于存储索引数据等其他。buffer:要绑定的顶点缓冲对象的名称。
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
void glBufferData(GLenum target, //target:指定缓冲对象的类型,可以是以下值之一GL_ARRAY_BUFFER:用于存储顶点属性数据,GL_ELEMENT_ARRAY_BUFFER:用于存储索引数据。
                  GLsizeiptr size, //指定要分配的缓冲区的大小(以字节为单位)。
                  const GLvoid* data, //指定要复制到缓冲区的初始数据。
                  GLenum usage); //指定缓冲区的预期使用方式,可以是以下值之一:GL_STATIC_DRAW:数据不会或几乎不会被修改,并且被绘制命令多次使用,GL_DYNAMIC_DRAW:数据会被频繁修改,并且被绘制命令多次使用,GL_STREAM_DRAW:数据会被修改,并且被绘制命令少量使用。
一旦调用glBufferData函数,数据就被复制到了OpenGL的缓冲对象中,并存储在GPU的显存
中。这意味着数据可以在GPU上被高效地访问和处理,而无需频繁地从CPU内存传输数据,
从而提高了渲染性能。
    const char* vertexShaderSource = R"(
        #version 300 es  //这是指定使用OpenGL ES 3.0版本的着色器语言
        precision mediump float;  //这是指定浮点数的精度为中等精度。在移动设备上,通常使用中等精度可以提高性能。
        layout (location = 0) in vec3 aPos;//顶点属性变量,名称为aPos,类型为vec3,并且指定它在顶点着色器中的位置索引为0。这个变量 aPos 接收刚才设置的来自顶点缓冲对象中的顶点数据。每次顶点着色器被调用时,aPos 就会被设置为当前处理的顶点的位置属性值。(数据从刚才顶点缓冲对象中获取,且已经存到GPU)
        void main() {
            gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);//gl_Position是opengles内置变量 来指定每个顶点的最终位置,这个位置是经过透视投影变换后的裁剪空间坐标。在顶点着色器中对 gl_Position 赋值后,图形渲染管线会将其进行进一步处理,然后将顶点投影到屏幕上的二维坐标。 w 为非零值时,顶点坐标会进行透视除法操作,即将 (x, y, z, w) 中的 (x/w, y/w, z/w) 作为最终的裁剪空间坐标。因此,当 w 的值为 1.0 时,透视除法不会对坐标进行任何变换,坐标值保持不变。
        }
    )";
    const char* fragmentShaderSource = R"(
        #version 300 es
        precision mediump float;
        out vec4 FragColor;  //输出片段颜色
        void main() {
            FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);//这里会每次会将一个片段(每个片段都有一个屏幕坐标(x, y),以及与之相关联的其他信息,如深度值、颜色值等。)设置为vec4(1.0f, 0.5f, 0.2f, 1.0f);其中分别代表红色、绿色、蓝色和 alpha 值。这意味着输出的颜色为浅橙色,完全不透明。这里的颜色不是从顶点着色器经过光栅化线性插值而获得的,而是直接赋值。
        }
    )";
顶点缓冲区对象,传入顶点着色器程序进行处理(如矩阵转换,mv矩阵,透视矩阵等,变换顶点位置,计算照明公式等)
再计算后的顶点进行图元装配(用于将顶点数据组装成几何图元,例如点、线段或三角形。在这个阶段)。
再进行光栅化(图元(例如三角形)转换为屏幕上的像素集合,如果顶点还设置了颜色,可以进行线性插值。
片段着色器,将光栅化输出,作为片段着色器的输入变量,将这些输出经过光照、纹理采样、颜色混合生成个新的颜色,深度,屏幕坐标位置等
逐片段操作:将片段着色器的输出,作为逐片段操作的输入,进行像素归属测试,剪裁测试
模板测试,深度测试,混合,抖动,前往帧缓冲区。

创建着色器程序,使用着色器程序

   GLuint vertexShader, fragmentShader, shaderProgram;
    // 创建顶点着色器
    vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr);
    glCompileShader(vertexShader);

    // 创建片段着色器
    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr);
    glCompileShader(fragmentShader);

    // 创建着色器程序
    shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);

    // 使用着色器程序
    glUseProgram(shaderProgram);
GLuint glCreateShader(GLenum shaderType);
用于创建一个指定类型(顶点着色器、片段着色器等)的着色器对象,并返回该对象的句柄。其中,shaderType 参数指定要创建的着色器类型,可以是 GL_VERTEX_SHADER(顶点着色器)或 GL_FRAGMENT_SHADER(片段着色器)等。
void glShaderSource(GLuint shader, GLsizei count, const GLchar **string, const GLint *length);
glShaderSource 函数用于设置着色器对象的源代码,其中参数:
shader:要设置源代码的着色器对象的标识符。
count:源代码字符串的数量。
string:指向源代码字符串的指针数组。
length:指向包含每个源代码字符串长度的整数数组,可以为 nullptr,表示每个字符串都以 null 结尾。
void glCompileShader(GLuint shader);
glCompileShader 函数用于编译指定的着色器对象,其中 shader 参数是要编译的着色器对象的标识符。
GLuint glCreateProgram(void);
glCreateProgram 函数用于创建一个新的着色器程序对象,该函数返回一个新创建的着色器程序对象的标识符。
void glAttachShader(GLuint program, GLuint shader);
glAttachShader 函数用于将一个着色器对象附加到一个着色器程序对象上
参数 program 是目标着色器程序对象的标识符,参数 shader 是要附加的着色器对象的标识符。
void glLinkProgram(GLuint program);

glLinkProgram 函数用于链接一个着色器程序对象,将附加到该程序对象的着色器链接成一个可执行的渲染管线。
参数 program 是要链接的着色器程序对象的标识符。
链接着色器程序时,OpenGL 将会执行以下操作:
将各个着色器对象中的代码合并成一个可执行的渲染管线。
执行连接器优化,以优化渲染管线的性能。
将 Uniform 变量和 Uniform 块的信息进行绑定。

void glUseProgram(GLuint program);
glUseProgram 函数用于激活指定的着色器程序对象。在调用
glUseProgram 之后,所有的渲染调用将会使用该着色器程序进行处理。

在glCompileShader时还可以使用以下代码检查错误

 // Compile the shader
 glCompileShader ( shader );

 // Check the compile status
 glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled );

 if ( !compiled )
 {
    GLint infoLen = 0;

    glGetShaderiv ( shader, GL_INFO_LOG_LENGTH, &infoLen );

    if ( infoLen > 1 )
    {
       char *infoLog = malloc ( sizeof ( char ) * infoLen );

       glGetShaderInfoLog ( shader, infoLen, NULL, infoLog );
       esLogMessage ( "Error compiling shader:\n%s\n", infoLog );

       free ( infoLog );
    }

    glDeleteShader ( shader );
    return 0;

在使用 glLinkProgram可使用如下代码检查错误

 // Link the program
 glLinkProgram ( programObject );

 // Check the link status
 glGetProgramiv ( programObject, GL_LINK_STATUS, &linked );

 if ( !linked )
 {
    GLint infoLen = 0;

    glGetProgramiv ( programObject, GL_INFO_LOG_LENGTH, &infoLen );

    if ( infoLen > 1 )
    {
       char *infoLog = malloc ( sizeof ( char ) * infoLen );

       glGetProgramInfoLog ( programObject, infoLen, NULL, infoLog );
       esLogMessage ( "Error linking program:\n%s\n", infoLog );

       free ( infoLog );
    }

    glDeleteProgram ( programObject );
    return FALSE;
 }

确定顶点属性数组的配置,包括顶点属性在缓冲区中的布局和格式

void glVertexAttribPointer(GLuint index,//指定要修改的顶点数组的起始索引 它与顶点着色器中的属性变量绑定。(layout (location = 0) in vec3 aPos;)
                           GLint size, //指定每个顶点属性的分量个数
                           GLenum type,//指定每个顶点属性分量的类型
                           GLboolean normalized,//指定在访问顶点数据时是否将其映射到[0, 1]或[-1, 1]范围内
                           GLsizei stride,//指定顶点属性之间的偏移量,如果是精密性排列可以设置为0
                           const void *pointer);//指定顶点属性数组的起始位置
void glEnableVertexAttribArray(GLuint index);
glEnableVertexAttribArray 函数用于启用指定索引的顶点属性数组。glEnableVertexAttribArray(0)
它与顶点着色器中的属性变量绑定layout (location = 0) in vec3 aPos;
glVertexAttribPointer 函数用于指定顶点属性数组的配置,包括属性索引、每个顶点属性的组件数量、数据类型、是否归一化、步长和偏移量,
glEnableVertexAttribArray 函数来启用特定顶点属性数组,使它们在渲染时生效。当做完这些,渲染时,OpenGLES 就会根据当前的顶点着色器程序
和启用的顶点属性数组,根据配置的格式,从相应的 VBO 中提取数据并传递给顶点着色器。

绘制图元

void glDrawArrays(GLenum mode, //参数指定了要绘制的图元的类型,比如 GL_TRIANGLES 表示绘制三角形。
                  GLint first,// 参数指定了要绘制的顶点数组的起始索引。
                  GLsizei count  //参数指定了要绘制的顶点数量
                  );
glDrawArrays 函数用于根据当前绑定的顶点数组和顶点属性,以及其他设置,绘制图元
EGLBoolean eglSwapBuffers(EGLDisplay dpy, // EGL 显示连接
                          EGLSurface surface);//要交换其缓冲区的 EGL 表面
eglSwapBuffers 函数用于交换前后缓冲区的内容,将渲染结果显示在屏幕上
  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
以下是一个简单OpenGL ES 2.0的代码片段: ```java import android.opengl.GLES20; import android.opengl.GLSurfaceView; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class MyGLRenderer implements GLSurfaceView.Renderer { @Override public void onSurfaceCreated(GL10 unused, EGLConfig config) { // 设置背景颜色 GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); } @Override public void onSurfaceChanged(GL10 unused, int width, int height) { // 设置视口尺寸 GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 unused) { // 清除颜色缓冲区 GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); } } ``` 这个代码片段展示了一个基本的OpenGL ES 2.0渲染器的实现。在onSurfaceCreated方法中,我们设置了背景颜色为黑色。在onSurfaceChanged方法中,我们设置了视口尺寸为View的尺寸。在onDrawFrame方法中,我们清除了颜色缓冲区。 请注意,这只是一个简单示例代码,你可以根据自己的需求进行更多的OpenGL ES 2.0编程。希望对你有帮助!<span class="em">1</span><span class="em">2</span> #### 引用[.reference_title] - *1* [Android OpenGl ES使用原理总结与代码示例](https://blog.csdn.net/u013914309/article/details/124688638)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [OBJ2OpenGLES 示例代码](https://download.csdn.net/download/alexliull/10019784)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值