OpenGL ES 2.0 系列一

OpenGL ES 2.0 系列一

闲言少叙,先从一段最简单的示例开始吧,这样可以直观一点。

// Hello_Triangle.c
//
//    这是一段绘制三角形的代码,使用了最最简单的顶点着色器和片元着色器,
//    目的在于演示一下 OpenGL ES 2.0 的渲染流程
#include <stdlib.h>
// 这里引入了需要用到的工具方法库,其与具体渲染流程关系不大,可以先不必纠结
// 工具方法均为esXXX()格式
#include "Common/esUtil.h"

typedef struct
{
    // programObject 引用
    GLuint programObject;

} UserData;


// 创建shader
GLuint LoadShader(GLenum type, const char *shaderSrc)
{
    GLuint shader;
    GLint compiled;

    // 生成一个shader引用
    shader = glCreateShader(type);

    if (shader == 0)
        return 0;

    // 载入shader的源代码
    glShaderSource(shader, 1, &shaderSrc, NULL);

    // 编译shader
    glCompileShader(shader);

    // 看看shader的编译有没有哪里报错
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
    // 这个if语句块中的内容可以暂时忽略,主要是获取OpenGL的一些状态信息
    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;
    }

    return shader;

}

///
// 初始化shader
//
int Init(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLbyte vShaderStr[] =
        "attribute vec4 vPosition;    \n"
        "void main()                  \n"
        "{                            \n"
        "   gl_Position = vPosition;  \n"
        "}                            \n";

    GLbyte fShaderStr[] =
        "precision mediump float;\n"\
        "void main()                                  \n"
        "{                                            \n"
        "  gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n"
        "}                                            \n";

    GLuint vertexShader;
    GLuint fragmentShader;
    GLuint programObject;
    GLint linked;

    // 调用上面载入 shader 的方法
    vertexShader = LoadShader(GL_VERTEX_SHADER, vShaderStr);
    fragmentShader = LoadShader(GL_FRAGMENT_SHADER, fShaderStr);

    // 创建一个 program 引用
    programObject = glCreateProgram();

    if (programObject == 0)
        return 0;

    // 分别将 vertex/fragment shader 添加到 program 中
    glAttachShader(programObject, vertexShader);
    glAttachShader(programObject, fragmentShader);

    // 绑定 vPosition 到 attribute 0   
    glBindAttribLocation(programObject, 0, "vPosition");

    // 链接 program
    glLinkProgram(programObject);

    // 看看有没出错
    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;
    }

    // 存储一下 program 引用
    userData->programObject = programObject;

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    return TRUE;
}

///
// 利用上面的 shader 来进行三角形的绘制
//
void Draw(ESContext *esContext)
{
    UserData *userData = esContext->userData;
    GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f };

    // 设置 viewport
    glViewport(0, 0, esContext->width, esContext->height);

    // 清一下颜色 buffer
    glClear(GL_COLOR_BUFFER_BIT);

    // 这里告诉 OpenGL 你要使用这个 program 来进行后续的绘制
    glUseProgram(userData->programObject);

    // 将三角形的顶点信息传递到 vertex shader 中
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
    glEnableVertexAttribArray(0);

    glDrawArrays(GL_TRIANGLES, 0, 3);

    eglSwapBuffers(esContext->eglDisplay, esContext->eglSurface);
}


int main(int argc, char *argv[])
{
    ESContext esContext;
    UserData  userData;

    esInitContext(&esContext);
    esContext.userData = &userData;

    // 创建一个用于绘制的窗口
    esCreateWindow(&esContext, "Hello Triangle", 320, 240, ES_WINDOW_RGB);

    if (!Init(&esContext))
        return 0;

    esRegisterDrawFunc(&esContext, Draw);

    esMainLoop(&esContext);
}

上面的这段代码展示了如何绘制一个三角形,功能很基础。但是,好像步骤可不少呢,下面结合这个三角形的示例,来介绍一下OpenGL ES 2.0的渲染流程。

渲染管线

上面这个图片很清晰地展示了每个图形是怎样渲染出来的。其中灰色方框中”Vertex Shader”和”Fragment Shader”就是我们可以对整个渲染过程进行干预的最主要场所。

GLfloat vVertices[] = { 0.0f, 0.5f, 0.0f,
        -0.5f, -0.5f, 0.0f,
        0.5f, -0.5f, 0.0f };

这段代码就是图中的Vertex Arrays,为最初始的原料,三角形的三个顶点坐标。

然后就是准备好Vertex Shader:

GLbyte vShaderStr[] =
        "attribute vec4 vPosition;    \n"
        "void main()                  \n"
        "{                            \n"
        "   gl_Position = vPosition;  \n"
        "}                            \n";

这一小段代码就是我们的顶点着色器,它有一个attribute修饰的vec4类型变量vPosition,很明显这就是用来存放顶点坐标的。

shader = glCreateShader(type);
glShaderSource(shader, 1, &shaderSrc, NULL);
glCompileShader(shader);

每个 shader 都需要经过这几个步骤,在编译成功之后进行接下来的处理

// 创建一个 program 引用
programObject = glCreateProgram();
// 分别将 vertex/fragment shader 添加到 program 中
glAttachShader(programObject, vertexShader);
glAttachShader(programObject, fragmentShader);
// 链接 program
glLinkProgram(programObject);

这样,最终有了一个可以在渲染中使用的 program ,由 vertex shader 和 program shader 编译最终生成的。

接下来要做的就是将需要渲染的原料(顶点数组)传入到 shader 中,让它来进行处理。

// 绑定 vPosition 到 attribute 0  
glBindAttribLocation(programObject, 0, "vPosition");
// 这里告诉 OpenGL 你要使用这个 program 来进行后续的绘制
glUseProgram(userData->programObject);

// 将三角形的顶点信息传递到 vertex shader 中
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vVertices);
glEnableVertexAttribArray(0);

这样,顶点数组 vVertices 就被传递到 vertex shader 中了,被赋给了 vPosition 这个变量。在 vertex shader 中我们并没有对顶点做什么特殊的处理,而是把它赋值给gl_Position = vPosition; gl_position 这个变量是 vertex shader 中自带的变量之一,它会自动被传递到渲染管线的下一个流程。

Primitive Assembly 和 Rasterization 阶段简要说来就是进行图形的组装,根据顶点来生成图形,同时进行一些坐标的变换。这两个阶段也可以通过 OpenGL 的API来进行一些简单的设置,已达到不同的效果。

我们没有使用纹理,接下来就到了 Fragment Shader 了。

GLbyte fShaderStr[] =
        "precision mediump float;\n"\
        "void main()                                  \n"
        "{                                            \n"
        "  gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n"
        "}                                            \n";

顶点着色器中的代码是以顶点为单位进行处理的,片元处理器则是对每个片元(可暂时理解为需要绘制的每个像素)进行处理,也就是有多少个像素,main() 中的代码就会执行多少次。在其中,我们可以对每个片元进行任何想做的操作,比如改变其颜色、位置等等来达到想要的效果。在我们三角形的示例中仅仅是简单地为每个片元设置了颜色
gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );
其中 gl_FragColor 是 Fragment Shader 内置的变量,它用来存储每个片元的颜色信息,然后传递到渲染管线的下一个流程里。当然,同时传递到下一个流程的还有片元的其它额外信息。

Per-Fragment Operations 包括了下面这些操作,都是针对每个片元进行的
这里写图片描述
同样 OpenGL 提供了一些 API 可以对这些步骤进行设置来达到不同的效果。

FrameBuffer 就是最终的渲染结果了,只要把它显示到屏幕上就可以了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值