Hello GLES2
和ES1.0不同,ES2.0引入了可编程管线,如下图中,可编程阶段为VertexShader,FragmentShader顶点和片段着色器阶段。
顶点着色器被使用在传统的基于顶点的操作,例如位移矩阵、计算光照方程、产生贴图
坐标。顶点着色器被应用指定,应用于客户端的顶点转化。顶点着色器需要一个位置和颜色数据作为输入属性,输入位置数据是 4×4 的矩阵,输出是变换后的位置和颜色。
片段着色器不需定义输出,这是因为片段着色器仅仅的输出是gl_FragColor。
现在我们用上一节的Android入口渲染一个三角形。这里借用NDK包里的例子hello-gl2。项目代码看里面的lesson2的tag
宏文件
#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添加成员变量:
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的一些操作,便于使用:
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提供了一套运行期动态编译的流程:
1.创建着色器:glCreateShader
2.指定着色器源代码字符串:glShaderSource
3.编译着色器:glCompileShader
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
运行脚本,将可以看一个绿色的三角形,并且背景在不断变化。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。