スポンサーリンク

gluPerspectiveを自前実装する

gluPerspectiveが作成する行列

  • fovy … 視野角(ラジアン)
  • aspect … アスペクト比
  • zNear … 一番近いz位置
  • zFar … 一番遠いz位置

参考:https://manpag.es/RHEL5/3+gluPerspective

C/C++の配列にした場合

C++で実装

ソースコード

#include <cmath>

namespace nu {

  typedef float real_t;

  inline real_t cot(real_t theta) {
    return (real_t)1.0 / tan(theta);
  }

  inline real_t to_radian(real_t degree) {
    return degree * (real_t)3.14159265358979 / (real_t)180.0;
  }

  //! @brief 透視投影行列の作成 (視野角で指定)
  //! @param [out] m 結果の4x4行列
  //! @param [in] fovy_degree 視野角
  //! @param [in] aspect アスペクト比
  //! @param [in] zNear 一番近いz位置
  //! @param [in] zFar 一番遠いz位置
  //! @return なし
  inline void myPerspective(
    real_t* m,
    real_t fovy_degree,
    real_t aspect,
    real_t zNear,
    real_t zFar) {

    real_t fovy_rad = to_radian(fovy_degree);


    real_t f = cot(fovy_rad / (real_t)2.0);

    m[0] = f / aspect;
    m[1] = 0.0;
    m[2] = 0.0;
    m[3] = 0.0;

    m[4] = 0.0;
    m[5] = f;
    m[6] = 0.0;
    m[7] = 0.0;

    m[8] = 0.0;
    m[9] = 0.0;
    m[10] = (zFar + zNear) / (zNear - zFar);
    m[11] = -1.0;

    m[12] = 0.0;
    m[13] = 0.0;
    m[14] = (2 * zFar * zNear) / (zNear - zFar);
    m[15] = 0.0;

  }

}

テスト

gluPerspectiveの結果はgluPerspectiveを呼び出した後でglGetFloatvで取得する。

#include <windows.h>
#include <gl/GLU.h>
#include <gl/freeglut.h>
#include <cstdio>

//検証用
#include <cassert>

//自前実装版gluPerspective
#include "myperspective.h"

#pragma comment(lib,"opengl32.lib")
#pragma comment(lib,"glu32.lib")
#pragma comment(lib,"freeglut.lib")

void display(void)
{
  
  GLfloat glumat[16]; //glu関数で生成
  GLfloat mypmat[16]; //自分関数で生成

  //gluPerpsective,myPerspectiveへの入力の値を色々変える
  static int i = 0;
  static float j = 0.0f;

  GLfloat fovy=45.0f + i;
  GLfloat aspect=0.2f + j;
  GLfloat zNear=0.1f + j;
  GLfloat zFar=10.0f + j*2;

  i++;
  j += 0.01;


  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  //gluで計算
  gluPerspective(fovy, aspect, zNear, zFar);
  glGetFloatv(GL_PROJECTION_MATRIX, glumat);

  //自前計算
  nu::myPerspective(mypmat, fovy, aspect, zNear, zFar);

  puts("----------------------------------------------");
  for (int k = 0; k < 16; k++) {
    //自前計算
    printf("m[%2d] %+5.5lf %+5.5lf \n", k,glumat[k], mypmat[k]);

    //自前計算とgluPerspective計算の値があまりにかけ離れていたらassertで落ちる
    assert(abs(glumat[k] - mypmat[k]) < 0.00001);

  }
  puts("----------------------------------------------");

}
void mouse(int button, int state, int x, int y) {
  display();
}

int main(int argc, char* argv[])
{

  glutInit(&argc, argv);
  glutInitDisplayMode(GLUT_RGBA);
  glutCreateWindow(argv[0]);
  glutInitWindowPosition(0, 0);
  glutInitWindowSize(100,100);
  glutDisplayFunc(display);
  glutMouseFunc(mouse);
  glutMainLoop();

  return 0;

}

テスト結果

コメントを残す

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

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


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