OpenGL で RANDU 　

#FortranでOpenGL
##F03GL + FreeGlut
Fortran でグラフィックスをやる方法として OpenGL を利用するという手があります。Intel Visual Fortran のサンプルにも例題がありますが、Windows プログラミングも兼ねているので若干敷居が高い所があります。またプラットフォーム依存性が出てしまうという問題もあります。

より簡便な OpenGL 利用法として GLUT というのが昔からあります。Fortran2003ではC言語との連携が強化されたため、GLUT 利用も以前より楽になったようです。

ここでは、[ここ](http://fortran66.hatenablog.com/entry/2014/09/17/014626)の記事からノウハウを拝借して、F03GL + FreeGlut の組み合わせで OpenGL を利用して三次元のグラフをプロットすることにします。

##乱れてない乱数 RANDU
コンピュータでの乱数生成は、大型計算機では専用ハードウェアで熱雑音などで行われたりしますが、多くの場合は特殊な漸化式による疑似乱数列が用いられます。疑似乱数列は決定論的に定まるので本当は乱数では無いのですが、利用する状況に応じて十分乱数とみなせれば良いとされます。

しかしながら、一見乱数と思われたものに明らかな規則性が現れる場合もあります。その有名な例が、1960年代に IBM で利用されていた RANDU 疑似乱数列($r_0=1,r_{n+1}=65539 * r_n\, mod\,\, 2^{31}$)で、今に至るまで教科書などによく出てきます。

##実行結果
RANDU により生成された数列を三次元の点座標（ $x_n=r_{3n+1}/2^{31},$ $y_n=r_{3n+2}/2^{31},$ $z_n=r_{3n+3}/2^{31}$）としてプロットしたものを以下に示します。各点は ｘ、ｙ、ｚ方向に [0,1] の範囲の立方体の中におさまっています。一見ランダムに点が打たれているように見えますが、特定角度から見ると幾枚かの平面上に点が分布しているのが見て取れます。
![名称未設定-1.gif](https://qiita-image-store.s3.amazonaws.com/0/44522/8ac04966-d742-6fde-9ae6-efb86d6f1f1f.gif)

###キャプチャ例
![RANDU1.png](https://qiita-image-store.s3.amazonaws.com/0/44522/82305918-1e2e-a068-20b5-898c2450f6f1.png)
![RANDU3.png](https://qiita-image-store.s3.amazonaws.com/0/44522/ae3de163-fdf5-e1f4-026b-87d4c5317528.png)
![Randu2.png](https://qiita-image-store.s3.amazonaws.com/0/44522/357e5a96-2bf9-c67a-0e84-2cdb67dc0ce5.png)


##プログラム
以下のプログラムは F03GL のサンプル中の pointsz.f90 を土台に、いくらか書き直したものです。カーソルによって視点方向を動かすことができます。

```fortran
module m_gl  
  use OpenGL_GL
  use OpenGL_GLUT
  implicit none  
  real, save :: xrot = 0.0, yrot = 0.0
contains
  subroutine SetupRC() bind(c)
    ! Black background
    call glClearColor(0.0, 0.0, 0.6, 1.0 )
    ! Set drawing color to green
    call glColor3f(1.0, 0.0, 0.0)
  end subroutine SetupRC
  
  subroutine KeyPressFunc(key, x, y) bind(C)
    integer(GLbyte), intent(in), value :: key
    integer(GLint ), intent(in), value  :: x, y
    if ( key == 27 ) stop ! ESC
  end subroutine KeyPressFunc
  
  subroutine KeySpecialFunc(key, x, y) bind(C)
    integer(GLint), intent(in), value  :: key, x, y
    if ( key == GLUT_KEY_UP    ) xRot = xrot - 5.0
    if ( key == GLUT_KEY_DOWN  ) xRot = xrot + 5.0
    if ( key == GLUT_KEY_LEFT  ) yRot = yrot - 5.0
    if ( key == GLUT_KEY_RIGHT ) yRot = yrot + 5.0 
    if ( xrot > 356.0 )then
      xRot = 0.0
    else if ( xrot < -1.0 )then
      xRot = 355.0
    endif
    if ( yrot > 356.0 )then
      yRot = 0.0
    else if ( yrot < -1.0 )then
      yRot = 355.0
    endif
    ! Refresh the Window
    call glutPostRedisplay
  end subroutine KeySpecialFunc
  
  subroutine ChangeSize(win, hin) bind(C)
    integer(GLcint), intent(IN), value :: win, hin
    integer(GLcint) :: w, h
    real(GLdouble)  :: Zero, One, Range = 100.0, Aspect
    w = win
    h = hin
    ! Prevent a divide by zero, when window is too short
    ! (you cant make a window of zero width).
    if( h == 0 ) h = 1
    ! Set the viewport to be the entire window
    call glViewport(0, 0, w, h)
    ! Reset coordinate system
    call glMatrixMode(GL_PROJECTION)
    call glLoadIdentity
    ! Establish clipping volume (left, right, bottom, top, near, far)
    aspect = float(w)/float(h)
    ! Keep the square square
    if( w <= h )then
      call glOrtho( -Range, Range, &
                    -Range / Aspect, Range / Aspect,&
                    -Range, Range  )
    else
      call glOrtho( -Range * Aspect, Range * Aspect, &
                    -Range, Range, &
                    -Range, Range  )
    endif
    call glMatrixMode(GL_MODELVIEW)
    call glLoadIdentity
  end subroutine ChangeSize 
  
  subroutine RenderScene() bind(C)
    integer, parameter :: n = 10**4
    real(glfloat), save :: dx(n - 1), dy(n - 1), dz(n - 1)
    logical, save :: first = .true.
    integer :: i
    ! make Lorenz attractor data        

    if (first) then
      block  
        integer(8) :: m = 2_8**31
        integer(8) :: ix 
        real(glfloat) :: x(n), y(n), z(n)
        first = .false.
        ix = 1
        do i = 1, n
          ix = mod(65539 * ix, m)  ! ;  print *, ix
          x(i) = real(ix) / m 
          ix = mod(65539 * ix, m)  ! ;  print *, ix
          y(i) = real(ix) / m
          ix = mod(65539 * ix, m)  ! ;  print *, ix
          z(i) = real(ix) / m
        end do
        dx = (x(2:) - x(:n - 1)) * 80.0_glfloat ! scale data 
        dy = (y(2:) - y(:n - 1)) * 80.0_glfloat
        dz = (z(2:) - z(:n - 1)) * 80.0_glfloat
      end block  
    end if  
    ! Clear the window with current clearing color
    call glClear(GL_COLOR_BUFFER_BIT)
    ! Save matrix state and do the rotation
    call glPushMatrix
    call glRotatef(xRot, 1.0, 0.0, 0.0)
    call glRotatef(yRot, 0.0, 1.0, 0.0)
    ! draw Lorenz attractor
    do i = 1, n - 1
      call glTranslatef( dx(i), dy(i), dz(i) )
      call glutSolidCube(0.5_gldouble )
    end do
    !
    call glPopMatrix
    ! Flush drawing commands
    call glutSwapBuffers
  end subroutine RenderScene
end module m_gl

program RANDU
   use m_gl
   implicit none
   integer :: iwin
   call glutInit()
   call glutInitDisplayMode(ior(GLUT_DOUBLE,ior(GLUT_RGB,GLUT_DEPTH)))
   call glutInitWindowSize(800, 600)
   iwin = glutCreateWindow('RANDU'//char(0))
   call glutReshapeFunc ( ChangeSize     )
   call glutKeyboardFunc( KeyPressFunc   )
   call glutSpecialFunc ( KeySpecialFunc )
   call glutDisplayFunc ( RenderScene    )
   call SetupRC()
   call glutMainLoop()
end program RANDU
```