OpenGL ES2 學習教程2——HelloOpenGLES2.0

Hello GLES2

和ES1.0不一樣,ES2.0引入了可編程管線,以下圖中,可編程階段爲VertexShader,FragmentShader頂點和片斷着色器階段。android

圖片描述

頂點着色器被使用在傳統的基於頂點的操做,例如位移矩陣、計算光照方程、產生貼圖
座標。頂點着色器被應用指定,應用於客戶端的頂點轉化。頂點着色器須要一個位置和顏色數據做爲輸入屬性,輸入位置數據是 4×4 的矩陣,輸出是變換後的位置和顏色。
片斷着色器不需定義輸出,這是由於片斷着色器僅僅的輸出是gl_FragColor。git

如今咱們用上一節的Android入口渲染一個三角形。這裏借用NDK包裏的例子hello-gl2。項目代碼看裏面的lesson2的taggithub

宏文件

#ifndef _APPMACROS_H__
#define _APPMACROS_H__

#include <android/log.h>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <stdio.h>
#include <stdlib.h>

#define  LOG_TAG    "GLES-Tutorial"

#define  LOGV(...)  __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)

static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

#endif

這裏除了android裏的Log外添加了gl2.h, gl2ext.h頭文件,這兩個文件能夠在NDK包(platforms/android-19/)中找到,這裏使用了android裏的libGLESv2.so庫。printGLString能夠獲取到OpenGL屬性的狀態,checkGlError能夠檢測OpenGL狀態是否有錯誤。編程

使用OpenGLES2

先給Director添加成員變量:less

GLProgram *_glProgram;    // opengl狀態機
    GLuint _vPositionHandle;  // 獲取到的頂點位置屬性
#include "Director.h"
#include "AppMacros.h"

// 頂點着色器
static const char gVertexShader[] = 
    "attribute vec4 vPosition;\n"
    "void main() {\n"
    "  gl_Position = vPosition;\n"
    "}\n";
// 片斷着色器
static const char gFragmentShader[] = 
    "precision mediump float;\n"
    "void main() {\n"
    "  gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n"
    "}\n";
// 三角形頂點數據
const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f,
                                     -0.5f, 0.5f, -0.5f };
// ... 省略掉一些

void Director::setFrameSize(float width, float height)
{
    LOGI("Director::setFrameSize(%lf, %lf)", width, height);
    _fFrameWidth = width;
    _fFrameHeight = height;

    // 建立一個空源的OpenGL狀態機
    _glProgram = new GLProgram();
    // 將着色器裝載和編譯
    _glProgram->initWithVertexShaderByteArray(gVertexShader, gFragmentShader);
    // 連接着色器
    _glProgram->link();
    // 使用此狀態機,着色器將能運用上
    _glProgram->use();
    // 獲取到位置屬性,以便用來繪製圖形;頂點着色器裏的vPosition屬性
    _vPositionHandle = _glProgram->getAttribLocation("vPosition");
    // 設置OpenGL視口,一個2D的長方形區域(Android裏GLSurfaceView窗體的大小)
    glViewport(0, 0, width, height);
    checkGlError("glViewport");
}

void Director::mainLoop()
{
    // 每一幀都讓灰度值疊加
    static float grey;
    grey += 0.01f;
    if (grey > 1.0f) {
        grey = 0.0f;
    }
    // 用顏色來填充清除深度和顏色緩衝區
    glClearColor(grey, grey, grey, 1.0f);
    glClear( GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    // 將頂點位置屬性獲取到
    glVertexAttribPointer(_vPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
    checkGlError("glVertexAttribPointer");
    glEnableVertexAttribArray(_vPositionHandle); // 並將三角形頂點設置進頂點矩陣
    checkGlError("glEnableVertexAttribArray");
    glDrawArrays(GL_TRIANGLES, 0, 3);  // 繪製三角形圖元
    checkGlError("glDrawArrays");
}

GLProgram是我包裝了OpenGL的一些操做,便於使用:oop

class GLProgram
{
public:
    GLProgram();
    ~GLProgram();

    /*
     * init GLProgram with vertex shader array data and fragment shader array data
     */
    bool initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray);

    /*
     * link shader
     */
    bool link();

    /*
     * use this opengl program
     */
    void use();

    /*
     * get location attrib
     */
    GLuint getAttribLocation(const GLchar* attrib);

private:
    GLuint loadShader(GLenum shaderType, const GLchar* shaderSrc);

    GLuint _uProgram;
    GLuint _uVertShader;
    GLuint _uFragShader;

};
bool GLProgram::initWithVertexShaderByteArray(const GLchar* vShaderByteArray, const GLchar* fShaderByteArray)
{
    _uVertShader = loadShader(GL_VERTEX_SHADER, vShaderByteArray);
    if (!_uVertShader) {
        return false;
    }

    _uFragShader = loadShader(GL_FRAGMENT_SHADER, fShaderByteArray);
    if (!_uFragShader) {
        return false;
    }

    _uProgram = glCreateProgram();
    if (!_uProgram) {
        return false;
    }
    
    glAttachShader(_uProgram, _uVertShader);
    checkGlError("glAttachShader");
    glAttachShader(_uProgram, _uFragShader);
    checkGlError("glAttachShader");
    
    return true;
}

bool GLProgram::link()
{
    glLinkProgram(_uProgram);

    GLint linkStatus = GL_FALSE;
    glGetProgramiv(_uProgram, GL_LINK_STATUS, &linkStatus);
    if (linkStatus != GL_TRUE) {
        GLint bufLength = 0;
        glGetProgramiv(_uProgram, GL_INFO_LOG_LENGTH, &bufLength);
        if (bufLength) {
            char* buf = (char*) malloc(bufLength);
            if (buf) {
                glGetProgramInfoLog(_uProgram, bufLength, NULL, buf);
                LOGE("Could not link _uProgram:\n%s\n", buf);
                free(buf);
            }
        }
        glDeleteProgram(_uProgram);
        _uProgram = 0;

        return false;
    }
    return true;
}

void GLProgram::use()
{
    glUseProgram(_uProgram);
    checkGlError("glUseProgram");
}

GLuint GLProgram::loadShader(GLenum shaderType, const char* shaderSrc)
{
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &shaderSrc, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); // check compile information
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* infoLog = (char*) malloc(infoLen);
                if (infoLog) {
                    glGetShaderInfoLog(shader, infoLen, NULL, infoLog); // seek infor log 
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, infoLog);
                    free(infoLog);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint GLProgram::getAttribLocation(const GLchar* attrib)
{
    GLuint retAtt = glGetAttribLocation(_uProgram, attrib);
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"%s\") = %d\n", attrib, retAtt);
    return retAtt;
}

OpenGL ES提供了一套運行期動態編譯的流程:ui

  • 1.建立着色器:glCreateShaderthis

  • 2.指定着色器源代碼字符串:glShaderSourcespa

  • 3.編譯着色器:glCompileShaderdebug

  • 4.建立着色器可執行程序:glCompileShader

  • 5.向可執行程序中添加着色器:glAttachShader

  • 6.連接可執行程序:glLinkProgram

Run

編譯以前,須要修改下Android.mk

LOCAL_SRC_FILES := \
com_richard_glestutorial_GLRenderer.cpp \
../core/Director.cpp \
../core/GLProgram.cpp
                
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)
$ndk-build  && ant debug  && adb install -r bin/GlesTutorial-debug.apk

運行腳本,將能夠看一個綠色的三角形,而且背景在不斷變化。

圖片描述

相關文章
相關標籤/搜索