おまけ:RICOH THETAのJPG画像を平面-球変換してOpenGLで表示する(その2)
ズームを引いていくと裏面が見えてしまう問題の解消は、面を張って、視線と対抗しない法線を持つ面を描画しなければ良い、と適当に説明しっぱなしになっていたのが気がかりだったので、ソースコードを乗っけておきます。
メッシュを三角ポリゴンで表示するように指定します。
//mesh.setMode(OF_PRIMITIVE_POINTS);
mesh.setMode(ofPrimitiveMode::OF_PRIMITIVE_TRIANGLES);
原点に中心がある球の法線ベクトルは球表面の点の座標値と同じ向きになるので、法線ベクトルの定義は簡単です。法線は、頂点に定義します。
// loop through the image in the x and y axes
float r = 10.0f; // radius
for(int y = 0; y < img.getHeight(); y++){
for(int x = 0; x < img.getWidth(); x++){
ofColor cur = img.getColor(x, y);
cur.a = 255;
float theta = (float)y * M_PI / (float)img.getHeight();
float phi = -(float)x * 2.0f * M_PI / (float)img.getWidth();
float px = r * sin(theta) * cos(phi);
float py = r * sin(theta) * sin(phi);
float pz = r * cos(theta);
mesh.addColor(cur);
ofVec3f pos(px, py, pz);
mesh.addVertex(pos);
mesh.addNormal(ofVec3f(px,py,pz).normalize());
}
}
画像座標に対して、次の絵のように頂点を選んで三角ポリゴンを定義していきます。ある注目する頂点に対して右回りに赤緑青の矢印の順番で2つの面を張りながら、画像座標を走査していきます。x=0を跨ぐメッシュを張るときに少しだけ注意が必要です。
for(int y = 0; y < img.getHeight()-1; y++){
for(int x = 0; x < img.getWidth(); x++){
int idx1 = y * img.getWidth() + x;
int idx2 = y * img.getWidth() + x + 1;
if(x==img.getWidth()-1){
idx2 = y*img.getWidth();
}
int idx3 = (y+1) * img.getWidth() + x;
mesh.addTriangle(idx1,idx2,idx3);
idx2 = idx3;
idx3 = (y+1) * img.getWidth() + (x-1);
if(x==0){
idx3 = (y+1) * img.getWidth() + img.getWidth()-1;
}
mesh.addTriangle(idx1,idx2,idx3);}
}
球の中心から球の裏面を眺めるので、表示面をこのように指定します。
glEnable(GL_CULL_FACE);
glCullFace(GL_FRONT); //GL_BACK);
右手でTHETAを持って撮影した画像を引いて見てみます。ズームを引いても、ひっくり返らなくて済むようになりました。
・・・さぁ、遊んでないでお仕事をしましょう。