图形渲染 OpenGL ES 之 着色器

一、序言

1. 着色器是用来实现图形渲染的、代替固定渲染管线的可编程程序,着色器替代了传统的固定渲染管线,可以实现2D、3D图形学计算中的相关计算,由于其可编程性,可以实现各种各样的图像效果。

2. 着色器语言专门用来为着色器编程的编程语言,着色器语言有 HLSL、GLSL等等语言,

HLSL是高阶着色器语言(High Level Shader Language)的简称,HLSL独立的工作在 Windows 平台上,只能供微软的Direct3D使用;

GLSL是OpenGL着色语言(OpenGL Shading Language)的简称,是一种类C语言的高阶着色器语言,可以跨平台运行,存在于windows、android、Mac OS等系统。

3. OpenGL(Open Graphics Library,开放图形库)是用于渲染2D、3D矢量图形的跨平台的应用程序编程接口(API)。OpenGL家族主要包括桌面OpenGL和OpenGL ES,OpenGL ES(OpenGL for Embedded Systems)是以和嵌入式设备为目标的图形应用程序编程接口。

4. OpenGL ES 1.0 支持固定功能渲染管线,OpenGL ES 2.0开始采用可编程着色功能的图形管线,OpenGL ES 3.0向后兼容2.0,但是

OpenGL ES 2.0/3.0不支持OpenGL ES 1.0的固定功能管线。OpenGL ES 3.0由OpenGL ES着色语言3.0规范和OpenGL ES 3.0 API规范组成。

二、示例代码

本例子介绍如何使用着色器渲染一个彩色三角形的程序,我们需要创建一个顶点着色器(vertex shader)和一个片段着色器(fragment shader),

一个着色器需要创建两个基本的对象:着色器对象和程序对象,可以类比C语言的编程器和链接程序,C编译器为一段源代码生成.o目标文件,然后

C链接程序将目标文件链接成最后的可执行程序。OpenGL ES也是类似的,着色器源码代码提供给着色器对象,着色器对象被编译成一个目标文件,

然后着色器对象可以链接到一个程序对象,一般过程包括7个步骤:

* 1. 创建一个顶点着色器对象和一个片段着色器对象

* 2. 将源代码提供给着色器对象

* 3. 编译着色器对象

* 4. 创建一个程序对象

* 5. 程序对象关联编译后的着色器对象

* 6. 链接程序对象,生成可以执行的硬件指令

* 7. 设置程序对象为活动程序

main.cpp

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <EGL/egl.h>
#include <GLES3/gl31.h>

#include "WindowSurface.h"

EGLDisplay eglDisplay;
EGLSurface eglSurface;

GLuint programObject;

EGLBoolean egl_init(){
    
    eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
    if(eglDisplay == EGL_NO_DISPLAY){
        return EGL_FALSE;
    }

    EGLint major, minor;
    if(!eglInitialize(eglDisplay, &major, &minor)){
        return EGL_FALSE;
    }
    printf("[%s +%d]: major-%d minor-%d\n", __func__, __LINE__, major, minor); //major-1 minor-5

    EGLConfig eglConfig;
    EGLint numConfigs;
    EGLint eglConfigAttribList[] = {
        EGL_SURFACE_TYPE, EGL_WINDOW_BIT,  //EGL_WINDOW_BIT
        EGL_RED_SIZE, 8,
        EGL_GREEN_SIZE, 8,
        EGL_BLUE_SIZE, 8,
        EGL_ALPHA_SIZE, 8,
        EGL_NONE
    };
    if(!eglChooseConfig(eglDisplay, eglConfigAttribList, &eglConfig, 1, &numConfigs)){
        return EGL_FALSE;
    }
    printf("[%s +%d]: numConfigs-%d\n", __func__, __LINE__, numConfigs);  //numConfigs-1

    EGLNativeWindowType window;
    window = getWindowSurface();
    eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, window/* 需要先获取到NativeWindow*/, NULL);
    if(eglSurface == EGL_NO_SURFACE){
        EGLint eglError= eglGetError();
        printf("eglCreateWindowSurface failed, eglError-0x%x\n", eglError);
        return EGL_FALSE;
    }

    EGLContext eglContext;
    EGLint eglContextAttribList[] = {
        EGL_CONTEXT_CLIENT_VERSION, 3,
        EGL_NONE
    };
    eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, eglContextAttribList);
    if(eglContext == EGL_NO_CONTEXT){
        return EGL_FALSE;
    }

    if(!eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)){
        printf("eglMakeCurrent failed\n");
        return EGL_FALSE;
    }

    glViewport( 0, 0, 1080 , 2440);

    return EGL_TRUE;
}
    
GLuint loadShader(GLenum type, const char *shaderSrc){
    GLuint shader;
    GLint compiled;
    /* 1. 创建着色器对象,根据type创建顶点着色器或者片段着色器,返回值是指向着色器对象的句柄 */
    shader =  glCreateShader(type);
    if(shader == 0){
        return 0;
    }

    /* 2. 给着色器对象提供源代码 */
    glShaderSource(shader, 1, &shaderSrc, NULL);

    /* 3. 编译已经存在着色器对象的着色器源代码。和常规的语言编译器一样,你需要知道是否编译报错,可以glGetShaderiv查询 */
    glCompileShader(shader);
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); //查询是否编译报错
    if(!compiled){
        GLint infoLen = 0;

        glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); //查询编译错误log的长度
        if(infoLen > 1){
            char *infoLog = (char *)malloc(sizeof(char) * infoLen);
            glGetShaderInfoLog(shader, infoLen, NULL, infoLog); //获取编译错误log
            printf("Error compiling shader: %s\n", infoLog);
            free(infoLog);
        }

        glDeleteShader(shader);
        return 0;
}

return shader;
}    

/* 加载顶点着色器 */
GLuint loadVertexShader(){
    /* vShaderStr是顶点着色器源代码,第一行声明采用OpenGL ES着色语言3.0版本(GL ES SL 3.0)。
    * 顶点着色器输入(或者称作顶点属性):vPosition和aColor,分别对于后面代码传入的顶点数组的vVertices和vColor。
    * 顶点着色器输出(或者称作可变变量):vColor,在图元光栅化阶段,为每个生成的片段计算顶点着色器的输出值,并作为片段着色器的输入
    */
    char vShaderStr[] =
    "#version 300 es                         \n 
    layout(location = 0) in vec4 vPosition;  \n 
    layout(location = 1) in vec3 aColor;     \n 
    out vec3 vColor;                         \n 
    void main()                              \n 
    {                                        \n 
    gl_Position = vPosition;             \n 
    vColor = aColor;                     \n 
    }                                        \n ";
    return loadShader(GL_VERTEX_SHADER, vShaderStr);    
}   

/* 加载片段着色器 */
GLuint loadFragmentShaer(){
    /* fShaderStr是片段着色器源代码,第一行声明采用OpenGL ES着色语言3.0版本(GL ES SL 3.0)。
    * 片段着色器输入变量(或者称作可变变量):vColor,在图元光栅化阶段,为每个生成的片段计算顶点着色器的输出值,作为片段着色器的输入
    * 片段着色器输出颜色:fragColor,传递到管线的逐片段操作部分。
    */
    char fShaderStr[] =
    "#version 300 es                         \n 
    precision mediump float;                 \n 
    in vec3 vColor;                          \n 
    out vec3 fragColor;                      \n 
    void main()                              \n 
    {                                        \n 
    fragColor = vColor;                  \n 
    }                                        \n ";
    return loadShader(GL_FRAGMENT_SHADER, fShaderStr);
}

GLuint loadProgram(){
    GLuint vertexShader;
    GLuint fragmentShader;
    GLint linked;
    vertexShader = loadVertexShader();
    fragmentShader = loadFragmentShaer();

    /* 4. 创建程序对象,返回值是指向程序对象的句柄 */
    programObject = glCreateProgram();
    if(programObject == 0){
        return 0;
    }

    /* 5. 程序对象关联编译好的着色器对象 */
    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);

    /* 6. 程序对象链接,生成可以执行的硬件指令,和常规的链接一样,你需要知道是否链接成功,可以glGetProgramiv查询 */
    glLinkProgram(programObject);
    glGetProgramiv(programObject, GL_LINK_STATUS, &linked);  //查询是否链接报错
    if(!linked){
        GLint infoLen = 0;
        glGetProgramiv(programObject, GL_INFO_LOG_LENGTH, &infoLen); //查询链接错误log的长度
        if(infoLen > 1){
            char *infoLog = (char *)malloc(sizeof(char) *infoLen);
            glGetProgramInfoLog(programObject, infoLen, NULL, infoLog); //获取链接错误log
            printf("Error linking program: %s\n", infoLog);
            free(infoLog);
        }
        glDeleteProgram(programObject);
        return GL_FALSE;
    }

    /* 7. 设置程序对象为活动程序 */
    glUseProgram(programObject);

    return GL_TRUE;
}

void drawTriangle(){
    GLfloat vVertices[] = {
    0.0f, 0.5f, 0.0f,
    -0.5f, -0.5f, 0.0f,
    0.5f, -0.5f, 0.0f
    };
    GLfloat vColor[] = {
        1.0f, 0.0f, 0.0f,
        0.0f, 1.0f, 0.0f,
        0.0f, 0.0f, 1.0f
    };

    //glViewport(0, 0, 1080, 2400);
    glClear(GL_COLOR_BUFFER_BIT);

    /* 顶点数组指定每个顶点的属性,是保存在应用程序地址空间(OpenGL ES客户空间)的缓冲区 */
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices); //顶点位置数组,数据类型为浮点型
    glEnableVertexAttribArray(0); //启用索引为0的顶点数组

    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, vColor);//顶点颜色数组,数据类型为浮点型
    glEnableVertexAttribArray(1); //启用索引为1的顶点数组

    /* 绘制图元,OpenGL ES 3.0可以绘制点、直线和三角形图元,这里绘制三角形 */
    glDrawArrays(GL_TRIANGLES, 0, 3);
}    

int main(){
    EGLint ret;
    ret = egl_init();
    if(ret == EGL_FALSE){
        printf("[%s +%d]: egl_init failed\n", __func__, __LINE__);
        return -1;
    }

    ret = loadProgram();
    if(ret == GL_FALSE){
        printf("[%s +%d]: loadProgram failed\n", __func__, __LINE__);
        return -1;
    }

    while(1){
        drawTriangle();
        eglSwapBuffers(eglDisplay, eglSurface);
        sleep(1);
    }
    return 0;
}

 将代码放在frameworks/native目录下进行编译,生成opengl-es-triangle,然后push到/system/bin/目录下执行。

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE              := opengl-es-triangle
LOCAL_MODULE_TAGS := tests
LOCAL_SHARED_LIBRARIES    := libEGL libGLESv2 libGLESv3 libutils liblog libgui libui
LOCAL_SRC_FILES           := main.cpp WindowSurface.cpp
include $(BUILD_EXECUTABLE)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

薛文旺

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

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

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

打赏作者

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

抵扣说明:

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

余额充值