blog

Android OpenGL ES 2.0描画:テクスチャの描画

androidはopenGLを使って、特別なビューをベースとして提供します。テクスチャは、OpenGLではグラフィックカードのグラフィックメモリにロードされた画像として理解できます。...

Apr 21, 2025 · 12 min. read
シェア

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などのテクニックを使えば、データをビデオメモリにキャッシュして、実行時のパフォーマンスを向上させることができます。

Read next

クレジットカード流出についてあなたが知らないこと

Ctripのクレジットカード流出事件は、ハッカー界ではそれほど大きな事件ではないようです。似たような事件が多すぎるのです。例えば、2007年にハッカーのアルバート・ゴンザレスがアメリカのディスカウントショップチェーンのTJMaxxに侵入してクレジットカード情報を盗み、4000万人のユーザーを巻き込みました。

Apr 21, 2025 · 2 min read