スポンサーリンク

OpenGLの座標変換を時前でやってみる

OpenGLがglVertex3d等で与えられた座標をどうやってピクセル座標に変換しているかは以下のサイトなどで説明されている。

http://www.songho.ca/opengl/gl_transform.html

これを実際に計算してみる。

なおベクトルの変換などは作り置きの関数vmmath.hppを使う

https://www.study.suzulang.com/3dcg-functions/vmmath-hpp

mygl-transform.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;
}

使用例

NBytesのデータを扱うクラス

ブレゼンハムで線分描画

#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);


}

コメントを残す

メールアドレスが公開されることはありません。

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)


この記事のトラックバックURL: