OpenGLにおけるテクスチャとは、グラフィックスカードのビデオメモリに読み込まれるイメージのことです。
Androidデバイスは2.2からOpenGL ES2.0をサポートし始めました。以前はES1.0とES1.1でした。
簡単に言うと、OpenGL ESはデバイスに組み込むためにOpenGLを切り詰めたバージョンです。 ES 2.0はバージョン1.xと互換性がありません。違いや互換性についてはアンドロイドの公式ドキュメントを参照してください。
Javaコード
public class MyGLSurfaceView extends GLSurfaceView {
public MyGLSurfaceView(Context context) {
スーパー;
setFocusableInTouchMode;
// Tell the surface view we want to create an OpenGL ES 2.0-compatible
// context, and set an OpenGL ES 2.0-compatible renderer.
"これ".setEGLContextClientVersion(2);
"これ".setRenderer(new MyRenderer());
}
}
アンドロイドビューのレンダリング操作はレンダーインターフェースを実装する必要があり、GLSurfaceViewのレンダーインターフェイスはandroid.opengl.GLSurfaceView.Rendererです。
Javaコード
public class MyRenderer implements Renderer {
public void onDrawFrame(GL10 gl) {}
public void onSurfaceChanged(GL10 gl, int width, int height) {}
public void onSurfaceCreated(GL10 gl, EGLConfig config) {}
}
このインターフェイスは、描画、描画領域の変更、領域の作成に対応する3つのメソッドを実装しています。パラメータ GL10 gl は OpenGL ES version 1.x オブジェクトであることに注意してください。ここでは使用しません。もう一つのポイントは、onDrawFrameメソッドにはシステムコールがあるので、手動で呼び出す必要がないことです。システムは一定の頻度でコールバックし続けます。
次に、ES2.0の使い方を、まずコード上で説明しましょう:
Javaコード
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
// Active the texture unit 0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
ロード頂点();
initShader();
loadTexture();
}
1.2Dテクスチャの有効化
これはどういう意味かというと、長くなるので後述します。簡単に言うと、OpenGLはステートベース、つまり、たくさんのステート設定やトグルがあることを覚えておいてください。ここでGL_TEXTURE_2Dを有効にすることは、ステートをオンにすることで、OpenGLが2Dテクスチャを使えることを示します。
アクティブテクスチャユニットとは何ですか?これはハードウェアと少し関係があり、OpenGLはグラフィックスカードがテクスチャを保存するための複数の領域を持つことを望んでいます。ここではエリアユニット0を使っていますが、複数のテクスチャ描画をオンにすることもできます。次に、3つの関数が呼び出されます。頂点の読み込み、シェーダーの初期化、テクスチャの読み込みです。
2、ロード頂点
OpenGLは、後からリンクされた頂点を元にグラフィックを描画します。実はこれ、デザインとして非常に強力なんですね。頂点は、とりあえず位置情報を含む座標点として理解すればいいでしょう。
Javaコード
private void loadVertex() {
// float size = 4
"これ".vertex = ByteBuffer.allocateDirect(quadVertex.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
"これ".vertex.put(quadVertex).position( 0);
// short size = 2
"これ".index = ByteBuffer.allocateDirect(quadIndex.length * 2)
.order(ByteOrder.nativeOrder())
.asShortBuffer();
"これ".index.put(quadIndex).position(0);
}
private FloatBuffer vertex;
private ShortBuffer index;
private float[] quadVertex = new float[] {
-0.5f, 0.5f, 0.0f, // Position 0
0, 1.0f, // TexCoord 0
-0.5f, -0.5f, 0.0f, // Position 1
0, 0, // TexCoord 1
0.5f , -0.5f, 0.0f, // Position 2
1.0f, 0, // TexCoord 2
0.5f, 0.5f, 0.0f, // Position 3
1.0f, 1.0f, // TexCoord 3
};
private short[] quadIndex = new short[] {
, // Position 0
, // Position 1
, // Position 2
, // Position 2
, // Position 3
, // Position 0
};
FloatBufferとShortBufferはローカルデータ構造をカプセル化するラップオブジェクトです。quadVertexデータは矩形の座標とテクスチャの座標です。これを1、2文で説明するのは難しいのですが、openGLの古典的な座標系のいくつかが関わってくるので、その話は次回にします。まとめると、openGLの座標は単位化されていて、すべて浮動小数点0.0-1.0、画面の中心は.そしてテクスチャの座標は左下です。 ここで、quadVertexは、おそらくイメージを貼り付けた矩形で、position0が左上の点で、左下、右下、右上の順で、テクスチャの座標は同じです。
quadIndxはこれらの頂点の頂点インデックスの配置です。ここでは矩形は4つの頂点を持ち、それぞれ3つの位置座標と2つのテクスチャ座標を持ちます。つまり頂点は5つのfloatデータを持っています。なぜこのように頂点が配置されているかというと、次回は「2つの三角形が矩形を形成している」ということを説明しようと思います。
つまり、このコードは矩形の位置とテクスチャの座標を取得し、後で使えるようにローカルデータに保存しているだけです。
3、シェーダーの初期化
このシェーダはES2.0の特徴であり、プログラマブルシェーダとも呼ばれ、ES1.xとの違いのエッセンスです。ここでは簡単に紹介します。プログラマブルシェーダは一種のスクリプトで、構文はC言語に似ており、スクリプトは頂点シェーダとフラグメントシェーダに分けられ、openGLの異なるレンダリングプロセスに対応しています。
頂点シェーダー:
Javaコード
uniform mat4 u_MVPMatrix;
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
void main()
{
Gl_Position = a_position;
v_texCoord = a_texCoord;
}
フラグメントシェーダー:
Javaコード
precision lowp float;
varying vec2 v_texCoord;
uniform sampler2D u_samplerTexture;
void main()
{
Gl_FragColor = texture2D(u_samplerTexture, v_texCoord);
}
頂点シェーダーは頂点に対して実行され、フラグメントシェーダーはピクセルポイントに対して実行されます。先ほどの矩形は頂点だけで4つあり、それぞれの頂点にこのスクリプトが適用されます。つまり、頂点は位置に関する情報で、フラグメントはカラーテクスチャに関する情報です。
2つのスクリプトはテキストで、ES2.0で使用するためにはコンパイルやリンクなどが必要です。このプロセスは、C言語のコンパイルプロセスのようなものです。
Javaコード
private void initShader() {
String vertexSource = Tools.readFromAssets("VertexShader.glsl");
String fragmentSource = Tools.readFromAssets("FragmentShader.glsl");
// Load the shaders and get a linked プログラム
program = GLHelper.loadProgram(vertexSource, fragmentSource);
// Get the attribute locations
属性位置 = GLES20.glGetAttribLocation(program, "a_position");
属性TexCoord = GLES20.glGetAttribLocation(program, "a_texCoord");
ユニフォーム・テクスチャ = GLES20.glGetUniformLocation(program,
"u_samplerTexture");
GLES20.glUseProgram(program);
GLES20.glEnableVertexAttribArray(attribPosition);
GLES20.glEnableVertexAttribArray(attribTexCoord);
// Set the sampler to texture unit 0
GLES20.glUniform1i(uniformTexture, 0);
}
ご覧のように、頂点とフラグメントが一緒になって、openGLで使用できるプログラム、メモリに格納されたコンパイルされたスクリプトプログラムを形成しています。 GLES20.glGetAttribLocationとGLES20.glGetUniformLocation このステートメントは何をするものですか。簡単に言うと、Javaプログラムとシェーダースクリプトのデータ通信です。スクリプトが外部パラメータの変更に基づいてリアルタイムでopenGLパイプラインのレンダリング処理を変更できるように、パラメータを渡すようなものです。
シェーダをロードするためのカプセル化されたヘルパーメソッドです:
Javaコード
public static イント loadProgram(String vertexSource, String
fragmentSource) {
// Load the vertex shaders
int vertexShader = GLHelper.loadShader( GLES20.GL_VERTEX_SHADER,
vertexSource);
// Load the fragment shaders
int fragmentShader = GLHelper.loadShader(GLES20.GL_FRAGMENT_SHADER,
fragmentSource);
// Create the program object
int program = GLES20.glCreateProgram();
if (program == 0) {
throw new RuntimeException("Error create program.");
}
GLES20.glAttachShader(program, vertexShader);
GLES20.glAttachShader(program, fragmentShader);
// Link the program
GLES20.glLinkProgram(program);
int[] linked = new int[1];
// Check the link status
GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linked, 0);
if (linked[0] == 0) {
GLES20.glDeleteProgram(program);
throw new RuntimeException("Error linking program: " +
GLES20.glGetProgramInfoLog(program));
}
// Free up no longer needed shader resources
GLES20.glDeleteShader(vertexShader);
GLES20.glDeleteShader(fragmentShader);
return program;
}
Javaコード
public static イント loadShader(int shaderType, String source) {
// Create the shader object
int shader = GLES20.glCreateShader(shaderType);
if (shader == 0) {
throw new RuntimeException("Error create shader.");
}
int[] compiled = new int[1];
// Load the shader source
GLES20.glShaderSource(shader, source);
// Compile the shader
GLES20.glCompileShader(shader);
// Check the compile status
GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0);
if (compiled[0] == 0) {
GLES20.glDeleteShader(shader);
throw new RuntimeException("Error compile shader: " +
GLES20.glGetShaderInfoLog(shader));
}
return shader;
}
なぜopenGLの多くの操作のターゲットはint型なのですか?なぜなら、openGLはメモリ上にアドレスを生成するかバインドするだけで、idを返し、後で内部状態を変更するためにハンドルに相当するidを使用するからです。
4.テクスチャの読み込み
イメージのデータをビデオメモリにアップロードして、後で使うだけです。テクスチャイメージ***の縦横サイズは2のN乗です。
Javaコード
static イント[] loadTexture(String path) {
イント[] textureId = new int[1];
// Generate a texture object
GLES20.glGenTextures(1, textureId, 0);
int[] 結果 = null;
if (textureId[0] != 0) {
InputStream それは = Tools.readFromAsserts(path);
Bitmap ビットマップ;
試す {
ビットマップ = BitmapFactory.decodeStream(is);
} finally {
試す {
is.close();
} catch (IOException e) {
throw new RuntimeException("Error loading Bitmap.");
}
}
結果 = new int[3];
結果[TEXTURE_ID] = textureId[0]; // TEXTURE_ID
結果[TEXTURE_WIDTH] = bitmap.getWidth(); // TEXTURE_WIDTH
result[TEXTURE_HEIGHT] = bitmap.getHeight(); // TEXTURE_HEIGHT
// Bind to the texture in OpenGL
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId[0]);
// Set filtering
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER,
GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER,
GLES20.GL_NEAREST);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S,
GLES20.GL_CLAMP_TO_EDGE);
GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T,
GLES20.GL_CLAMP_TO_EDGE);
// Load the bitmap into the bound texture.
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
// Recycle the bitmap, since its data has been loaded into OpenGL.
bitmap.recycle();
} else {
throw new RuntimeException("Error loading texture.");
}
return result;
}
ここでは、アンドロイドのツールを使って、ビットマップをopenGLテクスチャ形式に直接変換します。手順としては、グラフィックカード上にテクスチャIDを作成し、後でそのIDに基づいてテクスチャデータをアップロードし、後でそのIDを保存します。
これでこのテクスチャを操作できます。テクスチャのフィルタ設定のいくつかについては、今後説明します。
あとは描画するだけという感じで、頂点情報を準備し、頂点に対応するテクスチャ座標を用意しました。シェーダーを初期化して、テクスチャイメージをアップロード。次のステップは、もう一緒に描画することです。
Javaコード
public void onDrawFrame(GL10 gl) {
// clear screen to black
GLES 20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
頂点.position(0);
// load the position
// 3(x , y , z)
// (2 + 3 )* 4 (float size) = 20
GLES20.glVertexAttribPointer(attribPosition,
3, GLES20.GL_FLOAT,
偽, 20, 頂点);
vertex.position(3);
// load the texture coordinate
GLES20.glVertexAttribPointer(attribTexCoord,
2, GLES20.GL_FLOAT,
偽, 20, vertex);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT,
index);
}
コードをシンプルに保つために、OpenGLの状態ベースの表現であるbind関数はどこにでもあります。bindTextureはopenGLにそのidを使うように知らせるテクスチャイメージです。次の操作はバインドイメージのためのものです。描画するには、何を描画するかをopenGLに知らせる必要があります。glVertexAttribPointerは、openGLが好む形式で頂点データをグラフィックスカードにアップロードするためのものです。したがって、テクスチャ座標はアップロードされたテクスチャイメージを使用して描画されます。
結局のところ、ビデオメモリとメモリは同じ場所ではなく、OpenGLはCSモードを使用します。もちろん、VBOなどのテクニックを使えば、データをビデオメモリにキャッシュして、実行時のパフォーマンスを向上させることができます。





