OpenGLがglVertex3d等で与えられた座標をどうやってピクセル座標に変換しているかは以下のサイトなどで説明されている。
http://www.songho.ca/opengl/gl_transform.html
これを実際に計算してみる。
なおベクトルの変換などは作り置きの関数vmmath.hppを使う
https://www.study.suzulang.com/3dcg-functions/vmmath-hpp
#pragma once #include <iostream> #include <array> #include "vmmath.hpp" // 参考 //http://www.songho.ca/opengl/gl_transform.html void opengltransformation( std::array<double, 4>& wnd, const std::array<double, 4>& obj, const std::array<double, 4>& norm, const std::array<double, 16>& modelview, const std::array<double, 16>& projection, const std::array<double, 2>& depthrange, const std::array<int, 4>& viewport ) {
std::array<double, 4> exyz; szl::mathm::mult_m4_v4(exyz, modelview, obj);
/////////////////////////////////////////////// #if 0 // 現在動作確認を取っていないのでとりあえず無効化 //法線計算 std::array<double, 16> modelInvT; szl::mathm::inverse44(modelInvT, modelview); szl::mathm::transpose44(modelInvT); std::array<double, 4> nxyz; szl::mathm::mult_m4_v4(nxyz, modelInvT, norm); #endif ///////////////////////////////////////////////
std::array<double, 4> clip; szl::mathm::mult_m4_v4(clip, projection, exyz);
///////////////////////////////////////////////
std::array<double, 3> ndc; ndc[0] = clip[0] / clip[3]; ndc[1] = clip[1] / clip[3]; ndc[2] = clip[2] / clip[3];
//////////////////////////////////////
double x = viewport[0]; double y = viewport[1]; double w = viewport[2]; double h = viewport[3]; double n = depthrange[0]; double f = depthrange[1]; wnd[0] = w / 2 * ndc[0] + (x + w / 2); wnd[1] = h / 2 * ndc[1] + (y + h / 2); wnd[2] = (f - n) / 2 * ndc[2] + (f + n) / 2;
}
#pragma warning(disable:4996) #include <GL/glut.h> #pragma comment(lib,"opengl32.lib") #include "NByteData.hpp" #include "mygl-transform.hpp" #include "Bresenham.hpp" #include<vector> std::vector < NByteData<3> > myimage;
void my_transformation( std::array<double, 4>& wndcoord, const std::array<double, 4>& obj, const std::array<double, 4>& norm ) { std::array<double, 16> model; std::array<double, 16> proj; std::array<double, 2> depthrange{ 0, 1 }; std::array<int, 4> viewport; glGetDoublev(GL_MODELVIEW_MATRIX, model.data());//行列とビューポート取得 glGetDoublev(GL_PROJECTION_MATRIX, proj.data()); glGetIntegerv(GL_VIEWPORT, viewport.data()); opengltransformation( wndcoord,//結果のピクセル座標 obj,//モデルの頂点座標 norm, model, proj, depthrange, viewport ); printf("%lf %lf %lf\n", wndcoord[0], wndcoord[1], wndcoord[2]); }
//保存の時に画像を上下反転する void reverse_Y(const int width, const int height, NByteData<3>* p); void pnmP3_Write(const char* const fname, const int vmax, const int width, const int height, const unsigned char* const p);
void my_draw( const std::array<double, 4>& A, const std::array<double, 4>& B, const std::array<double, 4>& C ) { std::array<double, 4> wA; std::array<double, 4> wB; std::array<double, 4> wC; //法線は今回は使用しない std::array<double, 4> dmy{ 1,1,1,1 }; my_transformation(wA, A, dmy);//三次元座標をピクセル座標に変換 my_transformation(wB, B, dmy); my_transformation(wC, C, dmy); ////////////////////////////// int width = glutGet(GLUT_WINDOW_WIDTH); int height = glutGet(GLUT_WINDOW_HEIGHT); myimage.resize(width* height); std::fill(myimage.begin(), myimage.end(), NByteData<3>{255, 255, 255}); //三角形の描画
Bresenham(&myimage[0], width, height, wA[0], wA[1], wB[0], wB[1], NByteData<3>{0, 0, 0}); Bresenham(&myimage[0], width, height, wB[0], wB[1], wC[0], wC[1], NByteData<3>{0, 0, 0}); Bresenham(&myimage[0], width, height, wC[0], wC[1], wA[0], wA[1], NByteData<3>{0, 0, 0}); //上下反転 reverse_Y(width, height, &myimage[0]); pnmP3_Write(//ファイルに出力 R"(C:\test\data\a.ppm)", 255, width, height, myimage.begin()->data() ); }
void display(void) { glClear(GL_COLOR_BUFFER_BIT); std::array<double, 4> pa{ -0.3,-0.4,0,1 }; std::array<double, 4> pb{ 0.3,-0.4,0.2,1 }; std::array<double, 4> pc{ 0.1,0.2,-0.2,1 }; std::array<double, 4> norm{ 1,1,1,1 }; glMatrixMode(GL_PROJECTION);//透視投影行列 glLoadIdentity(); gluPerspective(60, GLUT_SCREEN_WIDTH / (double)GLUT_SCREEN_HEIGHT, 0.01, 10); glMatrixMode(GL_MODELVIEW);//モデルビュー変換行列 glLoadIdentity(); glPushMatrix(); glRotated(30, 1, 1, 1); glTranslated(-0.2, -0.2, -1); glColor3d(1, 0, 0); glPointSize(5); glBegin(GL_TRIANGLES); glVertex3dv(pa.data()); glVertex3dv(pb.data()); glVertex3dv(pc.data()); glEnd(); my_draw(pa,pb,pc);//座標変換のテスト glPopMatrix(); glFlush(); }
void init(void) { glClearColor(0.0, 0.0, 1.0, 1.0); } int main(int argc,char*argv[]) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGBA); glutCreateWindow(argv[0]); glutDisplayFunc(display); init(); glutMainLoop(); return 0; } //画像の上下を反転する void reverse_Y(const int width, const int height, NByteData<3>* p) { size_t HalfHEIGHT = height / 2; for (int ha = 0; ha < HalfHEIGHT; ha++) { int hb = (height - ha) - 1; NByteData<3>* pha = p + ha * width; NByteData<3>* phb = p + hb * width; if (ha != hb) { for (size_t i = 0; i < width; i++) { std::swap(pha[i], phb[i]); } } } } //! @brief PPM(RGB各1byte,カラー,テキスト)を書き込む //! @param [in] fname ファイル名 //! @param [in] vmax 全てのRGBの中の最大値 //! @param [in] width 画像の幅 //! @param [in] height 画像の高さ //! @param [in] p 画像のメモリへのアドレス //! @details RGBRGBRGB....のメモリを渡すと、RGBテキストでファイル名fnameで書き込む void pnmP3_Write(const char* const fname, const int vmax, const int width, const int height, const unsigned char* const p) { // PPM ASCII FILE* fp = fopen(fname, "w"); fprintf(fp, "P3\n%d %d\n%d\n", width, height, vmax); size_t k = 0; for (size_t i = 0; i < (size_t)height; i++) { for (size_t j = 0; j < (size_t)width; j++) { fprintf(fp, "%d %d %d ", p[k * 3 + 0], p[k * 3 + 1], p[k * 3 + 2]); k++; } fprintf(fp, "\n"); } fclose(fp); }