Графическое программирование. Я обожаю программирование графики

18.05.2019

Всех, кто не любит Android, Live Wallpaper, Minecraft, велосипеды, поток сознания, который слабо привязан к теме и всё около того, хочу сразу предупредить, что их может огорчить содержание этого поста, поэтому продолжайте чтение на свой страх и риск. Оставлю тут также и предупреждение для пользователей мобильного или просто небезлимитного интернета: дальше последует довольно много картинок .

Здравствуйте!
Я разработчик ПО с многолетним стажем. Нельзя сказать, что моя работа однообразна и скучна. Я решаю задачи разного уровня, иногда придумываю решения, иногда сам же и реализую их, иногда ищу уязвимости в системах, а иногда и просто рассказываю, почему монитор - это не компьютер. Пожалуй именно то, что я не занят только кодингом, вносит долю разнообразия и творчества в рабочий процесс.

Бывает также, что из-за различных проблем (бюджет или время на разработку ограничены или просто кого-то из вышестоящих петух клюнул не туда, куда было необходимо), приходится искать готовые решения для многих вещей, что превращает мою работу в детскую игру с кубиками, где требуется “класть кубик на кубик” и смотреть, как манагеры хлопают в ладоши. Пожалуй это и есть та вещь, которая превращает мою работу в рутину (хотя да, даже в моменты “игры с кубиками”, я обычно продолжаю рассказывать, что монитор - это не компьютер). Чтобы хоть как-то избежать рутины и не забыть, каково же реализовывать простые вещи, я люблю в свободное время программировать “всё подряд”. И я заметил, что обожаю именно программирование графики…

О чём речь?
Начну с начала, без особых отступлений о том, кто я и почему. Купив новый телефон (тут уже вырисовывается тенденция моих статей на хабре...), мне захотелось поставить себе “живую обоину”, т.к. уж не знаю почему, но нравится мне, когда на экране что-то шевелится. Наверное всё потому, что тогда я чувствую, что купил телефон с четырёхядерным процессором не зря. Погулял по стору, подумал, что бы я хотел видеть на экране и решил, что хочу что-то в стиле майнкрафта, но к сожалению не нашёл ничего, что бы должным образом радовало меня. Тут то я и имел неосторожность решить сделать всё сам…
Первые сомнения на этот счёт...
Следует немного отвлечься от основного рассказа и пояснить, почему у меня были сомнения на счёт идеи “а напишу - ка я себе что-то сам”. В школьные годы на компьютерах была установлена игра (как я узнал совсем недавно, игра называлась “Клад” для БК-0010), где белый человечек (за кого и следовало играть) собирал что-то (в моей памяти это были именно ключи, хотя, как выяснилось позже, это должны были быть сундуки), а чёрный человечек за что-то очень ненавидел белого и убивал его прикосновением. Не знаю почему, но мысли об игре вызывали у меня ностальгические чувства, и поэтому я решил “а напишу-ка я её сам”.

Чтобы не утомлять Вас рассказом о процессе разработки и прочих деталях (не о том моя история), просто опишу смысл: написал, работало именно так, как я запомнил, поиграл один раз, бросил, т.к. уже “наигрался” в процессе отладки .

Для тех, кому интересно, результат получился вот такой:

Внутренний голос и тут говорил мне о том, что я не стану пользоваться результатом своих трудов, т.к. слишком насмотрюсь на него за время разработки, но я, как обычно, решил, что “уж в этот раз такого точно не будет” (сила самоубеждения, ага). Забегу вперёд и скажу, что внутренний голос был прав…

А где тут программирование графики?!
Попробую вернуться к основному рассказу. Я сразу решил, что не буду использовать ни OpenGL, ни ещё чего-то, что помогло бы мне реализовать задачу - только хардкор. Тем более, меня всегда интересовало взаимодействие Java кода с нативным кодом под Android, а тут ещё и подвернулась неплохая возможность попробовать свои силы в решении этой задачи.
Сразу решил проверить, смогу ли я вообще реализовать прорисовку с достаточной частотой кадров, с постоянным вызовом нативной библиотеки. Для проверки я реализовал следующую задачу - заполнение экрана картинками с каким-то произвольным коэффициентом “затенения” (по сути просто с параметром яркости, где исходная картинка считается максимально яркой). Написал вариант на Java и C++. Прогнал оба варианта с грубым тестом подсчёта времени и увидел, что в среднем вариант на C++ работал немного быстрее, даже несмотря на то, что сам вывод готового изображения “на экран” всё-же делала Java. В качестве картинок я сразу взял одну из симпатичных, на мой взгляд, текстур для майнкрафта, результат получился примерно таким:

Поскольку я с самого начала решил, что стану всё реализовывать сам и не буду подглядывать в литературу или искать помощь в интернете, то в края моего изображения выглядели примерно так:

Очевидно, что неверно обрабатываются изображения, от которых следовало нарисовать только часть, исправляем…
Итак, задача решаема, а значит можно браться за дело и начинать искать решение и оптимальную реализацию.

Поскольку для реализации я выбрал решение с использованием JNI, то в результате разработку вёл в смешанном режиме. Основную логику я писал и проверял под Windows, генерируя сразу изображение для 10-и экранов (это и есть те широкие изображения, которые последуют далее в статье), а время от времени я проверял решение на телефоне.

“Результаты”

Итак, линия горизонта (пока случайными блоками, даже не знаю, почему сделал случайными - пишу статью и сам с себя удивляюсь):


Большие картинки кликабельны, но habrastorage немного уменьшил их размер (оригинал был 7200 х 1280)

Теперь от случайного мусора, переходим к осмысленному содержанию. Линия горизонта «осмысленно»:

Далее было необходимо создать “пещеры” (углубления в поверхности), чтобы рельеф не смотрелся так примитивно. Т.к. к тому моменту реализация была ещё сырая, то проверка алгоритма создания пещер представляла собой рисование “пещер” другой текстурой:

Следующим шагом было решено разделить всё на передний и задний план, причём задний план должен был отличаться более тёмной текстурой:


Это уже похоже на что-то, но ещё очень далеко от результата.

Поменял текстуры, решил добавить внизу источник света - лаву, но ошибся с текстурой, поэтому низ был «заполнен факелами»:

Исправляю и добавляю ещё один источник света - факелы:

Понял, что соотношение блоков переднего и заднего плана меня не устраивает и поменял коэффициенты:


Забегая вперёд, следует отметить, что это та часть, которую я менял много раз, чтобы добиться такого результата, чтобы успокоиться и больше не трогать его.

Целью добавления источников света было более адекватное освещение - освещение от источников света. Источники света были поделены на три группы:

  1. Освещение от неба. Самый яркий источник света, но изначально была задумана смена времени суток, а значит и освещение от неба зависит от времени.
  2. Освещение от лавы. Менее яркий источник света, чем небо днём, но яркость не меняется во времени.
  3. Освещение от факелов. Наименее яркий источник света. Яркость также постоянна.
В результате на освещение блока стали влиять два параметра - расстояние до неба и расстояние от статического источника света:


Слева распространение света от источников, а справа просто “задний план темнее, чем передний”.

Тут стала напрашиваться прозрачность, ведь нельзя оставлять факелы с белым фоном. Чтобы избежать вопросов “и в чём же проблема?”, напомню, что у меня есть массив одних циферок (пикселей) и других циферок (тоже пикселей) и все правила переноса и прорисовки необходимо было ещё написать. Хотели прозрачность - вот вам прозрачность (ещё чуть чуть прозрачнее и было бы необходимо рисовать картинку с камеры телефона, чтобы было достаточно “прозрачно”):


Исправляем…

Исправляя фон, случайно “закрасил” и пещеры землёй:


Я знаю, что может показаться, что-то из этого было сделано умышлено, но уверяю вас, что все результаты были получены случайно. По правде говоря, я даже затрудняюсь сказать, как мне это удалось, но результат такой, какой он есть. Исправляем и это…

Освещение блока за факелом вычисляется неверно (блок за факелом темнее, чем соседние блоки):

Ошибка довольно глупая, но сразу я как-то и не подумал, что факел может быть в хорошо освещённом месте и считал освещённость блока максимальным значением яркости факела. Решений могло быть по крайней мере два - исправить освещение или убрать факелы из освещённых мест. Я решил исправить освещение.

Теперь я решил, что необходимо сделать возможность указывать строку (seed), которая задаёт уникальную “карту”, а значит нужна была и своя реализация генерации случайных чисел (на самом деле не была нужна, т.к. хватило бы и обычного rand, но просился велосипед):


и

Вышло довольно “случайно”, если не сказать больше.

Следующим шагом, были добавлены дополнительные блоки (этот шаг я упускаю, уж извините, и так очень много картинок), чтобы разнообразить внешний вид рельефа и тут пришла очередь деревьев.

Про деревья я хочу отдельно сказать пару слов… К середине процесса разработки у меня уже был комментарий в коде, несколько записей на бумаге и одна пометка к скриншотам, примерно такого содержания: “i h8 3s”. И на то были причины. Деревья сразу пошли как-то сложно. Каждая мелочь, каждая правка кода обязательно сказывалась на деревьях. В целом, как бы смешно это не звучало, но самой большой занозой оказались именно деревья.

Итак, первая итерация мучений с деревьями:


Для начала было решено сделать ствол дерева и листья над стволом (пока без листьев по бокам), но даже тут я перепутал блоки и получил перевёрнутый результат.

В результате, после смены количества деревьев, получился вот такой результат:

Затем я решил, что алгоритм генерации линии горизонта огорчает меня и что было бы неплохо его поправить, что я и сделал. Результат был довольно предсказуемый - ничего хорошего:

Потом последовал обычный для многих разработчиков quick-fix, без особого вникания в суть проблемы (ведь я же только что писал этот код, очевидно, что я могу его исправить не задумываясь!), что, как и можно было предположить, к положительному результату это не привело:


Довольно любопытно то, что линия горизонта не совершенно ровная, а имеет небольшое искажение в начале.

В результате я поэкспериментировал с разными параметрами и после множества результатов успокоился и перестал думать о исправлениях для линии горизонта. Вот несколько результатов для линии горизонта:

После того, как я получил работающий прототип, пришло время отладить его, почистить код и заняться оптимизацией. Начал я с того, что стал избавляться от magic numbers, которых было около 10 и которые привязывали большую часть параметров к размеру экрана и блоков на момент тестов.

А деревья всё продолжали огорчать - необходимо было сделать так, чтобы они генерировались только на земле, для объёма добавил затенение на некоторые блоки листьев (что кстати тоже к тому моменту не работало так, как хотелось бы):

Стало ясно, что нужно отладить функцию прорисовки затенённый блоков, а заодно и оптимизировать в ней кое-что. Снова быстрое исправление “ошибок”, снова довольно забавный результат:

Тут меня почти моментально “осенило”, что же я сделал неправильно и новое исправление не заставило себя долго ждать:

Тут следует сразу оговориться, что обычно я не пишу код в таком стиле (т.е. в стиле “сначала пишу, потом думаю”). Но в данном проекте я находил это очень забавным. Ведь каждая моя ошибка, каждая глупость, обязательно приводила к результату, причём очень редко я мог предсказать этот результат или сразу объяснить “почему так”.

К этому моменту, текстура листьев и травы (земля с травой) была определённого зелёного цвета. Просилась реализация, которая позволяла бы менять цвет, позволяя малой кровью менять время года. Да, я отлично знаю, что это можно было легко сделать в джаве и не придумывать ничего, но спортивный интерес был слишком силён. Для этого, текстура была изменена и была написана функция для “покраски текстуры”:

Саму функцию пришлось несколько раз исправлять, т.к. она создавала эффект разнокалиберного полосатика (два изображения в середине), но не делала того, что необходимо (правое изображение). Вот несколько результатов работы алгоритма:

Время от времени, я правил те или иные методы, чтобы привести в порядок код и время от времени получал самые разные результаты. Ещё один из примеров:

Время от времени всплывали и довольно забавные ситуации из мира отладки. Например, ошибка в прорисовке градиента (очередной генератор разноцветных полосатиков):

Во время отладки этой ошибки, когда у меня уже стали заканчиваться идеи, отладчик выдал вот такое значение цвета:

В тот день отладку я оставил и решил отдохнуть, чтобы отладчик перестал рассказывать про плохое качество еды.

На этом моменте я решил начать параллельно тестировать результат и на телефоне. В телефоне порядок цветов оказался немного другим, поэтому красная и синяя составляющие цвета поменялись местами:

Кстати, я забыл лишний раз напомнить, что ненавижу деревья… Деревья ночью вели себя странно:

В это время, у телефона вообще был свой собственный взгляд на то, как следует рисовать картинку после смещения (скролинга пальцем):
и

Ладно, цвет на картинке слева такой, потому что я забыл про положение синей и красной составляющей, а вот модный эффект motion blur - это уже “спасибо” android за то, что он совершенно верно рисовал моё изображение, у которого я не подумал про альфа канал (в альфа канале к тому моменту могло быть всё что угодно).

Кстати! Давно я не показывал Вам свои деревья! Вот:


Проблем тут довольно много - и неверное освещение некоторых блоков листьев, и неверная прорисовка “прозрачных пикселей”.

Параллельно я начал работу над системой waypoint’ов или, говоря простым языком, алгоритмом поиска путей. Путь был нужен, чтобы была возможность добавить зомби и прочих персонажей и при этом не пришлось бы каждый раз просчитывать их поведение на несколько шагов вперёд (чтобы они не тупили на месте). Для себя я стал отмечать пути визуально, чтобы оценить качество алгоритма:

Более продвинутый вариант визуализации смотрелся вот так:

В какой-то момент, когда я пытался исправить внешний вид деревьев, получил ещё один “положительный”:

Вот ещё несколько любопытных багов с телефона, которые напрямую связаны с прозрачностью (альфа каналом):
и

Затем были и ошибки из-за добавления дополнительных текстур (а значит и сменой индексов текстур):

Потом я “поправил” что-то в алгоритме прорисовки и получил довольно странный эффект (скорее всего напутал с размером и положением текстур):

В результате последних оптимизаций алгоритма прорисовки, я получил сразу две ошибки (снова разнокалиберные полосатики):
и

Процесс прорисовки этого чуда смотрится так (очевидно, что это самый оптимальный вариант):

Последней вещью, которую было необходимо реализовать, был эффект обьёма (грани блоков), чтобы картинка не была такой плоской и простой. Для граней необходимо было написать два алгоритма - алгоритм изменения размера картинки и алгоритм наклона изображения.

Начну с алгоритма уменьшения размера картинки:

Сверху вниз - результаты работы алгоритма. В самом низу - желаемый результат (тест проводился на большой картинке, для наглядности).

Больше отличился алгоритм поворота:

Слева наверху - начальное изображение, а справа внизу - желаемый результат, а в середине всё то, что получалось по пути к результату.

Когда алгоритмы были готовы, сделать объём уже было довольно просто. Блок составляли 3 грани (взгляд с одной стороны, псевдо-3д или так называемый 2.5d). Для красоты на грани был нанесён линейный градиент, который тоже пришлось отладить, чтобы получить желаемый результат:

Поскольку с размером граней блока я так и не смог определиться, сделал возможность изменять этот параметр:

После всех изменений, было необходимо оптимизировать процесс прорисовки, чтобы не отрисовывать ничего из того, что не нужно рисовать. Результатом этой оптимизации снова стала проблема с каналом прозрачности, которая всплыла во время тестов на телефоне. Должен признать, что полученный результат смотрелся довольно любопытно:
и

Результатом исправления этой ошибки стал не столь интересный эффект:

Итоги
В конце концов все критические ошибки были отлажены, добавил текстур и плюшек в виде эффектов. Были сделаны две версии для стора - бесплатная (очень упрощённая) и платная (со всеми плюшками).

Техническую сторону решил даже не поднимать в этой статье, т.к. и так не уверен, что кто-то дочитает её до конца. Если людям, которые осилили эту статью, будет интересно узнать о технической стороне этой истории, то напишите в комментариях или в личку, и я обязательно расскажу про сложности с которыми столкнулся, особенностями реализации и прочими деталями.

Надеюсь, что это подтолкнёт ещё кого-то на создание чего-либо своими руками, а не только используя готовые фреймворки и библиотеки.

Спасибо тем, кто хоть долистал до конца статьи!

Богатые возможности Delphi и C++ Builder легко развиваются подключением других библиотек компонентов. В настоящее время существует множество различных библиотек для этих сред программирования, начиная от библиотек визуальных компонентов до мощных библиотек математического анализа. Особенный интерес при разработке программного обеспечения исследовательского оборудования представляет библиотека Component Works, разработанная американской фирмой National Instruments. Эта библиотека функционально повторяет библиотеку инструментов других продуктов этой компании - LabWindows/CVI и LabView, существенно расширяя спектр возможностей программ, созданных на Delphi или на C++ Builder.

Таким образом, средства визуального программирования, основанные на ООП - Borland Delphi и C++ Builder, благодаря скорости разработки программ и функциональным возможностям наиболее привлекательны для использования при разработке программного обеспечения исследовательского оборудования нового поколения практически в любой его части, а особенно в части программного обеспечения высшего уровня. Использование этих средств возможно и при разработке ответственных частей программного обеспечения, таких как программное обеспечение серверов, модули работы с сетью или модули управления оборудованием благодаря как возможности использования функций API в составе программы, так и возможностью написания программы с применением только функций API.

Средства графического программирования

Средства графического программирования занимают особое место в ряду средств разработки программного обеспечения. Для разработки программы с помощью средств графического программирования кроме некоторых навыков, как правило, не надо знать языков программирования, владеть методикой программирования в среде Windows и т. д. Все программирование производится на уровне структуры и алгоритма программы.

Изначально средства графического программирования были предназначены для упрощения доступа инженеров и научных работников, не знакомых с программированием, к разработке систем автоматизации. В основном, имелось в виду программное обеспечение для управления измерительным оборудованием и обработки результатов измерений. Но постепенно развитие графических средств программирования позволило существенно расширить сферу их применения вплоть до разработки программ мониторинга и управления производством или технологическими процессами. Особого прогресса в данной области добилась фирма National Instruments. Ее продукты LabView, LookOut и BridgeView следует рассмотреть отдельно.

LookOut представляет собой систему мониторинга и управления технологическими процессами: работа в реальном времени, поддержка большого количества стандартных измерительных и управляющих устройств, несколько уровней секретности и объявления нештатных ситуаций. Программная система в LookOut строится по принципу логической схемы, что оптимально для технологического процесса и абсолютно не приемлемо для более сложных задач (например, научных исследований), так как не позволяет строить сложные алгоритмы управления различными устройствами, осуществлять серьезную математическую обработку данных и многое другое. Поэтому для разработки программного обеспечения исследовательского оборудования LookOut непригоден.

LabView функционально ничем не уступает другим продуктам National Instruments (LabWindows/CVI или Component Works). LabView содержит подобные инструменты для создания интерфейса пользователя, работы с измерительным и управляющим оборудованием, математической обработки данных, работы в сети и т. д. К LabView также можно подключать программные модули, созданные в Других средах программирования, например, C++ или LabWindows/CVI. Программирование в LabView ведется на уровне Диаграмм. Диаграммы в LabView - это схемы алгоритмов. Основные элементы "алгоритмического языка" Lab View практически повторяют основные конструкции языка программирования Си.

При наличии определенных навыков создание достаточно сложной программы на LabView занимает у разработчика времени примерно на два порядка меньше, чем разработка такой же программы, например, на C++. Однако, основу LabView составляет runtime-engine, подобный аналогичному средству в LabWindows/CVI. Но в LabView оно выполняет значительно больше задач, благодаря чему LabView является практически самой быстрой и самой надежной системой в своем классе.

Однако LabView при всей своей привлекательности предназначена для создания небольших измерительно-управляющих систем, работающих в реальном времени, и не предназначена для разработки мощных, развитых программных комплексов, а тем более системного программного обеспечения, так как для этого LabView слишком громоздкая и медленная система. При создании подобных систем LabView будет проигрывать программам, созданным на C++, как по скорости работы, так и по необходимым компьютерным ресурсам. Поэтому LabView не подходит для создания программного обеспечения исследовательского оборудования.

BridgeView представляет собой построенную на основе LabView систему управления предприятиями и технологическими процессами с элементами разграничения доступа и системы предупреждений, заимствованных из LookOut, и ее использование при создании программного обеспечения для исследовательского оборудования также нецелесообразно.

Операционные системы

Операционные системы разделяют на два основных класса: ОС общего назначения и ОС реального времени (ОС РВ), хотя в последнее время границы между ними заметно стираются - многие задачи реального времени успешно решаются современными версиями систем общего назначения.

Операционные системы общего назначения Применительно к задачам автоматизации наиболее популярны такие ОС как Windows 3.1/95/NT, HP-UX, Solaris, MacOS, UNIX. Все они являются многозадачными системами и, в основном, используются в решении задач автоматизации с централизованным управлением, когда в системе имеется конкретная управляющая ЭВМ. Для ОС общего назначения характерна единая среда, используемая как для разработки прикладных программ, так и для их исполнения. Операционные системы общего назначения по сравнению с ОС РВ дешевле, проще в использовании и отладке приложений.

Операционные системы реального времени. Основными преимуществами систем реального времени по сравнению с ОС общего назначения являются:

· гарантированное время реакции системы на запросы и прерывания от внешних устройств при возникновении непредвиденных ситуаций;

· разделение среды разработки прикладного ПО и среды его исполнения.

ОС РВ предназначены, как правило, для применения в распределенных многопроцессорных системах с децентрализованным управлением, поэтому они дороже и сложнее в использовании. Среди наиболее известных ОС РВ можно назвать следующие системы: OS-9/OS-9000, VxWorks, LynxOS, VMEexec.

Средства разработки баз и хранилищ данных

Структура RAM DAC


Информация в видеобуфере хранится в байтах. Каждый байт может определять цвет одного или несколько пикселов.

Для видеобуфера , где хранятся атрибуты изображения, применяются специальные микросхемы, которые имеют 2 входных канала:

1. Для регенерации изображения (адаптер только считывает информацию);

2. Для записи изображения.

Оба канала действуют параллельно и независимо.

Функции видеоконтроллера : (формирование сигналов горизонтальной и вертикальной синхронизации, счетчик адресов видеобуфера, формирует форму и позицию курсора).

Некоторые универсальные языки программирования могут иметь стандартную поддержку графики. Например, язык Pascal имеет возможность создавать несложные графические приложения. Для программирования графики на языке Pascal существует модуль Graph, при помощи процедур которого можно рисовать изображения.

var GraphDriver,GraphMode: integer;

InitGraph(GraphDriver,GraphMode, ‘’);

<процедуры и функции для рисования>

Это было удобно при MS DOS, где основным режимом был текстовый. С появлением Windows основным режимом стал графический.

При программировании графических приложения под Windows, используя обработчик события WM_PAINT или OnPaint (последнее - если используется какой-нибудь объектный каркас для Windows-приложения), нельзя рисовать долго, поскольку программа на время его работы блокируется. А если рисовать в других обработчиках, то нарисованное стирается при следующей перерисовке окна.

С появлением языка Pascal ABC, в котором графическая библиотека подключается крайне просто, многие проблемы были решены. В этом языке графическое приложение похоже на обычную программу, в которой подключается графический модуль GraphABC и в блоке операторов после begin end можно писать графические команды.

<процедуры и функции для рисования>

По умолчанию графический экран PasсalABC содержит 640 точек по горизонтали и 400 точек по вертикали. Начало отсчета – левый верхний угол. При запуске такой программы возникает специальное графическое окно, и все рисование происходит именно на нем. Изображение на нем не пропадает при перерисовке, программа не окажется заблокированной на время рисования и можно рисовать сколь угодно долго и не сложно осуществлять анимацию.

Например, программа для рисования узора:

var x,y:integer;

for x:=0 to WindowWidth-1 do

for y:=0 to WindowHeight-1 do

SetPixel(x,y,RGB(2*x-y,x-3*y,x+y));

Например, программа цифровых часов:

uses GraphABC,Utils;



Var x0,y0:integer;

s1,s2,s3:string;

SetWindowCaption("My program");

SetWindowSize(500,100);

SetFontName("Arial");

SetFontStyle(fsBoldItalic);

SetFontSize(70);

x0:= (WindowWidth - TextWidth("00: 00: 00 ")) div 2;

y0:= (WindowHeight - TextHeight("00: 00: 00 ")) div 2;

t:= CurrentDateTime;

//s:= string.Format("{0:d2}:{1:d2}:{2:d2}",t.Hour,t.Minute,t.Second);

str(t.Hour:2,s1);

str(t.Minute:2,s2);

str(t.Second:2,s3);

TextOut(x0,y0,s1+" : "+s2+" : "+s3+" ");

Возможности модуля растровой графики GraphABC практически совпадают с графическими возможностями Borland Delphi. Процедуры и функции рисования и установки параметров рисования аналогичны методам и свойствам класса TCanvas в Delphi. Например, вместо свойства Canvas.Brush.Color используется пара: процедура SetBrushColor(color) и функция BrushColor.

Модуль GraphABC представляет собой простую графическую библиотеку и предназначен для создания несобытийных графических и анимационных программ в процедурном и частично в объектном стиле. Рисование осуществляется в специальном графическом окне, возможность рисования в нескольких окнах отсутствует. Кроме этого, в модуле GraphABC определены простейшие события мыши и клавиатуры, позволяющие создавать элементарные событийные приложения. Основная сфера использования модуля GraphABC - обучение.

Модуль GraphABC основан на графической библиотеке GDI+, но запоминает текущие перо, кисть и шрифт, что позволяет не передавать их в качестве параметров при вызове графических примитивов. К свойствам пера, кисти и шрифта можно получать доступ как в процедурном, так и в объектном стиле. Например, для доступа к цвету текущего пера используется процедура SetPenColor(c) и функция PenColor, а также свойство Pen.Color.

В модуле GraphABC можно управлять самим графическим окном и компонентом GraphABCControl, на котором осуществляется рисование. По умолчанию компонент GraphABCControl занимает всю клиентскую часть графического окна, однако, на графическое окно можно добавить элементы управления, уменьшив область, занимаемую графическим компонентом (например, так сделано в модулях Robot и Drawman).

Для работы с рисунками используется класс Picture, позволяющий рисовать на себе те же графические примитивы, что и на экране.

Режим блокировки рисования на экране (LockDrawing) позволяет осуществлять прорисовку лишь во внеэкранном буфере, после чего с помощью метода Redraw восстанавливать все графическое окно. Данный метод используется для ускорения анимации и создания анимации без мерцания.

Модуль GraphABC содержит константы, типы, процедуры, функции и классы для рисования в графическом окне. Они подразделяются на следующие группы:

1. Графические примитивы.

2. Действия с цветом.

3. Действия с пером.

4. Действия с кистью.

5. Действия со шрифтом.

6. Действия с рисунками.

7. Действия с графическим окном.

8. Задание режимов вывода

Наиболее используемые графические процедуры и функции.

1. Управление экраном.

SetWindowWidth(w). Устанавливает ширину графическогоокна;

SetWindowHeight(h) -Устанавливает высоту графическогоокна;

2. Графические примитивы.

Точка SetPixel(x,y,color). Закрашиваетодин пиксел с координатами (x,y) цветом color. Например, SetPixel(300,200,clred).

Линии Line(x1,y1,x2,y2). Рисует отрезок с началом в точке (x1,y1) и концом в точке (x2,y2). Например, line(100,50,500,250).

Процедура MoveTo(x,y). Передвигает невидимое перо к точке с координатами (x,y). Эта процедура обычно работает в паре с процедурой LineTo(x,y).

Процедура LineTo(x,y). Рисует отрезок от текущего положения пера до точки (x,y). Координаты пера при этом также становятся равными (x,y).

Прямоугольник Rectangle(x1,y1,x2,y2). Рисует прямоугольник, заданный координатами противоположных вершин (x1,y1) и (x2,y2). Например, Rectangle(50,50,200,200).

Процедура Polygon(А, n) строит ломаную линию по n точкам, координаты которых заданы в массиве А элементов типа Point.

Процедура Polyline(А, n) строит замкнутую ломаную линию по n точкам, координаты которых заданы в массиве А элементов типа Point.

Окружность Circle(x,y,r). Рисует окружность с центром в точке (x,y) и радиусом r. Например, Circle(500,200,100).

Дуга окружности Arc(x,y,r,a1,a2). Рисует дугу окружности с центром в точке (x,y) и радиусом r, заключенной между двумя лучами, образующими углы a1 и a2 с осью OX (a1 и a2 – вещественные, задаются в градусах и отсчитываются против часовой стрелки). Например, Arc(300,250,150,45,135);

3. Некоторые используемые цвета

Random(16777215) – случайный цвет из всей палитры цветов Паскаля

Функция RGB(r,g,b) возвращает целое значение, являющееся кодом цвета, который содержит r красную, g зеленую и b синюю составляющие (r, g и b – целые в диапазоне от 0 до 255, причем, 0 соответствует минимальной интенсивности, 255 – максимальной). Например, RGB(Random(255),Random(255),Random(255)), где функция Random(х) возвращает случайное целое в диапазоне от 0 до x-1.

Процедура цвет линии SetPenColor(color) устанавливает цвет пера, задаваемый параметром color. Например, setpencolor(clred).

Процедура заливка цветом FloodFill(x,y,color) заливает область одного цвета цветом color, начиная с точки (x,y). Например, Rectangle(50,50,200,200); FloodFill(100,100,clBlue)

4. Шрифты.

Процедура procedure SetFontName(name: string) устанавливает наименование шрифта.

Процедура procedure SetFontStyle(style: integer); - устанавливает стиль шрифта.

Процедура procedure SetFontSize(sizesz: integer); - устанавливает размер шрифта в пунктах.

Процедура procedure SetFontColor(color: integer); - устанавливает цвет шрифта.

Процедура procedure TextOut(x, y, s) выводит строку s в позицию (x,y). Точка (x,y) задает верхний левый угол прямоугольника, который будет содержать текст из строки s).

Например,

SetFontName("Arial");

SetFontStyle(fsBoldItalic);

SetFontSize(40);

SetFontColor(RGB(Random(255),Random(255),Random(255)));

Расширенный перечень графических процедур и функций.

Графические примитивы.

procedure SetPixel(x,y,color: integer); - закрашивает один пиксел с координатами (x,y) цветом color.

function GetPixel(x,y): integer; -возвращает текущее значение цвета для пиксела с координатами (x,y).

procedure MoveTo(x,y: integer); - передвигает невидимое перо к точке с координатами (x,y); эта функция работает в паре с функцией LineTo(x,y).

procedure LineTo(x,y: integer); - рисует отрезок от текущего положения пера до точки (x,y); координаты пера при этом также становятся равными (x,y).

procedure Line(x1,y1,x2,y2: integer); - рисует отрезок с началом в точке (x1,y1) и концом в точке (x2,y2).

procedure Circle(x,y,r: integer); - рисует окружность с центром в точке (x,y) и радиусом r.

procedure Ellipse(x1,y1,x2,y2: integer); - рисует эллипс, заданный своим описанным прямоугольником с координатами противоположных вершин (x1,y1) и (x2,y2).

procedure Rectangle(x1,y1,x2,y2: integer); - рисует прямоугольник, заданный координатами противоположных вершин (x1,y1) и (x2,y2).

procedure RoundRect(x1,y1,x2,y2,w,h: integer); - рисует прямоугольник со скругленными краями; (x1,y1) и (x2,y2) задают пару противоположных вершин, а w и h – ширину и высоту эллипса, используемого для скругления краев.

procedure Arc(x,y,r,a1,a2: integer); - рисует дугу окружности с центром в точке (x,y) и радиусом r, заключенной между двумя лучами, образующими углы a1 и a2 с осью OX (a1 и a2 – вещественные, задаются в градусах и отсчитываются против часовой стрелки).

procedure Pie(x,y,r,a1,a2: integer); - рисует сектор окружности, ограниченный дугой (параметры процедуры имеют тот же смысл, что и в процедуре Arc).

procedure Chord(x,y,r,a1,a2: integer); - рисует фигуру, ограниченную дугой окружности и отрезком, соединяющим ее концы (параметры процедуры имеют тот же смысл, что и в процедуре Arc).

procedure TextOut(x,y: integer; s: string); - выводит строку s в позицию (x,y) (точка (x,y) задает верхний левый угол прямоугольника, который будет содержать текст из строки s).

procedure FloodFill(x,y,color: integer); - заливает область одного цвета цветом color, начиная с точки (x,y).

procedure FillRect(x1,y1,x2,y2: integer); - заливает прямоугольник, заданный координатами противоположных вершин (x1,y1) и (x2,y2), цветом текущей кисти.

procedure Polygon(var a; n: integer); строит ломаную по n точкам, координаты которых заданы в массиве a элементов типа Point.

procedure Polyline(var a; n: integer); -строит замкнутую ломаную по n точкам, координаты которых заданы в массиве a элементов типа Point.

Цветовые константы и функции для работы с цветом

Модуль GraphABC содержит константы и функции для работы с цветами. Тип ColorType, описывающий цвет, определен следующим образом:

type ColorType=integer; - стандартные цвета задаются символическими константами:

Для работы с цветами используются следующие функции.

function RGB(r,g,b: integer): ColorType; - возвращает целое значение, являющееся кодом цвета, который содержит красную, зеленую и синюю составляющие с интенсивностями r, g и b соответственно (r, g и b – целые в диапазоне от 0 до 255, причем, 0 соответствует минимальной интенсивности, 255 – максимальной).

function GetRed(color: ColorType): integer; - выделяет красную составляющую из цвета color (целое в диапазоне от 0 до 255);

function GetGreen(color: ColorType): integer; - выделяет зеленую составляющую из цвета color (целое в диапазоне от 0 до 255);

function GetBlue(color: ColorType): integer; - выделяет синюю составляющую из цвета color (целое в диапазоне от 0 до 255).

Действия с пером

function PenX: integer;

function PenY: integer; - возвращают текущие координаты пера.

procedure SetPenColor(color: integer); - устанавливает цвет пера, задаваемый параметром color.

function PenColor: integer; - возвращает текущий цвет пера.

procedure SetPenWidth(w: integer); - устанавливает ширину пера, равную w пикселам.

function PenWidth: integer; - возвращает текущую ширину пера.

procedure SetPenStyle(ps: integer); - устанавливает стиль пера, задаваемый параметром ps.

function PenStyle: integer; - возвращает текущий стиль пера.

Стили пера задаются следующими именованными константами:

psSolid Сплошная линия (установлено по умолчанию)

psDash Штриховая линия

psDot Пунктирная линия

psDashDot Штрихпунктирная линия

psDashDotDot Линия, чередующая штрих и два пунктира

psClear Отсутствие линии

procedure SetPenMode(m: integer); - устанавливает режим пера, задаваемый параметром m.

function PenMode: integer; - возвращает текущий режим пера. Режим пера определяет, как цвет пера взаимодействует с цветом поверхности.

Режимы пера задаются следующими именованными константами:

pmCopy – обычный режим; при рисовании цвет поверхности заменяется цветом пера;

pmNot – режим инвертирования; при рисовании цвет поверхности инвертируется (становится негативным), а цвет пера при этом игнорируется.

Действия с кистью

procedure SetBrushColor(color: integer); - устанавливает цвет кисти, задаваемый параметром color.

function BrushColor: integer; - возвращает текущий цвет кисти.

procedure SetBrushPicture(fname: string); - устанавливает в качестве образца для закраски кистью образец, хранящийся в файле fname, при этом текущий цвет кисти при закраске игнорируется.

procedure ClearBrushPicture; - очищает рисунок-образец, выбранный для кисти.

procedure SetBrushStyle(bs: integer); - устанавливает стиль кисти, задаваемый параметром bs.

function BrushStyle: integer; - возвращает текущий стиль кисти.

Стили кисти задаются следующими именованными константами:

bsSolid bsCross bsClear bsDiagCross
bsHorizontal bsBDiagonal bsVertical bsFDiagonal

Действия со шрифтом

procedure SetFontColor(color: integer); - устанавливает цвет шрифта.

function FontColor: integer; - возвращает текущий цвет шрифта.

procedure SetFontSize(sz: integer); - устанавливает размер шрифта в пунктах.

function FontSize: integer; - возвращает текущий размер шрифта в пунктах.

procedure SetFontName(name: string); - устанавливает наименование шрифта.

function FontName: string; - возвращает текущее наименование шрифта.

По умолчанию установлен шрифт, имеющий наименование MS Sans Serif. Наиболее распространенные шрифты – это Times, Arial и Courier New. Наименование шрифта можно набирать без учета регистра.

procedure SetFontStyle(fs: integer); - устанавливает стиль шрифта.

function FontStyle: integer; - возвращает текущий стиль шрифта.

Стили шрифта задаются следующими именованными константами:

fsNormal – обычный;

fsBold – жирный;

fsItalic – наклонный;

fsBoldItalic – жирный наклонный;

fsUnderline – подчеркнутый;

fsBoldUnderline – жирный подчеркнутый;

fsItalicUnderline – наклонный подчеркнутый;

fsBoldItalicUnderline – жирный наклонный подчеркнутый.

function TextWidth(s: string): integer; - возвращает ширину строки s в пикселях при текущих настройках шрифта.

function TextHeight(s: string): integer; - возвращает высоту строки s в пикселях при текущих настройках шрифта.

Действия с графическим окном

procedure ClearWindow; - очищает графическое окно белым цветом.

procedure ClearWindow(c: ColorType); - очищает графическое окно цветом c.

function WindowWidth: integer; - возвращает ширину графического окна.

function WindowHeight: integer; - возвращает высоту графического окна.

function WindowLeft: integer; - возвращает отступ графического окна от левого края экрана.

function WindowTop: integer; - возвращает отступ графического окна от верхнего края экрана.

function WindowCaption: string; - возвращает заголовок графического окна.

procedure SetWindowWidth(w: integer); - устанавливает ширину графического окна.

procedure SetWindowHeight(h: integer); - устанавливает высоту графического окна.

procedure SetWindowLeft(l: integer); - устанавливает отступ графического окна от левого края экрана.

procedure SetWindowTop(t: integer); - устанавливает отступ графического окна от верхнего края экрана.

procedure SetWindowSize(w,h: integer); - устанавливает ширину и высоту графического окна.

procedure SetWindowPos(l,t: integer); - устанавливает отступ графического окна от левого и верхнего края экрана.

procedure SetWindowCaption(s: string); - устанавливает заголовок графического окна.

procedure SetWindowTitle(s: string); - устанавливает заголовок графического окна. Синоним SetWindowCaption.

procedure SaveWindow(fname: string); - сохраняет содержимое графического окна в файл с именем fname.

procedure LoadWindow(fname: string); - выводит в графическое окно рисунок из файла с именем fname. Файл ищется вначале в текущем каталоге, а затем в каталоге PascalABC\Media\Images.

procedure FillWindow(fname: string); - заполняет графическое окно мозаикой из рисунка, содержащегося в файле с именем fname.

procedure FillWindow(n: integer); - заполняет графическое окно мозаикой из рисунка с описателем n.

procedure CloseWindow; - закрывает графическое окно.

function ScreenWidth: integer; - возвращает ширину экрана.

function ScreenHeight: integer; - возвращает высоту экрана.

procedure CenterWindow; - центрирует графическое окно по центру экрана.

procedure MaximizeWindow; - максимизирует графическое окно на экране.

procedure NormalizeWindow; - восстанавливает положение графического окна на экране.

Все размеры устанавливаются и возвращаются в пикселах.

Задание режимов вывода

procedure SetDrawingSurface(n: integer); - устанавливает в качестве канвы для рисования рисунок с описателем n. В результате весь графический вывод осуществляется не на экран, а на рисунок; настройки кисти, пера и шрифта также осуществляются для рисунка.

procedure SetDrawingSurface(p: Picture); - устанавливает в качестве канвы для рисования рисунок с описателем n. В результате весь графический вывод осуществляется не на экран, а на рисунок; настройки кисти, пера и шрифта также осуществляются для рисунка.

procedure RestoreDrawingSurface; - устанавливает в качестве канвы для рисования графическое окно.

procedure Redraw; - осуществляет перерисовку окна вывода при заблокированном выводе в графическое окно.

procedure LockDrawing; - блокирует вывод в графическое окно, осуществляя рисование только во внеэкранном буфере. Для перерисовки графического окна требуется вызвать процедуру Redraw. Если графический вывод перенаправлен в рисунок вызовом процедуры SetDrawingSurface, то не оказывает никакого воздействия на вывод.

procedure UnlockDrawing; - снимает блокировку вывода в графическое окно.

procedure LockScreenBuffer; - блокирует вывод во внеэкранный буфер графического окна. После вызова этой процедуры рисование незначительно ускоряется, однако, изображение графического окна перестает восстанавливаться.

procedure UnlockScreenBuffer; - снимает блокировку вывода во внеэкранный буфер графического окна.

function DrawingIsLocked: boolean; - возвращает True, если вывод в графическое окно заблокирован, и False в противном случае.

procedure SetRedrawProc(procedure RedrawProc); - устанавливает пользовательскую процедуру для перерисовки содержимого графического окна, вызываемую автоматически в тот момент, когда требуется его перерисовка. В настоящее время используется в модуле ABCObjects для автоматической перерисовки всех графических объектов и фона.

Блокировка вывода в графическое окно с последующим вызовом Redraw используется для простейшего создания анимации без мерцания.

С детства я очень люблю играть в видео игры и мне хотелось написать какую-нибудь собственную игру. Изучив основы программирования, программированием 3D графики я занялся на 2-ом курсе. Я имею 2 года опыта работы с 3D графикой в компании Soft-Xpansion. В Soft-Xpansion я восновном моделирую шестерёнки, болты, подшипники, валы, тригера и т.д.

В мире освещены различные аспекты разработки трёхмерных приложений, однако основное внимание уделяется вопросам программирования - в частности, представления трехмерных объектов, их визуализации с учетом свойств материала объектов, освещения, перспективы, а также таким специфическим вопросам трехмерной визуализации, как создание различных визуальных спецэффектов и т.п. Рассмотрим такие задачи: визуализация трехмерного объекта, создание и параметризация объекта, алгоритмы центрального и параллельного проецирования, алгоритмы различных преобразований.

Программирование 3D графики рассмотрим на примере разработки графического редактора для работы с параметризированными трехмерными объектами. Существует множество прикладных программ, для работы с трехмерными графическими объектами. Всевозможные графические редакторы позволяют работать с объектами разного типа. При этом важными факторами являются точное отображение реальных объектов и скорость работы. В результате возникает задача графического моделирования объектов на ЭВМ, при решении которой главным фактором является наиболее точное отображение реального объекта.

1. Разработка полигональной модели объекта

1.1 Составляющие элементы объекта

Полигональная модель в компьютерной графике - это образ объекта, составленный из множества многоугольников. Мы будем разрабатывать полигональную модель объекта «самолёт». Трехмерное изображение объекта представлено на рис. 1.1.

Объект состоит из нескольких видов поверхностей, которые в свою очередь разбиваются на полигоны для удобства хранения и обработки. Объект состоит из следующих видов фигур:

  • капсула;
  • параллелепипед;
  • призма, в основании которой прямоугольный треугольник.

Капсула задаётся базовой точкой, радиусом, высотой цилиндрической части капсулы. Параллелепипед задаётся базовой точкой, шириной, длиной и высотой, на основе которых происходит расчет координат восьми точек вершин параллелепипеда. Призма задаётся базовой точкой, 2-мя катетами треугольника, который лежит в основании призмы, и высотой призмы.

1.2 Триангуляция поверхности объекта

Триангуляция поверхностей – это процесс разбиения сложных объектов на треугольные полигоны. Триангуляция удобна при программировании графики, т.к.:

  • треугольник является простейшим полигоном, вершины которого однозначно задают грань;
  • любую область можно гарантированно разбить на треугольники;
  • вычислительная сложность алгоритмов разбиения на треугольники существенно меньше, чем при использовании других полигонов;
  • реализация процедур визуализации более проста для области, ограниченной треугольником;
  • для треугольника легко определить три его ближайших соседа, имеющих с ним общие грани.

Над объектом производится триангуляция следующим образом. Все примитивы, из которых состоит объект разбиты на треугольники. Все прямоугольные грани разбиваются диагональю на два равных треугольника. Таким образом, параллелепипеды состоят из 12 треугольных полигонов. Боковая поверхность цилиндра (часть нашей капсулы) разбита на части по высоте и каждая часть разбивается на равные прямоугольники, которые после разбиваются на треугольники.

Для получения координат вершин капсулы используются формулы перехода из цилиндрической системы координат в декартову систему координат (рис. 1.2). В формулах цилиндр имеет радиус R и высоту ZC. Полной окружности соответствует диапазон изменения параметра угла от 0 до 2Пи. Если рассматривать некоторое фиксированное число, равномерно распределенных, точек по окружности, то приращение параметра между точками можно считать константой (рис. 1.3). На концах капсулы находятся полусферы, которые получаются путём разбиения конуса на части по высоте и вдоль основания, после этого нормализуем координаты всех вершин так что бы они лежали на нашей сфере и полученные прямоугольники разбиваем на полигоны.

Призма, в основании которой прямоугольный прямоугольник состоит из 2 треугольников и 3 прямоугольников, которые легко разбиваются на полигоны.

На рисунке 1.4 представлено каркасное изображение объекта.

2. Описание алгоритмов аффинных преобразований

Любое изображение, выводимое на экран монитора, состоит из точек. Каждая точка в трехмерном пространстве, в свою очередь содержит три координаты – X (абсцисса), Y (ордината), Z (аппликата). Координаты точки однозначно определяют ее положение в системе координат. Мы будем использовать правую декартовую прямоугольную систему координат, общий вид которой показан на рисунке 2.1.

Здесь буквами x, y, z обозначены положительные направления осей Ox, Oy и Oz соответственно. Для вывода сцены на экран используются 3 системы координат:

  • СК сцены;
  • экранная СК;
  • СК камеры.

Начальной СК является СК сцены (либо камеры, при переходе в вид из камеры), конечной – экранная СК. Для перехода из одной системы координат в другую используются формулы поворота, смещения и масштабирования.

После выполнения геометрических преобразований, координаты (x, y, z) точки переходят в новые координаты (x*, y*, z*). Общие формулы преобразования координат показаны на рисунке 2.2. Но более удобной является матричная форма записи трехмерного преобразования (рис. 2.3). Здесь к координатам (x ,y, z) точки добавилась четвертая координата, равная единице и необходимая для выполнения преобразований в матричной форме. Такие координаты называются однородными.

2.1 Перемещение объекта

Одним из простейших и часто применяемых аффинных преобразований является перемещение (сдвиг, перенос). Матрица A перемещения на вектор показана на рисунке 2.4. Алгоритм представлен на рисунке 2.5.

Рисунок 2.4 – Матрица перемещения

2.2 Вращение объекта

Аффинным преобразованием, которое позволяет смотреть на объект под разными углами и с разных сторон, является поворот объекта относительно координатных осей. Матрицы поворотов вокруг координатных осей показаны на рисунках 2.6, 2.7, 2.8 и вокруг произвольной оси на рисунке 2.9.

Рисунок 2.9 – Матрица поворота вокруг произвольной оси

Алгоритм поворота вокруг произвольной оси представлен на рисунке 2.10.

Рисунок 2.10 – Алгоритм поворота вокруг произвольной оси

2.3 Масштабирование объекта

Другим важнейшим аффинным преобразованием является масштабирование. Масштабирование - изменение размера изображения с сохранением пропорций. Под масштабированием подразумевается как увеличение изображения, так и его уменьшение. Матрица масштабирования представлена на рисунке 2.11, а алгоритм масштабирования на рисунке 2.12.

3. Описание алгоритмов проекционных преобразований

Проецирование - это процесс полу¬чения изображения предмета на какой-либо поверх¬ности, получившиеся при этом изображение, называют проекцией предмета.

Элементами, с помощью которых осуществляется проецирование, являются (рис. 3.1):

  • центр проецирования - точка, из которой производится проецирование;
  • объект проецирования - изображаемый предмет;
  • плоскость проекции - плоскость, на которую производится проецирование;
  • проецирующие лучи - воображаемые прямые, с помощью которых производится проецирование.

Результатом проецирования является изображение или проекция объекта. Различают центральное и параллельное проецирование. При центральном проецировании все проецирующие лучи исходят из одной точки - центра проецирования, находящегося на определённом расстоянии от плоскости проекций (рисунок 3.1). При параллельном проецировании все проецирующие лучи параллельны между собой. На рисунке 3.2 показано, как получается параллельная прямоугольная проекция. Центр проецирования предполагается условно удалённым в бесконечность. Тогда параллельные лучи отбросят на плоскость проекций тень, которую можно принять за параллельную проекцию изображаемого предмета.

В проекте реализовано переключение между центральным и параллельным проецированием. Отличия при реализации системы проецирования появляются при вычислении координат проекций на аксонометрическую плоскость проекций камеры. Эта плоскость имеет размеры (–1,+1) по ширине и (–1/ar,+1/ar) по высоте, где ar – форматное отношение. Если при центральном проецировании имеет место формула, показанная на рисунке 3.3, то для параллельного проецирования справедлива формула, представленная на рисунке 3.4.

Рисунок 3.3 – Формула вычисления координат проекций при центральном проецировании

Рисунок 3.4 – Формула вычисления координат проекций при параллельном проецировании

В этой формуле a - половина ширины окна вывода, а b - половина его высоты.

4. Описание алгоритмов произвольных преобразований камеры

Для построения изображения на экранной плоскости используется модель представления при помощи камеры. Она отвечает за предоставление пользователю мощнейшего механизма движения среди объектов сцены.

Камера задается следующими параметрами:

  • углом обзора;
  • фокусным расстоянием (от точки нахождения камеры до плоскости, на которую мы проецируем);
  • ближними и дальними плоскостями, которые участвуют в отсечении по пирамиде видимости.

Для камеры были реализованы: поворот, смещение и панорамирование.

Камера расположена в определенной точке пространства, а также имеет целевую точку. На основании этих двух точек рассчитываются три вектора: вверх, вправо и вперед, которые однозначно определяют направление камеры и образуют оси системы координат камеры (рисунок 4.1).

Получаем из МСК координаты точек объекта в координатной системе камеры. Схема представлена на рисунке 4.2.

Основные функции для работы с камерой представленны на рисунках 4.3, 4.4, 4.5.

Для того чтобы получить координаты точки, представленной в мировых координатах, в координатах камеры необходимо эту точку умножить на матрицу камеры трансформации из МСК. Точку следует представить в однородных координатах. Матрица трансформации камеры приведена на рисунке 4.6. В этой матрице U – вектор «вправо», V – вектор «вверх», N – вектор «вперед», camPos – позиция камеры в МСК.

Языки визуального программирования появились в начале 90-х годов и содержат большое число стандартных функций и библиотек, а также готовых средств визуализации. Они позволяют создавать очень удобные и эффектные программы, однако достигается это за счет резкого увеличения объема программного кода. Поэтому языки визуального программирования, как и текстовые, по-прежнему не позволяют модифицировать алгоритмы силами технологов без участия профессиональных программистов.

Настоящую революцию в программировании систем автоматизации сделали языки графического программирования. Одним из первых в этом классе был графический язык среды Simulink, входящей в состав Matlab (MathWorks Inc), а также языки LabVIEW (National Instruments) и HP-VEE (Hewlett Packard). Они были предназначены и успешно использовались для сбора данных, моделирования систем автоматизации, автоматического управления, обработки собранных данных и их визуального представления в виде графиков, таблиц, звука, с помощью компьютерной анимации. Графические языки были настолько простыми и естественными, что для их освоения зачастую было достаточно метода проб и ошибок без использования учебников и консультаций. Человек, не знакомый с программированием на алгоритмических языках, пользуясь только логикой и понимая постановку прикладной задачи, мог собрать работающее приложение из готовых компонентов, набрасывая их мышкой на экран монитора и проводя графические связи для указания потоков информации.

Первые языки программирования алгоритмов работы систем автоматизации были нестандартными. Каждая фирма, создававшая контроллер или SCADA-пакет, предлагала свой язык. Это требовало от системных интеграторов дополнительных усилий и затрудняло освоение новых SCADA пакетов и средств программирования контроллеров.

Контроллеру требуется программа , в соответствии с которой он взаимодействует с объектом. В одних случаях речь идет только о сборе данных с объекта, в других - о логическом управлении (например, выполнении блокировок). Наконец, одно из основных применений контроллера - реализация функций непрерывного управления отдельными параметрами или технологическим аппаратом (процессом) в целом.



Фирмы, производящие оборудование для пост­роения систем автоматизации, всегда стреми­лись сопровождать свою продукцию на­бором программных инструментов, с помощью которых пользователь по оп­ределенным правилам и соглашениям мог бы описывать логику работы контрол­лера. На раннем этапе развития этих программных средств набор поддерживаемых ими функ­ций обеспечивался нестандартными языками. Со временем правила и соглашения совершенствовались и на определенном этапе были оформ­лены в виде специальных языков про­граммирования, образовав то, что сейчас называется CASE -инструментарием.

Поэтому появление в 1993 году стандарта на языки программирования контроллеров МЭК 61131-3 было большим шагом в направлении создания открытых систем автоматизации и обеспечило снижение стоимости разработки, сокращение сроков, повышение качества реализации алгоритмов автоматизации и возможность детального изучения языков программирования, пригодных для любого контроллера. МЭК 61131-3 устанавливал стандарты для пяти языков программирования, рассчитанных на специалистов разных профессий, не связанных с программированием.

Были выдвинуты требования открытости системы, выполнение которых позволило бы унифицировать программные средства и упростить разработку:

Возможность разработки драйверов для контроллеров самими пользователями, т.е. сопровождение программных продуктов по программированию контроллеров специальными инструментальными средствами;

Наличие коммуникационных средств (интерфейсов) для взаимодействия с другими компонентами системы управления;

Возможность портации ядра системы на ряд программно-аппаратных платформ.

На рынке появилось большое коли­чество пакетов, удовлетворяющих вышеописанным требованиям. Практически во всех этих пакетах среда разработки реализована в Windows -интерфейсе, имеются средства загрузки разработанного приложения в исполнительную систему.

Названия некоторых из этих пакетов приведены ниже:

RSLogix 500, RS Logix 5, RSLogix 5000 фирмы Rockwell Software для программирования контроллеров различных семейств Allen-Bradley;

DirectSOFT для контроллеров семейства Direct Logic фирмы Koyo;

Пакеты PL7и Concept- ПО для программирования контроллеров различных семейств компании Schneider Electric;

Пакеты STEP 5, STEP 7 Micro, STEP 7 для программирования контроллеров семейств S5 и S7 фирмы Siemens;

Пакет Toolbox для конфигурирования контроллеров семейства Moscad;

Пакет TelePACEдля программирования контроллеров серий

TeleSAFE Micro 16 и SCADAPack фирмы Control Microsystems.

Стандартом МЭК 1131-3 определены пять языков программирования контроллеров: три графических (LD, FBD, SFC) и два текстовых (ST, IL).

LD (Ladder Diagram) - графический язык диаграмм релейной логики. Язык LD применяется для описания логических выражений различного уровня сложности.

FBD (Function Block Diagram) - графический язык функциональных блоковых диаграмм. Язык FBD применяется для построения комплексных процедур, состоящих из различных функциональных библиотечных блоков - арифметических, тригонометрических, регуляторов и т.д.).

SFC (Sequential Function Chart) - графический язык последовательных функциональных схем. Язык SFC предназначен для использования на этапе проектирования ПО и позволяет описать «скелет» программы - логику ее работы на уровне последовательных шагов и условных переходов.

ST (Structured Text) - язык структурированного текста. Это язык высокого уровня, по мнемонике похож на Pascal и применяется для разработки процедур обработки данных.

IL (Instruction List) - язык инструкций. Это язык низкого уровня класса ассемблера и применяется для программирования эффективных, оптимизированных процедур.

Графический интерфейс

Создание графических интерфейсов пользователя на компьютере явилось большим достижением в направлении развития средств диспетчерского управления. Главным эффектом от применения графического интерфейса является существенное снижение количества ошибок, допускаемых оператором (диспетчером) в стрессовых ситуациях при управлении производственными процессами. Проектирование пользовательского интерфейса основано на следующих принципах :

· узнаваемость : назначение элементов экрана должно быть понятно без предварительного обучения, допустимые манипуляции с этими элементами также должны быть понятны интуитивно. Пользовательский интерфейс не должен содержать излишней детализации;

· логичность : пользователь, имеющий опыт работы с одной программой, должен быть способен быстро, практически без обучения, адаптироваться к любой аналогичной программе;

· отсутствие "сюрпризов" : знакомые из прошлого опыта операции с элементами на экране должны вызывать знакомые реакции системы;

· восстанавливаемость : система не должна быть чувствительна к ошибкам оператора. Оператор должен иметь возможность отменить любое свое неправильное действие. Для этого используются многократные подтверждения, отмены, возврат на несколько шагов назад, установка контрольных точек и т. п.;

· наличие удобной справки , подсказок, встроенных в пользовательский интерфейс, средств контекстного поиска и замены;

· адаптация к опыту пользователя : начинающий пользователь должен иметь более простой интерфейс с большим количеством подсказок. Для опытного пользователя количество подсказок должно быть уменьшено, поскольку они мешают в работе.

Похожие статьи