I. はじめに
前回のブログでは、OpenGL ESのコア言語であるGLSLを実装して、イメージを画面にレンダリングしました。 最終的には成功したのですが、ちょっと問題があって、レンダリングされたイメージが思ったように上下反転しないのです。その理由は、OpenGLではY軸の0.0座標がイメージの一番下にあり、Y座標が下から上に向かって増えていくのに対し、イメージテクスチャのY軸である0.0座標はデフォルトでイメージの一番上にあり、Y座標が上から下に向かって増えていくからです。つまり、最終的に表示されるイメージは、180°逆さまに回転したような効果になります。
解決方法
さて、何が反転を生み出しているのかがわかったところで、それを修正しましょう。
プログラム1 - 行列変換
行列はOpenGL/OpenGL ESでイメージの回転や変位によく使われるので、まずは行列を使ってイメージを180°回転させて普通に表示するところから始めます。主なコードは以下の通りです:
-(void)rotateTextureImage
{
//なお、シェーダー内の変数を取得したい場合は、ここでglLinkProgramの後ろ、後ろ、後ろに行くのを忘れないこと!
//1. rotate shaderv.vshのユニフォーム属性はrotateMatrix
GLuint rotate = glGetUniformLocation(self.myPrograme, "rotateMatrix");
//2.回転円弧を取得する
float radians = 159f / 180.0f;
//3.のラジアンを求める。sin\cos
float s = sin(radians);
float c = cos(radians);
//4.なぜなら、3Dコースでは横ベクトルを使い、OpenGL ESでは列ベクトルを使うからだ。
/*
Z軸回転行列
*/
GLfloat zRotation[16] = {
c,-s,0,0,
s,c,0,0,
0,0,1,0,
0,0,0,1
};
//5.回転行列を設定する
/*
glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
location : シェーダーの場合ID
count :
transpose :
value :
*/
glUniformMatrix4fv(rotate, 1, GL_FALSE, zRotation);
}
また、行列を渡すように頂点シェーダーのコードを修正しました:
attribute vec4 position;
attribute vec2 textCoordinate;
uniform mat4 rotateMatrix;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
vec4 vPos = position;
vPos = vPos * rotateMatrix;
gl_Position = vPos;
}
次に - renderLayer を呼び出します。
//11. テクスチャ・サンプラーを設定する sampler2D
glUniform1i(glGetUniformLocation(self.myPrograme, "colorMap"), 0);
//テクスチャの原因を解決する(方法)1)
[self rotateTextureImage];
//12.
glDrawArrays(GL_TRIANGLES, 0, 6);
//13.レンダーバッファからスクリーンへ
[self.myContext presentRenderbuffer:GL_RENDERBUFFER];
注意点:1.取得する変数はシェーダのものなので、glLinkProgramの後に実行しないと、シェーダファイルがまだコンパイル・リンクされておらず、シェーダ内の変数を取得することができません。2.昔習った、あるいは知っている行列は通常列ベクトルですが、OpenGL/OpenGL ESでは列ベクトルなので、回転行列を作るときは注意が必要です。
オプション2 - 設定時にテクスチャを回転させる
テクスチャは反転しているので、テクスチャを正しい位置に回転させれば十分です。- (GLuint)setupTexture:(NSString *)fileNameこのステップは主にキーコードという関数で行います:
//5CGContextRefを使ってCGContextRefを作る。--> イメージを描く
/*
CGContextDrawImage UIKitフレームワークの原点は画面の左上だが、Core Graphicsフレームワークの原点は画面の左下だ。
CGContextDrawImage
パラメータ1:描画コンテキスト
パラメータ2:rect座標
パラメータ3:描画イメージ
*/
CGRect rect = CGRectMake(0, 0, width, height);
//6.デフォルトで描画する
CGContextDrawImage(spriteContext, rect, spriteImage);
//テクスチャの反転
CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
//テクスチャ描画
CGContextDrawImage(spriteContext, rect, spriteImage);
//7描画が終わったらコンテキストを解放する
CGContextRelease(spriteContext);
テクスチャフリップコードプロセスの4行:1、キャンバスの全体的な変位2、イメージの高さ3のサイズを移動するには、正の方向にY軸に沿ってキャンバスは、Y軸180°4に沿ってキャンバスを反転し、元のポイントに戻ってキャンバスを移動します。
これで正しい表示が得られます。
オプション 3 - チップ・シェーダー・テクスチャ・フリップ
オプション 2 と同様に、反転はテクスチャに対して行われますが、前者はテクスチャが読み込まれるときに行われ、後者はテクスチャが描画されるときに行われます。ピースソースシェーダを使って実装されているので、ピースソースシェーダ内のコードを修正すれば十分です:
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
//gl_FragColor = texture2D(colorMap, varyTextCoord);
gl_FragColor = texture2D(colorMap, vec2(varyTextCoord.x,1.0-varyTextCoord.y));
}
テクスチャ座標の範囲は1.0-varyTextCoord.yなので、反転したテクスチャ座標を取得するには1.0-varyTextCoord.yを使用します。
オプション 4 - バーテックス・シェーダー・テクスチャ・フリップ
このスキームはスキーム 3 と同じ原理で動作し、シェーダを通してテクスチャを反転させますが、スキーム 3 がスライスソースシェーダに実装されているのに対し、このスキームはバーテックスシェーダに実装されている点が異なります。コード
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
//varyTextCoord = textCoordinate;
varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
gl_Position = position;
}
シートソースシェーダのテクスチャ座標は頂点シェーダから渡されるので、頂点シェーダ内で反転しても問題ありません。
オプション 5 -- 頂点座標の修正
テクスチャは与えられた頂点座標に従って描画されるので、与えられた頂点座標が反転してもテクスチャは正しく表示されます。- (void)setupPositionDataこの解決策は.NETで実装されています:
//6.頂点座標とテクスチャ座標を設定する
//最初の3つは頂点座標で、最後の2つはテクスチャ座標だ。
// GLfloat attrArr[] =
// {
// 0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
// -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
// -0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
//
// 0.5f, 0.5f, -1.0f, 1.0f, 1.0f,
// -0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
// 0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
// };
GLfloat attrArr[] =
{
0.5f, -0.5f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -1.0f, 0.0f, 1.0f,
0.5f, 0.5f, -1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -1.0f, 1.0f, 1.0f,
};
上の頂点配列が一番最初のデータで、下が反転した頂点です。
オプション 6 -- バーテックス・シェーダによる頂点座標の変更
これはオプション5と同じように、頂点を反転させることで実装されます。しかし、これは頂点シェーダ内で実装されます。原理はオプション1と似ていますが、より単純です。
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
/// 1
vec4 vPos = position;
vPos *= vec4(1, -1, 1, 1);
gl_Position = vPos;
/// 2
// gl_Position = vec4(position.x, -position.y,1,1);
}
###### ここで注意してほしいのは、頂点座標の値の範囲は-1〜1なので、直接の逆数であるのに対し、テクスチャ座標は0〜1なので、テクスチャ座標の1から座標点を引くということではなくなりますので、区別に注意してください。また、ここでも2通りに分けられます:
- 行列を使った頂点反転
- Y軸座標を手動で反転
まとめ
OpenGL ESのロードテクスチャイメージの反転については、いくつかのソリューションが語られていますが、原理的にいくつかの方法が異なる以上。テクスチャが反転するので、誰が何をするかの原則の精神では、テクスチャ部分を処理するのが最善です。テクスチャ部分で反転を処理するのがベストです。また、シェーダーで座標を処理すると、別の描画結果が生じる可能性があるので、私はオプション2を推奨します。ということで、選択肢2をお勧めしますが、もちろん、自分の好みや必要に応じて臨機応変に使ってください。





