emscriptenとOpenGL ES2.0でhello, world

emscriptenOpenGL ES 2.0(WebGL)+GLUTをサポートしているので、OpenGL ES2.0で"hello, world"。emscriptenのセットアップはこちら

を参考に作成。(200行くらいになってしまったので記事の最後にhello.cを貼りました。)
glutBitmapCharacterは使えるか不明だったのでテクスチャで"HELLO, WORLD"を表示する。

まずは、GLEWを使ってgccでビルド。

$ sodo apt-get install freeglut3-dev libglew1.6-dev
$ gcc hello.c -l glut -lGLEW -o hello
$ ./hello

ウインドウ上で"HELLO, WORLD"が表示された。

次にemscriptenでビルド。

$ emscripten/emcc hello.c -o hello.html

clang: warning: argument unused during compilation: '-nostdinc++'
と出るがビルドはできた。

結果はこちら。いやいやすごい。(Chrome19,Firefox13で確認。表示を確認するにはブラウザの設定が「WebGL有効」である必要があります。)

[hello.c]

#ifdef EMSCRIPTEN
#define GL_GLEXT_PROTOTYPES
#define EGL_EGLEXT_PROTOTYPES
#else // glew
#include
#endif // EMSCRIPTEN

#include
#include

#define TEX_W (128)

typedef struct
{
GLuint programObject;
GLuint vertexShader, fragmentShader;
GLuint textureId;
} glhandle;

static const char s_vShaderStr[] =
"attribute vec4 a_position; \n"
"attribute vec2 a_texCoord; \n"
"varying vec2 v_texCoord; \n"
"void main() \n"
"{ \n"
" gl_Position = a_position; \n"
" v_texCoord = a_texCoord; \n"
"} \n";


static const char s_fShaderStr[] =
"#ifdef GL_ES\n"
"precision mediump float; \n"
"#endif\n"
"varying vec2 v_texCoord; \n"
"uniform sampler2D s_texture; \n"
"void main() \n"
"{ \n"
" gl_FragColor = texture2D( s_texture, v_texCoord );\n"
"} \n";


static const unsigned char s_acDotImg[8][12] =
{
0x63,0x3f,0x30,0x30,0x3e,0x00,0x00,0x63,0x3e,0x7e,0x30,0x7c,
0x63,0x30,0x30,0x30,0x63,0x00,0x00,0x63,0x63,0x63,0x30,0x66,
0x63,0x30,0x30,0x30,0x63,0x00,0x00,0x6b,0x63,0x63,0x30,0x63,
0x7f,0x3e,0x30,0x30,0x63,0x00,0x00,0x7f,0x63,0x67,0x30,0x63,
0x63,0x30,0x30,0x30,0x63,0x30,0x00,0x7f,0x63,0x7c,0x30,0x63,
0x63,0x30,0x30,0x30,0x63,0x10,0x00,0x77,0x63,0x6e,0x30,0x66,
0x63,0x3f,0x3f,0x3f,0x3e,0x20,0x00,0x63,0x3e,0x67,0x3f,0x7c,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
};

static const GLfloat s_vertices[] = {
-1.0f, 1.0f, 0.0f, // Position 0
0.0f, 0.0f, // TexCoord 0
-1.0f, -1.0f, 0.0f,
0.0f, 0.0625f,
1.0f, -1.0f, 0.0f,
0.75f, 0.0625f,
1.0f, 1.0f, 0.0f,
0.75f, 0.0f,
};
static const GLushort s_indices[] = { 0, 1, 3, 2 };

static GLuint load_shader( GLenum type, const char *src )
{
GLuint shader;

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

return shader;
}

static GLuint setup_tex()
{
int i, j, k;
GLuint textureId;
GLubyte pixels[TEX_W*TEX_W * 3]; // ちょっと大きいけど

glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
glGenTextures( 1, &textureId );
glBindTexture( GL_TEXTURE_2D, textureId );

// ドットイメージテクスチャ化
for( i = 0; i < 8; i++ )
{
for( j = 0; j < 12; j++ )
{
for( k = 0; k < 8; k++ )
{
GLubyte val = ( (s_acDotImg[i][j] & (1<<(7-k))) ? 0xff : 0 );
int index = (i*128+j*8+k)*3;
pixels[index+0] = val;
pixels[index+1] = val;
pixels[index+2] = val;
}
}
}

glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, TEX_W, TEX_W, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
glActiveTexture( GL_TEXTURE0 );
glBindTexture( GL_TEXTURE_2D, textureId );

return textureId;
}

static void setup_vtx( glhandle *handle, GLuint positionLoc, GLuint texCoordLoc, GLuint samplerLoc )
{
GLuint s_vertexPosObject, s_indexObject;
glGenBuffers(1, &s_vertexPosObject);
glBindBuffer(GL_ARRAY_BUFFER, s_vertexPosObject );
glBufferData(GL_ARRAY_BUFFER, sizeof(s_vertices), s_vertices, GL_STATIC_DRAW );

glGenBuffers(1, &s_indexObject);
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, s_indexObject );
glBufferData( GL_ELEMENT_ARRAY_BUFFER, sizeof(s_indices), s_indices, GL_STATIC_DRAW );

glBindBuffer(GL_ARRAY_BUFFER, s_vertexPosObject);
glVertexAttribPointer( positionLoc, 3, GL_FLOAT, GL_FALSE, 5 * 4, 0 );
glVertexAttribPointer( texCoordLoc, 2, GL_FLOAT, GL_FALSE, 5 * 4, (const GLvoid *)(3 * 4) );
glEnableVertexAttribArray( positionLoc );
glEnableVertexAttribArray( texCoordLoc );

glUniform1i( samplerLoc, 0 );
glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, s_indexObject );
}

static void initialize( glhandle *handle )
{
GLuint positionLoc, texCoordLoc, samplerLoc;

handle->vertexShader = load_shader( GL_VERTEX_SHADER, s_vShaderStr );
handle->fragmentShader = load_shader( GL_FRAGMENT_SHADER, s_fShaderStr );

handle->programObject = glCreateProgram();
glAttachShader( handle->programObject, handle->vertexShader );
glAttachShader( handle->programObject, handle->fragmentShader );
glLinkProgram( handle->programObject );
glUseProgram( handle->programObject );

positionLoc = glGetAttribLocation( handle->programObject, "a_position" );
texCoordLoc = glGetAttribLocation( handle->programObject, "a_texCoord" );
samplerLoc = glGetUniformLocation( handle->programObject, "s_texture" );

handle->textureId = setup_tex();
setup_vtx(handle, positionLoc, texCoordLoc, samplerLoc);
}

static void release( glhandle *handle )
{
glDeleteTextures(1, &handle->textureId);
glDeleteShader(handle->fragmentShader);
glDeleteShader(handle->vertexShader);
glDeleteProgram(handle->programObject);
}

static void display()
{
glClear(GL_COLOR_BUFFER_BIT);

glDrawElements( GL_TRIANGLE_STRIP, sizeof(s_indices)/sizeof(s_indices[0]), GL_UNSIGNED_SHORT, 0 );

glutSwapBuffers();
}

int main(int argc, char *argv[])
{
glhandle handle;

glutInit(&argc, argv);
glutInitWindowSize(96*2, 8*2);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("hello, world");

#ifndef EMSCRIPTEN
glewInit();
#endif
initialize(&handle);

glutDisplayFunc(display);
glutMainLoop();

release(&handle);

return 0;
}