представить как большой конечный автомат. Каждая строчка кода – это запрос на изменение состояния. При-
чём этот автомат является глобальной переменной. Его текущее состояние зависит от всей цепочки преды-
дущих команд. Параметры рисования задаются глобальными переменными (тип StateVar).
OpenGLне зависит от конкретной оконной системы, она отвечает лишь за рисование. Для того чтобы
создать окно и перехватывать в нём действия пользователя нам понадобится отдельная библиотека. Для
этого мы воспользуемся GLFW, это библиотека также пришла в Haskell из С. Интерфейсы GLFWи OpenGLочень
похожи. Мы будем обновлять различные параметры библиотеки с помощью типа StateVar. Давайте создадим
окно и закрасим фон белым цветом:
module Main where
import Graphics.UI.GLFW
import Graphics.Rendering.OpenGL
import System.Exit
title =”Hello OpenGL”
width
=700
height
=600
main = do
initialize
openWindow ( Sizewidth height) [] Window
windowTitle $=title
clearColor $= Color41 1 1 1
windowCloseCallback $=exitWith ExitSuccess
loop
loop = do
display
loop
display = do
clear [ ColorBuffer]
swapBuffers
Мы инициализируем GLFW, задаём параметры окна. Устанавливаем цвет фона. Цвет имеет четыре пара-
метра это RGB-цвета и параметр прозрачности. Затем мы говорим, что программе делать при закрытии окна.
Мы устанавливаем функцию обратного вызова (callback) windowCloseCallback. В самом конце мы входим в
цикл, который только и делает, что стирает окно цветом фона и делает рабочий буфер видимым. Что такое
буфер? Буфер – это место в котором мы рисуем. У нас есть два буфера. Один мы показываем пользователю,
а в другом в это в время рисуем, когда приходит время обновлять картинку мы просто меняем их местами
командой swapBuffers.
Посмотрим, что у нас получилось:
$ ghc --make HelloOpenGL.hs
$ ./HelloOpenGL
Нарисуем упрощённое начальное положение нашей игры: прямоугольную рамку и в ней – красный шар:
290 | Глава 20: Императивное программирование
module Main where
import Graphics.UI.GLFW
import Graphics.Rendering.OpenGL
import System.Exit
title =”Hello OpenGL”
width, height :: GLsizei
width
=700
height
=600
w2, h2 :: GLfloat
w2 =(fromIntegral $width) /2
h2 =(fromIntegral $height)
/2
dw2, dh2 :: GLdouble
dw2 =fromRational $toRational w2
dh2 =fromRational $toRational h2
main = do
initialize
openWindow ( Sizewidth height) [] Window
windowTitle $=title
clearColor $= Color41 1 1 1
ortho ( -dw2 -50) (dw2 +50) ( -dh2 -50) (dh2 +50) ( -1) 1
windowCloseCallback $=exitWith ExitSuccess
windowSizeCallback
$=(\size ->viewport $=( Position0 0, size))
loop
loop = do
display
loop
display = do
clear [ ColorBuffer]
color black
line ( -w2) ( -h2) ( -w2) h2
line ( -w2) h2
w2
h2
line w2
h2
w2
( -h2)
line w2
( -h2)
( -w2) ( -h2)
color red
circle 0 0 10
swapBuffers
vertex2f :: GLfloat -> GLfloat -> IO()
vertex2f a b =vertex ( Vertex3a b 0)
-- colors
white = Color4(0 ::GLfloat)
black = Color4(0 ::GLfloat) 0 0 1
red
= Color4(1 ::GLfloat) 0 0 1
-- primitives
line :: GLfloat -> GLfloat -> GLfloat -> GLfloat -> IO()
Основные библиотеки | 291

line ax ay bx by =renderPrimitive Lines $ do
vertex2f ax ay
vertex2f bx by
circle :: GLfloat -> GLfloat -> GLfloat -> IO()
circle cx cy rad =
renderPrimitive Polygon $mapM_ (uncurry vertex2f) points
wheren =50
points =zip xs ys
xs =fmap (\x ->cx +rad *sin (2 *pi *x /n)) [0 ..n]
ys =fmap (\x ->cy +rad *cos (2 *pi *x /n)) [0 ..n]
Рис. 20.1: Начальное положение
Мы рисуем с помощью функции renderPrimitive. Она принимает метку элемента, который мы собира-
емся рисовать и набор вершин. Так метка Linesобозначает линии, а метка Polygon– закрашенные много-
угольники. В OpenGLнет специальной операции для рисования окружностей, поэтому нам придётся предста-
вить окружность в виде многоугольника (circle). Функция ortho устанавливает область видимости рисунка,
Читать дальше