Текст
                    ВТОРОЕ ИЗДАНИЕ
М
\
ная компьютеона
ВВОДНЫЙ КУРС НА БА3
OpenGL
Эдвард Эйнджел


SECOND EDITION Interactive Computer Graphics A top-down approach with OpenGL™ Edward Angel ADDISON-WESLEY Reading, Massachusetts • Menlo Park, California • Л/ew York • Harlow, England Don Miles, Ontario • Sydney • Mexico City • Madrid • Amsterdam
Второе издание Интерактивная компьютерная графика Вводный курс на базе OpenGL™ Эдвард Эйнджел Издательский дом "Вильяме" Москва ¦ Санкт-Петербург ¦ Киев 2001
ББК 32.973.26-018.2.75 Э64 УДК 681.3.07 Издательский дом "Вильяме" Перевод с английского и редакция канд.техн.наук ВТ. Тертыишого По общим вопросам обращайтесь в Издательский дом "Вильяме" по адресу: info@vvilliamspublishing.com, http://\vw\v.williamspublishing.com Эйнджел, Эдвард. Э64 Интерактивная компьютерная графика. Вводный курс на базе OpenGL, 2 изд.: Пер. с англ. — М.: Издательский дом "Вильяме", 2001. — 592 с: ил. — Парал. тит. англ. ISBN 5-8459-0209-6 (рус.) Книга представляет собой вводный курс компьютерной графики, в котором основной упор сделан на вопросах прикладного программирования. Она включает описание структуры графических систем и обсуждение основных концепций формирования изображений трех- трехмерных объектов и сцен. Рассматривается взаимодействие освещения и материалов, приво- приводятся основные сведения о методах тонирования освещенных поверхностей, принципах ие- иерархической организации графических моделей и новых возможностях современных аппа- аппаратных графических средств. В книгу включены те разделы линейной алгебры и геометрии. которые необходимы для понимания основ компьютерной графики. Обсуждаются методы построения кривых и поверхностей, языковые модели, фракталы и системы частиц, а также методика применения графических средств для визуализации результатов научных расчетов. Весь теоретический материал в книге иллюстрируется программами на OpenGL. Книга адресована в основном студентам старших курсов и аспирантам первого года обу- обучения, специализирующимся в области информатики и вычислительной техники, но будет также полезна и многим профессионалам. ББК 32.973.26-018.2.75 Все названия программных продуктов являются зарегистрированными торговыми марками соответст- соответствующих фирм. Никакая часть настоящего издания ни в каких целях не может быть воспроизведена в какой бы то ни было форме и какими бы то ни было средствами, будь то электронные или механические, включая фотокопирова- фотокопирование и запись на магнитный носитель, если на это нет письменного разрешения издательства Addison-Wesley Publishing Company, Inc. Authorized translation from the English language edition published by Addison-Wesley Publishing Company. Inc, Copyright © 2000 All rights reserved. No part of this book may be reproduced or transmitted in any form or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Russian language edition published by Williams Publishing House according to the Agreement with R&I Enterprises International, Copyright © 2001 ISBN 5-8459-0209-6 (рус.) © Издательский дом "Вильяме", 2001 ISBN 0-201-38597-Х (англ.) © Addison-Wesley Publishing Company. Inc. 2000
Оглавление Предисловие 18 Глава 1. Графические системы и модели 25 Глава 2. Графическое программирование 55 Глава 3. Ввод и взаимодействие с пользователем 103 Глава 4. Объекты и геометрические преобразования 147 Глава 5. Визуализация 205 Глава 6. Закрашивание 247 Глава 7. Алгоритмы формирования изображения 287 Глава 8. Иерархические графические модели 337 Глава 9. Операции с изображением на уровне растрового представления 375 Глава 10. Кривые и криволинейные поверхности 419 Глава 11. Процедурные методы 465 Глава 12. Визуализация данных научных исследований 493 Приложение А. Демонстрационные программы 523 Приложение Б. Абстрактные пространства в компьютерной графике 561 Приложение В. Матрицы 569 Литература 577
Содержание Предисловие 18 Нисходящий подход 18 Программирование на языке С с применением OpenGL 19 На кого рассчитана эта книга 20 Структура книги 20 Изменения, внесенные во второе издание 21 Дополнительные источники 23 Благодарности 23 Глава 1 Графические системы и модели 25 1.1. Области применения компьютерной графики 26 1.1.1. Отображение информации 26 1.1.2. Проектирование 27 1.1.3. Моделирование 28 1.1.4. Интерфейс пользователя 28 1.2. Графическая система 29 1.2.1. Пиксели и буфер кадра 29 1.2.2. Устройства вывода изображений 31 1.2.3. Устройства ввода 33 1.3. Изображение: физическое и синтезируемое 33 1.3.1. Объекты и наблюдатели 3 3 1.3.2. Свет и изображение 35 1.3.3. Трассировка лучей 36 1.4. Глаз человека 38 1.5. Камера-обскура 40 1.6. Моделирование камеры 41 1.7. Интерфейс программиста 43 1.7.1. Интерфейс прикладного профаммирования 44 1.7.2. Парадигма "моделирование — тонирование" 47 1.8. Архитектура графических систем 47 1.8.1. Дисплейные процессоры 48 1.8.2. Конвейерная архитектура 49 1.8.3. Геометрические преобразования 50 б Содержание
1.8.4. Отсечение 50 1.8.5. Проективное преобразование 51 1.8.6. Растровое преобразование 51 1.8.7. Производительность работы геометрического конвейера 51 1.9. Резюме 52 1.10. Рекомендуемая литература 52 Упражнения 53 Глава 2 Графическое программирование 55 2.1. Узор Серпинского 56 2.1.1. Перьевой плоттер 57 2.1.2. Системы координат 62 2.2. Прикладной интерфейс OpenGL 63 2.2.1. Графические функции 64 2.2.2. Интерфейс OpenGL 65 2.3. Примитивы и атрибуты 66 2.3.1. Многоугольники 67 2.3.2. Типы многоугольников в OpenGL 69 2.3.3. Текст 70 2.3.4. Криволинейные объекты 72 2.3.5. Атрибуты 72 2.4. Цвет 73 2.4.1. Цветовая система RGB 77 2.4.2. Индексируемый цвет 78 2.4.3. Настройка атрибута цвета 80 2.5. Визуализация 80 2.5.1. Визуализация двухмерных объектов 81 2.5.2. Ортогональная проекция 82 2.5.3. Матричный режим проецирования 83 2.6. Функции управления 84 2.6.1. Взаимодействие с подсистемой окон 84 2.6.2. Соотношение сторон и видовые окна 85 2.6.3. Функции main(), display() и myinit() 87 2.6.4. Структура программы 88 2.7. Программа Gasket 89 2.8. Многоугольники и рекурсия 90 2.9. Трехмерный узор Серпинского 92 2.9.1. Использование трехмерных точек 92 2.9.2. Использование многоугольников в трехмерном пространстве 94 2.9.3. Удаление невидимых поверхностей 95 2.10. Резюме 96 2.11. Рекомендуемая литература 97 Упражнения 98 Содержание 7
Глава 3 Ввод и взаимодействие с пользователем 103 3.1. Интерактивная компьютерная графика 103 3.2. Устройства ввода 105 3.2.1. Физические устройства ввода 105 3.2.2. Логические устройства 108 3.2.3. Показания и синхронизация 109 3.2.4. Режимы ввода 110 3.3. Клиенты и серверы 112 3.4. Дисплейный файл 113 3.4.1. Формирование дисплейного списка и преобразование его в изображение 115 3.4.2. Дисплейные списки и формирование текста 117 3.4.3. Шрифты библиотеки GLUT 120 3.5. Программирование ввода, управляемого событиями 121 3.5.1. Использование устройств указания 121 3.5.2. События окна 124 3.5.3. События клавиатуры 126 3.5.4. Функции отображения и простоя 126 3.5.5. Управление окнами 127 3.6. Меню 127 3.7. Указание объектов 129 3.8. Простая программа рисования 130 3.9. Интерактивные программы анимации 135 3.9.1. Вращающийся квадрат 135 3.9.2. Двойная буферизация 137 3.9.3. Проблемы с буферизацией 138 3.10. Разработка интерактивных графических программ 139 3.10.1. Инструментальные средства, экранные элементы управления и буфер кадра 139 3.11. Резюме 141 3.12. Рекомендуемая литература 142 Упражнения 142 Глава 4 Объекты и геометрические преобразования 147 4.1. Скаляры, точки и векторы 148 4.1.1. Геометрическое определение базовых типов 148 4.1.2. Математическое определение: векторное и аффинное пространства 150 4.1.3. Информационное определение 150 4.1.4. Геометрические абстрактные типы данных 151 4.1.5. Прямые 152 4.1.6. Аффинное сложение 153 8 Содержание
4.1.7. Выпуклость 153 4.1.8. Скалярное и векторное произведение 154 4.1.9. Плоскости 155 4.2. Трехмерные примитивы 156 4.3. Системы координат и фреймы 157 4.3.1. Замена систем координат 159 4.3.2. Пример изменения представления 161 4.3.3. Однородные координаты 162 4.3.4. Пример перехода из одного фрейма в другой 164 4.3.5. Фреймы и абстрактные типы данных 166 4.3.6. Фреймы в OpenGL 167 4.4. Модель разноцветного куба 169 4.4.1. Моделирование куба 170 4.4.2. Внешние и внутренние грани 170 4.4.3. Структура данных для представления объектов 171 4.4.4. Цвет куба 172 4.4.5. Билинейная интерполяция 173 4.4.6. Массивы вершин 174 4.5. Аффинные преобразования 176 4.6. Поворот, сдвиг и масштабирование 178 4.6.1. Плоскопараллельное смещение 179 4.6.2. Поворот 179 4.6.3. Масштабирование 181 4.7. Преобразования в однородных координатах 182 4.7.1. Сдвиг 182 4.7.2. Масштабирование 183 4.7.3. Поворот 184 4.7.4. Скос 185 4.8. Суперпозиция преобразований 186 4.8.1. Поворот вокруг произвольной фиксированной точки 187 4.8.2. Поворот вокруг произвольной оси 188 4.8.3. Преобразование экземпляра 189 4.8.4. Поворот вокруг произвольной оси 190 4.9. Матрицы преобразований в OpenGL 193 4.9.1. Текущая матрица преобразования 194 4.9.2. Поворот, сдвиг и масштабирование 195 4.9.3. Поворот вокруг фиксированной точки средствами OpenGL 195 4.9.4. Последовательность выполнения преобразований 196 4.9.5. Вращение куба 196 4.9.6. Загрузка матриц и использование стека матриц 198 4.10. Взаимодействие пользователя с трехмерными графическими приложениями 198 4.10.1. Использование областей экрана 199 4.10.2. Виртуальный трекбол 199 4.10.3. Плавное вращение 201 Содержание
4.11. Резюме 202 4.12. Рекомендуемая литература 203 Упражнения 203 Глава 5 Визуализация 205 5.1. Классическая и компьютерная визуализация 205 5.1.1. Визуализация в классической графике 207 5.1.2. Ортографические проекции 208 5.1.3. Аксонометрические проекции 208 5.1.4. Косоугольные проекции 209 5.1.5. Визуализация с учетом перспективы 210 5.2. Размещение камеры 211 5.2.1. Настройка положения фрейма камеры 212 5.2.2. Задание ориентации камеры 216 5.3. Проецирование 220 5.3.1. Перспективные проекции 221 5.3.2. Ортогональная проекция 223 5.4. Проективные преобразования в OpenGL 224 5.4.1. Перспективные преобразования в OpenGL 224 5.4.2. Параллельное проецирование в OpenGL 226 5.5. Удаление невидимых поверхностей 227 5.6. Путешествие с камерой по сцене 228 5.7. Матрицы параллельного проецирования 230 5.7.1. Нормализация проецирования 230 5.7.2. Матрицы ортогонального проективного преобразования 231 5.7.3. Косоугольная проекция 233 5.8. Матрицы перспективного проецирования 236 5.8.1. Перспективная нормализация 236 5.8.2. Перспективное преобразование в OpenGL 238 5.9. Проецирование и формирование теней 240 5.10. Резюме 242 5.11. Рекомендуемая литература 243 Упражнения 244 Глава 6 Закрашивание 247 6.1. Свет и материя 248 6.2. Источники света 250 6.2.1. Цвет излучения 251 6.2.2. Фоновое освещение 252 6.2.3. Точечный источник света 252 6.2.4. Прожекторы 253 10 Содержание
6.2.5. Удаленный источник света 254 6.3. Модель отражения Фонга 255 6.3.1. Отражение фонового света 257 6.3.2. Диффузное отражение 257 6.3.3. Зеркальное отражение 258 6.4. Вычисление векторов 261 6.4.1. Нормаль к поверхности 261 6.4.2. Угол отражения 263 6.4.3. Вектор половинного направления 264 6.4.4. Преломление света 265 6.5. Закрашивание многоугольников 266 6.5.1. Плоское закрашивание 266 6.5.2. Интерполяционное закрашивание и закрашивание по методу Гуро 268 6.5.3. Закрашивание по методу Фонга 269 6.6. Аппроксимация сферической поверхности рекурсивным разбиением 270 6.7. Описание источников света в OpenGL 273 6.8. Спецификация материалов в OpenGL 275 6.9. Закрашивание модели сферы 276 6.10. Глобальное тонирование 278 6.10.1. Трассировка лучей 279 6.10.2. Метод анализа излучательности 282 6.11. Резюме 283 6.12. Рекомендуемая литература 284 Упражнения 284 Глава 7 Алгоритмы формирования изображения 287 7.1. Четыре основные задачи 288 7.1.1. Моделирование 288 7.1.2. Геометрическая обработка 289 7.1.3. Растровое преобразование 289 7.1.4. Отображение 290 7.1.5. Базовые стратегии реализации 290 7.2. Реализация геометрических преобразований 292 7.3. Отсечение отрезков 294 7.3.1. Алгоритм Коэна-Сазерленда 294 7.3.2. Алгоритм Лианга-Барского 296 7.4. Отсечение многоугольников 298 7.5. Отсечение примитивов других типов 301 7.5.1. Прямоугольные оболочки 301 7.5.2. Кривые, поверхности и надписи 302 7.5.3. Отсечение в буфере кадра 302 7.6. Отсечение в трехмерном пространстве 303 Содержание 11
7.7. Удаление невидимых поверхностей 305 7.7.1. Удаление нелицевых граней 307 7.7.2. Алгоритм z-буфера 308 7.7.3. Сортировка по глубине 310 7.7.4. Алгоритм построчного сканирования 312 7.8. Растровое преобразование 313 7.9. Алгоритм Брезенхэма 315 7.10. Растровое преобразование многоугольников 317 7.10.1. Тест принадлежности внутренней области 318 7.10.2. Обработка многоугольников общего вида в OpenGL 319 7.10.3. Растровое преобразование с использованием z-буфера 320 7.10.4. Заполнение внутренней области и сортировка 321 7.10.5. Заливка 322 7.10.6. Алгоритмы заполнения построчным сканированием 322 7.10.7. Особые случаи 324 7.11. Сглаживание ступенек на изображении линий 324 7.12. Отображение информации 326 7.12.1. Цветовые системы 327 7.12.2. Гамма-коррекция 330 7.12.3. Формирование полутонов 330 7.13. Резюме 331 7.14. Рекомендуемая литература 332 Упражнения 333 Глава 8 Иерархические графические модели 337 8.1. Символы и экземпляры 338 8.2. Иерархические модели 339 8.3. Модель руки робота 341 8.4. Обход деревьев 344 8.4.1. Алгоритм обхода с использованием стека 345 8.5. Обход древовидных структур 348 8.6. Анимация 352 8.7. Графические объекты 353 8.7.1. Методы, атрибуты и сообщения 354 8.7.2. Объект cube 355 8.7.3. Объекты и иерархия 358 8.7.4. Геометрические объекты 358 8.8. Граф сцены 359 8.9. Другие типы древовидных структур 361 8.9.1. Деревья в конструктивной геометрии тел 361 8.9.2. Бинарные деревья разделения пространства 364 8.9.3. 4-арное и 8-арное деревья 366 12 Содержание
8.10. Графикой Web 368 8.10.1. Сети и протоколы 368 8.10.2. Гипермедиа и HTML 370 8.10.3. Базы данных и VRML 371 8.10.4. Java и аплеты 372 8.11. Резюме 372 8.12. Рекомендуемая литература 373 Упражнения 373 Глава 9 Операции с изображением на уровне растрового представления 375 9.1. Буферы и наложение 376 9.2. Наложение проективных текстур 377 9.2.1. Наложение двухмерных проективных текстур 378 9.2.2. Проективное наложение текстуры в системе OpenGL 383 9.2.3. Генерация образцов текстур 387 9.3. Наложение изображения окружающих предметов 388 9.4. Наложение микрорельефа 390 9.5. Запись в буферы 391 9.5.1. Режимы записи битовых блоков 392 9.5.2. Режим записи XOR 393 9.6. Операции с пикселями в OpenGL 394 9.6.1. Буферы OpenGL 395 9.6.2. Использование растровых образов символов шрифта 396 9.6.3. Пиксели и изображения 397 9.6.4. Таблицы соответствия цветов 398 9.6.5. Использование буферов в процедуре указания объекта 400 9.7. Технология комбинирования изображений 400 9.7.1. Поглощение света и смешивание изображений 400 9.7.2. Смешивание изображений 402 9.7.3. Смешивание изображений в OpenGL 402 9.7.4. Сглаживание погрешностей дискретизации 403 9.7.5. Тонирование сцен со множеством полупрозрачных объектов 405 9.7.6. Эффект тумана и создание иллюзии глубины пространства 406 9.8. Использование буфера-накопителя 406 9.9. Дискретизация изображения 409 9.9.1. Теория квантования по независимым переменным 410 9.9.2. Восстановление непрерывной функции по дискретным выборкам 413 9.9.3. Квантование по уровню 415 9.10. Резюме 416 9.11. Рекомендуемая литература 417 Упражнения 417 Содержание 13
Глава 10 Кривые и криволинейные поверхности 419 10.1. Представление кривых линий и поверхностей 419 10.1.1. Представление в явной форме 420 10.1.2. Неявная форма представления 421 10.1.3. Параметрическая форма представления 422 10.1.4. Параметрические полиномиальные кривые 423 10.1.5. Параметрические полиномиальные поверхности 423 10.2. Общая характеристика полиномиальной параметрической формы представления 424 10.3. Параметрически заданные кубические кривые 426 10.4. Интерполяция 427 10.4.1. Функции смешивания 429 10.4.2. Порция кубической интерполяционной поверхности 430 10.5. Эрмитова форма представления кривых и поверхноаей 432 10.5.1. Форма Эрмита 432 10.5.2. Геометрическая и параметрическая непрерывность 434 10.6. Кривые и поверхности в форме Безье 435 10.6.1. Кривые Безье 435 10.6.2. Порции поверхности в форме Безье 437 10.7. Кубические В-сплойны 438 10.7.1. Кубические В-сплайны 438 10.7.2. В-сплайны и базисные функции 441 10.7.3. Сплайновые поверхности 442 10.8. Обобщенные В-сплайны 442 10.8.1. Рекурсивно определенные В-сплайны 443 10.8.2. Равномерные В-сплайны 444 10.8.3. Неравномерные В-сплайны 445 10.8.4. NURBS — неравномерный рациональный В-сплайн 445 10.9. Построение кривых и поверхностей 446 10.9.1. Методы вычисления полиномов 447 10.9.2. Рекурсивное разбиение кривых Безье 448 10.9.3. Построение других типов полиномиальных кривых методом разбиения 450 10.9.4. Разбиение поверхности Безье 451 10.10. Пример: формирование изображения чайника 452 10.11. Алгебраические поверхности 454 10.11.1. Квадратичные поверхности 454 10.11.2. Вычисление точек на квадратичной поверхности методом приведения лучей 455 10.12. Кривые и поверхности в OpenGL 456 10.12.1. Кривые Безье 456 10.12.2. Поверхности Безье 457 10.12.3. Отображение чайника Юта 458 14 Содержание
10.12.4. Функции отображения NURBS-кривых и поверхностей 460 10.12.5. Квадратичные поверхности 460 10.13. Резюме 461 10.14. Рекомендуемая литература 462 Упражнения 462 Глава 11 Процедурные методы 465 11.1. Особенности процедурных моделей 465 11.2. Физические модели и система частиц 467 11.3. Ньютоновы частицы 468 11.3.1. Несвязанные частицы 470 11.3.2. Силы упругости 470 11.3.3. Силы взаимного притяжения и отталкивания 472 11.4. Решение системы уравнений 473 11.5. Ограничения 475 11.5.1. Столкновения 475 11.5.2. Частицы внутри сферической оболочки 477 11.5.3. Мягкие ограничения 479 11.6. Языковые модели 479 11.7. Рекурсивные методы и фракталы 483 11.7.1. Масштаб и длина 483 11.7.2. Размерность фрактала 484 11.7.3. Разбиение в средней точке и броуновское движение . 486 11.7.4. Формирование изображения горы с помощью фракталов 487 11.8. Множество Мандельброта 488 11.9. Резюме 491 11.10. Рекомендуемая литература 491 Упражнения 491 Глава 12 Визуализация данных научных исследований 493 12.1. Данные+геометрия 493 12.2. Поля превышений и линии уровня 494 12.2.1. Сети 495 12.2.2. Вычерчивание линий уровня 498 12.2.3. Метод маркированных квадратов 498 12.3. Визуализация поверхностей и скалярных полей 505 12.3.1. Объемное множество данных 505 12.3.2. Визуализация функций, заданных в неявной форме 506 12.4. Изоповерхности и метод маркированных кубиков 508 12.5. Непосредственное отображение объема 510 12.5.1. Управление цветом и коэффициентом прозрачности 511 Содержание 15
12.5.2. Отображение скалярного поля с помощью отпечатков 12.5.3. Трассировка лучей в скалярном поле 12.5.4. Наложение текстуры на объем 12.6. Визуализация векторных полей 12.6.1. Отрезки переменной длины 12.6.2. Бусинки 12.6.3. Цвет 12.6.4. Треки частиц и линии потока 12.7. Визуализация тензорных полей 12.8. Резюме 12.9. Рекомендуемая литература Упражнения Приложение А Демонстрационные программы А.1. Двухмерный узор Серпинского А.2. Рекурсивный алгоритм построения узора Серпинского А.З. Трехмерный узор Серпинского А.4. Рекурсивный алгоритм построения трехмерного узора Серпинского А.5. Программа вычерчивания квадрата А.6. Программа рисования А.7. Программа отображения с двойной буферизацией А.8. Программа отображения вращающегося куба А.9. Вращение куба с использованием массива вершин А. 10. Вращающийся куб, управляемый трекболом А.11. Изменение положения наблюдателя А. 12. Построение сферы Приложение Б Абстрактные пространства в компьютерной графике Б.1. Скаляры Б.2. Векторное пространство Б.З. Аффинное пространство Б.4. Евклидово пространство Б.5. Проекции вектора Б.6. Ортогонализация Грама-Шмидта Б.7. Рекомендуемая литература Упражнения 512 513 514 515 515 516 516 517 519 520 520 521 523 524 526 528 529 532 534 541 544 547 549 554 556 561 561 562 564 566 567 567 568 568 16 Содержание
Приложение В Матрицы 569 8.1. Основные определения 569 8.2. Операции над матрицами 570 8.3. Матрицы-строки и матрицы-столбцы 571 8.4. Ранг матрицы 572 8.5. Изменение представления 573 8.6. Векторное произведение 574 8.7. Рекомендуемая литература 575 Упражнения 575 Литература 577 Содержание 17
Предисловие Книга, которую вы держите в руках, представляет собой вводный курс компьютерной графики, в котором основной акцент сделан на вопросах прикладного программирова- программирования. В первом издании, которое вышло в свет в 1997 году, я отмечал, что за семь лет, прошедших после публикации моей предыдущей книги по компьютерной графике, в этой об- области произошли разительные изменения — она развивалась со скоростью, превзошедшей самые смелые ожидания специалистов, в том числе и мои собственные. За последние три го- года эти темпы не только не уменьшились, а даже возросли. Полнометражные кинофильмы, в производстве которых использована компьютерная анимация, имеют не только громадный зрительский, но и коммерческий успех. Включение в кинофильмы эффектных сцен, сделан- сделанных с помощью компьютера, стало в последние годы не исключением, а нормой, и подчас та- такие сцены неотличимы от снятых на "натуре". Особый интерес к графическим приложениям вызывает их применение в среде Internet. За последние годы не только возросли функциональные возможности средств компью- компьютерной графики, но и значительно снизилась стоимость графических рабочих станций, при- причем это характерно для установок всех классов, как простейших, так и профессиональных. В течение нескольких лет стоимость графической станции, способной формировать около од- одного миллиона трехмерных многоугольников в секунду с учетом эффектов освещения и на- наложения текстуры, понизилась со $100 000 до нескольких тысяч. Значительно более доступ- доступными по цене стали и специализированные графические платы для персональных компьюте- компьютеров. При цене в несколько сотен долларов эти платы обеспечивают почти такие же возможности, как и графические рабочие станции. В области программного обеспечения также произошли серьезные изменения. OpenGL стала своего рода стандартным интерфейсом для программистов как при написании прикладных программ, так и при разработке про- программных продуктов высокого класса— от интерактивных игр до систем визуализации ре- результатов научных исследований. Нисходящий подход Отмеченные достижения еще более упрочили мою веру в преимущество нисходящего (сверху вниз) подхода при изложении вводного курса компьютерной графики. Хотя на фа- факультетах информатики и вычислительной техники многих вузов читают несколько курсов лекций, имеющих отношение к этой дисциплине, большинство студентов предпочитают про- прослушать только какой-либо один из них. Обычно такой курс включается в учебную програм- программу после прослушивания курсов по основам программирования, структурам данных, теории алгоритмов, технологии программирования и базовых математических курсов. Сам предмет компьютерной графики позволяет преподавателю так организовать этот курс, что он будет не только познавательным, но и увлекательным для слушателей. При пла- планировании занятий я стараюсь предоставить студентам возможность как можно раньше при- приступить к программированию задач трехмерной графики, а анализ алгоритмов нижнего уров-
ня, таких как вычерчивание линий и заливка многоугольников, откладываю на более позднее время. Я возвращаюсь к ним тогда, когда студенты уже могут самостоятельно программиро- программировать построение графических изображений. Джон Кемени (John Kemeny), один из пионеров преподавания этой дисциплины в США, использовал для обоснования такой концепции преподавания понятную многим аналогию с вождением автомобиля. Для того чтобы сесть за руль и стронуться с места, совсем не обяза- обязательно знать, что происходит под капотом, но тот, кто не знаком с правилами движения и ос- основными приемами управления автомобилем, обречен сидеть на заднем сиденье, а не на мес- месте водителя. Кемени различает три подхода к обучению. Первый — алгоритмический — обу- обучать сначала тому, как функционирует автомобиль в целом и отдельные его агрегаты: двигатель, трансмиссия, топливная система и т.д. Второй подход — потребительский — на- нанять шофера, устроиться на заднем сиденье и созерцательно глядеть на мир за окном. Третий подход— это подход программиста, который я и отстаиваю в этой книге,— научиться во- водить автомобиль и ориентироваться в обстановке на дороге, пренебрегая детальным знанием о том, что происходит под капотом. Овладевший этими знаниями, всегда сможет добраться до места назначения (если, конечно, автомобиль будет исправен — но это уже другая песня). Те, кто давно делает бизнес на прокате автомобилей, выразили эту мысль простой формулой: "Предоставьте нам усадить вас на место водителя". Программирование на языке С с применением OpenGL В прежние времена наиболее серьезные сложности при чтении курса компьютерной гра- графики и подготовке учебников по этой дисциплине были связаны с отсутствием общепринятой графической библиотеки или интерфейса прикладного программирования (API — Application Programming Interface). Из-за этого приходилось приобретать специализированные средства по довольно высокой цене, которые были недоступны большинству студентов вне пределов вуза, страдали отсутствием общности и были довольно сложны в освоении. Появление OpenGL в значительной мере сняло эти вопросы, в чем на собственном опыте убедились те, кто ранее использовал в практике преподавания как другие пакеты API (такие как GKS и PUIGS), так и "доморощенное" программное обеспечение. На сегодняшний день графическая система OpenGL поддерживается большинством производителей рабочих графических стан- станций, а независимые разработчики создали средства ее поддержки для большинства сущест- существующих аппаратных и программных платформ. Эта система доступна тем, кто работает в операционной среде Microsoft Windows (как 98, так и NT), а пользователей компьютеров Ap- Apple фирма-изготовитель известила о том, что OpenGL будет включена в качестве базового компонента в будущие версии операционной системы. Свободно распространяются исходные программные коды системы Mesa — пакета API на базе OpenGL, — которые можно компи- компилировать в большинстве операционных систем, в том числе и Linux. Конечно, содержание курса компьютерной графики выходит далеко за пределы простого руководства по применению конкретного пакета API, но наличие хорошего базового пакета значительно упрощает как преподавание, так и усвоение студентами ключевых тем этой дис- дисциплины — создание изображений трехмерных объектов, закрашивание, графические систе- системы с архитектурой "клиент/сервер", моделирование и создание прикладных графических сис- систем. Я полагаю, что широкие функциональные возможности OpenGL и тщательно продуман- продуманная структура этой системы послужат хорошим фундаментом для изложения как теоретических, так и практических аспектов этого предмета, в том числе и новейших идей, таких как наложение текстур и их комбинирование (эти функции не поддерживались преж- прежними пакетами API). Я перешел на использование OpenGL в своей преподавательской практике примерно 5 лет назад, и уже первые результаты меня изумили — в середине первого семестра каждый сту- Предисловие 19
дент мог написать довольно сложную программу построения изображения трехмерных объ- объектов, в которой требуется знание не только математики трехмерного моделирования, но и умение работать с событиями. За предыдущие 15 лет преподавания я и близко никогда не подходил к подобным результатам. Опыт преподавания курса на новой базе подвел меня к мысли полностью переделать написанный ранее учебник. Настоящая книга представляет собой учебник по компьютерной графике, а не руково- руководство пользователя по работе с OpenGL. Поэтому я не считал необходимым освещать в ней все тонкости использования этого языка, а описывал только то, что имеет непосредственное отношение к теоретическим и практическим задачам, рассматриваемым в книге. Я старался таким образом использовать OpenGL для иллюстрации тех или иных идей, чтобы те читате- читатели, которые пользуются другим пакетом API, могли без особого труда воспринимать изла- излагаемый материал. Помимо этого я использую в книге язык С. У читателей может возникнуть вполне резон- резонный вопрос: "Почему С, а не C++, Java или любой другой объектно-ориентированный язык программирования?" Мой выбор обоснован двумя соображениями. Во-первых, система OpenGL не относится к объектно-ориентированным системам программирования, а потому использование C++ или Java не внесет ничего особенного в большинство излагаемых тем. Исключение составляет тема структуры библиотеки промежуточных программ между OpenGL и пользователем, но я решил вообще не касаться ее, поскольку это сделает книгу ме- менее доступной для студентов, владеющих навыками программирования, но незнакомых с ме- методикой объектно-ориентированного программирования. На кого рассчитана эта книга В первую очередь я адресую эту книгу студентам старших курсов и аспирантам первого года обучения, специализирующимся в области информатики и вычислительной техники, а также студентам других специальностей, имеющим достаточный опыт программирования. Книга будет полезна и многим профессионалам. Я провел около сотни краткосрочных курсов по компьютерной графике для профессионалов, и опыт преподавания для такой аудитории оказал немалое влияние на отбор материала для этой книги. Необходимым условием для успешного усвоения материала книги является достаточно глубокие знания языка программирования С, знакомство с основными идеями объектно- ориентированного программирования и хотя бы начальные познания в области линейной ал- алгебры и тригонометрии. Я полагаю, что математическая подготовка студентов и аспирантов, специализирующихся в области информатики и вычислительной техники, играет очень важ- важную роль в изучении большинства дисциплин. Поэтому я постарался включить в материал книги те разделы линейной алгебры и геометрии, которые необходимы для понимания основ компьютерной графики. Этот материал я выделил в два отдельных приложения. Структура книги Предлагаемая вашему вниманию книга состоит из 12 глав. В главе 1 представлен обзор методов формирования изображений оптическими приборами, где читатель сразу же позна- познакомится с основными концепциями создания изображений трехмерных объектов. В главе 2 читатель знакомится с методикой программирования с использованием OpenGL. Хотя первая программа, рассматриваемая в этой главе (а в каждой главе рассматривается одна или не- несколько законченных программ), имеет дело с двухмерными объектами, они "встраиваются" в трехмерную среду. В главе 3 обсуждаются современные тенденции создания интерактивных графических систем типа "клиент/сервер" и методика разработки графических программ, управляемых событиями. В главах 4 и 5 основное внимание уделено концепциям формирова- 20 Предисловие
ния изображений трехмерных объектов и сцен; в главе 4 речь идет о математическом аппара- аппарате описания трехмерных объектов, а в главе 5 рассматриваются методы их отображения. В главе 6 представлен вводный материал о взаимодействии освещения и материалов и о мето- методах закрашивания освещенных поверхностей. Материал первых шести глав следует изучать в той последовательности, в которой он изложен в данной книге. Это должно занять примерно 10 недель при 15-недельном семестре. Материал следующих пяти глав можно изучать в произвольном порядке. Эти главы не имеют столь жесткой структуры изложения, как предыдущие, и читатель может просмот- просмотреть их и получить общее представление или выбрать отдельные темы, и изучить их более детально, чем остальные. В главе 7 представлен обзор методов закрашивания поверхно- поверхностей, используемых в компьютерной графике. Читатель имеет возможность подробно по- познакомиться с одним-двумя алгоритмами выполнения каждого из основных этапов процес- процесса построения изображения. Глава 8 включает несколько тем, касающихся иерархической организации графических моделей и использования объектно-ориентированного подхода. Рассмотренные в ней темы охватывают обширный материал от создания моделей, инкап- инкапсулирующих отношения между компонентами, до использования графики в Internet. В гла- главе 9 рассматриваются новые возможности современных аппаратных графических средств и методы поддержки этих средств, реализованные в OpenGL. Эти методы предполагают ин- интенсивное использование разнообразных буферов. Заключительные разделы этой главы посвящены обсуждению вопросов квантования графического изображения и минимизации возникающих при этом искажений. В главе 10 рассматриваются кривые и поверхности. Процедурные методы построения геометрических моделей на базе многоугольников описаны в главе 11. В этой же главе чита- читатель познакомится с языковыми моделями, фракталами и системами частиц. В главе 12 речь идет о применении графических средств при визуализации результатов научных расчетов. Эта область применения машинной графики позволяет продемонстрировать весь рассмот- рассмотренный в предшествующих главах арсенал методов и средств. Программы, приведенные в основном тексте книги, собраны отдельно в приложении А. Тексты программ доступны и в электронном виде. Изменения, внесенные во второе издание Реакция читателей на появление первого издания этой книги была исключительно пози- позитивной. Особой похвалы удостоились описание методики программирования задач компью- компьютерной графики на OpenGL и принятый в книге нисходящий подход к изложению материала. Но, тем не менее, я включил в настоящее издание много нового материала, а прежний значи- значительно переработал. Внесенные изменения я бы разделил на три категории. Во-первых, я переработал изложе- изложение некоторых математических вопросов (надеюсь, это пошло на пользу полноте и ясности материала) и добавил новые примеры. Во-вторых, я значительно дополнил материал о совре- современных областях применения компьютерной графики, в особенности в научных исследова- исследованиях. В-третьих, в книгу включено значительно больше практических программ на базе OpenGL, причем в них использованы расширения, появившиеся в OpenGL 1.1, касающиеся, в частности, массивов вершин. В результате мне пришлось значительно переработать про- программы, приведенные во второй половине книге. Я добавил в главу 2 два новых примера. Первый относится к использованию рекурсии при построении узора Серпинского и не только демонстрирует, как организуется рекурсия в OpenGL, но и показывает, какие интересные программы могут создавать студенты, даже не изучив досконально все возможности OpenGL. Второй пример демонстрирует нюансы по- построения трехмерного узора Серпинского, и при создании этой программы студенты знако- Предисловие 21
мятся с элементарными приемами создания изображений трехмерных объектов, не требую- требующими использования сложных преобразований координат. Большая часть материала, отно- относящегося к свойствам многоугольников, была перенесена в главу 7. В главе 3 существенной переработке подверглась программа вычерчивания. Из нее удале- удалены часы, поскольку для их реализации использовались функции, специфичные для операци- операционной системы UNIX, но вопросы использования временной синхронизации в этой главе по- прежнему обсуждаются. В главу включен новый пример, демонстрирующий вращение квад- квадрата на экране, который иллюстрирует описанные в основном тексте методы анимации и применение двойной буферизации. В главе 4 более детально описаны абстрактные геометрические объекты и дополнены примерами, в которых показано, как выполняется изменение координат и фреймов. Показано также, как использовать при формировании геометрических моделей массивы вершин — но- новинку версии OpenGL 1.1. Еще один новый пример демонстрирует использование виртуаль- виртуального трекбола для интерактивной реализации преобразований координат в трехмерном про- пространстве. Я надеюсь, что изложение математических основ в главе 5 стало более доступным для студентов по сравнению с первым изданием. Я включил в эту главу также и пример форми- формирования теней, в котором использованы матрицы проецирования. Глава 6 осталась практиче- практически в том же виде, что и в первом издании. Глава 7 также подверглась очень незначительным изменениям — я отредактировал ее название, которое теперь, на мой взгляд, точнее передает смысл представленного материала, и перенес в нее из других глав детальное описание мето- методов манипуляции с многоугольниками. В главе 8 значительно расширен материал, касающийся древовидных структур. В главу включен второй пример построения изображения робота, в котором использован обобщен- обобщенный рекурсивный алгоритм обхода дерева. Переработан и материал, касающийся графов сцен. Я рассматриваю эту тему как фундаментальную, имеющую исключительно важное зна- значение для понимания идей объектно-ориентированного подхода в компьютерной графике. В этой главе рассматриваются и побочные применения древовидных структур — бинарные де- деревья разделения пространства (BSP — binary spatial-partition tree), 4- и 8-арные деревья {quadtree и octree). В заключительную часть этой главы включен материал об использовании компьютерной графики в Internet, о языках VRML и Java (при этом от читателя не требуется знание каждого из этих языков). Глава 9 включает материал о наложении текстур и использовании буферов, представлен- представленный в главе 10 первого издания. В данном издании я поменял местами материал глав 9 и 10, учитывая важность применения методов наложения в современных графических приложени- приложениях. В главе 9 читатель найдет и подробное описание средств OpenGL, применяемых для вы- выполнения наложения. Глава 10 настоящего издания представляет собой переработанную гла- главу 9 первого издания, причем в нее добавлены новые примеры формирования кривых и по- поверхностей с помощью OpenGL. Глава 11 базируется на тех сведениях о процедурном моделировании, которые изложены в главе 8 первого издания. В нее добавлен более пространный материал о системах моделиро- моделирования поведения частиц, в частности описание методов решения дифференциальных уравне- уравнений движения частиц в силовом поле. Включены также примеры моделирования поведения системы взаимодействующих частиц при наличии между ними пружинных связей отталки- отталкивающих сил. В главе 12 собран материал о визуализации пространственных объектов, разбросанный в первом издании по трем главам. Такая концентрация материала позволяет связно описать ме- методы визуализации различных объектов, начиная с контуров и сеток и заканчивая скалярны- скалярными, векторными и тензорными полями. Материал о векторных и тензорных полях, представ- представленный в этой главе, а также примеры в прежнем издании отсутствовали. 22 Предисловие
Дополнительные источники Текущую информацию о дополнениях к этой книге вы можете получить на Web-сервере издательства Addison-Wesley по адресу http://wvw.aw.com/cseng. Материал, доступный в электронном виде, включает решение некоторых упражнений, связи с другими ресурсами и примеры текстов программ. Эту информацию, а также некото- некоторую другую можно получить на моем Web-сервере по адресу http://www.cs.unm.edu/angel. Тексты программ из этой книги и другие примеры можно получить по Internet на ftp- сервере по адресу ftp.cs.unm.edu в каталоге pub/angel/BOOK. Буду рад получить от читателей предложения о любых других дополнительных материа- материалах и замечания по материалу книги. Мой адрес angel@cs.unm.edu. Благодарности В течение нескольких последних лет я имел удовольствие работать с прекрасными студентами университета Нью-Мексико. Именно они пробудили во мне интерес к OpenGL, и я многому научился благодаря общению с ними. Это — Хью Бамгарнер- Керби (Hue Bumgarner-Kirby), Пат Кроссно (Pat Crossno), Томми Дэниэл (Tommie Daniel), Лиза Десджарле (Lisa Desjarlais), Ким Эдлунд (Kim Edlund), Ли Анн Фиск (Lee Ann Fisk), Ма- Мария Галлегос (Maria Gallegos), Брайан Джонс (Brian Jones), Кристофер Джордан (Christopher Jordan), Макс Х&зелриг (Max Hazelrigg), Томас Келлер (Thomas Keller), Пат Мак-Кормик (Pat McCormick), Эл Мак-Ферсон (Al McPherson), Мартин Мюллер (Martin Muller), Джим Пин- Пинкертон (Jim Pinkerton), Джим Прейетт (Jim Prewett), Дейв Роджерс (Dave Rogers), Хэл Смайер (Hal Smyer), Дейв Вик (Dave Vick) и Брайан Уайли (Brian Wylie). Именно они разработали большинство примеров и подготовили цветные иллюстрации. Первое издание этой книги я готовил в период годичного отпуска. Работая над ним, я ус- успел побывать в пяти странах и приобрел громадный опыт работы на портативном компьюте- компьютере и общения через Internet. Как бы там ни было, но к концу отпуска я справился с задачей, в чем немалую роль сыграла помощь множества людей и организаций. Я в неоплатном долгу перед Джонасом Монтилва (Jonas Montilva) и Крисом Биркбеком (Chris Birkbeck) из универ- университета де Лос-Андес (Венесуэла), Родриго Галлегосом (Rodrigo Gallegos) и Аристид Новоа (Aristides Novoa) из Технологического университета Экиночиал (Эквадор), Лонг Вен Ченем (Long Wen Chang) из Национального университета Цин Хуа (Тайвань), а также Кин Хонг Вонгом (Kin Hong Wong) и Пень Ан Хенгом (Pheng Ann Heng) из Китайского университета в Гонконге. Все эти визиты стали возможными благодаря Рамиро Джордану (Ramiro Jordan) из университета Нью-Мексико. Где бы я ни был, я всегда поддерживал связь с Джоном Брайе- ром (John Brayer) и Джесоном Стюартом (Jason Stewart) из университета Нью-Мексико и Хе- Хелен Гольдштейн (Helen Goldstein) из издательства Addison-Wesley, которые очень помогли мне в подготовке материала для этой книги. Второе издание было целиком подготовлено в университете Нью-Мексико, в чем большую помощь мне оказали сотни читателей, приславших замечания и пожелания к первому изданию. Я не могу не поблагодарить руководство компаний Silicon Graphics и Apple Computer, ко- которые предоставили в мое распоряжение необходимое оборудование. Я постоянно консуль- консультировался с Джоном Шимпфом (John Schimpf) из Silicon Graphics относительно OpenGL. Фирмы Conix Enterprises, Portable Graphics, Template Graphics and Metrowerks благосклонно предоставили мне необходимое программное обеспечение, и я смог протестировать все про- программы на разных платформах. Предисловие 23
Множество других людей оказали мне очень существенную помощь в подготовке этой книги. Я благодарен Гонсало Картагенова (Gonzalo Cartagenova), Тому Коделу (Tom Caudell), Кетти Коллинз (Kathi Collins), Кетлин Дениэлсон (Kathleen Danielson), Роджеру Эриху (Roger Enrich), Чаку Хансену (Chuck Hansen), Марку Хенне (Mark Henne), Бернарду Море (Bernard Moret), Дику Нордхаусу (Dick Nordhaus), Хелене Соана (Helena Saona), Гвин Силвэн (Gwen Sylvan) и Мэсону Во (Mason Woo). Сообщество пользователей OpenGL должно знать имена Марка Килгарда (Mark Kilgard), Брайана Пола (Brian Paul) и Нейт Робине (Nate Robins), кото- которые подготовили программы, необходимые для проверки OpenGL-кода на разных платфор- платформах. Я особенно благодарен Бену Бедерсону (Ben Bederson) и его студентам, которые на себе "испытали" черновую рукопись этой книги. Цветные иллюстрации на вклейке — дело рук че- четырех студентов этого курса. Перед тем как я отдал рукопись в издательство, ее тщательно просмотрели многие спе- специалисты, чтобы оценить полноту и ясность изложения материала для разных категорий чи- читателей. Я благодарен рецензентам, от которых получил множество ценных замечаний, — Хамиду Арабниа (Hamid Arabnia) из университета Джорджия, Уэйену Карлсону (Wayne Carl- Carlson) из университета штата Огайо, Норману Чину (Norman Chin) из Silicon Graphics, Скотту Гриссому (Scott Grissom) из университета штата Иллинойс (Спрингфилд), Дику Филлипсу (Dick Phillips), ранее работавшему в Национальной лаборатории в Лос-Аламосе, Тому Мак- Рейнолдсу (Tom McReynolds) из Silicon Graphics, Джейн Вильгельме (Jane Wilhelms) из Ка- Калифорнийского университета (Санта-Круз) и Эдварду Вонгу (Edward Wong) из Бруклинского политехнического института. Хотя окончательный вариант может и не отображать всех вы- высказанных точек зрения — которые у рецензентов значительно расходятся, — каждое из них так или иначе повлияло на содержание этой книги. Я не могу не поблагодарить и всех тех сотрудников издательства Addison-Wesley, благо- благодаря которым эта книга появилась на полках магазинов и библиотек. Мой редактор, Питер Гордон (Peter Gordon), — это человек, работать с которым одно удовольствие, и я, призна- признаюсь, иногда сожалею, что книга уже закончена. Особо благодарен я Лин Дюпре (Lyn Dupre). Я ведь не "литератор от Бога". Если бы читатели имели возможность взглянуть на первый ва- вариант рукописи, они смогли бы оценить тот вклад, который внесла Лин в эту книгу. Моя жена, Роз-Мари Мольнар (Rose Mary Molnar), подготовила рисунки к предыдущему изданию, многие из которых остались и в этом издании. Со свойственной ей рассудительно- рассудительностью она не стала возражать против использования для этой работы нашего единственного ноутбука и тем самым сохранила мир и сердечность наших отношений, за что, среди прочего, заслуживает дополнительных тысяч слов благодарности. 24 Предисловие
ГЛАВА 1 Графические системы и модели Совершенно очевидно, что в новом тысячелетии информационные и коммуникацион- коммуникационные технологии будут играть важнейшую роль во всех сферах жизни человечества. В таких областях, как кинематография, издательское и банковское дело, в образователь- образовательных учреждениях, внедрение этих технологий уже произвело поистине революционный пере- переворот. Интеграция в единой системе компьютерной графики отдельных компьютеров, сетей и систем машинного зрения открыла новые пути отображения информации, "проникновения" в виртуальный мир и организации взаимодействия человека и машины. Компьютерная графика (computer graphics) — это область информатики (науки о ком- компьютерах — computer sciences), в сферу интересов которой входят все аспекты формирования изображений с помощью компьютеров. Эта область начала развиваться около 40 лет назад. В те годы удавалось добиться отображения нескольких десятков отрезков на экране электрон- электронно-лучевой трубки (ЭЛТ), а современные системы машинной графики позволяют создавать изображения, практически неотличимые по качеству от фотографических снимков. Обще- Общепринятой практикой стало сейчас обучение пилотов с помощью систем моделирования ре- реальной ситуации, как она видится из кабины самолета во время пилотирования, создание изо- изображений виртуального динамического мира во всем его многообразии в реальном масштабе времени. На экран выходят полнометражные кинофильмы, в которых нет ни одного кадра, снятого "на натуре" или в павильоне киностудии, а все действие "разворачивается" в памяти компьютера. В этой главе мы начнем нашу "экскурсию" в мир компьютерной графики с краткого об- обсуждения областей ее применения. Затем будет дан обзор графических систем и способов формирования изображений. На протяжении всей книги я буду постоянно обращать ваше внимание на тесную связь методов компьютерной графики с "традиционными" способами формирования изображений, такими как рисование "от руки" или фотография. Вы увидите, насколько такие аналогии помогают при создании прикладных графических программ, биб- библиотек и структурном синтезе графических систем. В процессе изложения материала в данной книге широко используется одна из сущест- существующих на сегодняшний день программных графических систем — OpenGL, которая в по-
следнее время рассматривается многими разработчиками как своего рода стандарт для созда- создания графических приложений. Изучение OpenGL не представляет никакой сложности для ма- мало-мальски опытного программиста, и в то же время эта система располагает всем набором средств, характерных для большинства современных графических систем. При изложении материала мы будем использовать нисходящий подход (сверху вниз). Это означает, что я предлагаю вам как можно раньше приступить к разработке практических программ, способ- способных создавать графические картинки, пусть поначалу и несложные. И уже после того как вы добьетесь появления картинки на экране с помощью созданных программ, мы обсудим, как выполняются отдельные графические функции библиотеки и каким образом аппаратно реали- реализуется формирование изображения на экране. Материал этой главы должен дать вам общее представление о том, чего можно достичь, создавая графические программы. 1.1. Области применения компьютерной графики Развитие компьютерной графики определяется двумя факторами: реальными потребно- потребностями потенциальных пользователей и достижениями в области аппаратного и программного обеспечения. Хотя компьютерная графика используется в самых различных сферах жизни со- современного общества, можно выделить четыре главные области ее применения. 1. Отображение информации. 2. Проектирование. 3. Моделирование. 4. Пользовательский интерфейс. Хотя во многих практических приложениях можно обнаружить характерные признаки двух или более перечисленных групп, развитие каждой из этих групп шло своим путем. 1.1.1. Отображение информации Классические графические технологии развивались как средство передачи информации в человеческом обществе. Хотя аналогичную роль играет и язык (как в устной, так и в пись- письменной форме), зрительная система человека обладает гораздо большими возможностями (специалист по информатике сказал бы "большей пропускной способностью"), поскольку вы- выполняет функции и обработки данных, и распознавания образов. Еще 4000 лет назад древние вавилоняне использовали графические планы при строительстве каменных сооружений. Древние греки еще в конце первого тысячелетия до н.э. были способны преподносить свои архитектурные идеи в графическом виде, хотя соответствующие математические методы появились только в эпоху Ренессанса. Сегодня подобного рода информация создается архи- архитекторами, конструкторами и чертежниками с помощью компьютерных систем. На протяжении многих столетий картографы и астрономы вычерчивали карты, чтобы представить информацию о расположении небесных тел и географических областей. Нет смысла говорить о том, какое значение имеют такие карты сегодня не только для навигации на Земле и в Космосе, но и для решения повседневных задач человечества с помощью геоин- геоинформационных систем. Сейчас любую карту можно в считанные минуты получить и обрабо- обработать с помощью Internet. За последние 100 лет статистики использовали самые разные технологии для представле- представления в графическом виде первичных данных и результатов их статистической обработки. Та- Такая форма представления множества собранных данных является наиболее информативной. Сегодня и в этой области не обойтись без компьютеров, которые не только обрабатывают со- собранные данные, но и формируют соответствующие графики, используя самые разнообраз- разнообразные способы их представления, в том числе и с применением цвета. Только такая форма 26 Глава 1. Графические системы и модели
представления позволяет человеку без труда интерпретировать информацию, содержащуюся в гигабайтах собранных первичных данных. Множество важных и интересных проблем анализа данных ставит и медицина. Новые технологии визуализации состояния человеческого организма, такие как компьютерная томо- томография, магниторезонансное обследование, ультразвуковое зондирование и позитронно- эмиссионная томография, позволяют получать трехмерную информацию, которая может быть впоследствии обработана вычислительными методами. Хотя сами первичные данные формируются специальной медицинской аппаратурой, последующая компьютерная обработ- обработка и созданное цветное изображение позволяют специалистам достаточно просто интерпре- интерпретировать их. С появлением суперкомпьютеров стало возможным исследовать проблемы, ранее отне- отнесенные к классу неразрешимых. В области визуализации результатов экспериментов и науч- научных расчетов средства компьютерной графики являются мощным инструментом для пра- правильной интерпретации огромных массивов первичных данных. Исследования в таких облас- областях, как течение жидкостей, молекулярная биология и математика, не обходятся без преобразования первичных данных в зримые геометрические образы, что помогает лучше понять суть происходящих процессов. Например, на ил. 9 цветной вклейки представлено изо- изображение динамики движения жидкости в мантии Земли. Исходные данные были получены в результате математического моделирования процесса, а затем использовались различные ме- методы визуализации этих данных, описанные в главах 8, 9 и 12 настоящей книги. 1.1.2. Проектирование Проектирование является одной из основных стадий создания изделий и сооружений в технике и строительстве. Задавшись спецификацией основных характеристик разрабатывае- разрабатываемого изделия, конструктор ищет решение, оптимальное с точки зрения затрат и технических параметров. Процесс проектирования по самой своей природе является итеративным — очень редко бывает так, что заданные характеристики допускают только один вариант реше- решения. Как правило, исходная формулировка задачи проектирования оказывается недоопреде- ленной, т.е. допускает множество решений, или переопределенной, другими словами, в таком виде задача оказывается неразрешимой. В первом случае конструктору или проектировщику нужно перебрать множество вариантов перед тем, как остановиться на одном из них, причем часто получается так, что очередной вариант появляется в результате анализа и устранения недостатков предыдущего. Во втором случае приходится добиваться изменения исходной формулировки задачи, что также включает поиск вариантов, наиболее близких по характери- характеристикам к заданной спецификации. Достоинства парадигмы взаимодействия конструктора с изображением проектируемой конструкции на экране ЭЛТ впервые подметил Айвен Сазерленд (Ivan Sutherland) еще лет со- сорок назад. Сегодня уже ни у кого не вызывает сомнения прогрессивность применения средств графического взаимодействия конструктора и компьютера в системах автоматизации проек- проектирования (САПР). Такие системы применяются в самых разнообразных отраслях техники — от проектирования микросхем со сверхвысокой степенью интеграции (СБИС) до автомоби- автомобилей, самолетов и космических аппаратов. Не менее широкое применение такого рода системы нашли в архитектурном проектировании и строительстве. В различных областях проектиро- проектирования графические средства используются по-разному. Например, при проектировании СБИС интерфейс между конструктором и компьютером (т.е. программами автоматического синтеза электронных схем) реализуется посредством меню и пиктограмм. После того как таким спо- способом будет создан вариант проектируемой конструкции, система анализирует ее параметры и представляет характеристики изделия также в графическом виде. Не менее широкие воз- возможности открывают САПР и перед архитекторами. На ил. 3 и 4 цветной вклейки показаны 1.1. Области применения компьютерной графики 27
два вида спроектированного здания, созданных с помощью САПР. Эти рисунки демонстри- демонстрируют, какими возможностями обладают современные САПР при отображении одного и того же объекта на разных стадиях проектирования. 1.1.3. Моделирование Как только графические системы стали обладать достаточной производительностью для создания сложных динамических изображений, возникла идея применить их в качестве сред- средства моделирования реальной обстановки (симулятора) на разного рода тренажерах. Первы- Первыми такие системы освоили авиаторы и использовали для обучения пилотов на земле. Это по- позволило значительно снизить стоимость обучения, гарантируя при этом его высокое качество и безопасность. Использование в современных системах специальных БИС позволило на- настолько снизить стоимость подобного рода устройств, что они дошли до уровня детских иг- игрушек. Например, с помощью средств компьютерной графики возможно создагие графиче- графической модели робота, которая используется при планировании технологических процессов и подготовке программ управления роботами, работающими в окружения множества других элементов производственной системы. В телевидении, кинематографии и рекламном деле в последнее время также широко ис- используются средства компьютерной графики, позволяющие создавать динамические изобра- изображения, практически неотличимые от снятых "на натуре". Именно их мы часто видим на экра- экранах телевизоров и в кинотеатрах. Подобного рода изображения (правда, статические) заполо- заполонили и страницы популярных периодических изданий. Стоимость создания полнометражного кинофильма с помощью компьютера сравнима со стоимостью съемки такого же фильма на "натуре" и в павильонах студии, но при этом можно создавать такие спецэффекты и трюки, которые недоступны для "живой" съемки. В главах 6 и 9 будут рассмотрены различные мето- методы моделирования освещения, которые используются при построении изображений, близких по качеству к фотографиям (для краткости будем их впредь именовать фотореалистически- фотореалистическими). Ил. 14 на цветной вклейке демонстрирует возможность создания компьютерными сред- средствами изображения, которое невозможно получить иными средствами, хотя в нем полно- полностью соблюдаются все законы оптики. Изображения на ил. 10 и 11 цветной вклейки также созданы компьютером с использованием современных методов тонирования. В последнее время появилась еще одна область применения средств компьютерной графики, которая получила наименование формирование виртуальной реальности (VR— virtual reality). В VR-системах человек-наблюдатель пользуется специальным шлемом с парой миниатюрных дисплеев, на экранах которых формируются разные изображения для правого и левого глаза. В результате создается стереоэффект. Кроме того, положение и ориентация головы наблюдателя постоянно анализируются и соответственно изменяется изображение на экранах дисплеев. Если добавить к этому еще и средства "влияния" на эту среду вроде перчаток с силомоментными дат- датчиками, а также звуковое сопровождение, то создается полная иллюзия погружения в виртуаль- виртуальную среду. Наблюдатель чувствует себя участником происходящего. Это уже не просто развле- развлечение, а инструмент для практического применения. Например, с помощью такой системы хи- хирург может отработать методику проведения операции, астронавт может подготовиться к выходу в открытый космос и проведению ремонтных работ. 1.1.4. Интерфейс пользователя В последнее время визуальная парадигма стала доминирующей в сфере взаимодействия пользователя с компьютером. Визуальный метод предполагает использование различного ро- рода окон, пиктограмм, меню и устройств указания, таких как мышь. С точки зрения пользова- пользователя оконные операционные системы — X Window, Microsoft Windows и операционная сис- система Macintosh — отличаются только деталями. Сейчас уже миллионы людей пользуются ус- 28 Глава 1. Графические системы и модели
лугами сети Internet. Доступ к этой сети немыслим без графических программ-броузеров, та- таких как Netscape и Internet Explorer, которые используют, по сути, одни и те же графические средства интерфейса. Мы настолько к ним привыкли, что часто и не задумываемся, что эти средства также относятся к инструментам компьютерной графики. Хотя мы уже и привыкли к такому стилю в организации пользовательского интерфейса в большинстве графических рабочих станций, развитие компьютерной графики позволяет ис- использовать и другие формы интерактивного взаимодействия. 1.2. Графическая система Система компьютерной графики является прежде всего вычислительной системой и, как таковая, включает все компоненты вычислительной системы общего назначения. Нач- Начнем наш обзор с блок-схемы, представленной на рис. 1.1, на которой показаны основные компоненты системы: ¦ процессор; ¦ память; ¦ буфер кадра; ¦ устройства вывода; ¦ устройства ввода. Рис. 1.1. Структура графической системы Эта модель имеет достаточно общий характер и отображает структуру и графической ра- рабочей станции, и персонального компьютера, и графического терминала большой вычисли- вычислительной системы, работающей в режиме разделения машинного времени, и интеллектуальной системы формирования изображений. Хотя все компоненты представленной блок-схемы при- присутствуют и в стандартном компьютере (кроме, возможно, буфера кадра), именно специали- специализация каждого компонента в соответствии с требованиями задач компьютерной графики и делает систему графической. 1.2.1. Пиксели и буфер кадра В настоящее время практически все графические системы используют растровый прин- принцип создания изображения. Суть его заключается в том, что изображение рассматривается как массив — растр — простейших элементов, или пикселей (pixels). Каждый пиксель 1.2. Графическая система 29
имеет четко заданное положение на экране (рис. 1.2). Массив кодов, определяющих за- засветку пикселей на экране, хранится в отдельной области памяти, которая называется бу- буфером кадра (frame buffer). В системах особо высокого качества для буфера кадра исполь- используются специальные типы микросхем — видеопамять с произвольным доступом (VRAM — video random-access memory) или микросхемы динамической памяти с произвольным дос- доступом (DRAM — dynamic random-access memory), которые позволяют быстро вывести со- содержимое буфера на экран. Глубина (depth) буфера кадра характеризует количество бит информации, определяющих засветку каждого отдельного пикселя, в частности количество цветов, которое может быть представлено на экране данной системы. Например, буфер глубиной 1 бит позволяет выводить только двухградационное изображение, а буфер глуби- глубиной 8 бит может выводить изображение, состоящее из элементов 28 = 256 цветов. Современные полноиветные (full-color) системы характеризуются глубиной буфера 24 бита (а иногда и больше). Такие системы способны создавать по-настоящему фотореалистиче- фотореалистическое изображение. Иногда их называют системами с правильной цветопередачей (true- color), или RGB-системами, поскольку в кодировке засветки каждого пикселя можно вы- выделить отдельные группы битов, характеризующие интенсивность засветки по каждому из основных цветов,— красному (red), зеленому (green) и синему (blue). В более простых системах буфер кадра выделяется в основной памяти компьютера. Размер буфера кадра определяет, в конце концов, одну из главных характеристик графической системы — раз- разрешающую способность (или разрешение). Рис. 1.2. Пиксели: а — изображение кота Йети; б — деталь изображения (глаз кота), на которой различимы отдельные пиксели В простых системах, как правило, используется единственный процессор, на который воз- возлагается решение как "обычных" задач, так и задач компьютерной графики. Основные гра- графические функции, в принципе, сводятся к преобразованию описания графического примити- примитива (отрезка прямой, окружности или многоугольника), сформированного прикладной про- программой, в коды засветки определенных пикселей в буфере кадра. Процесс преобразования описания графического примитива в коды засветки пикселей получил наименование растро- растрового преобразования (rasterization) или сканирующего преобразования (scan conversion). В современных высокопроизводительных графических системах для такого преобразования ис- используются специализированные процессоры, причем не один, а несколько, каждый из кото- которых выполняет свой набор графических функций. 30 Глава 1. Графические системы и модели
1.2.2. Устройства вывода изображений Доминирующее положение среди устройств вывода изображений занимают электронно- электроннолучевые трубки (ЭЛТ). В упрощенном виде конструкция ЭЛТ представлена на рис. 1.3. При попадании сфокусированного электронного луча на люминофор, покрывающий экран трубки, излучается свет. Направление электронного луча и, следовательно, положение точки засветки экрана управляются двумя парами отклоняющих пластин. Значения координат точки засвет- засветки, формируемые компьютером, преобразуются с помощью цифро-аналоговых преобразова- преобразователей в управляющие напряжения, которые подаются на пластины, отклоняющие луч по осям х ну. В результате на экране появляется засвеченное пятно, положение которого соответству- соответствует заданным координатам. Электронная пушка Фокусирующая система Рис. 1.3. Электронно-лучевая трубка Если сигналы, подаваемые на отклоняющие пластины, изменяются с постоянной скоро- скоростью, то луч вычертит на экране прямую линию — вектор. Такие устройства получили на- наименования ЭЛТ с произвольным отклонением {random-scan CRT) или векторные ЭЛТ (calligraphic CRT), поскольку в них луч может перемещаться по произвольной траектории1. Если электронный луч заперт (его интенсивность равна нулю), то можно переместить его в новое положение, не оставляя следа на экране. Именно по такому принципу работали многие системы отображения, созданные в 60-х годах, которые затем были вытеснены современны- современными растровыми системами. Сформированное на экране изображение остается видимым в течение очень короткого промежутка времени — это определяется характеристиками люминофора, покрывающего эк- экран, так называемого времени послесвечения. Как правило, время послесвечения не превыша- превышает нескольких миллисекунд. Исключение составляют специальные запоминающие ЭЛТ, ко- которые сохраняют на экране сформированное изображение около часа. При использовании обычных ЭЛТ изображение должно обновляться (регенерироваться) не реже 50 раз в секунду. В растровых системах луч всегда перемещается по одной и той же траектории, а изо- изображение создается за счет изменения интенсивности луча синхронно со считыванием кодов засветки пикселей из буфера кадра. За время одного цикла регенерации изображения полно- полностью считывается содержимое буфера кадра. Частота регенерации выбирается таким обра- Строго говоря, любая ЭЛТ как электронный прибор допускает произвольное отклонение луча. Вектор- Векторным или растровым может быть все устройство — дисплей. — объединяющее в себе ЭЛТ. отклоняющую систему и генератор отклоняющих сигналов. Поэтому в отечественной литературе используются термины "векторный дисплей" и "растровый дисплей". — Прим. ред. 1.2. Графическая система 31
зом, чтобы человеческий глаз не замечал мелькания изображения вследствие конечного вре- времени послесвечения люминофора. Существуют два способа формирования растра на экране. Первый получил наименование прогрессивной развертки {noninterlaced) — в течение каждо- каждого очередного периода кадровой развертки луч последовательно проходит по всем строкам разложения. Частота кадровой развертки соответствует частоте регенерации и составляет 50- 85 Гц. Другой принцип — чересстрочная {interlaced) развертка — предполагает, что в од- одном кадре луч проходит по всем четным строкам разложения, а в следующем — по всем не- нечетным. Чересстрочная развертка используется в обычных телевизионных системах. При этом, хотя частота кадровой развертки составляет 60 Гц (в Европе — 50 Гц), изображение на экране полностью обновляется только 30 раз в секунду. Для наблюдателя, который сидит достаточно близко к экрану, разница между прогрессивной и чересстрочной разверткой весь- весьма заметна. Дисплеи с прогрессивной разверткой занимают в настоящее время доминирую- доминирующее положение, хотя для их нормальной работы и приходится использовать в качестве буфе- буфера кадра высокоскоростные модули памяти. В цветных ЭЛТ экран покрывается точками трех разных типов люминофора— одни под воздействием электронного луча излучают красный свет, другие — зеленый, а третьи — си- синий. Точки люминофоров разного цвета размещены триадами. Большинство цветных ЭЛТ имеет три электронные пушки (соответственно трем цветам точек люминофора). Между от- отклоняющей системой и экраном в такой цветной ЭЛТ располагается теневая маска — метал- металлический экран со множеством отверстий соответственно триадам точек люминофора (рис. 1.4). Теневая маска обеспечивает попадание луча, испускаемого определенной пушкой, только наточки люминофора "своего" цвета в соответствующей триаде. Рис. 1.4. Цветная ЭЛТ с теневой маской Несмотря на то что ЭЛТ являются наиболее распространенным средством создания изображений в системах компьютерной графики, в последнее время интенсивно разраба- разрабатываются приборы, основанные на других физических принципах. Но и в них используется тот же растровый способ создания изображения. В портативных компьютерах используют- используются .жидко-кристаллические дисплеи {LEDs — liquid-crystal displays). Такие приборы также требуют регенерации изображения, а устройства создания "твердых" копий на бумажных носителях хотя и не требуют регенерации, но используют все тот же растровый принцип разложения изображения. 32 Глава 1. Графические системы и модели
1.2.3. Устройства ввода В большинстве графических систем в качестве хотя бы одного из возможных устройств ввода используется обычная алфавитно-цифровая клавиатура. Но более специфическими уст- устройствами, предназначенными для ввода именно графической информации, являются мышь, джойстик (joystick) и планшет. Каждое из этих устройств способно передавать в систему ин- информацию о положении и каждое оснащено, как минимум, парой кнопок, формирующих управляющие сигналы. По отношению ко всем подобным устройствам применяется термин устройство указания {pointing devices). Мы рассмотрим подробно их конструкцию и харак- характеристики в главе 3. 1.3. Изображение: физическое и синтезируемое При изучении дисциплины "компьютерная графика" специалисты по педагогике рекомен- рекомендуют начинать с обсуждения методов формирования растрового изображения простых двух- двухмерных геометрических примитивов (например, точек, отрезков прямых и многоугольников) в буфере кадра. Такой подход хорош, если задаться целью научить студентов формировать простые штриховые изображения, например чертежи. Но для работы с современными графи- графическими системами нужно в первую очередь иметь представление о том, какими возможно- возможностями обладают применяемые в них аппаратные и программные средства в части создания реалистических изображений трехмерных объектов. Эта задача предполагает знакомство с физическими принципами формирования изображений, основанными на законах, описываю- описывающих взаимодействие света с веществом. Поэтому я предпочитаю начинать изложение курса компьютерной графики именно с таких основополагающих принципов. Изображение, формируемое компьютером, по самой своей природе является искусственно синтезируемым, поскольку зачастую оно не существует физически. Но при этом используют- используются те же физические законы, которые действуют и в отношении изображений физических объектов, воспринимаемых зрением человека. Поэтому я и начну изложение с рассмотрения принципов создания изображений в оптических системах, таких как фотокамера или глаз че- человека. Мы сформируем модель процесса формирования изображения, которая в дальнейшем позволит воспроизвести этот процесс программными средствами. В этой главе я буду использовать математику только там, где без нее совершенно невоз- невозможно обойтись. Главное для нас сейчас — познакомиться с принципами создания изобра- изображения и представить себе архитектуру вычислительной системы, которая могла бы их реали- реализовать. Все детали процесса, как математические, так и технические, будут изложены в по- последующих главах. 1.3.1. Объекты и наблюдатели Нам выпало жить в мире трехмерных объектов. Обычно мы характеризуем положение не- некоторой точки на объекте в терминах подходящей относительной системы координат. Мы можем измерять расстояние между точками и таким образом расстояние между объектами. Именно желание концептуально систематизировать такие простые идеи, как измерение раз- размеров и расстояний, и привело к развитию многих областей математики, в частности геомет- геометрии и тригонометрии. Часто мы стараемся представить свое представление о пространствен- пространственных отношениях между объектами в форме чертежей или изображений, например карт, тех- технических чертежей, рисунков или фотографий. Точно так же изобретение многих физических приборов, включая фотокамеры, микроскопы и телескопы, было тесно связано с желанием визуализировать пространственные отношения между объектами. Следовательно, существует фундаментальная связь между физикой и математикой описания процесса формирования 7.5. Изображение: физическое и синтезируемое 33
изображения. Именно эту связь мы и будем использовать в процессе разработки методов соз- создания изображений с помощью компьютеров. В любом процессе формирования изображений — физическом или формальном (математическом) — присутствуют две сущности: объект и наблюдатель. Объект сущест- существует в пространстве независимо от процесса создания изображения и соответственно от наблюдателя. В компьютерной графике мы будем иметь дело не с реальным физическим объектом, а с воображаемым, синтезированным компьютерной программой. Такой вооб- воображаемый объект формируется через спецификацию положения в пространстве разнооб- разнообразных геометрических примитивов — точек, отрезков прямых или многоугольников. В большинстве графических систем для описания или аппроксимации объектов оказывается достаточно множества описаний точек в пространстве, или вершин (vertices). Например, отрезок прямой характеризуется двумя вершинами, многоугольник — упорядоченным спи- списком вершин, а сфера— двумя вершинами, одна из которых соответствует центру, а дру- другая — любой точке на поверхности сферы. Одна из главных функций САПР — обеспечить пользователю возможность формировать такую синтетическую модель проектируемого из- изделия и окружающей это изделие среды. В главе 2 будет показано, как формировать моде- модели простых объектов с помощью OpenGL, а в главе 8 — как описать пространственные от- отношения между объектами. Любая система отображения должна обладать средствами формирования изображений наблюдаемых объектов. Для этого нам понадобится некто (или нечто), "рассматривающий" эти объекты. Этим некто или нечто может быть человек, фотокамера или дигитайзер. Именно наблюдатель формирует изображение объектов. Если наблюдателем является человек, то изображение формируется на сетчатке глаза, а в фотокамере — на поверхности фотопленки. Часто понятие объекта и его изображения смешиваются. Мы обычно смотрим на объект с определенной точки и забываем, что другие наблюдатели, рассматривая его из другой точки, могут получить совершенно другое изображение того же самого объекта. На рис. 1.5,а пока- показано изображение сцены, как ее видит наблюдатель А. В этой сцене присутствуют два других наблюдателя — В и С. Для наблюдателя А они являются объектами сцены. На рис. 1.5,6,в по- показано, как увидят эту же сцену наблюдатели В и С. 6) в) Рис. 1.5. Как видят изображение одной и топ же сцены три разных наблюдателя: а — наблюдатель А; б — наблюдатель В; в — на- наблюдатель С На рис. 1.6 показано, как формируется изображение здания на пленке фотокамеры. Хотя и наблюдатель, и наблюдаемый объект существуют в одном и том же трехмерном мире, соз- созданное на плоскости пленки изображение является двухмерным. Суть процесса формирова- формирования изображения и состоит в том, чтобы, зная положение (и характеристики) наблюдателя и положение объекта, описать получаемое при этом двухмерное изображение. Именно это мы и рассмотрим детально в последующих разделах. 34 Глава 1. Графические системы и модели
Рис. 1.6. Создание изображения в фотокамере 1.3.2. Свет и изображение В упрощенном описании процесса формирования изображения были опущены многие важные детали. В частности, ничего не было сказано об источниках света и об их влиянии на создание изображения. Совершенно очевидно, что без источника света объект просто- напросто погрузится в "тьму кромешную" и ни о каком его изображении просто не может быть речи (хотя сам объект и будет реально существовать). Точно так же мы ничего не сказа- сказали и о цвете или о том, как влияет на изображение объекта текстура его поверхности. Анализ физики процесса начнем с простейшего случая, представленного схематически на рис. 1.7. На нем показана сцена, в которой присутствуют физический объект, наблюдатель (фотокамера) и источник света. Свет, излучаемый источником, по-разному взаимодействует с разными участками поверхности объекта, и часть отраженной световой энергии попадает в объектив фотокамеры. Количество энергии, попавшей в фотокамеру, зависит от характера взаимодействия между падающим светом и поверхностью (точнее, разными участками поверхности). Думаю, читателям известно, что свет— это элек- электромагнитное излучение в определенном диапазоне длин волн или частот.2 В полном спектре электромаг- электромагнитного излучения (рис. 1.8) диапазон видимого света располагается между диапазонами радиоволн и инфра- инфракрасного (теплового) излучения. Видимый свет имеет длину волны в диапазоне от 350 до 780 нанометров (нм). Каждый источник света характеризуется цветом, т.е. спектральным составом излучения. Например, лазер формирует монохроматическое излучение, т.е. излуче- излучение одной длины волны, а в излучении, формируемом лампой накаливания, присутствует множество состав- составляющих частот. В компьютерной графике, к счастью, практически никогда не приходится иметь дело с волновой природой света. Вместо этого мы будем следовать традиционному подходу, который вполне корректен при достаточно высоком уровне интенсивности света, когда его волновая природа не является суще- существенным фактором. В геометрической оптике принято моделировать источники света в пред- Рис. /. 7. Фотокамера и источник света ' Соотношение между частотой f и длиной волны Я имеет вид fA = с, где с — скорость света. 1.3. Изображение: физическое и синтезируемое 35
положении, что интенсивность излучаемой ими световой энергии постоянна. Свет распростра- распространяется в однородной среде по прямой от источников к тем объектам, с которыми он взаимодей- взаимодействует. Идеальный точечный источник света излучает из заданной точки в пространстве свет равной интенсивности во всех направлениях. Реальные источники имеют конечные геометриче- геометрические размеры излучающей поверхности и характеризуются пространственной диаграммой ин- интенсивности, поскольку предположение о равномерности излучения по всем направлениям в та- таком случае уже не отражает физики процесса. Кроме того, диаграмма интенсивности может из- изменяться для разных длин волн. Пока что мы будем рассматривать только точечные источники света, а моделирование реальных источников отложим до главы 6. Радиоволны | Видимый свет i Инфракрасное (тепловое) излучение 350 A(nm) 780 Рис. 1.8. Спектр электромагнитных волн Другая существенная характеристика источника света— спектр излучения. В этой главе мы рассмотрим только источники монохроматического излучения, которые имеют единст- единственную спектральную составляющую. Поскольку в отношении разных световых частот мож- можно применять принцип суперпозиции, это предположение не влияет на корректность полу- полученных результатов и позволяет распространить их на полихромные источники. Но такое предположение позволит нам сейчас ограничиться только одной характеристикой источни- источника — его светимостью (brightness), в то время как при работе с полихромными источниками нужно вводить в рассмотрение еще и параметр цветовой тон (hue). Здесь есть определенная аналогия с отличием черно-белого телевидения от цветного. 1.3.3. Трассировка лучей Начнем создание модели процесса формирования изображения с исследования траекто- траектории световых лучей, испускаемых источником. Рассмотрим сцену, ..редставленную на рис. 1.9, в которой присутствует только один точечный источник света. Наблюдатель в сцене нужен по той простой причине, что нас, в конце концов, интересует свет, который достигнет глаз наблюдателя. Наблюдателем может быть и фотокамера, как показано на рис. 1.10. Луч представляет собой прямую, ограниченную с одной стороны, т.е. исходящую из определен- определенной точки и уходящую в бесконечность. Поскольку свет распространяется по прямой, то его можно анализировать в терминах геометрических лучей, исходящих из точечного источника во всех направлениях. Часть этих лучей и вносит свой вклад в образование изображения на пленке в фотокамере. Например, если источник видим из камеры, то некоторые лучи прямо попадают через объектив на пленку. Большая часть испускаемых источником лучей уходит в бесконечность, не попадая прямо ни в объектив камеры, ни на один из "пассивных" объектов сцены. Эти лучи не вносят никакого вклада в создание изображения, хотя их и могут видеть другие наблюдатели. Оставшиеся лучи падают на объекты сцены и освещают их. Лучи, па- падающие на объект, могут взаимодействовать с ним по-разному. Например, если поверхность объекта зеркальная, то отраженные лучи могут (в зависимости от ориентации поверхности) 36 Глава 1. Графические системы и модели
достичь объектива и внести определенный вклад в изображение. Другие поверхности, кото- которые называются рассеивающими, или диффузными (diffuse surfaces), рассеивают падающий на них свет во всех направлениях. Если поверхность прозрачная, то световой луч от источни- источника может пройти через нее, но при этом возникает его преломление (или рефракция) и час- частичное отражение. В результате преломленный и отраженный лучи потом могут взаимодей- взаимодействовать с другими объектами и, возможно, попасть в объектив камеры. Как обычно, большая часть этих лучей потом уходит в бесконечность. На рис. 1.10 представлены шесть вариантов взаимодействия лучей, испускаемых источником, с окружающей средой. Рис. 1.9. Сцена с единственным точечным источником света Рис. 1.10. Трассировка лучей. Луч А попадает непосредственно в объектив камеры. Луч В уходит в бесконечность, не встретив на своем пути никаких препятствий. Луч С отразкается от зеркальной поверхности и попадает в объектив камеры. Луч D попадает на диффузную по- поверхность, и в результате образуется бесконечное множество других лучей, распростра- распространяющихся во всех направлениях, часть из которых, вполне вероятно, и попадет в объектив камеры. Луч Е попадает на частично прозрачную поверхность и в результате расщепляется на преломленный луч и отраженный. Луч F, отраженный от зеркальной поверхности, попа- попадает на другой объект, который может его поглотить 1.3. Изображение: физическое и синтезируемое 37
Трассировка лучей — это метод моделирования процесса формирования изображения, осно- основанный на анализе описанных оптических явлений. Этот метод в последнее время все шире применяется в компьютерной графике. Метод трассировки световых лучей можно применять для моделирования оптических эффектов в сколь угодно сложной среде — все зависит только от производительности программы и компьютера. Хотя эта модель и достаточно точно описы- описывает реальные физические процессы, соответствующие алгоритмы требуют довольно большого объема вычислений при реализации на компьютере. Можно, однако, в дальнейшем упростить модель, что облегчит ее алгоритмическую реализацию. С физической точки зрения, если на- наблюдатель улавливает излучение, исходящее от некоторого объекта, для него не имеет значения, каким образом образовалось это излучение — в результате преобразования одного вида энергии в другой (физический источник света) или в результате отражения падающего света. Если, на- например, зеркало отражает свет от источника в объектив камеры, то для камеры оно является та- таким же источником. Если мы видим какой-либо объект, то он или освешен светом от какого- либо источника, или сам является источником, или и то и другое. Глядя на объект, мы не можем сказать категорически, какая комбинация этих вариантов имеет место в данной ситуации. Предположим, что все объекты имеют одинаковую яркость, — сценарий, который трудно реализовать в реальном мире, но который очень часто является вполне резонным при размеще- размещении источников в среде. С точки зрения наблюдателя красный треугольник имеет одинаковую яркость красного света в каждой точке, и его нельзя отличить от равномерно излучающего ис- источника того же цвета и формы. Задавшись таким предположением, можно освободиться от собственно физических источников света и с помощью простых тригонометрических методов "просчитать" изображение. В главе 6 будут рассмотрены модели, включающие сложные источ- источники света и учитывающие свойства материалов реальных объектов. В следующих разделах мы рассмотрим две физические системы создания изображения — глаз человека и камеру-обскуру (или камеру с точечным отверстием). Глаз человека— это исключительно сложная оптическая система, но она подчиняется тем же физическим зако- законам, что и прочие. Мы рассмотрим ее не только в качестве примера реальной оптической системы, но и потому, что правильное представление о ее характеристиках поможет в даль- дальнейшем анализировать возможности компьютерных графических систем. Камера-обскура — это одна из простейших оптических систем, анализ которой поможет уяснить принципы ра- работы и других, более сложных оптических приборов. Мы будем эмулировать характеристики такой камеры при построении модели процесса формирования изображения. 1.4. Глаз человека Хотя глаза и являются очень сложной биологической системой, они обладают всеми ком- компонентами искусственно созданных оптических систем, таких как фотокамера и микроскоп. Структура глаза схематически показана на рис. 1.11. Свет по- попадает в глаз через роговицу и хрусталик (своего рода линзу). Роговица— это прозрачная субстанция, предохраняющая хрусталик. Радужная оболочка глаза играет роль диафрагмы, регулируя количество пропускаемого внутрь света. Хрусталик формирует изображение на двухмерной поверхности сетчат- сетчатки — внутренней поверхности глазного яблока. Расположен- Расположенные на сетчатке фоторецепторные клетки — палочки и кол- колбочки — играют роль приемников света, которые восприни- воспринимают электромагнитные колебания в диапазоне длин волн от Нерв ^^шшяяв^ 350 до 780 нм. _ . ,, „ Палочки являются высокочувствительными приемниками Рис 1.11. Строение глаза чело- „ _ ,. J _ г излучения и работают в условиях слабого освещения 38 Глава 1. Графические системы и модели
(ночью), а колбочки несут свою "вахту" при обычном, дневном освещении. Размеры палочек и колбочек в совокупности с оптическими свойствами хрусталика и роговицы определяют разрешающую способность глаза. Этот параметр характеризует способность глаза, как, впрочем, и любой другой оптической системы, различать мельчайшие видимые объекты. Ес- Если говорить техническим языком, то разрешающая способность характеризует минимальное расстояние между точками, различимое данной системой. Чувствительные клетки глаза неодинаково реагируют на электромагнитные колебания раз- разной длины волны. Существуют три типа колбочек и один тип палочек. В то время как интенсив- интенсивность есть мера энергии света, воздействующего на глаз, яркость — это мера восприятия этого воздействия. Глаз человека по-разному воспринимает монохроматический красный цвет и мо- монохроматический зеленый цвет равной интенсивности, поскольку чувствительность колбочек разного типа отличается. На рис. 1.12 приведена интегральная кривая спектральной чувстви- чувствительности глаза, известная как стандартная кривая Ме.ждународной светотехнической комис- комиссии (CIE — Commision Internationale de L'Eclairage). Из нее видно, что глаз наиболее чувствите- чувствителен к зеленому цвету, а наименее — к чисто красному и синему. Эта кривая очень близка к кри- кривым спектральной чувствительности светоприемников камер черно-белого телевидения, и по ней сверяют свою продукцию изготовители черно-белых фотопленок. Яркость — это интегральная мера нашего восприятия поступившего света. Возможность цветового восприятия по- появляется у человека благодаря наличию "узкополосных" приемников излучения — колбочек трех типов. Каждый тип характеризуется своей кривой спектральной чувствительно- чувствительности, причем диапазон длин волн для каждого типа сущест- существенно уже полного спектра световых волн (рис. 1.13). Мак- Максимум чувствительности на одной из этих кривых приходит- приходится на синий цвет, на другой — на зеленый, на третьей — на желтый.3 Одним из следствий раскрытия механизма цвето- восприятия глаза явилось то, что по этому же принципу вы- выделения основных, или первичных, цветов строятся и техни- технические системы приема цветных изображений — телевизи- телевизионные и фотографические. Эти первичные цвета можно использовать для приближенного представления любого "промежуточного" цвета. Обсуждение проблемы цветовосприятия и создания цветных изо- изображений мы отложим до главы 2. 350 Л(пт) 780 Рис. 1.12. Интегральная кривая спектральной чувстви- чувствительности глаза человека (стандартная кривая CIE) 6) в] О X X Рис. 1.13. Кривые чувствительности колбочек 3 Кривую, имеющую максимум интенсивности на длине волны желтого цвета, часто называют красной кривой. Это сделано только для терминологического единообразия с трехцветными системами в фотогра- фотографии и на телевидении. Главное же состоит в том, что во всех трехцветных системах — и технических, и биологических — используются три типа чувствительных элементов, обладающих цветовой избирательно- избирательностью. 1.4. Глаз человека 39
Хотя начальная фаза обработки изображения, появившегося на внутренней поверхности сетчатки глаза, во многом походит на то, что происходит в технических системах, далее все идет совершенно по-иному (по крайней мере, по сравнению с существующим на сегодняш- сегодняшний день техническими системами). Нервные клетки связаны с палочками и колбочками ис- исключительно сложным образом, и этот симбиоз в чем-то напоминает чрезвычайно сложный сигнальный процессор (или скорее, наоборот, создатели сигнальных процессоров пытаются воспроизвести то, что происходит в зрительной системе человека). Окончательная обработка происходит в коре головного мозга, где выполняются такие сложные функции, как распозна- распознавание образов. Мы не будем вдаваться в обсуждение этих вопросов, поскольку они далеко выходят за рамки проблематики компьютерной графики. В дальнейшем нас будет интересо- интересовать только восприятие изображения на уровне первичных сенсоров — палочек и колбочек. 1.5. Камера-обскура Камера-обскура, схематически показанная на рис. 1.14, позволяет простейшим образом описать процесс формирования изображения с помощью геометрической модели. Она пред- представляет собой светонепроницаемый ящик, в центре одной из стенок которого "проколото" малюсенькое отверстие (в идеале это должна быть геометрическая точка, имеющая беско- бесконечно малый размерL. На внутренней стороне противоположной стенки ящика закрепляется фоточувствительная пластина. Совместим с этим отверстием начало прямоугольной системы координат. Ось - этой системы координат направим нормально к плоскости светочувстви- светочувствительной пластины. Обозначим через d расстояние по нормали между отверстием и светочув- светочувствительной пластиной. Рассматривая вид камеры сбоку (рис. 1.15), несложно вычислить ко- координаты (х, у, г) точки пересечения светового луча, прошедшего через отверстие, с плоско- плоскостью z = -d светочувствительной пластины. Используя подобие двух треугольников на рис. 1.15, получим, что световой луч, исходящий из точки с координатой у на объекте, попа- попадет на световую пластину в точке с координатой ур\ У,=~ У z/d Анализируя таким же образом вид камеры сверху, получим и значение координаты: х =—¦ z/d Рис. 1.14. Камера-обскура 4 Отсюда и название этого прибора на английском языке — pinhole camera, т.е. камера с отверстием, проколотым иглой. — Прим. перев. 40 Глава 1. Графические системы и модели
Точка (xf>, ур, -d) называется проекцией точки (х, у, г). В нашей идеализированной модели цвет точки на плоскости светочувствительной пластины будет таким же, как и цвет точки {х, у, z). Поле, или угол зрения, камеры — это угол, характеризующий наибольший объект, изображение которого умещается на светочувствительной пластине, полностью занимающей заднюю стенку камеры. Этот параметр можно вычислить на основании геометрической мо- модели, изображенной на рис. 1.1 б5. Если обозначить через h высоту светочувствительной пла- пластины, то угол зрения 6 будет равен 9 = 2 arctg—. 2d (УР< - ) t 1 — -. 2i» Рис. 1.15. Камера-обскура (вид сбоку) Рис. 1.16. Угол зрения камеры-обскуры Идеальная камера-обскура будет иметь бесконечную глубину резкости, т.е. все видимые точки изображения будут в фокусе. Изображением точки будет опять же точка. Камера- обскура имеет два недостатка. Во-первых, бесконечно малый размер отверстия (в идеале че- через это отверстие должен пройти только один луч, исходящий из некоторой точки на объек- объекте) приводит к тому, что на светочувствительную пластину попадает очень мало света. Во- вторых, угол зрения камеры определенного размера никак не может быть изменен. В принципе, современный фотоаппарат отличается от камеры-обскуры только тем, что вместо точечного отверстия в нем используется система линз (объектив). Но это позволяет устранить оба отмеченных недостатка. Во-первых, объектив позволяет "собрать" значительно больше света — чем больше апертура объектива, тем больше световой энергии проходит через него. Во-вторых, угол зрения фотокамеры определяется фокусным расстоянием используемого объектива, а его можно изменять, меняя объективы либо применяя объектив с регулируемым фокусным расстояни- расстоянием. Современные объективы позволяют увеличить угол зрения почти до 180°. Но, к сожалению (а фотохудожники говорят "к счастью"), объективы не обеспечивают бесконечной глубины резкости. В данной главе мы будем рассматривать только идеальную модель камеры-обскуры, для которой фокусным расстоянием является расстояние d от передней до задней стенки камеры. В компьютерной графике также в большинстве случаев не моделируется эффект ограничен- ограниченной глубины резкости. 1.6. Моделирование камеры Рассматриваемые модели оптических систем формирования изображения непосредствен- непосредственно подводят нас к фундаментальным концепциям современной трехмерной компьютерной графики. Посмотрим, каким образом можно алгоритмически синтезировать изображение, по- ¦ Если рассматривать эту задачу в трехмерной, а не в двухмерной постановке, то следует вместо пара- параметра h подставить размер светочувствительной пластины по диагонали. См. упр. 1 в конце данной главы. 1.6. Моделирование камеры 41
добное тому, которое реально создается в оптической системе. Эта парадигма получила в ли- литературе название модель синтезированной камеры {synthetic-camera model). Рассмотрим систему формирования изображения, представленную на рис. 1.17. На ней мы снова видим объект и наблюдателя. В данном случае наблюдателем является камера с регулируемым рас- расстоянием между объективом и плоскостью светоприемника (пластины или пленки). Наша цель — эмулировать процесс создания изображения на плоскости светоприемника. Рис. 1.17. Система формирования изо- изображения Во-первых, отметим, что спецификация (формальное описание) объекта не зависит от спецификации наблюдателя. Следовательно, при организации графической библиотеки мож- можно отделить функции описания объекта от функций описания наблюдателя. Во-вторых, параметры изображения можно вычислить, используя простейшие тригоно- тригонометрические соотношения. Рассмотрим плоскость светоприемника камеры и объект (рис. 1.18). Схема в левой части рисунка абсолютно аналогична той, которую мы рассматри- рассматривали при анализе камеры-обскуры. В правой части рисунка приведена схема, в которой плос- плоскость изображения передвинута и расположена перед объективом камеры, что соответствует размещению элементов сцены на рис. 1.19. Для построения изображения некоторой точки объекта мы проводим проецирующий луч от этой точки к центру объектива — центру про- проецирования (center of projection). Обратите внимание на то, что все проецирующие лучи схо- сходятся в центре проецирования. В нашей синтезированной камере плоскость светоприемника, которую мы передвинули и разместили перед объективом, называется картинной плоско- плоскостью, или плоскостью проекции {projection plane). Изображение точки размешается в точке пересечения проецирующего луча с картинной плоскостью. В главе 5 этот процесс рассмат- рассматривается подробно с использованием соответствующего математического аппарата. Камера о) б) Рис. 1.18. Схема формирования изображения: а — изображение формируется на задней поверхности камеры; б — изображение формируется перед объективом камеры 42 Глава 1. Графические системы и модели
Рис. 1.19. Синтезированная камера Следует принять во внимание и конечные размеры изображения. Как мы видели, из-за ог- ограниченности угла зрения камеры-обскуры на плоскость светоприемника попадает изображе- изображение отнюдь не всех объектов. В нашей синтезированной камере аналогичное ограничение можно смоделировать, разместив на плоскости проекции отсекающий прямоугольник (clipping rectangle), или отсекающую рамку (clipping window) (рис. 1.20). Этот прямоуголь- прямоугольник действительно играет роль рамки окна, через которое наблюдатель, находящийся в цен- центре проекции, смотрит на окружающий мир. Задав положение центра проекции, положение и ориентацию плоскости проекции и размеры отсекающего прямоугольника, можно однознач- однозначно определить, какие объекты появятся на сформированном изображении. а) 6) Рис. 1.20. Отсечение: а — исходное положение рамки; б смещена рамка 1.7. Интерфейс программиста Существует множество способов взаимодействия пользователя с графической систе- системой. В современных системах автоматизации проектирования пользователь создает изо- изображение, взаимодействуя с системой отображения посредством устройств ввода ин- информации, таких как мышь и клавиатура. В типичной системе построения чертежей (рис. 1.21) для представления возможных операций используются меню и панели инст- 1.7. Интерфейс программиста 43
рументов. Щелкая на тех или иных элементах графического интерфейса, пользователь активизирует соответствующие функции и формирует изображение, не прибегая к соб- собственно программированию. Рис. 1.21. Графический интерфейс программы вычерчивания Естественно, перед этим кто-то должен разработать программы этого приложения, и мно- многие из тех, кто держит в руках эту книгу, будут самостоятельно создавать такого рода про- программы (и даже получать от этого удовольствие), несмотря на наличие на рынке огромного количества программных продуктов аналогичного назначения. 1.7.1. Интерфейс прикладного программирования Интерфейс между прикладной программой и графической системой — это множество функций, которые в совокупности образуют графическую библиотеку. Спецификация этих функций и есть то, что мы называем интерфейсом прикладного программирования (API — application programmer's interface). Модель системы прикладного программирования показана схематически на рис. 1.22. Для программиста, занимающегося разработкой прикладной про- программы, существует только API, и он избавлен, таким образом, от необходимости вникать в подробности работы аппаратуры и программной реализации функций графической библиоте- библиотеки. С точки зрения прикладного программиста те функции, к которым он обращается через API, должны соответствовать концептуальной модели описания изображения. Основой для этого часто является описанная выше модель синтезированной камеры. Она используется во множестве разнообразных API, таких как OpenGL, PHIGS, Direct3D, VRML и JAVA-3D. Клавиатура Прикладная программа Мышь ЭЛТ-монитор Рис 1.22. Структура прикладной графической системы 44 Глава 1. Графические системы и модели
Следуя концепции модели синтезированной камеры, в составе API должны присутство- присутствовать функции, которые позволяли бы описывать: ¦ объекты; ¦ наблюдателя; ¦ источники света; ¦ свойства материалов объектов. Для описания объектов чаще всего используются массивы вершин. Для простых геомет- геометрических объектов — отрезков прямых, прямоугольников и многоугольников — существует достаточно очевидное соответствие между списком вершин и формой объектов, основанное на простых математических соотношениях. Более сложные объекты могут быть по-разному определены с помощью списка вершин. Например, круг можно определить тремя точками (вершинами) на окружности или центром и одной точкой на окружности. В большинстве API в распоряжение пользователя предоставляется практически один и тот же набор примитивов. Такие примитивы обычно довольно быстро отображаются аппа- аппаратными средствами. Типовой набор включает точки, отрезки прямых, многоугольники и иногда текст. В OpenGL примитивы описываются списком вершин. Ниже приведен фраг- фрагмент программного кода на OpenGL, в котором с помощью вызова пяти функций API постро- построен многоугольник с тремя вершинами: glBegin(GL_POLYGON); glVertex3f@.0, 0.0, 0.0); glVertex3f@.0, 1.0, 0.0); glVertex3f@.0, 0.0, 1.0); glEnd( ); Обращаю ваше внимание на то, что, добавив в список дополнительные вершины, можно описать произвольный многоугольник. Изменив параметр типа GL_POLYGON, можно на тех же самых вершинах построить иной примитив. Например, примитив типа GL LINE_STRIP соот- соответствует двум связанным отрезкам прямых, проходящих через те же точки, а примитив типа GL_POINTS сформирует изображения трех точек. Некоторые API позволяют пользователю работать напрямую с буфером кадра— считы- считывать и записывать коды засветки отдельных пикселей. Иногда в число примитивов включа- включаются отрезки кривых и участки поверхностей, хотя чаще такие объекты приходится аппрок- аппроксимировать более простыми примитивами, причем эта задача возлагается на саму приклад- прикладную программу. В OpenGL поддерживается доступ к отдельным пикселям буфера кадра, а также создание криволинейных отрезков и участков поверхностей. Описать наблюдателя или камеру можно разными способами. Доступные на сегодняшний день графические API отличаются как гибкостью, которую они обеспечивают при выборе па- параметров камеры, так и количеством имеющихся в распоряжении пользователя методов ее описания. Для камеры, представленной на рис. 1.23, существует четыре типа параметров, од- однозначно определяющих характеристики создаваемого ею изображения. 1. Положение камеры задается положением центра проекции (ЦП). 2. Ориентация. Расположив центр проекции в определенной точке пространства, можно совместить с ним начало локальной системы координат камеры и вращать ее относи- относительно осей этой системы координат, изменяя таким образом ориентацию объектива. 3. Фокусное расстояние объектива камеры фактически определяет размер изображения на плоскости проекции. 1.7. Интерфейс программиста 45
4. Плоскость светоприемника. Задняя стенка камеры имеет конечные размеры в высоту и ширину. Некоторые API позволяют настраивать ориентацию плоскости светоприем- светоприемника независимо от ориентации объектива. Такую спецификацию можно сформировать разными способами. Один из них состоит в том, что положение и ориентация камеры задаются набором координатных преобразований. Эти координатные преобразования используются для вычисления положения точек объектов, заданных вершинами, в системе координат камеры. Такой подход очень удобен и с точки зрения его программной реализации, и с точки зрения гибкости графической системы, по- поскольку позволяет довольно просто формировать различные виды объекта, "перемещая" ка- камеру. Именно такой подход мы и будем использовать в дальнейшем, начиная с главы 5. Необходимость настройки значений множества параметров также представляет определен- определенную сложность. Частично причины этой проблемы кроются в самой модели синтезированной камеры. В классических методах визуализации, применяемых, в частности, в архитектурном проектировании, акцент делается именно на взаимосвязи наблюдателя и среды, в то время как модель предполагает независимость наблюдателя и наблюдаемых объектов. Так, классическая двухточечная перспектива куба на рис. 1.24 формируется именно вследствие учета взаимосвязи наблюдателя и плоскостей граней куба (см. упр. 1.6). API OpenGL позволяет создавать преобра- преобразования самого разного вида и обеспечивает программисту возможность использовать некото- некоторые нетрадиционные функции. Рассмотрим, например, обращение к двум функциям: gluLookAt(cop_x, cop_y, cop_z, at_x, at_y, at_z,...)J glPerspective(field_of_view, . . .); Вызов первой функции ориентирует камеру в направлении указанной точки пространства, а вызов второй — устанавливает параметры объектива для формирования изображения с уче- учетом перспективы. Но ни одна из существующих графических библиотек, построенных на ос- основе концепции синтезированной камеры — OpenGL, PH1GS, VRML, — не имеет в своем со- составе функций для задания взаимосвязей между камерой и объектом. Рис. 1.23. Параметры спе- спецификации камеры Рис. 1.24. Двухточечная перспектива куба Источник света характеризуется положением, интенсивностью и цветом излучения и его направленностью. В составе большинства API имеются функции для задания такого рода па- параметров, причем в сцене может присутствовать несколько источников света с разными ха- характеристиками. Существуют и функции спецификации оптических свойств материалов по- поверхностей объектов. Эти функции вызываются на этапе создания моделей объектов. Набор программируемых свойств источников света и материалов определяется принятой в данном API моделью взаимодействия света с окружающей средой. Подробно такие модели рассмат- рассматриваются в главе 6. 46 Глава 1. Графические системы и модели
1.7.2. Парадигма "моделирование-тонирование" В процессе формирования изображения можно следовать и другому подходу: во многих САПР-приложениях и программах создания фотореалистических изображений, например для включения в кинофильм, моделирование сцены и ее тонирование (rendering) представляют со- собой две последовательные стадии процесса (рис. 1.25). Хотя решаемые задачи и не отличаются от тех, которые мы рассматривали выше, такая организация позволяет разделить программные и аппаратные средства реализации этих двух стадий. Например, рассмотрим процесс создания од- одного кадра мультипликационного фильма. Сначала определяются компоновка кадра и располо- расположение в нем персонажей и объектов фона. Этот процесс требует активного участия художника- мультипликатора, но на первом этапе воспроизводить все детали изображения, связанные с оп- оптическими эффектами, нет необходимости. Следовательно, этот этап предпочтительнее выпол- выполнять на специализированной рабочей станции, располагающей средствами активного диалога с пользователем. После того как сцена будет скомпонована, можно приступать к ее раскрашива- раскрашиванию. На этой стадии используется все многообразие средств моделирования оптических эффек- эффектов — освещения, фактуры поверхностей объектов и т.п. Этот этап значительно меньше связан с вмешательством пользователя, но требует больших объемов вычислений, а потому для его реализации имеет смысл использовать высокопроизводительные компьютеры. Не только аппа- аппаратные, но и программные средства реализации этих этапов разительно отличаются, а потому грамотное разделение процесса может существенно повысить его эффективность. Моделирование Промежуточный файл Закрашивание Рис. 1.25. Двухэтапный процесс "моделирование-то- "моделирование-тонирование" Связь между компонентами системы, которые отвечают за моделирование и тонирование, осуществляется с помощью промежуточного файла. В структуре такого файла предусматривается возможность хранения как описания объектов сцены, созданных в результате моделирования, так и источников света, информации о положении наблюдателя, свойствах материалов и т.п. Этот под- подход реализован в формате Renderman Interface, который позволяет передать информацию о модели в текстовом формате. У этого подхода есть и другое немалое достоинство — он позволяет оптими- оптимизировать структуру и функциональные возможности того компонента системы, который отвечает за моделирование, в соответствии со спецификой конкретного приложения. При этом второй ком- компонент может быть использован без изменений для совершенно разных графических приложений. Возможен и другой вариант, когда промежуточный файл может обрабатываться разными про- программами тонирования, которые имеют разные функциональные возможности и работают на раз- разных моделях компьютеров. В принципе, по крайней мере теоретически, при таком подходе можно вообще не использовать программу моделирования, а описать всю сцену в текстовом виде с помо- помощью обычного текстового редактора. Но, конечно, на практике удобнее использовать интерактив- интерактивные программы моделирования, в которых применяется все та же модель синтезированной камеры для создания изображений, хоть и не раскрашенных. 1.8. Архитектура графических систем В схеме, приведенной на рис. 1.22, по одну сторону API располагается прикладная про- программа, а по другую — комбинация программных и аппаратных средств, которая реапизует функции, специфицированные в API, и формирует изображение на экране. В течение послед- 1.8. Архитектура графических систем 47
них 40 лет было предложено множество вариантов структурной организации средств, реали- реализации функций графического API. В первых графических системах использовались вычислительные машины общего назна- назначения со стандартной архитектурой, предложенной еще фон Нейманом. В таких вычисли- вычислительных машинах был один процессор, который в каждый момент времени "исполнял" одну инструкцию. В простейшем виде такая структура представлена на рис. 1.26. В качестве уст- устройства отображения использовалась ЭЛТ с необходимым аппаратным обрамлением — все вместе это именовалось дисплеем. Дисплей формировал на экране отрезки прямых между двумя заданными точками или дуги по трем точкам. Главный (и единственный в такой систе- системе) компьютер выполнял прикладную программу и вычислял координаты характеристиче- характеристических точек примитивов в системе координат экрана. Эта информация передавалась в дисплей с достаточно высокой скоростью, чтобы избежать мелькания изображения на экране. В те не столь уж далекие времена скорость вычислений была настолько низкой, что вывод на экран даже пары сотен отрезков требовал использования практически всех вычислительных ресур- ресурсов машины. Главный компьютер Цифро-аналоговый преобразователь Рис. 1.26. Структура ранних графических систем 1.8.1. Дисплейные процессоры Первые попытки создания специализированных графических систем в первую очередь преследовали цель освободить главный компьютер от рутинных операций регенерации изо- изображения на экране дисплея. В состав дисплея включили "собственный" дисплейный процес- процессор (рис. 1.27), который взял на себя задачу регенерации. Дисплейный процессор чаще всего имел ту же архитектуру, что и процессор общего назначения, но дополнительно в его систему команд были включены и инструкции формирования графических примитивов. Главное пре- преимущество такой структурной организации всей системы состояло в том, что от главного компьютера требовалось только сформировать описание изображения и передать его в виде дисплейного файла в дисплейный процессор. Последний сохранял дисплейный файл в собст- собственной памяти и считывал его с частотой регенерации, освобождая таким образом главный компьютер от рутинной работы. Этот вариант архитектуры в общих чертах воспроизведен в современных графических системах со звучным названием "клиент/сервер", которые рас- рассмотрены в главе 3. Главный компьютер Рис. 1.27. Структура графической системы с дис- дисплейным процессором 48 Глава 1. Графические системы и модели
1.8.2. Конвейерная архитектура Совершенствование архитектуры графических систем шло параллельно с совершенство- совершенствованием аппаратного обеспечения рабочих станций. В обеих областях существенный прогресс наметился после появления на рынке специализированных интегральных микросхем сверх- сверхбольшой степени интеграции (СБИС). Кроме того, радикальное снижение стоимости инте- интегральных запоминающих схем привело к повсеместному переходу на растровый принцип создания изображения в системах компьютерной графики. С точки зрения задач компьютерной графики наибольшее значение имеет возможность реа- реализовать в специализированных СБИС конвейерный принцип обработки информации. Схема на рис. 1.28 иллюстрирует этот принцип на примере простейших арифметических вычислений. Рис. 1.28. Арифметический конвейер Эта простейшая конвейерная структура состоит из сумматора и умножителя. Пусть необхо- необходимо вычислить значения выражений вида а+(Ьхс), что требует одной операции сложения и од- одной операции умножения на каждое очередное выражение. Если вычисляется одно выражение, то время вычислений в такой структуре будет таким же, как и в единственном процессоре, ис- использующем электронику с теми же динамическими характеристиками. Но если нужно выпол- выполнить подряд несколько однотипных операций с разными значениям операндов a, b и с, то умно- умножитель, выполнив умножение пары операндов первого набора, передаст результат на сумматор, и пока последний будет выполнять сложение, успеет выполнить умножение второй пары опе- операндов. В результате время выполнения всей операции сократится вдвое (если, конечно, умно- умножитель и сумматор выполняют свою работу с одинаковой скоростью). Таким образом, инте- интегральная пропускная способность системы возрастает (в данном случае удваивается). По этому же принципу можно создать и конвейеры с более сложной структурой, которые позволяют еще больше повысить пропускную способность специализированного вычислителя. Естественно, применять подобный конвейер имеет смысл только в том случае, если необходимо многократно выполнять вычисления по одним и тем же формулам с разными данными. Но в за- задачах компьютерной графики мы имеем именно такой случай — нужно многократно обрабаты- обрабатывать по одним и тем же формулам список вершин, характеризующих отображаемые объекты. Предположим, что имеется множество вершин, определяющих графические примитивы, из которых формируется изображение. Поскольку все объекты представлены в терминах ко- координат положения точек в пространстве, можно рассматривать множество типов примити- примитивов и вершин как геометрические данные. Сложная сцена описывается тысячами, если не миллионами, вершин. Все их нужно обработать по одному алгоритму и в результате сформи- сформировать в буфере кадра описание растра. Если рассматривать этот процесс в терминах геомет- геометрических операций с исходными данными, то можно представить его в виде схемы рис. 1.29. На этой схеме показаны четыре основных этапа процесса: ¦ геометрическое преобразование; ¦ отсечение; ¦ проективное преобразование; ¦ растровое преобразование. 1.8. Архитектура графических систем 49
Вершины—»- Геометрическое 1^ Отсечение |—>¦":Пр™™^:Ц_^^?<Юр<*<»^Л^ п н преобразование! I преобразование!преобразование! Рис. 1.29. Структура геометрического конвейера В последующих разделах мы обсудим детали реализации каждого из этих этапов. Но главный вывод, который следует из всего изложенного, состоит в том, что если для всех объ- объектов сцены выполняются единообразные преобразования, то быстрее всего можно реализо- реализовать этот процесс с помощью специализированного процессора с конвейерной архитектурой. 1.8.3. Геометрические преобразования Большинство этапов обработки графической информации можно описать в форме гео- геометрических преобразований представления объектов сцены в разных системах координат. Например, рассматривая модель синтезированной камеры, приходим к выводу, что основная часть процесса визуализации представляет собой преобразование представления объектов из базовой (мировой) системы координат в систему координат камеры. Другой пример — стадия размещения изображения на поле экрана ЭЛТ или другого устройства вывода. Внутреннее представление геометрических объектов — будь то в системе координат камеры или в любой другой подходящей системе координат, используемой в графическом API, — должно быть преобразовано на этой стадии в представление в системе координат устройства отображения. Каждое такое преобразование можно представить в матричной форме, причем последова- последовательные преобразования выражаются перемножением {конкатенацией — concatenating) со- соответствующих матриц элементарных преобразований. В результате формируется матрица комплексного преобразования. В главе 4 будет подробно рассмотрен математический аппарат описания геометрических преобразований. Поскольку произведение двух матриц есть также матрица, этот математический метод является прекрасным кандидатом для реализации в форме конвейерных вычислений. Кроме того, поскольку матрицы преобразований в компью- компьютерной графике имеют небольшие размеры Dx4), сам процесс умножения матриц также можно распараллелить в блоке, который выполняет эту операцию. 1.8.4. Отсечение Вторая важная операция в графическом конвейере— отсечение (clipping). Необходи- Необходимость в ней возникает по той простой причине, что имеющиеся в нашем распоряжении сред- средства отображения сами по себе имеют конечные размеры. Сетчатка глаза человека имеет ог- ограниченный угол зрения примерно 90°, а наша синтезированная камера (как, впрочем, и ре- реальный фотоаппарат) — ограниченное поле "фотоприемника". В реальной фотокамере поле (угол) зрения можно настраивать, подбирая объективы с разным фокусным расстоянием (или перестраивая фокусное расстояние объектива-трансфокатора). Этот процесс в синтезируемой камере моделируется изменением размеров (а иногда и положения) отсекающей прямоуголь- прямоугольной рамки {clipping rectangle) на плоскости проекции (см. рис. 1.20). Объекты, проекция ко- которых попадает во внутреннюю область отсекающей рамки, "участвуют" в формировании изображения. Те объекты, проекции которых пересекают отсекающую рамку, будут частично видимы. Отсечение выполняется на разных этапах формирования изображения. Отсечение геомет- геометрических примитивов можно выполнить, анализируя только координаты, и, следовательно, этот процесс несложно встроить в геометрический конвейер. Сам процесс отсечения можно детализировать и разбить на последовательность элементарных операций, также поддающих- поддающихся распараллеливанию, а следовательно, совместимых с принципом конвейерной обработки (см. упр. 1.4 и 1.5). Эффективные алгоритмы отсечения будут рассмотрены в главе 7. 50 Глава 1. Графические системы и модели
1.8.5. Проективное преобразование Как правило, при обработке геометрической информации трехмерное описание объектов стараются сохранить как можно дольше по мере продвижения "по конвейеру". Но после стадий геометрических преобразований и отсечения неизбежно наступает момент, когда те объекты, которые попадают в поле видимости, нужно преобразовать из трехмерной формы в двухмер- двухмерную. Существует множество видов проективного преобразования, которые мы рассмотрим в главе 5. Некоторые из них позволяют использовать математический аппарат операций с матри- матрицами размером 4x4 и, следовательно, могут быть реализованы в том же самом конвейере. 1.8.6. Растровое преобразование Последний этап процесса — преобразование описания двухмерных объектов в коды засветки пикселей в буфере кадра. Как выполняется эта операция для стандартных примитивов — линий и многоугольников, — будет подробно рассмотрено в главе 7. Поскольку регенерация изображения выполняется аппаратно, этот процесс практически скрыт от прикладного программиста и можно считать, что последняя операция геометрического конвейера — это растровое преобразование. 1.8.7. Производительность работы геометрического конвейера В рассматриваемой структуре обработки геометрической информации используются опе- операции двух типов. На начальных стадиях выполняются операции со значениями координат вершин, представленными в форме чисел с плавающей точкой. Эти операции идеально под- подходят для реализации в конвейерной вычислительной структуре. Разработанный фирмой Sili- Silicon Graphics геометрический процессор на базе СБИС стал базовым элементом большинства графических рабочих станций. Для операций с 4х4-матрицами преобразования в нем исполь- используются специальные микросхемы арифметических расширителей, подобные Intel i860. В ре- результате программа перемножения двух матриц сводится к единственной инструкции. В гра- графических рабочих станциях и графических платах для профессиональных ПК используются специализированные СБИС, которые берут на себя выполнение большинства необходимых преобразований и выполняют их аппаратно с огромной скоростью. Хотя в подавляющем большинстве профессиональных графических рабочих станций ис- используется конвейерный принцип обработки, следует учитывать, что по мере добавления в конвейер новых операций время реакции системы увеличивается, и нужно найти оптимальное сочетание времени реакции и функциональных возможностей системы. Все стадии обработки после растрового преобразования требуют выполнения побитовых операций на уровне содержимого буфера кадра. Характер этих операций существенно отличает- отличается от характера вычислений с матрицами, выполняемых при геометрических и проективных преобразованиях. Чаще всего при работе с буфером кадра приходится выполнять перемещение содержимого участка памяти. Общая производительность системы определяется скоростью "перемещения" геометрических примитивов по конвейеру и количеством пикселей, которые могут быть изменены в буфере кадра за определенное время. Поэтому наиболее производитель- производительные графические рабочие станции используют СБИС с конвейерной архитектурой для выполне- выполнения преобразований трехмерных и двухмерных геометрических объектов и параллельные бито- битовые процессоры на конечной стадии обработки изображения в растровой форме. Конвейерная архитектура занимает доминирующее положение среди существующих на сегодняшний день структур аппаратных средств графических систем, в особенности тех из них, которые должны формировать динамические изображения в реальном масштабе време- времени. Но следует отметить, что сам по себе принцип распараллеливания вычислений годится и для организации программного обеспечения при реализации высокопроизводительных гра- графических пакетов API. Причем в обоих случаях — аппаратной и программной реализации — используются возможности, предоставляемые все той же моделью синтезированной камеры. 1.8. Архитектура графических систем 51
1.9. Резюме В этой главе описаны основные стадии разработки графических приложений, как они представляются при использовании нисходящего подхода к проектированию и обучению. Имея перед собой общую картину, читатель сможет приступить к разработке простейших графических программ уже при изучении материала следующей главы. Мы делаем акцент на том, что в компьютерной графике главную роль играют методы, ос- основанные на тех же физических принципах формирования изображения, которые действуют и в реальном мире, в частности в оптических системах, таких как фотокамера или глаз чело- человека. В главе 2 будут рассмотрены вопросы ввода цвета в изображение, и вы увидите, что по- понимание основных принципов функционирования зрительной системы человека позволяет искусственно создавать изображения, воспринимаемые наблюдателем как реальные. В этой главе мы рассмотрели в общих чертах три различные парадигмы формирования изображения с помощью компьютера. Описанная модель синтезируемой камеры имеет два очень важных для практики следствия. Во-первых, она предполагает независимость изобра- изображаемых объектов и наблюдателя, что влияет на способ организации библиотеки графических функций. Во-вторых, описанная модель открывает путь к использованию конвейерных прин- принципов построения системы компьютерной графики. В этой главе мы также познакомили читателей с идеей трассировки лучей при построении изображения сцены. Эта парадигма играет существенную роль в понимании процессов взаимо- взаимодействия света и материала, на которой основан процесс формирования физического изображе- изображения. Метод трассировки лучей требует альтернативного варианта организации системы компь- компьютерной графики, и мы попытались сделать набросок такого варианта системы. Однако уже по- поверхностный анализ показывает, что на этом пути нас ожидают весьма серьезные трудности, поскольку реально только малая часть всех лучей участвует в формировании изображений, а время, ушедшее на анализ большей их части, оказывается потраченным впустую. В главе 6 мы вернемся к этому методу и расскажем о способах повышения его эффективности. Далее в этой главе было показано, какое важное значение имеет парадигма "моделиро- "моделирование-тонирование". В современной графической системе с конвейерной организацией в 1 секунду формируются миллионы отрезков прямых или многоугольников с разрешением до 1280x1024 пикселей. Такая система успевает выполнять в реальном масштабе времени удале- удаление невидимых поверхностей и тонирование примитивов, но в последней процедуре прихо- приходится использовать упрощенные алгоритмы, поскольку на большее не хватает производи- производительности аппаратных средств. Если стоит цель сформировать картинку, не уступающую по качеству той, которая получается при съемке кинофильма "на натуре", необходимо добиться разрешения порядка 4000x6000 пикселей и учитывать характеристики источников света и материалов отображаемых объектов, причем все расчеты должны выполняться с такой ско- скоростью, чтобы воспроизводить динамическую сцену в реальном масштабе времени. Несмотря на непрерывное возрастание производительности аппаратных и программных средств, ис- используемых в системах компьютерной графики, принцип разделения фаз моделирования и тонирования будет и в будущем сохранять свою актуальность. В этой же главе мы затронули и аспекты реализации прикладной части системы. Было по- показано, что API OpenGL, поддерживаемый на большинстве существующих аппаратных и про- программных платформ, является мощным средством изучения принципов формирования изо- изображения и построения прикладных систем. 1.10. Рекомендуемая литература На сегодняшний день опубликовано множество прекрасных книг по компьютерной гра- графике. Первое место в этом ряду занимает книга Ньюмена (Newman) и Спрулла (Sproull) [New73J, в которой впервые с современных позиций была описана модель синтезированной 52 Глава 1. Графические системы и модели
камеры. В следующем десятилетии ни одна работа по компьютерной графике не обходилась без ссылки на книги Фоли (Foley) и соавторов [Fol90, Fol94]. Среди других книг, на которые я хотел бы обратить внимание читателей, работы Херна (Неагп) и Бейкера (Baker) [Hea94] и Хилла (Hill) [HU90]. В своей работе Фоли, так же, как, впрочем, и Херн с Бейкером, исполь- использовали API PHIGS, а материал книги Анджела [Ang90] базируется на API GKS. Я рекомендую всем читателям регулярно просматривать журналы Computer Graphics, ко- который ежеквартально выпускается SIGGRAPH (Группа по компьютерной графике в составе Ассоциации по вычислительной технике —Association for Computing Machinery's Special In- Interest Group on Graphics), IEEE Computer Graphics and Applications и Visual Computer. Летний выпуск Computer Graphics, как правило, представляет собой сборник трудов ежегодной кон- конференции SIGGRAPH и содержит сообщения о результатах самых последних исследований в области компьютерной графики. Особый интерес для новичков представляют выпускаемые SIGGRAPH учебные видеофильмы и заметки о специальных учебных курсах, рекомендуемых на этой конференции. Последние распространяются на компакт-дисках. Докторская диссертация А. Сазерленда (I. Sutherland) "Project Sketchpad", вышедшая за- затем отдельной книгой Sketchpad: A Man-Machine Graphical Communication System [Sut63J, явилась, возможно, тем ростком, из которого выросло дерево современной компьютерной графики. Именно Сазерленд первым осознал, какие широкие перспективы открывает воз- возможность взаимодействия пользователя с компьютером через систему отображения на осно- основе ЭЛТ. Видеофильм о его оригинальной работе до сих пор пользуется популярностью. В книгах Тафта (Tufte) [TuJ83, Tuf?0, Tuf97] описана история развития компьютерной графики. В статье Карлбома (Carlbom) и Пасьорека (Paciorek) [Car78] обсуждается связь ме- между классической теорией зрения, которая используется испокон веков в архитектуре, и ме- методами визуализации, используемыми в компьютерных приложениях. Существует огромное множество книг, описывающих зрительную систему человека. Кни- Книга Пратта (Pratt) [Pra78] посвящена принципам создания растровых изображений. Я бы сове- советовал также познакомиться с работами Гласснера (Glassner) [Gla95], Визецкого (Wyszecki) и Стайлса (Stiles) [Wys82] и Холла (Hall) [Hal89]. Упражнения 1.1. Фокусное расстояние линзы в фотокамере— это расстояние между центром линзы и той точкой, в которой сходятся параллельные лучи после их преломления линзой. В ка- камере-обскуре фокусное расстояние измеряется как расстояние между передней и задней стенками камеры. Размер кадра на 35-миллиметровой пленке примерно равен 24x36 мм. Будем считать, что глаз человека характеризуется углом зрения 90°. Каково должно быть фокусное расстояние линзы в фотокамере, использующей 35-миллиметровую пленку, чтобы изображение на ней было подобно тому, которое видит глаз человека? 1.2. В компьютерной графике криволинейные поверхности, например сферическая, ап- аппроксимируются объектами, состоящими из множества плоских многоугольников (многогранниками). Первым объектом такого рода являет тетраэдр, четыре грани которого являются треугольниками. Отыщите координаты всех его вершин, пола- полагая, что начало системы координат находится в центре аппроксимируемой сферы, а одна из вершин располагается на оси>>. Разработайте алгоритм, позволяющий полу- получать все более точную аппроксимацию сферической поверхности правильными многогранниками, используя последовательное деление граней тетраэдра. 1.3. Рассмотрите отсечение линейного отрезка, заданного крайними точками. Покажите, что, для того чтобы определить, рассекается ли отрезок прямоугольной рамкой, весь он видим или весь невидим, достаточно знать только положение его крайних точек. Упражнения 5 3
1.4. Покажите, что отсечение прямолинейного отрезка верхней стороной отсекающей рамки может быть выполнено независимо от отсечения прочими сторонами рамки. На основании полученных результатов покажите, как можно распараллелить про- процесс отсечения в системе с конвейерной архитектурой, имеющей четыре независи- независимо работающих блока. 1.5. Распространите методы, использованные при выполнении упр. 1.3 и 1.4, на трех- трехмерный случай. 1.6. Рассмотрите перспективные изображения куба, представленные на рис. 1.30. Изо- Изображение, представленное в левой части рисунка, называется одноточечной пер- перспективой (one-point perspective), поскольку параллельные линии — ребра верхней грани — на изображении пересекаются в одной точке схода (vanishing point). В правой части рисунка показана двухточечная перспектива (two-point perspective) того же куба. Подумайте над тем, какие отношения существуют между наблюдате- наблюдателем (например, камерой) и объектом в обоих случаях. Рис. 1.30. Перспективные изображения куба 1.7. Память, используемая для хранения растрового представления изображения, долж- должна иметь быстродействие, достаточное для того, чтобы выведенное на экран изо- изображение не мелькало. В типовой графической рабочей станции разрешение экрана составляет 1280x1024 пикселей. Если изображение регенерируется 72 раза в секун- секунду, каково должно быть быстродействие памяти буфера кадра? Иными словами, сколько времени должно занимать считывание из буфера кода светимости одного пикселя? Каково будет значение этого параметра для дисплея с разрешением 480x640 пикселей при частоте регенерации 60 Гц и чересстрочной развертке. 1.8. В течение одной секунды кинозрителю последовательно "предъявляется" 24 кадра кинофильма. В принципе, эта частота не позволяет избежать мелькания изображе- изображения на экране. Каким образом удается избежать мелькания в кинопроекторе? (Подсказка: зрителю кажется, что он видит 48 кадров в секунду.) 1.9. Подумайте над тем, как организовать двухмерный графический пакет API для опре- определенного класса приложений, например проектирования СБИС. Перечислите все примитивы и атрибуты, которые необходимо включить в состав такого API. 1.10. Можно придумать такую конструкцию цветной ЭЛТ, в которой будет только одна электронная пушка и соответственно отпадет необходимость в теневой маске. Единственный электронный луч будет включаться и выключаться в нужные момен- моменты времени и таким образом возбуждать свечение того люминофора, который ну- нужен. Почему управлять такой ЭЛТ сложнее, чем ЭЛТ с теневой маской? 1.11. В типовой ЭЛТ с теневой маской для получения изображения с плавными цветопе- реходами размер пикселя должен быть примерно втрое больше размера триады. Предположим, что монитор должен иметь разрешение 1280x1024 пикселей, диа- диаметр экрана ЭЛТ равен 50 см, а глубина ЭЛТ— 25 см. Рассчитайте расстояние ме- между отверстиями в теневой маске. 54 Глава 1. Графические системы и модели
ГЛАВА 2 Графическое программирование Используемый в этой книге подход к изложению проблематики компьютерной графики ориентирован на программирование, и, следуя ему, я стараюсь вовлечь читателя в процесс программирования как можно раньше. Поэтому в данной главе будет описан минимальный набор функций в составе графического API, который достаточен для програм- программирования довольно широкого круга задач двух- и трехмерной графики и поможет читателям усвоить основные концепции построения графических программ. Двухмерная графика в данной книге рассматривается как частный случай трехмерной графики. Это позволяет нам начать практическую работу еще до детального знакомства с концепциями построения изображений трехмерных объектов. Те программы работы с двух- двухмерными объектами, которые читатели создадут с нашей помощью в процессе изучения ма- материала этой главы, затем без всяких изменений можно будет включать и в трехмерную гра- графическую систему. Изучая материал данной главы, мы будем анализировать разные аспекты достаточно простой и в то же время весьма информативной задачи — построения так называемого узора Серпинского (Sierpinski gasket). На примере этой задачи будет показано, как с по- помощью довольно небольшого набора функций можно строить сложные узоры. Как и бы- было обещано ранее, в качестве графического API будет использоваться OpenGL, но обсу- обсуждение основных концепций выходит далеко за рамки этого конкретного пакета и при- ложимо к большинству существующих графических систем, в том числе PHIGS (Programmer's Hierarchical Graphics System) и GKS (Graphical Kernel System). Те функ- функциональные возможности, с которыми читатель познакомится в этой главе, вполне дос- достаточны для разработки довольно сложных программ построения изображения двухмер- двухмерных объектов, которые, правда, не оснащены средствами взаимодействия с пользовате- пользователем. В конце этой главы читатели познакомятся с простой программой построения изображения трехмерных объектов.
2.1. Узор Серпинского Для демонстрации принципов программирования задач двухмерной графики мы будем использовать в качестве примера узор Серпинского. История исследования этого узора дос- достаточно длинная, а сам узор представляет особый интерес в такой области, как геометрия фракталов. Узор Серпинского является случайным объектом, определенным рекурсивно, но в пределе его форма стремится к детерминированному объекту. Предположим, что в исходном состоянии у нас есть три точки на плоскости, причем их положение описано координатами в некоторой подходящей системе координат1: {хь у{), (х2, уг) и (х2, уз). Далее выполняется следующая процедура. 1. Случайно выбирается некоторая точка внутри треугольника, образованного заданными вершинами. 2. Случайно выбирается одна из трех вершин. 3. Выбирается точка, равноотстоящая от первой выбранной точки и от выбранной вершины. 4. Новая точка включается в изображение и помечается маркером, например кружком. 5. Исходная точка заменяется этой новой точкой. 6. Повторяется вся процедура, начиная с шага 2. Таким образом, каждая новая точка, созданная на шаге 3, вклю- включается в изображение и выводится на устройство отображения. Весь описанный процесс проиллюстрирован на рис. 2.1, где р0— исход- исходная точка (см. шаг 1), a pi и р2— две точки, сформированные в процессе двух последовательных циклов описанного алгоритма. Прежде чем приступить к разработке программы, весьма желатель- желательно представить себе, какой вид будет иметь изображение, полученное в результате применения этого алгоритма. Попробуйте нарисовать его Рис. 2.1. Формирование вручную на листе бумаге — результат вас скорее всего удивит. узора Серпинского Ниже представлен возможный вариант графической программы построения узора. main( ) { initialize_the_system(); for(some_number_of_points) { pt = generate_a_point(); display_the_point(pt); } cleanup(); } Хотя в окончательном виде наша программа на OpenGL будет иметь несколько другой вид, но останется такой же простой. Процесс разработки программы будет прослежен по- поэтапно. Сначала сконцентрируем внимание на двух основных этапах — формирование точки и вывод ее на экран. Нужно ответить на два вопроса: "Как представить точки в пространст- пространстве?" и "Следует ли использовать двух-, трехмерное или другое представление?" В главе 4 мы рассмотрим эту же задачу в более общей постановке с использованием координатных фреймов. 56 Глава 2. Графическое программирование
2.1.1. Перьевой плоттер Большинство графических систем первого поколения были двухмерными. В них исполь- использовалась концепция модели перьевого плоттера (pen-plotter modelJ. Такое устройство фор- формирует изображение на бумаге за счет перемещения пера, закрепленного на двух подвижных направляющих (рис. 2.2). Одна направляющая перемещается в продольном направлении (вдоль оси у), а другая — в попе- поперечном (вдоль оси х). Специальный механизм поднимает и опускает перо. В опущенном состоянии перо при перемеще- перемещении оставляет след на бумаге — этот след и формирует изо- изображение. Такого рода устройства и поныне используются в рцс 22 Перьевой тоттер графических системах при построении технических черте- чертежей. Для программного управления графопостроителями применяются разнообразные пакеты API — такие как LOGO, GKS и PostScript. Хотя эти па- пакеты и отличаются друг от друга функциональными возможностями, но во всех используется та же самая идея формирования на носителе следа подвижного пера, т.е. фактически воспро- воспроизведения процесса ручного вычерчивания с помощью карандаша. Пользователь при этом имеет дело с участком плоскости, ограниченным размерами планшета. Поведение такой графической системы описывается двумя основными графическими функциями: moveto(x,y); lineto(x,y); При выполнении функции moveto() перо приподымается и затем перемещается в точку с координатами (х, у), не оставляя следа на носителе (при этом траектория перемещения не имеет значения — главное, что заканчиваться она должна в точке с заданными координата- координатами). При выполнении функции lineto() перо опускается и затем перемещается по прямой из текущего положения в точку с заданными координатами (х,у), оставляя след на носителе. Ес- Если добавить к этим функциям еще и функции настройки (выбор пера подходящего цвета и толщины), то получим законченную двухмерную графическую систему. Ниже приведен фрагмент программы вычерчивания несложного изображения с помощью такой системы: moveto@, 0); lineto(l, 0); lineto(l, 1); lineto@, 1); lineto@, 0); Эта программа формирует изображение, представленное на рис. 2.3,а. Добавив в про- программу еще несколько операторов, получим при ее выполнении изображение куба в изомет- изометрической проекции (рис. 2.3,6). moveto@, 1); lineto@.5, 1.866); linetoA.5, 1.866); linetoA.5, 0.866); lineto(l, 0); moveto(l, 1); lineto(l,5, 1.866); 2B me не столь давние времена в русскоязычной литературе использовался термин "графопостро- "графопостроитель ". — Прим. перев. 2.1. Узор Серпинского 57
а) 6) Рис. 2.3. Результат выполнения простой программы вычерчивания: а — квадрат; б — изометрическая проекция куба По этому принципу построены некоторые приложения, например системы компоновки страниц в полиграфии. Язык описания страниц PostScript является стандартным средством управ- управления принтерами, в котором реализована эта же идея. Но нас значительно больше интересует изо- изображение трехмерных объектов. Модель перьево- перьевого плоттера не очень "вписывается" в процесс формирования изображений трехмерных объек- объектов. Если, например, использовать эту модель для построения изображения трехмерного объекта на двухмерном планшете (то ли вручную, то ли с по- помощью компьютера), то в первую очередь нужно решить, где на планшете должна располагаться двухмерная точка, соответствующая опреде- определенной точке на изображаемом трехмерном объекте. Такие двухмерные точки являются, как было показано в главе 1, проекциями точек трехмерного пространства. Математически про- процесс формирования проекций описывается тригонометрическими соотношениями. Мы про- проанализируем эти соотношения в главе 5, но предпочтительнее использовать такой пакет API, который позволял бы пользователю формулировать свою задачу в терминах трехмерного пространства и не выполнять в прикладной программе никаких тригонометрических преоб- преобразований. Все операции, связанные с проективным преобразованием, должны взять на себя функции API. Такой подход очень удобен для пользователей, поскольку избавляет их от не- необходимости создавать доморощенные программы выполнения таких преобразований в со- составе своей прикладной системы. Пакет API, созданный профессионалами, решит эту задачу лучше и эффективнее. Решение двухмерных задач, таких как построение узора Серпинского, можно рассматри- рассматривать как частный случай работы в трехмерном пространстве. С точки зрения математики двухмерную плоскость или простую двухмерную криволинейную поверхность можно считать подпространством трехмерного пространства. Следовательно, выражения, справедливые для трехмерного пространства, будут справедливы и для его подпространства. Для простоты будем считать планшет бесконечным и совместим его поверхность с плос- плоскостью z=0. В таком случае точки р=(дг, у, 0) на планшете можно рассматривать или как точки трехмерного пространства, или как точки р-(х,у) двухмерного подпространства — плоскости планшета. OpenGL, как и большинство других трехмерных графических систем, позволяет пользователю использовать любое из описанных представлений, причем внутреннее пред- представление не зависит от той формы "внешнего" представления, которую выбрал для себя пользователь. Точку можно представить разными способами, но самый простой — рассмат- рассматривать ее как триаду в трехмерном пространстве: Р = Сейчас мы не будем обращать внимание на то, в какой системе координат задан компонент р. В этой книге мы чаще всего будем использовать термин вершина (vertex), а не точка {point). Вершина характеризует определенное положение в пространстве, причем в компью- компьютерной графике используются двух-, трех- и четырехмерные пространства. С точки зрения графической системы вершина представляет собой атомарный графический объект, который соответствует простейшему геометрическому объекту — точке. Двумя вершинами определя- 58 Глава 2. Графическое программирование
ется отрезок прямой — второй по простоте графический объект, тремя — треугольник и ок- окружность, четырьмя — четырехугольник и т.д. В OpenGL имеется множество форм представления объектов, из которых пользователь может выбирать ту, которая наилучшим образом соответствует специфике определенной за- задачи. Обобщенная форма представления вершины в OpenGL имеет вид glVertex* Здесь * можно заменить двумя или тремя символами в формате nt или ntv, где п задает количество размерностей B, 3 или 4), t описывает тип данных: целый (integer) — i, с пла- плавающей точкой одинарной точности (float) —f или с плавающей точкой удвоенной точности (double) — d. Третий символ V, если он присутствует в определении вершины, означает, что переменные (значения координат) задаются указателем на массив, а не списком аргументов. В дальнейшем в каждом частном случае мы будем выбирать именно ту форму представления вершины, которая наиболее подходит для данного случая. Необходимую дополнительную информацию о формах представления объектов в OpenGL читатель найдет в руководстве пользователя OpenGL Reference Manual [Ope97b]. Еще раз обращаю ваше внимание на то, что при любом внешнем представлении объекта (зададите вы вершину как двухмерный объ- объект или как трехмерный) его внутреннее представление будет одним и тем же. В главе 4 мы рассмотрим четырехмерное внутреннее представление, но сейчас вам нет нужды беспокоить- беспокоиться об этих деталях реализации. При программировании на языке OpenGL мы часто будем пользоваться базовыми типами переменных — GLf loat и GLint, — а не основными типами переменных языка С, такими как float и int. Эти типы определены в соответствующем заголовочном файле с помощью ди- директивы #def ine, например, так: #define GLfloat float Использование специализированных типов переменных в OpenGL обеспечивает дополни- дополнительную гибкость, поскольку позволяет переопределять эти типы (например, заменить фор- формат числа с одинарной точностью на формат с удвоенной точностью) и изменять таким обра- образом точность вычислений, не изменяя текст прикладной программы. Вернемся к функции определения вершины. Форма glVertex2i(GLint xi, GLint yi) подходит для представления вершины на двухмерной плоскости, причем координатами яв- являются целые числа. Представление glVertex3f(GLfloat х, GLfloat у, GLfloat z) задает вершину в трехмерном пространстве, причем значения координат будут представлены вещественными числами одинарной точности с плавающей точкой. Можно использовать для задания координат и массив. Сначала нужно определить массив значений координат: GLfloat vertex[3]; а затем задать вершину в приведенном ниже формате: glVertex3fv(vertex); Набор вершин позволяет описать разнообразные геометрические объекты, причем для каждого вида объектов требуется определенное количество вершин. Язык OpenGL позволяет сгруппировать любое количество вершин в описании объекта с помощью пары связанных функций glBegin() и glEnd(). Аргумент функции glBegin() задает тип геометрического объекта, который определяется следующим далее набором вершин. Например, отрезок пря- прямой задается следующим фрагментом программы: 2.1. Узор Серпинского 59
glBegin(GL_LINES); glVertex2f(xl,yl); glVertex2f(x2,y2); glEnd(); Те же вершины позволяют определить и пару геометрических объектов — точек: glBegin(GL_POINTS); glVertex2f(xl,yl); glVertex2f(x2,y2); glEnd(); Теперь можно приступить и к разработке программы, формирующей узор Серпинского. Будем считать, что все точки узора должны располагаться внутри единичного квадрата, ле- левый нижний угол которого находится в точке @, 0), — это обычный вариант, который при желании легко можно изменить. Сначала задумайтесь над тем, как представлять геометрические данные в программе. При работе в трехмерном пространстве можно использовать базовое представление в форме трех раздельных переменных x,y,z, но многие программисты предпочитают подход, более близ- близкий в объектно-ориентированному, например использовать структурный тип point3 для трехмерных вершин и point2 — для двухмерных. Тогда в объектно-ориентированной про- программе можно было бы определить операции с переменными такого структурного типа вроде приведенной ниже: new_point = old_point + random_number Но ни язык С, ни язык OpenGL не располагают такими возможностями (по крайней мере на сегодняшний день). Для представления двухмерных точек в качестве компромисса между низкоуровневым представлением и абстракцией на высоком уровне будем использовать мас- массив из двух элементов: typedef GLfloat point2[2]; Ниже приведен текст функции display(), которая формирует 5000 точек узора после ка- каждого вызова. Массив вершин базового треугольника узора vertices[3] определен в самой функции display (} как массив элементов типа point2. void display(void) { point2 vertices[3] = {{0.0, 0.0}, {250.0. 500.0}, {500.0, 0.0}}; /* Произвольный треугольник */ static point2 p = {75.0 ,50.0}; /* Установка исходной точки */ int j, k; int rand(); /* Стандартный генератор случайных чисел */ for(k=0;k<5000;k++) { /* Случайным образом выбрать индекс вершины из множества 0,1,2 */ j=rand()%3; /* Вычисление новой точки */ р[0] = (р[0] + triangle[j][0])/2; 60 Глава 2. Графическое программирование
• triangle[j][l])/2; /* Вывод на экран новой точки */ glBegin(GL_POINTS); glVertex2fv(p); glEnd(); glFlush(); Функция rand() в этой программе — стандартный генератор случайных чисел. Для фор- формирования случайной последовательности, элементами которой могут быть только три целых числа 0, 1 и 2, используется операция вычисления остатка при целочисленном делении. При небольшом количестве итераций статистические характеристики генератора случайных чисел большого значения не имеют, а потому в программе можно использовать любой другой гене- генератор, а не только тот, который вызывается стандартной функцией rand (). Функция glFlush() обеспечивает вывод формируемых точек на экран сразу же по окон- окончании вычислений их координат. Если не обращаться к этой функции, точки все равно поя- появятся на экране, но при этом будет ощутима задержка вывода, особенно при наличии сетево- сетевого окружения. Эту программу в таком виде еще нельзя считать завершенной. На рис. 2.4 по- показано, как будет выглядеть сформированный ею узор. .р% •\ /г Л /.¦> i\ Ъ( Д ?* Л^. -Vf :f.4 /t А м л-лл-л-1 i.A4: А. с- Рис. 2.^. Узор Серпинского, сформированный из 5000 случайных точек Нам осталось разобраться с некоторыми нюансами формируемого узора и, соответствен- соответственно, программы: ¦ Как задать цвет узора? ¦ В какой части экрана появится изображение? ¦ Как управлять размером изображения? ¦ Как выделить область экрана — видовое окно — для формируемого изображения? 2.1. Узор Серпинского 61
¦ Какая часть нашего "бесконечного" планшета появится на экране? ¦ Как долго созданное изображение будет оставаться на экране? Хотя получить ответы на перечисленные вопросы и весьма важно, на первый взгляд ка- кажется, что они стоят как-то в стороне от рассматриваемых сейчас базовых концепций. Как вы увидите далее, основной текст тех фрагментов программы, которые мы разработаем в про- процессе поиска ответов на эти вопросы, практически без изменений войдет и в другие програм- программы формирования изображений. Следовательно, затратив сейчас некоторое время на поиск ответов, мы будем вознаграждены в дальнейшем. 2.1.2. Системы координат Сейчас вы скорее всего гадаете, каким образом можно интерпретировать сформирован- сформированные программой значения координат — переменных х, у и z — в спецификации вершин. В каких линейных единицах они выражены — футах, метрах, микронах? Где находится точка отсчета, от которой откладываются соответствующие смещения вдоль осей? В любом случае простейший ответ будет таков — все зависит от программиста, т.е. от вас. При работе с ранними системами компьютерной графики требовалось, чтобы в приклад- прикладной программе вся геометрическая информация задавалась в терминах системы координат экрана используемого устройства отображения3. Если бы такой способ описания распростра- распространялся и на современные прикладные программы высокого уровня, нам пришлось бы говорить о точках в терминах пикселей экрана или сантиметров от угла экрана. Но этот метод кажется совершенно абсурдным для описания изображений, в которых расстояния измеряются в све- световых годах (при отображении данных, с которыми имеет дело астрономия), или таких, где расстояния измеряются микронами (при выводе изображений топологии интегральных мик- микросхем). Одно из достоинств современных графических программ — возможность использо- использования в них любых единиц измерения, которые сочтет необходимым выбрать пользователь, руководствуясь только спецификой решаемой задачи. Концепция графики, инвариантной к устройствам {device-independent graphics), освободила прикладных программистов от необ- необходимости учета деталей конструкции устройств ввода-вывода графической информации. По отношению к применяемой пользователем системе координат используется термин мировая система координат {world coordinate system) или прикладная система координат {problem coordinate system). Пользователь может использовать любой диапазон значений координат, причем единственным (и очень незначительным) ограничением является диапазон представ- представления вещественных чисел в современных компьютерах в формате с плавающей точкой. Система отсчета положения на экране конкретного устройства отображения ранее назы- называлась координатами физического устройства {physical-device coordinates), или просто ко- координатами устройства {device coordinates). Для растровых устройств, каковыми являются подавляющее большинство современных дисплеев, мы используем термин координаты рас- растра {raster coordinates), или координаты экрана {screen coordinates). Координаты растра все- всегда представляются целыми числами, поскольку пиксели занимают совершенно определенное положение в узлах фиксированной сетки (или, если выразиться более профессиональным языком, пиксели по самой своей природе являются дискретными объектами и, следовательно, адресуются целыми числами). Между координатами некоторой точки в системе координат прикладной программы и в системе координат растра существует определенное соответствие, как показано на рис. 2.5. Выполнение преобразования из системы координат прикладной программы в координаты растра выполняется графической системой, и программист беспокоиться об этом не должен. 3 Даже в современных системах некоторые команды нижнего уровня требуют задания положения в дюймах, причем диапазон допустимых значений соответствует реапьным размерам экрана. 62 Глава 2. Графическое программирование
Как будет показано в последующих разделах, в прикладной программе следует специфициро- специфицировать только несколько параметров, необходимых для выполнения такого преобразования, — область отображаемого мирового пространства и размер поля экрана. 1х у ) 1 min' ' min' Координаты прикладной Координаты растра программы Рис. 2.5. Отношение между координатами при- прикладной программы и координатами растра 2.2. Прикладной интерфейс OpenGL Поскольку функцию, выполняющую основной алгоритм построения узора, мы уже разработали, то теперь рассмотрим вспомогательные операции, которые позволяют управлять видом объектов на экране. Нам понадобится также управлять ходом выполне- выполнения программы и взаимодействовать с операционной системой. Сначала более детально рассмотрим API OpenGL — программный инструмент, которым нам предстоит пользо- пользоваться. Поскольку внутреннее представление вершин не зависит от того, определили мы их как двух- или трехмерные объекты, то все, что будет изложено в этом разделе, в рав- равной степени относится и к трехмерным задачам графики. Естественно, работа в трех- трехмерном пространстве предоставляет прикладному программисту гораздо более широкие возможности, но ведь мы находимся только в самом начале пути, и у нас еще все впере- впереди. Основная задача этой главы — познакомить читателей с методами спецификации графических примитивов, а о методах взаимодействия пользователя с системой мы по- поговорим позже, в главе 3. Структура OpenGL аналогична структуре большинства других графических API, в том числе PHIGS и GKS. Следовательно, все усилия, которые придется приложить при изуче- изучении OpenGL, будут оплачены сторицей, так как одновременно вы постигаете и возможно- возможности других аналогичных систем. По сравнению с другими API изучать OpenGL гораздо проще, но это не означает, что функциональные возможности его менее широкие, чем у "конкурентов". OpenGL поддерживает работу как с двухмерными, так и с трехмерными примитивами, что вы увидите в программах, представленных в главах 2-6. В этом пакете реализованы и современные технологии тонирования, что будет продемонстрировано в программах, описанных в главах 8-12. Поскольку основная цель этой книги — изучение методов решения задач, применяемых в компьютерной графике, то и OpenGL мы будем использовать именно в качестве вспомога- вспомогательного средства для достижения этой цели. Следовательно, вы не найдете в этой книге ис- исчерпывающего описания всех доступных функций OpenGL и многих деталей, но, тем не ме- менее, все представленные программы носят законченный вид. Более детальные сведения об источниках дополнительной информации, касающейся как OpenGL, так и других графиче- графических API, вы найдете в обзоре рекомендуемой литературы в конце главы. 2.2. Прикладной интерфейс OpenGL 63
2.2.1. Графические функции При знакомстве читателей с графическими пакетами мы будем использовать подход, ставший уже классическим в кибернетике, — концепцию черного ящика (black box). Этим термином в технике принято называть устройства и системы, функциональные возможности которых описываются только соответствием между определенной входной и выходной ин- информацией, а о внутреннем механизме работы никакой информации не имеется. Таким обра- образом, мы будем считать графическую систему подобным черным ящиком, входом которого являются различные функции, вызываемые из прикладной программы, информация, переда- передаваемая с помощью устройств ввода, таких как мышь или клавиатура, и сообщения, переда- передаваемые операционной системой. Выходом системы являются в первую очередь графические образы, формируемые на экране. На данном этапе входом для черного яшика будут только вызовы функций, а выходом — примитивы, отображаемые на экране ЭЛТ (рис. 2.6). Анало- Аналогичный подход можно использовать и при изучении других графических API. Вызовы функций Выход Прикладная I ^" Графическая I ^ Устройства I программа 1^ система |^ ввода-вывода [ Данные »»——««w—л Вход """'""" "*""г""" Рис. 2.6. Графическая система как черный ящик Описывать возможности API мы будем через функции его библиотеки. В состав мощного пакета API может входить несколько сотен функций, а потому желательно сразу же разделить их на категории. Мы рассмотрим функции шести категорий. ¦ Функции описания примитивов {primitive functions) определяют объекты нижнего уровня иерархии — примитивы, — которые способна отображать графическая систе- система. В большинстве графических API имеются такие примитивы, как точки, отрезки прямых линий, многоугольники, пиксели, текст и различного рода криволинейные от- отрезки и участки криволинейных поверхностей. ¦ Если с помощью примитивов определяется, что появится на экране, то с помощью ат- атрибутов определяется, как будут выглядеть отображаемые объекты, т.е. атрибуты оп- определяют способ вывода объектов на экран. Функции задания атрибутов (Attribute functions) позволяют прикладному программисту выполнять широкий круг операций настройки изображения — от выбора цвета до указания образца заливки внутренней области многоугольника или шрифта для надписей на графике. ¦ Нужно задать параметры используемой модели синтезированной камеры, с помощью которой создается изображение. Как было сказано в главе 1, от прикладного програм- программиста требуется выбрать положение и ориентацию камеры в мировой системе коорди- координат и параметры объектива, в частности фокусное расстояние. Зная эти параметры системы, он сможет не только правильно построить изображение, но и отсечь те объ- объекты, которые оказываются вне поля зрения. Последнее позволяет не тратить времени впустую. Функции визуализации (viewing functions) позволяют задать разнообразные виды, хотя разные типы API существенно отличаются возможностями манипулирова- манипулирования видами. ¦ Одна из наиболее интересных возможностей хорошего графического пакета — набор функций геометрических преобразований (transformation functions), которые позволя- позволяют пользователю выполнять различные преобразования объектов — поворот, плоско- плоскопараллельный перенос, масштабирование и т.п. Такого рода операции мы часто будем использовать при описании методов визуализации (глава 5) и моделирования (глава 8). 64 Глава 2. Графическое программирование
Для создания интерактивных приложений нужно, чтобы в составе API имелись и функции ввода графической информации {input functions). Эти функции играют роль промежуточного звена между устройствами ввода, такими как клавиатура, мышь, планшеты разного рода, дигитайзеры, и прикладной программой. В главе 3 читатели смогут познакомиться с функциями, использующими три разных режима ввода, и с различными устройствами ввода. В реальных приложениях программисту приходится думать и об управлении средой функционирования программы, особенно в случае, когда речь идет о многопроцессор- многопроцессорной системе, многооконной операционной среде или сетевой среде со множеством пользователей. Для управления процессом выполнения программы в состав API, как правило, включаются специальные управляющие функции (control functions). Они по- позволяют прикладной программе взаимодействовать с операционной системой, ини- инициализировать приложение и обрабатывать ошибки других графических функций. 2.2.2. Интерфейс OpenGL Имена функций OpenGL начинаются с букв gl , а сами функции хранятся в библиотеке, которую мы будем обозначать аббревиатурой GL. Помимо основной, существует и несколько дополнительных библиотек, которые мы также будем использовать в своей работе. Первая из них— библиотека графических утилит (GLU— graphics utility library). Функции этой биб- библиотеки обращаются только к функциям из GL и в ее состав входят функции формирования часто встречающихся сложных объектов вроде сферических поверхностей, которые пользо- пользователю не имеет смысл "изобретать" самостоятельно. Эта библиотека входит практически во все версии пакета OpenGL. Вторая библиотека отвечает за взаимодействие с системой окон. В качестве таковой мы будем использовать библиотеку GLUT (GL Utility Toolkit), которая со- содержит функции, обеспечивающие пользователя основными возможностями, характерными для большинства современных многооконных систем. С некоторыми функциями из состава GLUT читатель познакомится уже в этой главе, а более подробная информация о них содер- содержится в главе 3, где будут описаны возможности ввода информации и взаимодействия с поль- пользователем. На рис. 2.7 схематически представлена организация системы библиотек в той вер- версии OpenGL, которая работает под управлением операционной системы X Window. Обратите внимание на то, что из библиотек OpenGL вызываются функции, которые входят в состав других библиотек, но прикладная программа с ними напрямую не работает. Аналогичная ор- организация используется и в версиях OpenGL, работающих под управлением других операци- операционных систем, в частности Microsoft Windows. Рис. 2.7. Организация библиотеки OpenGL 2.2. Прикладной интерфейс OpenGL 65
Для улучшения "читабельности" текста программы в OpenGL предлагается использовать макросы. В частности, в заголовочных файлах (h-файлах) определены макросы GL_FILL и GL_POINTS. В большинстве версий для включения заголовочных файлов glut.h, gl.h и glu.h достаточно вставить в текст программы директиву linclude <GL/glut.h> или #include <glut.h> 2.3. Примитивы и атрибуты Среди специалистов в области компьютерной графики не прекращаются дебаты вокруг вопроса, носящего, казалось бы, совершенно схоластический характер: "Что считать прими- примитивом?" и "Какие примитивы следует поддерживать в составе API?" Скорее всего спорщики никогда не придут к единому мнению. Минималисты считают, что примитивами должны быть только такие объекты, которые в большинстве существующих графических систем формируются аппаратно. Кроме того, набор примитивов должен обладать свойством ортого- ортогональности, т.е. ни один из элементов этого набора нельзя было бы получить из других. Ми- Минимальные системы обычно поддерживают набор, состоящий из отрезка прямой, много- многоугольника и некоторой формы текста (последовательности символов). Все элементы набора могут быть эффективно сформированы аппаратно. В другом лагере— максималистов — придерживаются мнения, что в набор следует включить и такие объекты, как окружности, кривые разного рода, поверхности и сплошные тела. В качестве основного аргумента при этом выдвигается забота о пользователе, которого следует избавить от самостоятельного формирования таких объектов, ставших неотъемлемой частью большинства развитых прило- приложений. Но поскольку возможностью поддерживать на аппаратном уровне работу с большим набором довольно сложных примитивов обладают только немногие модели графических станций, программу, использующую такой большой набор, нельзя будет переносить с одной платформы на другую. В этом споре разработчики OpenGL заняли промежуточную позицию (как правило, самую удобную в споре). В базовой библиотеке используется небольшой набор примитивов, а в до- дополнительной библиотеке GLU содержатся функции построения более сложных объектов, которые с точки зрения прикладного программиста также являются примитивами. Базовые примитивы OpenGL специфицируются набором точек в пространстве (вершин). Формат определения объектов представлен ниже. glBegin(<ivw7>); glVertex*(...)? glVertex*(...); glEnd(); Значение <тий> определяет вид объекта и несет OpenGL информацию о том, каким обра- образом нужно интерпретировать следующий далее список вершин. Между вызовами функций glBegin() и glEnd() могут быть включены операторы вызовов других функций OpenGL. На- Например, здесь могут стоять операторы вызова функций задания атрибутов или вычисления значений координат вершин. Основное концептуальное отличие примитивов разного типа со- состоит в том, имеют ли они внутреннюю область. Все примитивы, кроме точек, определяются либо в терминах набора вершин, либо в терминах отрезков прямых линий (не следует путать отрезок прямой линии, который имеет ограниченные размеры, и прямую, которая представ- представляет собой геометрический объект бесконечной протяженности). Естественно, что отдельный 66 Глава 2. Графическое программирование
отрезок4 задается парой вершин, но отрезок играет настолько большую роль в описании про- прочих объектов, что его можно рассматривать как базовый примитив. Отрезки можно исполь- использовать для аппроксимации кривых линий или с помощью отрезков соединять соседние точки на графике функции. Отрезками отображаются и ребра замкнутых геометрических объектов, которые имеют внутреннюю область, например многоугольников. В OpenGL есть несколько способов формирования отрезков (рис. 2.8). Pi. Ро» P7# Pa * Рб • Рз • Рд •Ps GL_POINTS P7^ Pa Рб - Рз С GLJ.INES Pa Р7"~ЛРз Ро / ) Рд Р7^Л Рб GL_UNE_STRIP pv Ро/ \ GL. Ра Рб .LINE. "^ рз \ )Р4 Л .LOOP Рис. 2.8. Типы точек и отрезков Отрезки (GL_LINES) Тип GL LINES задает формирование отрезка по двум вершинам, которые рассматриваются как его начальная и конечная точки. Обратите внимание на то, что построенные таким обра- образом отрезки, как правило, не стыкуются друг с другом в крайних точках. Ломаные линии (GLLINESTRIP) Если необходимо сформировать ломаную линию (polyline) — последовательность стыкую- стыкующихся отрезков5, — то используется тип GL LINE_STRIP. Чаще всего ломаные линии использу- используются для аппроксимации кривых. Если желательно получить замкнутый контур, то можно либо указать в качестве конечной и начальной в списке одну и ту же вершину, либо использовать специальный тип, определяющий именно замкнутую ломаную, — GL_LINE_LOOP. В этом случае OpenGL самостоятельно свяжет отрезком последнюю вершину в списке с первой. 2.3.1. Многоугольники Отрезки и ломаные можно использовать для моделирования ребер более сложных объектов, но примитив типа замкнутая ломаная не имеет одного важного свойства — внутренней облас- области. Подобным свойством в OpenGL обладают примитивы другого типа— многоугольники (polygon) (рис. 2.9). литя по форме замкнутая ломаная ничем не отличается от многоугольника с совпадающими вершинами, она не имеет внутренней области6. Многоугольники играют особую роль в компьютерной фафике, поскольку в растровых системах изображение таких примитивов формируется очень быстро. С помощью многоугольников аппроксимируют криволинейные по- поверхности. Производительность фафических систем принято сейчас оценивать в количестве многоугольников, выводимых на экран в течение одной секунды. Изображение прямоугольника на экране может выглядеть по-разному. Можно выводить только контур, заливать внутреннюю область одним цветом или заполнять определенным рисунком (образцом — pattern), причем в 4 В дальнейшем мы не будем каждый раз оговаривать, что отрезок является прямолинейным. Только в случае, если рассматривается отрезок линии другого типа, например гиперболы uiu параболы, будет указы- указываться тип линии. — Прим. ред. ' Часто в литературе, особенно переводной, используется и термин полилиния, который является капь- кой с английского polyline. — Прим. перев. 6В некоторых графических системах, в частности в GKS. вместо термина многоугольник используется термин заполняемая область (fill area). 2.3. Примитивы и атрибуты 67
залитом многоугольнике ребра можно и не отображать (рис. 2.10). Хотя контур многоугольника определяется очень легко упорядоченным списком его вершин, неправильное (неоднозначное) определение его внутренней области может привести к некорректному ее заполнению. Для кор- корректного отображения внутренней области многоугольника он должен обладать тремя свойст- свойствами — быть простым, выпуклым и плоским. Рис. 2.9. Примитивы, имеющие внут- внутреннюю область Рис. 2.10. Способы отображения многоугольников Если в двухмерном многоугольнике нет пересекающихся ребер, то он называется простым. Как видно на рис. 2.11, простой многоугольник имеет однозначно определенную внутреннюю область. Выяснить, является ли многоугольник простым, можно по положению его вершин, но алгоритм проверки потребует множества вычислений (см. упр. 2.14). По- Поэтому в большинстве графических API предполагается, что такая проверка выполняется в прикладной программе. Вопрос, как должна поступить гра- графическая система в случае, если заданный для отображения многоугольник не является простым, будет рассмотрен в главе 7. С точки зрения алгоритмов заливки внутренней области недостаточ- недостаточно, чтобы многоугольник был простым. В некоторых API гарантируется правильное выполнение заливки только в случае, если многоугольник является выпуклым. Говорят, что геометрический объект является вы- выпуклым (convex), если все точки отрезка, соединяющего две любые точки внутренней области объекта, также принадлежат внутренней области (рис. 2.12). Хотя в этой главе мы будем иметь дело только с двухмерны- двухмерными (плоскими) объектами, это определение справедливо и для трехмер- трехмерных объектов. В число выпуклых объектов входят треугольники, тетра- тетраэдры, прямоугольники, круги, сферы и параллелеп»'"~цы (рис. 2.12). Су- Рис 2.11. Много- ществует множество алгоритмов проверки выпуклости объектов (см. угольники: уПр 2.20). Но следует отметить, что проверка выпуклости, как и провер- проверит простои, ка Пр0СТ0ТЫ многоугольника, требует достаточно сложных вычислений стой и' как пРавило> возлагается на прикладную программу. А Рис 2.12. К определению по- понятия ^выпуклости" Рис. 2.13. Выпуклые геометрические объекты 68 Глава 2. Графическое программирование
При работе с многоугольниками в трехмерном пространстве появляются дополнительные сложности. В этом случае многоугольник не обязательно является плоским, т.е. все его вер- вершины не обязательно лежат в одной плоскости. Одна из геометрических теорем утверждает, что любые три неколлинеарные точки (т.е. точки, не лежащие на одной прямой) однозначно определяют плоскость, в которой лежит треугольник, построенный на этих трех вершинах. Этот вывод используется в большинстве графических систем. Следовательно, если использо- использовать для аппроксимации криволинейных поверхностей только треугольники, то графическая система будет иметь дело исключительно с плоскими многоугольниками, которые всегда то- тонируются корректно. Кроме того, заливка треугольных областей выполняется аппаратно или программно значительно быстрее, чем заливка многоугольников другой формы. 2.3.2. Типы многоугольников в OpenGL Теперь вернемся к типам объектов в OpenGL. Существует несколько типов плоских объ- объектов, имеющих внутреннюю область (рис. 2.14). Р2 Р2 Р2 Р2 Pi. * *Рз Ро» »Рд Ро/ \рд Poi^^^7P4 Р° т Р7 Ps Рб Рб Рб Рб GL_POINTS GLPOLYGON GL_QUADS GL_TRIANGLES Рис. 2.14. Типы многоугольников в OpenGL Многоугольники (GLPOLYGON) Ребра объектов этого типа совпадают с отрезками замкнутой ломаной, построенной на том же наборе вершин. Внутренняя область объекта заполняется в соответствии с заданными значениями атрибутов заливки. Сами по себе ребра имеют конечную толщину. В большинст- большинстве графических систем можно либо заливать внутреннюю область определенным цветом (заполнять определенным образцом), либо вычерчивать ребра линиями определенной толщи- толщины, но нельзя совмещать эти два способа отображения. В OpenGL можно использовать функ- функцию glPolygonMode() для отображения ребер и тем самым отменять используемый по умол- умолчанию режим заливки. Если же желательно сформировать совмещенное изображение много- многоугольника в обоих режимах, придется фактически создать два объекта на одном и том же наборе вершин. Первый объект — это стандартный многоугольник, а второй — либо много- многоугольник, сформированный функцией glPolygonMode(), либо замкнутая ломаная. Треугольники и четырехугольники (GLTRIANGLES, GLQUADS) Для формирования треугольников и четырехугольников в OpenGL используются специ- специальные примитивы. Треугольник определяется тремя вершинами, а четырехугольник — че- четырьмя. Эти объекты тонируются быстрее, чем такие же по форме объекты типа GL_POLYGON. Полосы (GL_TRIANGLE_STRIP, GL_QUAD_STRIP, GL_TRIANGLE_FAN) Объекты этих типов представляют собой группу треугольников или четырехугольни- четырехугольников, в которых отдельные фигуры совместно используют некоторые вершины. В треуголь- треугольной полосе — объекте типа GL_TRIANGLE_STRIP — каждая последующая вершина в списке комбинируется с двумя предыдущими и определяет таким образом очередную треугольную ячейку полосы (рис. 2.15). В полосе, состоящей из четырехугольных ячеек — объекте типа 2.3. Примитивы и атрибуты 69
GL QUAD_STRIP, — каждая очередная пара вершин в списке комбинируется с предыдущей па- парой и задает очередную четырехугольную ячейку. Объект типа GL TRIANGLE_FAN — розет- розетка— строится на основе одной фиксированной вершины (центра), которая идет первой в списке вершин. Следующие две вершины в списке определяют первый треугольный "лепесток", а затем каждая очередная вершина вместе с предыдущей в списке и центральной определяет следующий "лепесток". Pi Рз Р5 Р7 Pi Рз Р5 Р7 Pi Рз Рб Р7 .' .' .' .' /\/\/\/ / / / / Ро Ра Ра Рб Ро Ра Рд Рб Ро Ра Рд Рб GL_POINTS GL_TRIANGLE_STRIP GL_QUAD_STRIP Рис. 2.15. Полосы из треугольников и четырехугольников 2.3.3. Текст Очень редко в графическое изображение не включаются поясняющие надписи. Хотя в не- неграфических приложениях вывод информации в текстовом виде используется повсеместно, в графических вывод текста вызывает определенные сложности. В неграфических приложени- приложениях мы обычно имеем дело с небольшим набором символов, отображаемых единообразно7. В графических приложениях текст, как правило, формируется из символов разного размера, на- начертания, цвета и т.д. В распоряжение прикладного программиста также предоставляется множество шрифтов, определяющих форму символов на экране, таких как Times, Computer Modern или Helvetica. Существуют два типа шрифтов — штриховой и растровый. Штриховой шрифт формиру- формируется по тому же принципу, что и прочие графические примитивы (рис. 2.16). Начертание символа определяется вершинами соответствующих прямолинейных и криволинейных отрез- отрезков. Если символ формируется из замкнутых контуров, то его внутреннюю область можно за- залить. Преимущество использования штриховых шрифтов заключается в том, что с ними можно обращаться в графической системе точно так же, как с любыми другими графически- графическими объектами. Несомненным достоинством штриховых шрифтов является и сохранение на- начертания во всех деталях при выполнении геометрических преобразований — масштабирова- масштабировании или повороте. Поэтому определение символов набора выполняется только для одного, базового, размера, а все прочие получаются после выполнения стандартных геометрических преобразований — масштабирования и поворота. Но следует отметить, что исходное описание шрифта из 128 или /~* i 256 символов может быть довольно сложным и занимать много мес- >^ О ГП О U ТС Г та в памяти. Стандартные шрифты PostScript создаются из отрезков ^^ . # полиномиальных кривых и демонстрируют все достоинства и недос- СЗ ГО О П IС S татки штриховых шрифтов. Существуют различные модификации * шрифтов PostScript для приложений с высоким и низким разрешени- Рис. 2.16. Штриховой ем- Д°вольно часто разработчики пытаются справиться с проблемой шрифт медленной обработки таких объектов в процессе тонирования изо- изображения, возлагая значительную часть связанных с этим операций на аппаратное обеспечение принтеров. Эта стратегия тесно связана с принципами организа- организации систем "клиент/сервер" и будет рассмотрена в главе 3. Любой пользователь современного текстового редактора скажет, что это утверждение, мягко гово- говоря, не совсем справедливо. — Прим. ред. 70 Глава 2. Графическое программирование
Растровый шрифт определяется значительно проще, а отображается быстрее (рис. 2.17). Символы шрифта определяются на прямоугольной области и представляют собой блоки би- битов (bit blocks). Каждый блок задает определенный символ в виде образа из нулей и единиц, которые соответствуют засвеченным и незасвеченным точкам растра. При отображении оп- определенный таким способом символ помещается в буфер кадра с помощью операции поби- побитового переноса (bitblt), которая выполняется очень быстро. Мы рассмотрим эту операцию подробно в главе 9. В составе OpenGL есть функции, которые позволяют прикладной про- программе напрямую манипулировать содержимым буфера кадра. Увеличить размер символов растрового шрифта можно только дублированием пикселей, что при большом увеличении приводит к формированию символов довольно "грубой" формы (рис. 2.18). Выполнять какие-либо геометрические преобразования растрового шрифта бес- бессмысленно. Кроме того, поскольку растровые шрифты, как правило, хранятся в постоянной памяти, они не переносятся с компьютера на компьютер. Рис. 2.17. Растровый шрифт ft Рис. 2.18. Изменения размера растрового шрифта Символы как штриховых, так и растровых шрифтов формируются из других примитивов, а потому в составе основной библиотеки OpenGL нет специального примитива для формиро- формирования текста. Но в составе дополнительной библиотеки GLUT имеется несколько наборов символов (как штриховых, так и растровых), которые определены программно, а следова- следовательно, являются переносимыми. Например, для включения в изображение символа растро- растрового шрифта размером 8x13 пикселей нужно вызвать функцию glutBitmapCharacter(): glutBitmapCharacter(GLUT_BITMAP_8_BY_13, с) Второй аргумент этой функции, с, представляет собой ASCII-код выводимого символа. Символ размещается в текущей позиции растра на поле экрана — это один из параметров, характеризующих текущее состояние графической системы. Изменение значения текущей позиции растра выполняется с помощью разных модификаций функции glRasterPos*. Мы еще вернемся к методам вывода текста на экран в главе 3, где речь пойдет об использова- использовании дисплейных списков. 2.3. Примитивы и атрибуты 71
2.3.4. Криволинейные объекты Все ранее рассмотренные примитивы полностью определялись набором вершин. Все они, за исключением точки, состояли из прямолинейных отрезков или использовали такие отрезки для определения границ некоторой области, которая заливалась определенным цветом или заполнялась образцом. В этом разделе будут рассмотрены два подхода к формированию рас- расширенного набора примитивов. Первый подход предполагает использование ранее определенных примитивов для ап- аппроксимации кривых и поверхностей. Если, например, нам нужен круг, то его можно аппрок- аппроксимировать правильным многоугольником с п вершинами. Точно так же сфера аппроксими- аппроксимируется правильным многогранником. В обшем случае любую криволинейную поверхность можно аппроксимировать сеткой из выпуклых многоугольников (мозаикой— tessellation), которая формируется либо на стадии тонирования, либо в прикладной программе. Другой подход, который мы подробно рассмотрим в главе 10, состоит в том, что создает- создается математическое описание криволинейного объекта, а затем разрабатываются графические функции реализации этого математического описания. Математический аппарат описания та- таких объектов, как квадратичные поверхности и параметрические полиномиальные кривые, достаточно хорошо разработан, и их можно строить, базируясь на наборе характеристических точек. Например, сферу можно построить, зная всего две точки — центр сферы и произволь- произвольную точку на ее поверхности. Кубическую полиномиальную кривую можно построить по че- четырем точкам, находящимся на этой кривой. В большинстве графических систем используются оба подхода. В OpenGL можно приме- применять функции аппроксимации наиболее часто используемых криволинейных поверхностей из библиотеки утилит GLU, но можно разработать и собственные функции построения разного рода специфических криволинейных объектов. Мы будем использовать эти возможности OpenGL при работе с параметрическими полиномиальными кривыми и поверхностями. 2.3.5. Атрибуты В современных системах компьютерной графики принято делить используемую инфор- информацию на две категории: информация, определяющая форму объекта, и информация, харак- характеризующая вид объекта на экране. Красная сплошная линия и зеленая штриховая линия формируются на основании одного и того же типа примитива, но выглядят совершенно по- разному. Атрибут — это любое свойство, которое определяет способ отображения заданного графического примитива. Чаще всего используются такие атрибуты, как цвет, толщина линий и образец заливки многоугольников. Рис. 2.19 поясняет физический смысл некоторых атри- атрибутов линий и многоугольников. f ' '* а) 6) Рис. 2.19. Атрибуты линий и многоугольников 72 Глава 2. Графическое программирование
Атрибуты ассоциируются или связываются с примитивами на разных этапах конвейерно- конвейерного процесса моделирования и тонирования. Связывание может не быть постоянным. В этой главе мы будем рассматривать только режим немедленного отображения {immediate-mode), в котором примитивы не сохраняются в памяти системы, а сразу же после формирования пере- передаются на отображение. Значения многих атрибутов входят в состав параметров, характери- характеризующих текущее состояние всей системы. При формировании любого примитива в програм- программе к нему применяются текущие значения атрибутов, приложимых к данному типу объектов, и соответственно формируется изображение объекта на экране. Поскольку примитив не со- сохраняется в памяти системы, то после очистки экрана он утрачивается. В главе 3 мы рассмот- рассмотрим дисплейный список (или дисплейный файл), в котором сохраняется информация о сфор- сформированных графических объектах. Такой режим работы системы позволяет повторно ис- использовать однажды сформированные объекты при создании нового изображения8. К каждому типу объектов приложим свой набор атрибутов. Например, на внешний вид точ- точки влияют только два атрибута— цвет и размер точки. На внешний вид отрезка влияют цвет, толщина линии и тип линии (сплошная, штриховая или пунктирная). Внешний вид объектов, имеющих внутреннюю область, определяется более широким набором атрибутов, посколь- поскольку требуется специфицировать множество параметров заливки. Можно заливать внут- внутреннюю область одним цветом или опреде- определенным образцом, можно задать режим ото- отображения многоугольника— с заполнением или контурный. Контур многоугольника мо- может выводиться одним цветом, а внутренняя область заливаться другим. В графических системах, поддерживающих работу со штриховыми наборами символов, Computer Graphics с Computer Graphics § Q_ О 6 Computer Graphics существуют еще и атриоуты, определяющие начертание таких символов. Как проявляются некоторые из них, продемонстрировано на рис. 2.20. Атрибуты этой группы позволяют регулировать ориентацию текстовой строки, соотношение между высотой и шириной сим- символов, наклон символов, вид начертания (полу- (полужирный, курсив, подчеркнутый) и т.д. О. Е о U Computer Graphics № SDjLjdDJQ J9jndtUCQ з с О -1 Q о Рис. 2.20. Атрибуты текста, использующего штриховой шрифт 2.4. Цвет Способность воспринимать цвет является одним из наиболее интересных свойств рецепторной системы человека, а возможность воспроизводить цвет — одним из главных достоинств современ- современных систем компьютерной фафики. Исчерпывающий анализ возможности человека воспринимать цвет требует значительно более глубокого изложения анатомии, физиологии и психологии, чем тот, который мы можем себе позволить в данной книге. Но, тем не менее, я постараюсь расширить представленную в главе 1 модель зрительной системы человека и получить на ее основе приемле- приемлемую для использования в компьютерной графике модель цветовосприятия. НВ OpenGL упор сделан именно на режиме немедленного отображения и использовании конвейерной ор- организации процесса, которая позволяет создавать динамические графические приложения, активно рабо- работающие с пользователем. В этом состоит одно из основных оппичип OpenGL от таких графических сис- систем, как PHIGS, в которых используется парадигма баз данных. В базе данных сохраняется информация о геометрических объектах, которую при желании можно использовать повторно. 2.4. Цвет 73
350 780 Как уже говорилось, видимый свет представляет собой электромагнитные колебания в диа- диапазоне длин волн между 350 и 780 нм. Колебания с более короткой длиной волны воспринима- воспринимаются как более близкие к синему цвету, а по мере увеличения длины цвет воспринимается как все более близкий к красному. Промежуточное положение между синей и красной частями спектра занимает зеленый цвет. Практи- Практически все источники света излучают колебания в достаточно ши- широком диапазоне частот. Исключение составляют источники коге- когерентных колебаний — лазеры. Каждый источник света характери- характеризуется функцией спектрального распределения С(Х), которая в графическом виде представлена на рис. 2.21. Аргументом функции является длина волны X, а значением функции — мощность излу- излучения соответствующей частоты. Функция С(Х) учитывает объективные физические свойства излучения, но никак не отражает способность зрительной системы человека воспринимать цвет этого излучения. Для того чтобы это различие стало более понятным, рассмотрим одну из ключевых задач использования цвета в компьютерной графике — задачу соответствия цветов. Предполо- Предположим, что нам нужно воспроизвести на экране цветовой эффект, создаваемый в реальном мире излучением, имеющим функцию распределения С(л). Должны ли мы для этого воспроизвести относительную мощность С для каждого значения Л? Монитор, который обладал бы подобной возможностью во всем диапазоне видимых цветов, пока что не создан. Но, к счастью, нам и не нужен такой монитор, поскольку зрительная система человека вполне удовлетворительно опи- описывается моделью, основанной на трех первичных цветах. Рис. 2.21. Функция спек- спектрального распределе- распределения светового излучения - хх- Рис. 2.22. Наложение первичных цветов Представление цвета в компьютерной фафике основано на теории трех первичных состав- составляющих (three-color theory)9. Аддитивная цветовая модель (additive color model) базируется на предположении, что любой цвет можно рассматривать как взвешенную сумму трех основных цве- 9Исключение составляют приложения для полиграфии, в которых используется четырехцветная обра- обработка. Но это связано не столько с фундаментальными концепциями теории, сколько с практическими ас- аспектами использования полиграфического оборудования. 74 Глава 2. Графическое программирование
тов. Достаточно близкая и понятная аналогия — использование трех прожекторов основных цве- цветов с регулируемой интенсивностью излучения каждого (рис. 2.22). Все прожекторы направлены в одну и ту же область темного экрана. Наши глаза воспринимают отраженный световой поток, ко- который представляет собой наложение трех первичных, но видит его как один смешанный цвет. При использовании такой модели можно, варьируя интенсивность излучения каждого из первичных прожекторов, добиться желаемого цвета. Хотя с помощью этой модели нельзя до- добиться абсолютно точного воспроизведения всех цветов, но если в качестве первичных ис- использовать красный, зеленый и синий, такое воспроизведение будет довольно близким. Фор- Формально этот процесс можно описать следующим образом. Пусть С— это тот цвет, который требуется воспроизвести, a R, G и В представляют наши три прожектора. Цветовое соответ- соответствие при этом выражается соотношением где 7*1, 7*2 и Г3 представляют интенсивность излучения соответствующих прожекторов. Синте- Синтезируемый цвет можно охарактеризовать триадой G*1, 7*2, Т3). Естественно, что точного соответствия между непрерывной функцией С(к) и наборами триад (Т|, 7*2, Т3) добиться не удастся, но именно так действует зрительная система человека. В ней существуют три типа цветовых рецепторов — колбочек. Колбочки типа /, / е {1, 2, 3}, имеют кривую чувствительности S,(k) и, когда на них попадает световой поток с функцией распределения С(Х), посылают в мозг сигнал А, = где интегрирование выполняется по всему диапазону длин волн видимого света. Таким обра- образом мозг получает информацию о цвете в виде триад (А], А2, А3), а не в виде отсчетов непре- непрерывной функции С(к). Подводя итог сказанному, получим основополагающий принцип трехкомпонентной тео- теории цветовое приятия: Если два цвета характеризуются одним и тем же набором первичных составляю- составляющих, они визуально неразличимы. Два цвета, которые воспринимаются глазом одинаково, образуют метаметрическую пару (metameric pairs). Такие цвета имеют те же самые значения интенсивности первичных со- составляющих, хотя их функции распределения С(Л) могут и не совпадать. Для построения со- соответствующих цветов достаточно воспроизвести в системе компьютерной графики интен- интенсивности первичных составляющих. Существует множество нюансов теории цветовосприятия, на которых мы в данной книге останавливаться не будем. Наибольшее внимание обычно уделяется выбору набора первич- первичных составляющих и ограничениям, проистекающим из физических принципов работы ре- реальных приборов. Первичные цвета, воспринимаемые рецепторами глаза, не совпадают ни с теми, которые используются в цветных ЭЛТ или принтерах, ни с теми, которые используются в цветных фото- и кинопленках. Кроме того, интенсивности первичных составляющих, кото- которые используются для получения соответствующих цветов на экране ЭЛТ, должны быть обя- обязательно положительны, поскольку по самой своей физической природе этот прибор позво- позволяет только добавлять какой-либо цвет, но никак не может "вычесть" его. Существует и пре- предел интенсивности свечения как излучаемого данным физическим прибором, так и воспринимаемого рецепторами зрительной системы. Следовательно, хотя математически мы и можем соотнести интенсивности первичных компонентов одного набора с аналогичными значениями другого набора, физически воспроизвести их не всегда возможно. Диапазон цве- 2.4. Цвет 75
с Зеленый Бирюзовый Синий У / i Белый / Черный / Желтый Красный __ / ¦ Фиолетовый тов, который можно воспроизвести в данной системе при заданном наборе первичных цветов, называется цветовой гаммой (color gamut). Аддитивная модель подходит для таких средств формирования цветного изображения, как ЭЛТ, прозрачные экраны и позитивные (слайдовые) фотопленки. В них в качестве первичных используются красный, зеленый и синий цвета. Цвет отдельной точки изображения можно охарактеризо- охарактеризовать положением в цветовом кубе, который изобра- изображен на рис. 2.23 и на ил. 6 цветной вклейки. Вдоль осей координатной системы такого цветового куба откладываются интенсивности первичных компо- компонентов. В нормализованном цветовом кубе любой цвет можно представить определенной точкой внут- внутри такого единичного куба. Вершины куба соответ- соответствуют черному цвету (интенсивность всех первич- первичных компонентов равна 0), красному, зеленому и си- синему цветам (два других первичных компонента имеют интенсивность 0), бирюзовому (сумма зеле- зеленого и синего), фиолетовому (сумма красного и си- синего) и желтому (сумма красного и зеленого) цве- цветам, а вершина, которая отстоит от начала системы координат куба по главной диагонали, соответствует белому цвету. Все цвета, располагающиеся вдоль главной диагонали, имеют равные соотношения первичных компонентов и проявляются в изображении в виде оттенков серого. С помощью такого цветового куба можно сравнивать системы воспроизведения цветов, построенные на разных наборах первичных компонентов. При работе с ЭЛТ цвет формируется в результате добавления первичных компонентов. В издательских системах используется иная модель, основанная на вычитании первичных ком- компонентов. В этом случае носитель изображения в исходном состоянии является белым (например, чистый лист бумаги). Окрашенные пигменты как бы удаляют определенную со- составляющую из белого цвета, отражаемого поверхностью носителя. Если считать, что по- поверхность засвечивается чисто белым цветом, то некоторая точка этой поверхности будет восприниматься красной в том случае, если все компоненты, кроме красного, будут поглоще- поглощены наложенными на эту точку пигментами. В таких системах первичные пигменты имеют дополняющий цвет. Это так называемая система CMY (по первым буквам английских назва- названий дополняющих цветов — cyan, magenta, yellow (рис. 2.24)). В данной книге мы не будем в дальнейшем использовать систему дополняющих цветов и сконцентрируемся только на адди- аддитивном методе формирования цвета. Рис. 2.23. Цветовой куб Синий Желтый Фиолетовый Зеленый Бирюзовый Бирюзовый Белый Зеленый Синий Рис. 2.24. Формирование цвета: а — наложение первичных цветов; б — вычитание первичных цветов 76 Глава 2. Графическое программирование
2.4.1. Цветовая система RGB В этом разделе мы рассмотрим, как выполняются манипуляции с цветом в графических системах с точки зрения программиста, т.е. какие функции для этого имеются в составе API. Существуют два довольно сильно отличающихся подхода к программным манипуляциям с цветом. Мы будем рассматривать только цветовую модель RGB, поскольку она играет ре- решающую роль в дальнейшем обсуждении вопросов моделирования освещения и реализации тонирования объектов сцены. По сравнению с моделью цветовых индексов, которая будет рассмотрена в следующем разделе, RGB-модель более сложная в аппаратной реализации, по- поскольку требует большего объема памяти, но в связи с тем, что стоимость памяти непрерывно снижается, это уже можно не считать ее существенным недостатком. В современных графи- графических API стало возможным обеспечить правильную цветопередачу, причем аппаратно уда- удается использовать алгоритмы аппроксимации, позволяющие достаточно приблизиться к пол- полноценной RGB-модели отображения. Концептуально в RGB-системе наложения цветов должны существовать три раздельных буфера кадра— по одному на каждый из первичных цветов. В каждом буфере за определен- определенным пикселем на экране закреплен фиксированный адрес в памяти (рис. 2.25). В типовой сис- системе, имеющей разрешение 1280x1024 пикселей, каждый из них описывается тремя байтами B4 битами): по одному байту соответственно на красный, зеленый и синий цвета. В результа- результате буфер кадра должен иметь объем порядка 3 Мбайт, причем весь буфер должен считывать- ся с частотой регенерации. До последнего времени необходимость в таком буфере являлась одним из наиболее существенных препятствий во внедрении графических систем с правиль- правильной цветопередачей. Буфер кадра Рис. 2.25. Цветовая система RGB Программисту желательно было бы иметь возможность задавать цвет в виде определен- определенного числа и не думать о том, каким образом соответствующая информация будет храниться в буфере кадра. Если считать, что на каждый цвет отводится по 1 байту, то в распоряжении программиста есть 224 возможных цветов (иногда говорят, что имеется 16 М цветов, понимая под М число 10242). В других графических системах используется 12 и более битов на каж- каждый цвет, но существуют и системы с 4 битами на цвет. Поскольку все графические API же- желательно делать аппаратно-независимыми, то программисту нужно позволить специфициро- специфицировать цвет независимо от объема памяти, отводимой в буфере кадра на каждый пиксель. Все остальные операции, связанные с приведением кода цвета к формату конкретной системы отображения, должны взять на себя драйверы и аппаратные средства. Наиболее естественным было бы воспользоваться моделью цветового куба и специфицировать цвет в виде трех дей- действительных чисел в диапазоне от 0 до 1.0. В OpenGL эта идея реализуется следующим обра- образом. Для того чтобы задать красный цвет, вызывается функция glColor3f (): glColor3fA.0, 0.0, 0.0); 2.4. Цвет 77
В результате выполнения этой функции устанавливается значение текущего атрибута цвета, со- соответствующее красному цвету. Поскольку атрибут цвета является одним из параметров текущего состояния системы, все последующие графические объекты будут отображаться красным цветом до тех пор, пока значение атрибута не изменится. Суффикс 3f в имени функции играет ту же роль, что и суффиксы в именах функций семейства glVertex*, и означает, что используется трехцветная (RGB) модель формирования цвета, а значения компонентов — аргументов функции — это веще- вещественные числа в формате float, как он определен в языке С. Если для задания компонентов будут использованы числа в формате integer или byte, то максимальное значение выбранного типа бу- будет соответствовать максимальному значению интенсивности первичного компонента, а мини- минимальное — полностью отключенному первичному компоненту. В дальнейшем мы рассмотрим четырехкомпонентную (RGBA) систему представления цвета. Четвертый компонент в такой системе (А-компонент) называется альфа-каналом {alpha channel), но сохраняется в буфере кадра, как и значения RGB-компонентов. В главе 9 будут рассмотрены различные варианты использования альфа-канала, которые позволяют, например, создать эффект тумана или комбинированного (смешанного) изображения. Значе- Значение альфа-составляющей в OpenGL трактуется как значение параметров прозрачности {transparency) или поглощения {opacity). Прозрачность и поглощение — это два взаимно до- дополняющих параметра, характеризующих оптические свойства среды. Поглощающий объект не пропускает свет через себя, а прозрачный объект пропускает весь падающий на него све- световой поток. При определенных значениях этого параметра объект может быть абсолютно черным телом, т.е. полностью поглощать световой поток, либо абсолютно прозрачным. Одна из первых операций, которые выполнятся в графической программе, — это очистка экрана, точнее той его части, в которой будет формироваться графическое изображение, — видового окна. При использовании четырехкомпонентной (RGBA) цветовой системы можно создать эффект взаимного наложения текущего видового окна с тем, которое "расположено" под ним. Это выполняется с помощью параметра поглощения окна при выполнении операции его очистки. Вызов функции glClearColorA.0, 1.0, 1.0, 1.0); настраивает белый цвет заполнения, поскольку первые три аргумента — цветовые компоненты RGB — равны 1.0, и режим непрозрачности, поскольку альфа-компонент также равен 1.0. Те- Теперь после вызова функции glClear() окно окрасится в белый цвет и станет непрозрачным. 2.4.2. Индексируемый цвет Во множестве графических систем используется буфер кадра с ограниченной глубиной. Например, в системе может использоваться буфер, рассчитанный на разрешение 1280x1024 пикселей, но каждый пиксель имеет только 8-битовый код засветки. Можно было бы разделить эти 8 бит на более мелкие группы и назначить по одной такой группе на каждый из первичных цветов — красный, зеленый и синий. Но такая технология, во-первых, слишком ограничивает возможности цветопередачи в системе, а во-вторых, разбиение на очень мелкие группы битов очень негативно сказывается на скорости работы буфера кадра. Вместо этого можно последовать аналогии с художником, который пишет картину. Он может воспроизвести на картине сколько угодно цветов, смешивая на палитре краски всего из нескольких тюбиков. Будем говорить, что художник имеет потенциально неограниченную палитру цветов, но в каждый момент времени он работает только с некоторыми из них, ко- которые уже смешаны (подготовлены) на палитре. Возвращаясь к нашей компьютерной модели, покажем, что, выбирая для определенного приложения ограниченный набор цветов из большого множества (нашу палитру), можно в подавляющем большинстве случаев создавать изображение достаточно высокого качества. 78 Глава 2. Графическое программирование
Индекс 0 2l'-l Красный 0 2m-1 0 Зеленый 0 0 2m-l Синий 0 0 0 Код пикселя при этом интерпретируется не как абсолютный код цвета, а как индекс в списке или таблице. Предположим, что каждый пиксель характеризуется кодом из к бит, т.е. имеет зна- значение индекса в диапазоне от 0 до 2*'. Будем также считать, что аппаратные средства отображения по- позволяют задавать каждый из основных цветов с точностью т бит, т.е. можно получить 2т градаций интенсивности любого из основных цветов. Следо- Следовательно, потенциально наш дисплей может вос- воспроизвести любой из 23т цветов, но в буфере кадра можно задать только 2* из них. Эти значения мож- можно "пропустить" через определенную пользовате- пользователем таблицу соответствия цветов (color-lookup table), которая имеет размер 2*x3/w (рис. 2.26). В **с. 2.26. Таблица соответствия цветов прикладной программе заполняется 2* строк табли- таблицы желаемыми кодами цветов, которые представляют собой набор из трех чисел, соответст- соответствующих интенсивности первичных компонентов (каждое имеет размер /я бит). Создав такую таблицу, пользователь может специфицировать любой из перечисленных в ней цветов его ин- индексом в таблице (рис. 2.27). При к=т=% (это стандартный вариант системы отображения) поль- пользователь имеет в своем распоряжении набор, включающий любые 256 цветов из общего числа 16 М. Таблица из 256 элементов и образует палитру пользователя данного приложения. тбит m бит тбит Буфер кадра Рис. 2.27. Индексация цвета В режиме индексированного цвета текущий цвет выбирается специальной функцией, ар- аргументом которой является значение индекса. В OpenGL эта операция выполняется функцией gllndexi(). Заполнение и модификация строк в таблице соответствия цветов требует взаи- взаимодействия с подсистемой управления окнами, которая в каждой операционной системе мо- может быть реализована по-своему. (Этот вопрос обсуждается в главе 3.) Основная сложность состоит в том, что в некоторых операционных системах и комплектах аппаратуры системы отображения поддерживается ограниченное количество цветов и подсистема управления ок- окнами располагает единственной таблицей соответствия цветов для всех окон. В других опе- операционных системах для каждого окна можно сформировать отдельную таблицу. Библиотека GLUT позволяет прикладному программисту организовать отдельную таблицу для каждого окна и заполнять ее, вызывая функцию glutSetColor(): void glutSetColor(int, GLfloat red, GLfloat green, GLfloat blue); Управление цветом с помощью индексации в таблице соответствия представляет в на- настоящее время интерес разве что с точки зрения исторической, поскольку снижение стоимо- стоимости памяти свело на нет основное преимущество этого метода. В то же время ограниченное количество цветов в одном приложении не позволяет создавать изображения высокого каче- качества, особенно динамические. Поэтому в дальнейшем будем полагать, если не оговорено про- противное, что используется полноцветная RGB-система. 2.4. Цвет 79
2.4.3. Настройка атрибута цвета В программе формирования узора Серпинского, которую мы будем в разных вариан- вариантах рассматривать далее в этой главе, используется полноцветная RGB-система управле- управления цветом. В этой программе необходимо установить значения трех атрибутов. Сначала настраивается цвет чистого экрана {clear color), что выполняется вызовом функции glClearColor(): glClearColorA.0, 1.0, 1.0, 1.0); Этот атрибут играет в OpenGL ту же роль, что и знакомый большинству программистов цвет фона {background color). Настройка цвета отображения точек осуществляется функ- функцией glColor3f (). Приведенный ниже оператор устанавливает текущим красный цвет: glColor3fA.0, 0.0, 0.0); Можно настроить и размер примитива "точка" при отображении его на экране. Для этого в OpenGL имеется функция glPointSize(). Приведенный ниже оператор устанавливает раз- размер примитива "точка" на экране равным двум пикселям: glPointSizeB.0); Обратите внимание на то, что атрибуты, определяющие линейные размеры графических элементов на экране, например размер точки или толщина линии, задаются в пикселях. Сле- Следовательно, при выполнении одной и той же программы на компьютерах, оборудованных мониторами с разными размерами экрана и разным разрешением, получится несколько отли- отличающееся в деталях изображение. В некоторых графических API все размеры задаются в единицах, независимых от используемой аппаратуры, но и в этом случае не всегда удается получить изображение одинакового качества на разных платформах. Разработчики OpenGL выбрали компромиссный подход, более практический с их точки зрения. 2.5. Визуализация Итак, в предыдущих разделах вы познакомились с теми графическими примитивами, из которых может состоять изображение, и с тем, как с помощью атрибутов задать их внешний вид на экране. Но до сих пор мы не рассматривали методы, позволяющие точно задать, какие из этих объектов должны появиться на экране. Точно так же, как фотограф по-разному ком- компонует кадр, изменяя объектив и точку съемки, программист должен настроить параметры визуализации в своей прикладной программе. В основе описанной в главе 1 модели синтезированной камеры лежит концепция незави- независимости параметров отображаемых объектов и параметров камеры. После того как в про- программе будут построены модели сцены и камеры, можно приступать к формированию изо- изображения этих объектов с помощью этой камеры. В фотографии камера формирует изобра- изображение, экспонируя фотопленку. Компьютерная система формирует изображение, выполняя последовательность операций, заложенную в конвейер визуализации (viewing pipeline). При- Прикладная программа должна заботиться только о спецификации параметров объектов и каме- камеры, так же, как и фотограф-любитель не думает о том, как работает затвор фотоаппарата или как изменяется химический состав фотоэмульсии под воздействием света. Существуют установленные по умолчанию параметры формирования изображения в ком- компьютере, что аналогично установленной на штативе фотокамере со штатным объективом. Но такая фиксированная камера вынуждает фотографа компоновать кадр, переставляя объекты съемки. Если нужно снять слона, то его придется втащить в павильон и заставить стоять на приличном удалении от камеры. А какого-нибудь паучка вообще не удастся снять, поскольку это не позволит сделать штатный объектив. Поэтому профессиональные фотографы пользу- 80 Глава 2. Графическое программирование
ются портативными камерами с набором сменных объективов или специальным объективом с переменным фокусным расстоянием — трансфокатором. Тот же подход реализуется и в системах компьютерной графики. 2.5.1. Визуализация двухмерных объектов Визуализация двухмерных объектов сводится к выбору прямоугольной области двухмер- двухмерного пространства и переносу ее содержимого на экран, как показано на рис. 2.28. Отобра- Отображаемая область пространства называется прямоугольником видимости (viewing rectangle), или прямоугольником отсечения (clipping rectangle). Объекты, попавшие внутрь этого прямо- прямоугольника, переносятся на экран, а не попавшие в него — отсекаются и не отображаются. Объекты, которые пересекаются контуром прямоугольника отсечения, будут частично види- видимыми. Какими должны быть размеры окна и где его расположить на экране — это отдельный разговор, который мы продолжим в разделе 2.6. а) 6) Рис. 2.28. Визуализация двухмерных объектов: а — объекты до отсечения; б — изображение после выполнения отсечения Не забывайте, что двухмерная графика является частным случаем трехмерной, а следова- следовательно, прямоугольник видимости лежит на плоскости z = О в трехмерной зоне видимости (viewing volume), как показано на рис. 2.29. Если зона видимости не задана явно, то в OpenGL используется установленная по умолчанию зона в виде куба видимости 2x2x2 с началом ко- координат в центре куба. В терминах двухмерной плоскости левый нижний угол прямоугольни- прямоугольника видимости имеет координаты (-1.0, -1.0), а правый верхний — координаты A.0, 1.0). Прямоугольник видимости 2=0 Рис. 2.29. Зона видимости 2.5. Визуализация 81
2.5.2. Ортогональная проекция Описанный двухмерный вид является частным случаем ортогональной проекции {orthographic projection), которую мы подробно рассмотрим в главе 5. При простом орто- ортогональном проецировании точка (xfy, z) на объекте проецируется в точку (х,у, 0) на плос- плоскости проекции (рис. 2.30). Поскольку двухмерное пространство есть плоскость z - 0, то никакого эффекта проецирования мы не заметим. Однако, что не менее важно, при форми- формировании ортогональной проекции двухмерных объектов можно использовать тот же меха- механизм, что и при проецировании трехмерных объектов. В OpenGL ортогональная проекция, характеризуемая параллелепипедом видимости, задается функцией glOrtho(), объявлен- объявленной следующим образом: void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble near, GLdouble far); Рис. 2.30. Ортогональная проекция При настройке по умолчанию камера направлена в сторону, противоположную направле- направлению оси z на рис. 2.31. При ортогональном проецировании видны только объекты, попавшие внутрь параллелепипеда видимости. В отличие от реальной камеры, видимыми полагаются и объекты, расположенные позади нее. Таким образом, до тех пор, пока плоскость z = 0 нахо- находится между ближней и дальней гранями параллелепипеда видимости, все объекты, попавшие в прямоугольник видимости, будут включены в изображение. Если прикладной программист испытывает определенный дискомфорт от необходимости использовать трехмерную зону ви- видимости при работе с двухмерными объектами, то можно воспользоваться специальной функцией gluOrtho2D(), которая специфицирует не параллелепипед видимости, а именно прямоугольник. Определение функции имеет вид void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top) Результат выполнения этой функции будет таким же, как и после вызова функции glOrtho(), где для аргументов near и far установлены значения -1.0 и 1.0 соответственно. В главах 4 и 5 будет рассмотрено, как организовать в программе перемещение камеры и фор- формирование более сложных видов. 82 Глава 2. Графическое программирование
Z у/ и L щ 1 II f 1 X / > X / (правый, верхний, дальний) (левый, нижний, ближний) Рис. 2.31. Размещение камеры при настройке по умолчанию параметров параллелепипеда види- видимости при ортогональном проецировании. Показаны соответствие аргументов вызова функции glOrthof) и положения вершин параллелепипеда 2.5.3. Матричный режим проецирования В системах компьютерной графики, использующих конвейерный принцип обработки, же- желательно, чтобы все операции формирования изображения выполнялись единообразно — в форме перемножения матриц. Ряд матриц преобразования являются переменными состояния системы OpenGL и действуют до тех пор, пока их принудительно не изменят. Из них наи- наибольшее значение для нас сейчас имеют матрицы вида {model-view) и проецирования (или проективного преобразования). В начальном состоянии обе эти матрицы имеют вид единич- единичных. В главе 4 читатели смогут познакомиться с имеющимися в составе OpenGL средствами выполнения операций с матрицами. Обычно этим матрицам присваиваются значения, кото- которые отличают их от единичных. Специальные функции позволяют манипулировать любыми типами матриц. Выбор конкретного типа матрицы осуществляется настройкой переменной режима работы с матрицами (matrix mode). Эта переменная, как и ряд других, характеризу- характеризует текущее состояние графической системы. По умолчанию переменной присваивается зна- значение, соответствующее работе с матрицей вида (матрицы model-view). Поэтому для настрой- настройки типа проецирования нужно первым делом изменить состояние переменной режима работы с матрицами. Ниже приведен типовой фрагмент программы, в котором выполняется настрой- настройка двухмерного прямоугольника видимости. glMatrixMode(GL_PROJECTION); glLoadldentityO; gluOrtho2D@.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); Значения аргументов функции gluOrtho2D() задают прямоугольник видимости размером 500x500, левый нижний угол которого находится в начале двухмерной системы координат. Последний оператор фрагмента восстанавливается режим работы с матрицей вида. В слож- сложной программе считается хорошим тоном восстанавливать значения параметров текущего со- 2.5. Визуализация 83
стояния после выполнения каких-либо манипуляций. Если следовать этому правилу, то при разработке любой подпрограммы всегда можно будет считать, что текущим является режим работы с матрицей вида. 2.6. Функции управления Мы рассмотрели практически все вопросы, необходимые для разработки программы по- построения на экране узора Серпинского. Осталось невыясненным единственное — как графи- графическая система взаимодействует с подсистемой окон и операционной системой. Не углубля- углубляясь в детали определенных платформ, таких как система X Window под UNIX или Microsoft Windows, сразу же отметим, что интерфейс между графической системой и конкретной опе- операционной системой может быть достаточно сложным. В деталях этот интерфейс очень тесно связан со спецификой определенной операционной системы, и их обсуждение далеко уведет нас от главной задачи — исследования методов компьютерной графики. Поэтому мы ограничимся минимальным набором операций, которые необходимы для нормальной работы прикладной графической системы. Большинство из них располагает ком- комплектом версий библиотек, предназначенных для работы с определенными операционными системами. В OpenGL эта роль принадлежит библиотеке GLUT (GL Utility Toolkit). Именно ее функции учитывают все особенности определенной операционной системы. Таким обра- образом, основной набор функций API можно считать независящим от используемой платформы. Практически вся настройка сводится к добавлению нового пути в набор путей поиска биб- библиотек. В данной главе и в главе 3 при описании средств взаимодействия современных гра- графических систем с операционной средой мы будем опираться на функции библиотеки GLUT. 2.6.1. Взаимодействие с подсистемой окон Термин окно (window) используется в информатике и программировании очень часто и имеет множество значений. В компьютерной графике термин окно, или окно экрана (screen window), означает прямоугольную область, на которой формируется изображение. Чаще всего это экран ЭЛТ, хотя в данном случае это и непринципиально. Окно характеризуется своими размерами (высотой и шириной), и поскольку в пределах окна выводится содержимое буфера кадра, положение на поле окна измеряется в оконных (window coordinates) или экранных ко- координатах (screen coordinates), причем в качестве единиц измерения используются пиксели10. Современная операционная среда позволяет выводить на один и тот же экран множество окон. Каждое из них может принадлежать разным приложениям или даже в пределах одного приложения выполнять совершенно разные функции. Мы в дальнейшем будем использовать термин подсистема окон, подразумевая под ним многооконную среду, поддерживаемую в та- таких операционных системах, как X Window или Microsoft Windows. Положение внутри этого окна отсчитывается от его базовой точки — обычно один из углов прямоугольной рамки ок- окна. В большинстве случаев им является левый нижний угол, который имеет оконные коорди- координаты @, 0), но учтите, что в некоторых операционных системах это соглашение не выполня- выполняется. Поскольку в растровом дисплее развертка выполняется по той же траектории, что и в коммерческом телевидении (слева направо и сверху вниз), то с этой точки зрения желательно было бы выбирать в качестве начала координат левый верхний угол. В OpenGL команды под- подразумевают, что начало оконной системы координат находится в левом нижнем углу, хотя информация, получаемая от подсистемы окон, в частности о положении указателя мыши, часто выражается в иной системе координат, которая имеет начало отсчета в левом верхнем IOB OpenGL система оконных координат трехмерная, а система экранных координат — двухмерная. В обеих системах в качестве единицы измерения применяются пиксели, но третий компонент в системе окон- оконных координат позволяет хранить информацию о глубине. 84 Глава 2. Графическое программирование
углу. Таким образом, приходится выполнять дополнительное преобразование данных, посту- поступающих от операционной системы. Обращаю ваше внимание и еще на одно обстоятельство. Хотя весь экран дисплея имеет определенное стандартное разрешение, скажем 1280x1024 пикселей, окно приложения может иметь любые размеры (но, конечно, не больше, чем размер полного экрана). Таким образом, буфер кадра приложения должен иметь разрешение соответственно размеру окна. Если ис- используется окно размером 300x400 пикселей, то нужно считать, что и буфер кадра имеет раз- разрешение 300x400, хотя при этом используется только часть памяти, отведенной для буфера. Перед тем как окно приложения будет открыто на экране, должен состояться сеанс связи между подсистемой окон и OpenGL. В составе библиотеки GLUT эта операция возлагается на функцию glutlnit(): void glutlnit(int *argcp, char **argv); Функция имеет два аргумента, через которые можно передать аргументы командной стро- строки, как в стандартной С-функции main(). После выполнения glutlnit() можно открыть окно OpenGL-программы, вызвав функцию glutCreateWindow(): int glutCreateWindow(char *title); Аргумент title этой функции — надпись, которая будет выведена в строке заголовка окна. При создании окна устанавливаются его параметры, назначенные графической системой по умолчанию, — размер, положение на экране и режим RGB управления цветом. Для на- настройки иных значений параметров нужно перед вызовом glutCreateWindow() обратиться к соответствующим функциям их установки. Например, в следующем фрагменте программы устанавливается размер окна 480x640 пикселей, а само окно размещается в левом верхнем углу экрана". glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSizeD80, 640); glutlnitWindowPosition@,0); Обратите внимание на то, что константы задания отдельных режимов объединяются в ар- аргументе функции glutInitDisplayMode() операцией побитового ИЛИ. Константа GLUT_RGB задает RGB-режим. Если предполагается использовать режим индексированных цветов, то вместо константы GLUT_RGB следует передать GLUT_INDEX. Вторая константа GLUT_DEPTH зада- задает использование буфера глубины при выполнении удаления невидимых поверхностей. Тре- Третья константа GLUT_DOUBLE задает двойную буферизацию (одиночная буферизация задается константой GLUT_SINGLE). Режим, заданный по умолчанию, который вполне достаточен для той задачи, которую мы будем решать в этой главе, — цвет RGB, отсутствие удаления невидимых поверхностей и одиночная буферизация. Эти настройки можно и не устанавливать в явном виде, но включе- включение их в программу делает ее текст более понятным для тех, кто только осваивает методику программирования на OpenGL. 2.6.2. Соотношение сторон и видовые окна Сам по себе параметр соотношение сторон {aspect ratio) применим к любому прямо- прямоугольнику и означает отношение его ширины к высоте. Существующая в графической системе независимость между объектами, визуализацией и настройкой окна приложения В таких системах, как X Window, запрашивается формирование окна с определенными свойствами. Но переданные значения свойств могут быть переопределены операционной системой, если они не соответст- соответствуют функциональным возможностям установленной версии. 2.6. Функции управления 85
может привести к нежелательным побочным эффектам, в частности искажению пропор- пропорций между продольными и поперечными размерами объектов на экране. Соотношение сторон прямоугольника видимости задается аргументами функции glOrtho() и может отличаться от соотношения сторон окна, которое задается аргументами функции glutInitWindowSize(). Именно такой случай продемонстрирован на рис. 2.32, и вы ви- видите, к каким искажениям пропорций в изображении объектов на экране это привело. Следует оговориться, что "тайной пружиной" такого искажения явилось не только несо- несоответствие между соотношениями сторон, но и назначенный по умолчанию режим ото- отображения, при котором все содержимое прямоугольника видимости должно быть пере- перенесено в заданное окно на экране. Чтобы избежать таких искажений, следует отменить этот режим — использовать концепцию видового окна. Видовое окно (viewport) — это прямоугольная область в пределах окна на экране (окно в окне). По умолчанию видовое окно занимает всю область экрана, запрошенную у операционной системы, но можно ус- установить и другие его размеры, вызывав функцию glViewport(): void glViewport(GLint х, GLint у, GLsizei w, GLsizei h); Первые два аргумента этой функции — х и у — положение левого нижнего угла прямо- прямоугольника видового окна относительно левого нижнего угла выделенной области экрана. Следующие аргументы — w и h — задают высоту и ширину видового окна. Все параметры задаются как целые числа и измеряются в пикселях. Соответствие между отсекающей рамкой (прямоугольником видимости) и видовым окном на экране иллюстрируется на рис. 2.33. При заданных размерах окна приложения таким способом можно в прикладной программе устра- устранить искажение пропорций изображения. а) б) Рис. 2.32. Искажение пропорций при несоответствии со- соотношения сторон прямоугольника видимости и окна приложения на экране: а — прямоугольник видимости; б — окно на экране Параметры видового окна также характеризуют текущее состояние графической систе- системы. Если изменить их между последовательными циклами тонирования изображения или циклами повторения вывода изображений одних и тех же объектов, то можно получить эффект появления нескольких изображений на поле окна, причем управлять можно не только положением видового окна, но и соотношением его сторон (результаты очень впе- впечатляют). В главе 3 мы еще вернемся к тому, как можно изменять размеры и форму окна приложения на экране. 86 Глава 2. Графическое программирование
-Видовое окно ¦Графическое окно Отсекающая рамка Рис. 2.33. Соответствие между отсекающей рамкой и видовым окном на экране 2.6.3. Функции main(), display() и myinit() В принципе, можно было бы скомбинировать приведенный выше фрагмент инициализа- инициализации окна и текст программы, приведенный в разделе 2.1, и получить завершенную OpenGL- программу формирования узора Серпинского. Но, к сожалению, не так просто все делается в современных операционных системах. Нам предстоит решить еще две проблемы: первая из них характерна для всех графических систем, а вторая касается еще одного аспекта взаимо- взаимодействия с операционной системой. В режиме немедленного графического вывода примитив выводится на экран, как только он формируется графической системой. При этом графическая система использует текущие значения атрибутов, характеризующие состояние системы. При выполнении нашей простой программы, в которой отсутствует взаимодействие с пользователем, изображение выводится на экран, и на этом процесс завершается. После завершения работы программы операционная система сразу же закрывает окно приложения, и пользователь вряд ли успеет заметить, что же там нарисовала программа. Самое простое решение этой проблемы — вставить в про- программу выдержку времени с помощью стандартной функции sleep(). Но даже в тривиальном приложении следует поискать более интересное решение. В главе 3 будет рассмотрен меха- механизм обработки событий, который предоставляет прикладному программисту значительно более широкие возможности контролировать процесс выполнения программы. А сейчас мы воспользуемся функцией glutMainLoop() из библиотеки GLUT. Определена эта функция сле- следующим образом: void glutMainLoop(void) Обращение к этой функции приведет к тому, что программа "войдет" в цикл обработки событий. Пока в очереди событий не будет заявки на обработку, программа будет находиться в состоянии ожидания и пользователь сможет рассматривать созданное изображение. Так бу- будет продолжаться до тех пор, пока пользователь не нажмет какую-либо клавишу. Собственно вывод графической информации выполняется с помощью функции отобра- отображения с обратным вызовом {display callback). Эта функция задается еще одной функцией из библиотеки GLUT, определение которой имеет вид void glutDisplayFunc(void (*func)(void)); Здесь аргумент f unc задает имя функции, которая будет вызываться операционной систе- системой, как только она придет к выводу, что окно приложения OpenGL должно быть обновлено. Одно из таких событий — открытие окна. Поскольку разрабатываемая нами программа не 2.6. Функции управления 87
реагирует на действия пользователя, то все процедуры формирования изображения должны быть сосредоточены в теле функции, имя которой передается в качестве аргумента glutDisplayFunc(). Эта функция будет выполнена однократно сразу же после открытия окна и выведет изображение узора. Но функция формирования изображения может вызываться не только при открытии окна, но и при его перемещении или закрытии окна другого приложения, частично или полностью закрывающего окно OpenGL-программы. Для большинства неинтерактивных графических приложений вполне подходит та про- программа main ( ), которая приведена в листинге 2.1. Листинг 2.1. Функция main() Iinclude <GL/glut.h> void raain(int argc, char **argv) { glutlnit(&argc,argv); glutlnitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutlnitWindowSizeE00,500); glutInitWindowPosition@,0); glutCreateWindow("Simple OpenGL example"); glutDisplayFunc(display); myinitj); glutMainLoop(); _} Для начальной установки переменных текущего состояния, отвечающих за настройку па- параметров визуализации и атрибутов, используется функция myinit( )l2. Эти параметры пред- предпочтительнее устанавливать один раз, независимо от функции формирования изображения. Стандартный заголовочный файл библиотеки GLUT включается в текст программы перед определениями любых функций. В большинстве систем программирования на языке С для этого используется директива компилятора iinclude <GL/glut.h> При этом одновременно в текст программы включаются и заголовочные файлы стандарт- стандартной библиотеки gl.h и библиотеки утилит OpenGL glu.h. В этих файлах находятся и макро- макроопределения таких констант, как GL_LINES и GL_RGB. 2.6.4. Структура программы Все программы, которые мы будем рассматривать в данной книге, имеют такую же струк- структуру, что и программа построения узора Серпинского. Во всех этих программах будет ис- использоваться библиотека GLUT. Функция main() включает вызовы функций из библиотеки GLUT, которые устанавливают параметры окна (или окон) приложения и обеспечивают под- поддержку со стороны операционной системы процесса отображения. В функции main() также объявляется имя функции отображения с обратным вызовом, которая, собственно, и фор- формирует изображение. Помимо нее, в программе могут существовать и другие функции с об- обратным вызовом, которые необходимы для организации взаимодействия с пользователем. В 12 Я надеюсь, что у читателей не возникнет путаница из-за того, что я использую те же имена функ- функций, которые упоминаются в руководстве OpenGL Programmer's Guide [Ope97,a] или в документации на биб- библиотеку GLUT [Kil94,a]. 88 Глава 2. Графическое программирование
функции myinit() настраиваются значения атрибутов и опций прикладной программы, для чего используются функции из библиотек GL и GLU. Хотя эти настройки можно выполнять и в функции main(), текст программы будет понятнее и его будет легче сопровождать, если об- обращения к функциям из библиотеки GLUT вынести в отдельный блок (в данном случае — функцию main()). Как правило, в программах OpenGL практически вся процедура формиро- формирования изображения выполняется в функции отображения с обратным вызовом, которую чаще всего именуют display (). 2.7. Программа Gasket В дополнение к функции main(), представленной в листинге 2.1, в программу Gasket формирования узора Серпинского нужно включить функции myinit() (листинг 2.2) и display () (листинг 2.3). Эта программа будет выводить точки красного цвета на белом фоне. При формировании изображения используются двухмерная система координат и прямо- прямоугольник (квадрат) видимости размером 500x500, причем начало координат располагается в левом нижнем углу этого квадрата. Листинг 2.2. Функция инициализации myinit() void myinit(void) { /* Атрибуты */ glClearColorA.0, 1.0, 1.0, 0.0); /* белый фон */ glColor3fA.0, 0.0, 0.0); /* вывод красным цветом */ /* Настройка параметров визуализации */ glMatrixMode(GL_PROJECTION); gluLoadIdentity(); gluOrtho2D@.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); Листинг 2.3. Функция формирования изображения display () void display( void ) { /* Определение типа данных для точек */ typedef GLfloat point2[2]; point2 vertices[3]={{0.0,0.0},{250.0,500.0},{500.0,0.0}}; /* Треугольник */ int i, j, k; int rand(); /* Стандартный генератор случайных чисел */ point2 p ={75.0,50.0}; /* Произвольная точка внутри треугольника */ glClear(GL_COLOR_BUFFER_BIT); /* Очистка окна */ 2.7. Программа Gasket 89
/* Вычисление и вывод на экран 5000 новых точек */ for( k=0; k<5000; j=rand()%3; /* Выбрать вершину случайным образом */ /* Вычислить координаты точки, лежащей посередине между вершиной и предыдущей точкой */ р[0] = (p[0]+vertices[j][0])/2.0; = (p[l]+vertices[j][l])/2.0; /* Вывести точку на экран */ glBegin(GL_POINTS); glVertex2fv(p); glEnd(); glFlush(); В теле функции формирования изображения определены произвольный треугольник (массив из трех вершин) и произвольная точка внутри этого треугольника. Далее следует цикл, в котором формируется фиксированное количество (в данном случае 5000) точек. В конце процедуры вызывается OpenGL-функция glFlush(); тем самым система прину- принуждается выводить сформированные точки на экран как можно скорее. Полный листинг этой программы читатель найдет в приложении А. 2.8. Многоугольники и рекурсия Результат работы описанной выше программы (см. рис. 2.4) демонстрирует весьма инте- интересную графическую структуру. Чем больше точек будет формировать эта программа, тем менее случайный характер будет носить изображение. Анализируя эту структуру, приходим к выводу, что независимо от количества сформированных точек, они никогда не появляются в середине некоторых треугольных областей. Если провести отрезки между средними точками прилежащих сторон исходного треугольника, то он будет разбит на четыре треугольника, причем в средний точки узора никогда не попадут (рис. 2.34). В других трех треугольниках (исключая центральный) можно повторить описанную процедуру разбиения. Возвра- Возвращаясь к узору Серпинского (см. рис. 2.4), вновь отметим, что и в средних треугольниках второго уровня разбиения точки узора не появляются. Эти рассуждения лежат в основе второго варианта алго- алгоритма построения узора Серпинского — варианта рекурсив- рекурсивного разбиения многоугольников. Этот алгоритм не требует генератора случайных чисел. Одно из достоинств второго ва- Рис 2.34. Один цикл разбиения рианта алгоритма состоит в том, что он позволяет использо- треугольника г г г вать заливку многоугольников на экране. Стратегия построе- построения узора довольно очевидна. Исходный треугольник делится на четыре, соединяя попарно середины прилежащих сторон, а затем средний треугольник изымается из дальнейших операций. Цикл повторяется с каждым из оставшихся трех тре- треугольников до тех пор, пока размеры сформированных треугольников не станут достаточно малы — сравнимы с размерами пикселя. 90 Глава 2. Графическое программирование
Этот процесс реализуется с помощью рекурсивно вызываемой функции, т.е. функции, вызы- вызывающей саму себя. Начинаем с построения исходного треугольника по трем заданным вершинам: void triangle( point2 a, point2 b, point2 с) { glBegin(GLJTRIANGLES); glVertex2fv(a); glVertex2fv(b); glVertex2fv(c); glEnd(); } Пусть вершины треугольника будут представлены элементами массива point2 v[3]; тогда средние точки треугольника также образуют массив ш[3], элементы которого вычис- вычисляются в приведенном ниже фрагменте программы: for(j=0; j<2; j++) m[0] for(j=0; j<2; j++ for(j=0; j<2; j++) m[2][j]=(v[l][j]+v[2][j])/2.0; Имея в своем распоряжении эти шесть точек, можно использовать функцию triangle () для формирования трех треугольников: вершины первого— (v[0], m[0], m[l]), второго — (v[2], m[l], m[2]), третьего—(v[l], m[2], m[0]). Нам не нужно выводить изображения этих треугольников на экран — они потребуются для выполнения дальнейшего деления. Та- Таким образом, процесс становится рекурсивным. Определим рекурсивную функцию di- vide_triangle(point2 a, point2 b, point2 c, int k), которая будет выводить изображе- изображение треугольника на экран только тогда, когда аргумент к будет равен нулю. В противном случае функция будет выполнять разбиение треугольника, заданного аргументами a, b и с, и уменьшать значение к. Ниже приведен текст этой функции: void divide_triangle(point2 a, point2 b, point2 с, int k) { point2 ab. ac, be; int j; if(k>0) { /* вычисление координат средних точек сторон */ for(j=0; j<2; j++) ab[j]=(a[j]+b[j])/2; for(j=0; j<2; j++) ac[j]=(a[j]+c[j])/2; for(j=0; j<2; j++) bc[j]= /* Разбиение всех треугольников, кроме среднего */ divide_triangle(a, ab, ас, k-1); divide_triangle(c, ac, be, k-1); divide_triangle(b, be, ab, k-1); } else(triangle(a,b,c)); /* Вывод треугольника на экран и завершение работы */ 2.8. Многоугольники и рекурсия 91
При такой организации программы функция display() становится тривиальной. В ней используется объявленное глобальным в функции main() значение переменной п, которое за- задает количество циклов рекурсии13, и вызывается функция divide_triangle(). void display(void) { glClear(GL_COLOR_BUFFER_BIT); divide_triangle(v[O], v[l], v[2], n); glFlush(); A aa aa AA AA AAAAAAAA Л A^ _ AAAAAA A ^ .A A A A А Л A A AA AAA AAA AAAAAAAA Л A A AAA Оставшаяся часть программы та же, что и в предыдущем варианте, за исключением того, что в ней считывается значение переменной п. Изображение, которое сформируется после пяти циклов выполнения рекурсивной проце- процедуры, представлено на рис. 2.35. Полный текст программы приведен в при- приложении А. Рис. 2.35. Изображение, которое сформируется после пяти циклов рекурсивного разбиения треугольников Рис. 2.36. Тетраэдр 2.9. Трехмерный узор Серпинского Я неоднократно подчеркивал, что двухмерная графика— это только частный случай трехмерной, но мы до сих пор не рассматривали програм- программы, в которых использовалось бы представление трехмерных объектов. В этом разделе будет показано, как преобразовать рассмотренную выше про- программу построения узора Серпинского в трехмерную. В этом разделе мы воспользуемся обоими методами формирования узора — и точечным, и ме- методом рекурсивного разбиения. В обоих случаях все начинается с про- пространственного аналога треугольника — тетраэдра (рис. 2.36). 2.9.1. Использование трехмерных точек Поскольку тетраэдр является выпуклым геометрическим телом, то средняя точка отрез- отрезка, проведенного между некоторой вершиной и любой точкой внутри тетраэдра, также ле- лежит внутри тетраэдра. Следовательно, можно использовать процедуру, подобную описан- описанной ранее, но на сей раз вместо трех вершин треугольника включить в рассмотрение четы- четыре вершины тетраэдра. Обратите внимание на то, что, до тех пор пока любые три вершины не будут коллинеарны (т.е. лежать на одной прямой), можно случайным образом выбирать любую из четырех вершин тетраэдра и это не повлияет на характер сформированного в ре- результате изображения. 13Обращаю ваше внимание на то, что, работая с OpenGL, нам часто придется передавать данные в функции с обратным вызовом через глобальные переменные. Хотя желательно избегать использования гло- глобальных переменных, но при обращении к функция» с обратным вызовом у нас просто нет иного выхода. 92 Глава 2. Графическое программирование
Первым делом нужно внести определенную коррекцию в функцию display (). Определим в ней тип данных для представления трехмерной точки: typedef GLfloat point3[3]; Затем определим вершины исходного тетраэдра: /* Вершины произвольного тетраэдра */ point3 vertices[4]={{0.0, 0.0, 0.0},{250.0, 500.0, 100.0}, {500.0, 250.0, 250.0},{250.0, 100.0, 250.0}}; /* Произвольная исходная точка */ point3 р={250.0,100.0,250.0}; Для формирования точек в программе будем использовать функцию glPoint3fv(). Одна из проблем, которая специфична именно для трехмерного случая, состоит в том, что форми- формируемые точки не лежат в одной плоскости, а потому на двухмерном изображении трудно бу- будет рассматривать трехмерную структуру. Чтобы "обойти" ее, не используя геометрические построения, о которых речь еще впереди, воспользуемся цветовым кодированием — цвет ка- каждой формируемой точки будет нести информацию о ее положении в глубину. Это несколько облегчит восприятие изображения, хотя до полной иллюзии пространства при этом, конечно же, очень далеко. Ниже приведен текст функции формирования изображения display (). void display() { /* Вычислить и вывести на экран единственную новую точку */ int rand(); int i; j=rand()%4; /* Случайным образом выбрать индекс вершины */ /* Вычислить координаты точки, расположенной на полпути между выбранной вершиной и точкой, сформированной в предыдущем цикле */ р[0] = (p[0]+vertices[j][O])/2.O; р[1] = (p[l]+vertices[j][l])/2.0; р[2] = (p[2]+vertices[j][2J)/2.0; /* Вывести точку на экран */ glBegin(GL_POINTS); glColor3f(p[0]/250.0,p[l]/250.0,р[2]/250.0); glVertex3fv( p ); glEnd(); /* Заменить координаты прежней точки */ old.x=new.x; old.y=new.y; old.z=new.z; glFlush(); } Поскольку мы теперь работаем в трехмерном пространстве, то в файле main.с нужно оп- определить не прямоугольник, а параллелепипед видимости: glOrtho(-500.0 , 500.0, -500.0, 500.0 ,-500.0, 500.0); 2.9. Трехмерный узор Серпинского 93
На рис. 2.37 и ил. 1 на цветной вклейки по- показано, что если сформировать достаточно большое количество точек, то изображение будет походить на исходный тетраэдр, из ко- которого "вынут" меньший тетраэдр в центре. Рис. 2.37. Трехмерный узор Серпинского 2.9.2. Использование многоугольников в трехмерном пространстве Теперь рассмотрим, каким образом в трехмерном пространстве можно реализовать второй подход к построению узора. Гранями тетраэдра являются четыре треугольника, заданные че- четырьмя вершинами. Для каждой из четырех граней можно применить алгоритм разбиения, точ- точно такой же, как тот, который применялся к единственному треугольнику при работе в двухмер- двухмерном пространстве. В результате текст новой программы в значительной мере воспроизводит текст программы, формирующей двухмерный узор по тому же принципу. Единственное отличие в базовой функции разбиения — использование трехмерных точек вместо двухмерных: void triangle( point3 a, point3 b; point3 с) { glBegin(GL_POLYGON); glVertex3fv(a); glVertex3fv(b); glVertex3fv(c); glEnd(); Функция divide_triangle() выполняет те же операции, но над трехмерными точками: void divide_triangle(point3 a, point3 b, point3 c, int k) point3 ab, ac, be; int j; if (k>0) for(j=0; j<3; for(j=0; j<3; for(j=0; j<3; divide_triangle(a ab[j]= ac[j]= bc[j]=(b[j]+c[j])/2; ab, ac, k-1); divide_triangle(c, ac, be, k-1); divide_triangle(b, be, ab, k-1); 94 Глава 2. Графическое программирование
else(triangle(a,b,c)); /* Вывод треугольника на экран и завершение работы */ } Для разделения четырех граней тетраэдра нужно применить эту функцию к каждой из них: void tetrahedron( int n ) glColor3fA.0,0.0,0.0); divide triangle(v[O], v[l], v[2], k); glColor3f@.0,1.0,0.0); divide triangle(v[3], v[2], v[l], k); glColor3f@.0,0.0,1.0); divide_triangle(v[0], v[3], v[l], k); glColor3f@.0,0.0,0.0); divide_triangle(v[0], v[2], v[3], k); } Здесь переменная п по-прежнему представляет количество циклов выполнения рекурсив- рекурсивной процедуры. При выводе на экран элементарных треугольников каждой грани им следует задать свое значение атрибута цвета, тогда изображение трехмерной структуры будет лучше восприниматься пользователем. Но перед нами стоит еще одна проблема, которую следует решить, если уж мы хотим получить нормальное изображение. 2.9.3. Удаление невидимых поверхностей При выполнении программы, рассмотренной в предыдущем разделе, результат не очень вас впечатлит. Программа будет выводить на экран треугольники в том порядке, в каком они формируются в процессе выполнения деления граней. Этот порядок не имеет отношения к расположению граней в пространстве, каждый треугольник будет заливаться своим цветом, причем те треугольники, которые формируются позже, перекроют нарисованные раньше. Совсем другая картинка открылась бы нашему взору, если бы узор создавался из реальных маленьких непрозрачных треугольников. В этом случае были бы видны только те из них, ко- которые ближе к наблюдателю. На рис. 2.38 схематически показано, в чем состоит суть про- проблемы невидимых поверхностей. Наблюдатель видит прежде всего ближайший к нему уча- участок А, а что касается остальных, то треугольник В оказывается вообще невидим, а в тре- треугольнике С видна только та часть, которая не перекрывается участком А. Даже не вдаваясь в детали алгоритмов анализа невидимых поверхностей, можно сразу заметить, что решающее значение имеет взаимное положение наблюдателя и отобра- отображаемых участков поверхностей, в данном случае— тре- треугольников. Алгоритмы упорядочения объектов в порядке их близости к наблюдателю называются алгоритмами анализа видимости поверхностей (visible-surface algorithms) или ал- алгоритмами удаления невидимых поверхностей (hidden- surface-removal algorithms) — все зависит от точки зрения. Эти алгоритмы мы подробно рассмотрим в главах 4 и 7. В данной программе будет использован алгоритм удаления невидимых поверхностей, получивший в литературе название алгоритма z-буфера (z-buffer algorithm), который поддержива- Рис' 2'38' Проблема удаления _ _;¦ J, невидимых поверхностей ется в OpenGL. Существуют средства управления этим алго- 2.9. Трехмерный узор Серпинского 95
ритмом, позволяющие легко отключать или включать его. В функции main() нужно запросить дополнительную память для работы этого алгоритма — z-буфер или буфер глубины. Это выпол- выполняется спецификацией константы GLUT_DEPTH при вызове функции glutInitDisplayMode(): glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); Разрешается выполнение алгоритма вызовом функции glEnable() с константой GL_DEPTH_TEST в качестве аргумента: glEnable(GL_DEPTH_TEST) Фрагмент с операторами настройки включается либо в файл main.с, либо в функцию инициализации (файл myinit.c). Поскольку алгоритм хранит информацию в буфере глубины, его требуется очищать перед формированием каждого нового изображения. Очистка выпол- выполняется в той части функции отображения, где инициализируются параметры процесса: glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); Функция отображения display () теперь примет вид void display() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); tetrahedron(n); glFlush(); Результат ее выполнения после пяти цик- циклов показан на рис. 2.39 и на ил. 2 цветной вклейки. Полный текст программы приведен в приложении А. Рис. 2.39. Трехмерный узор Серпинского после выполнения пяти циклов рекурсивной про- процедуры 2.10. Резюме В этой главе читатели познакомились с основными характеристиками API OpenGL и с тем, как в этой системе реализуются базовые концепции, представленные в главе 1. Хотя пер- первое из рассмотренных в этой главе приложений и является двухмерным, мы все время обра- обращали ваше внимание на то, что двухмерная графика в OpenGL — это только частный случай трехмерной. Было показано, как с минимальными усилиями распространить результаты, по- полученные для двухмерного случая, на трехмерный вариант постановки задачи. В качестве учебной задачи была рассмотрена нетривиальная задача построения узора Серпинского с помощью алгоритмов двух типов. Некоторые математические вопросы, свя- связанные с этой задачей, мы предлагаем вам проанализировать самостоятельно в упражнениях 96 Глава 2. Графическое программирование
после основного текста главы. Более пространное вступление в теорию фракталов читатель найдет в главе 11, где эта теория будет использована для формирования реалистически вы- выглядящих объектов случайной формы. Литературные источники, которые упоминаются в за- заключительном разделе главы, содержат много других примеров интересных кривых и по- поверхностей, которые можно реализовать в довольно простых программах. Обзор развития графических API показал, насколько важно как можно раньше начинать работать с задачами компьютерной графики в трехмерной постановке. Много лет концепту- концептуальная модель перьевого плоттера использовалась в качестве базовой при создании графиче- графических API, в частности Postscript. Разработка международных стандартов для графических API началась в 1970-х годах, и первым результатом в этом направлении было принятие в 1984 году Международной организацией по стандартизации (International Standards Organiza- Organization — ISO) стандарта GKS. Однако основанный на модели перьевого плоттера стандарт GKS распространялся только на двухмерные графические приложения и имел ограниченное при- применение в индустрии автоматизированного проектирования. Хотя позднее он был расширен и охватывал трехмерные приложения (стандарт GK.S-3D), ограничения, связанные с ориента- ориентацией на указанную концептуальную модель, все же остались. API PHIGS и PHIGS+, поначалу применявшиеся только в САПР, изначально создавались как трехмерные на основе модели синтезированной камеры. Парадигма "моделирование-тонирование" реализована в интерфейсе Renderman Interface. Хотя его спецификация охватывает только интерфейс между средствами моделирования и тонирования, этот программный продукт использует высококачественный алгоритм тониро- тонирования на основе трассировки лучей и наряду с другими широко используется при создании мультипликационных фильмов. Графическая система OpenGL появилась как развитие GL API, основанного на модели синтезированной камеры в приложении к конвейерной архитектуре. Пакет GL был разрабо- разработан фирмой Silicon Graphics для выпускаемых ею рабочих графических станций, в которых реализована конвейерная архитектура обработки на базе специализированных СБИС. Поэто- Поэтому, хотя между PHIGS и GL есть много общего, пакет GL был разработан специально для по- получения с высокой скоростью тонированных изображений, близких к реальным. Система OpenGL появилась в ответ на спрос со стороны прикладных программистов, убедившихся в достоинствах программирования с использованием GL и пожелавших реализовать эти прин- принципы на других платформах. При этом оказалось необходимым изъять из пакета функции, входящие в компетенцию операционных систем, и сосредоточить основное внимание на эф- эффективных методах моделирования и тонирования. Приведенные в этой главе примеры и программы касаются прежде всего описания и ото- отображения геометрических объектов. Если рассматривать материал данной главы с точки зре- зрения парадигмы "моделирование-тонирование"', то основное внимание в нем было уделено вопросам моделирования. Но созданные простейшие модели абсолютно неструктурированы. Объекты представлены только набором вершин и атрибутов. В главе 8 будет рассмотрен ие- иерархический подход к моделированию, который позволяет представить в модели отношения между объектами. Но, тем не менее, и на этой базе можно создавать довольно впечатляющие графические программы. 2.11. Рекомендуемая литература Узор Серпинского является отличным примером для ввода читателя в мистический мир геометрии фракталов, которая в разных аспектах обсуждается в работах [Bar93, HH90, Мап82. РгиЩ. Пакеты API, построенные на базе модели перьевого плоттера, используются в Postscript [Ado85] и LOGO [Pap81]. LOGO поддерживает так называемую "черепашью", или "паучью" 2.11. Рекомендуемая литература 97
графику (turtle graphics). Этот пакет несложен в изучении и пригоден для построения разного рода двухмерных математических кривых. О нем еще пойдет речь в главе 8 (см. упр. 2.4). Международные и национальные графические стандарты описаны в целом ряде технических документов: [ANS185] — GKS, [ISO88] — GKS-3D, [ANS188] —PH1GS, [PHIG89] — PHIGS+. Соответствующую документацию можно получить в Американском национальном институте стандартов (ANSI — American National Standards Institute) и Международной организации по стандартизации (ISO — International Standards Organization). Существует и множество книг, по- посвященных описанию этих API, — [Ang90, End84, Fol94, Hea94, Hop83, Нор91]. Операционная система X Window [Sch88] стала в последнее время фактическим стандар- стандартом для рабочих станций на платформе UNIX. Ее структура и возможности оказали большое влияние на развитие оконных систем на других платформах. Последние версии X Window включают и специальное расширение PHIGS — РЕХ. Интерфейс Renderman описан в работе [Ups89]. Два наиболее популярных источника информации о системе OpenGL — это руководство программиста OpenGL Programmer's Guide [Ope97,a] и справочник OpenGL Reference Manual [Ope97,b]. Существует и формальная спецификация OpenGL— [Seg92]. Во втором издании руководства программиста Programmer's Guide описана библиотека GLUT, разработанная Марком Килгардом (Mark Kilgard) [Kil94,b]. В это руководство включено множество приме- примеров использования функций OpenGL. Библиотека GLUT была разработана для операционной системы X Window [KH96J, но ее новые версии применимы и в операционных системах Win- Windows 98 и NT. В первой редакции руководства для интерфейса с X Window использован бо- более простой инструментальный пакет aux. Конвертирование программ, написанных с ориен- ориентацией на aux в программы, работающие с библиотекой GLUT, не представляет труда. Много информации об OpenGL и примеры программ можно найти и в Internet. Достаточно много адресов соответствующих страниц вы найдете в самом начале приложения А. Упражнения 2.1. Небольшая модификация алгоритма формирования узора Серпинского с помощью рекурсивного разбиения треугольников позволяет создавать с помощью треуголь- треугольников изображения фрактальных гор {fractal mountains) в компьютерных мульт- мультфильмах. После определения средних точек на каждой стороне треугольника нужно слегка исказить эти значения случайным образом, а затем выполнять разбиение. Разработайте программу, которая будет формировать такие треугольники без за- заливки. Позже можно будет распространить этот алгоритм и на трехмерный случай и добавить тонирование. После нескольких циклов разбиения получится достаточно детальное изображение пирамидальной горы с неровным рельефом. 2.2. Узор Серпинского, сгенерированный в предыдущем упражнении, демонстрирует геометрические объекты, которые изучаются в геометрии фракталов (подробно она будет рассмотрена в главе 8). Предположим, что формируется узор из идеаль- идеальных математических линий, т.е. двухмерных многообразий, имеющих длину, но не имеющих толщину. Какая часть области исходного треугольника окажется в преде- пределе не заполнена этими линиями после того, как центральный треугольник при каж- каждом разбиении изымается из дальнейшего процесса? Рассмотрите также значение периметра треугольников, оставшихся после удаления центрального. К какому зна- значению в пределе стремится суммарный периметр всех оставшихся треугольников? 2.3. На самом нижнем уровне обработки мы манипулируем с битами в буфере кадра. В составе OpenGL имеются команды работы с пикселями, позволяющие напрямую обращаться к буферу кадра. Вы можете поэкспериментировать с простыми растро- 98 Глава 2. Графическое программирование
выми алгоритмами формирования отрезков или окружностей, используя в качестве базовой OpenGL-функцию glPoint(). Разработайте библиотеку подобных функций формирования примитивов, которая позволит создать буфер кадра в памяти. Ядро такой библиотеки должны составить функции WritePixel() и ReadPixel (). В со- составе библиотеки также должны быть функции заполнения и отображения буфера кадра и запуска программы пользователя, которая будет записывать и считывать коды засветки отдельных пикселей. 2.4. " Черепашья " графика (turtle graphics) — это метод построения штриховых изо- изображений, альтернативный позиционному. В основе этого метода лежит идея вос- воспроизведения следа черепахи, ползущей по экрану с закрепленным в ее центре пе- пером (перо способно подниматься и опускаться). Текущее состояние черепахи опи- описывается триадой (л-, у, 9)— положением центра и углом ориентации. В типовом API, поддерживающем такой режим, есть следующие функции: init(x,y,theta); /* Установка положения и ориентации черепахи */ forward(distance); right(angle); left(angle); pen(up_down); Реализуйте библиотеку функций черепашьей графики на базе OpenGL. 2.5. Используя библиотеку, разработанную при выполнении предыдущего упражнения. напишите программу построения узора Серпинского и фрактального изображения горы (алгоритмы построения такого изображения рассматриваются в упр. 2.1 и 2.2). 2.6. Уже не одно столетие математиков интересуют свойства и методы построения кри- кривых заполнения пространства (space-filling curves). В пределе такие кривые беско- бесконечны, но, во-первых, пространство заполнения ограничено конечным прямоуголь- прямоугольником, а во-вторых, кривая не должна быть самопересекающейся. Многие из таких кривых могут быть сгенерированы с помощью итеративной процедуры. Рассмотри- Рассмотрите "правило", представленное на рис. 2.40, которое заменяет одиночный отрезок че- четырьмя более короткими. Разработайте программу, которая начинает работу с тре- треугольника, а затем итерационно применяет сформулированное правило замены ко всем отрезкам. Объект, который сформирует подобная программа, называется сне- снежинкой Коха (Koch snowflake). Другие типы кривых заполнения пространства опи- описаны в книгах [НИ90, Ваг93]. 111 1 Рис. 2.40. Формирование снежинки Коха 2.7. Изображение простейшего лабиринта можно построить, начав с прямоугольного мас- массива ячеек. Каждая ячейка имеет четыре стороны. В каждом цикле итерационной процедуры удаляется одна из сторон ячейки, но только не та, что лежит на периметре ограничивающего прямоугольника. Процедура продолжается до тех пор, пока все ячейки не станут связаны "проемами" в стенах. Затем создается вход и выход из лаби- лабиринта, для чего нужно удалить "наружную" стену в двух ячейках, примыкающих к ог- ограничивающему прямоугольнику. Простой лабиринт такого типа показан на рис. 2.41. Разработайте OpenGL-программу, которая принимает в качестве аргументов построе- построения лабиринта размером Л'хЛ/значения двух целых чисел Л; и А/. Упражнения 99
Рис. 2.41. Про- Простои ла- лабиринт 2.8. Подумайте, как адаптировать цветовую модель RGB, используе- используемую в OpenGL, для представления модели цветов с вычитанием основных компонентов. 2.9. В этой главе было показано, что базовой операцией графической системы является отображение точки (х,у), которая лежит во внутренней области отсекающего прямоугольника, в точку (дгЛ, yv), которая принадлежит видовому окну на экране. Предположим, что отсекающий прямоугольник и рамка видового окна определены вызовами функций OpenGL: glViewport(u, v, w , h); glu0rtho2D(x_min, xjnax, yjnin, yjnax); Отыщите математическое соотношение, связывающее пары координат (х, у) с па- парами (xs,ys). 2.10. Во многих графических API используется относительный способ задания положе- положения. В подобных API для вычерчивания отрезков и многоугольников имеются функции move_rel(x,y); line_rel(x,y); Функция move_rel() перемещает курсор на заданное расстояние в новое положе- положение. Текущая позиция курсора является внутренней переменной состояния систе- системы. Функция line_rel() не только перемещает курсор, но и вычерчивает отрезок с заданными проекциями на оси координат, начиная с предыдущей позиции кур- курсора. Каковы преимущества и недостатки относительного задания параметров графических элементов по сравнению с заданием в абсолютных значениях, как это делается в OpenGL? Каким образом реализовать относительный способ в рамках API OpenGL? 2.11. В разделе 2.3 при анализе положения точки относительно контура многоугольника были проигнорированы проблемы, которые могут возникнуть, если одна или не- несколько сторон многоугольника окажутся параллельными линиям растра. В чем, по- вашему, суть этих проблем? Как с ними справиться? 2.12. С практической точки зрения анализ каждой точки некоторой области на принад- принадлежность внутренней или внешней области многоугольника крайне неэффективен. Разработайте общую стратегию, которой нужно следовать, чтобы избежать анализа всего массива точек. 2.13. В разделе 2.3 было сказано, что в OpenGL многоугольник задается списком его вершин. Как можно в рамках системы OpenGL задать многоугольник его ребрами? 2.14. Разработайте тест простоты многоугольника. 2.15. На рис. 2.42 показано множество многоугольни- многоугольников, образующее полигональную сеть {polygonal mesh). Эти многоугольники имеют совместные грани и вершины. Разработайте один или не- несколько вариантов структур данных, описываю- описывающих такую сеть. "Хорошая" структура данных должна включать информацию о совместном ис- использовании вершин и ребер. В рамках системы OpenGL отыщите эффективный метод отобра- отображения сети, представленной такой структурой. Лс ш Полигональная сеть 100 Глава 2. Графическое программирование
2.16. В системе OpenGL с каждой вершиной можно связать некоторый цвет. Если вер- вершины отрезка имеют разные цвета, OpenGL при вычерчивании отрезка плавно из- изменяет (интерполирует) цвет линии по мере перемещения от начальной вершины к конечной. Точно так же вычерчиваются и ребра многоугольников. Воспользуйтесь этим свойством OpenGL и разработайте программу вычерчивания треугольника Максвелла {Maxwell triangle) — равностороннего треугольника, вершины которого имеют красный, зеленый и синий цвета. Как соотносятся треугольник Максвелла и цветовой куб? 2.17. Включив в модель описание некоторых физических явлений, можно получить до- довольно интересные изобразительные эффекты. Смоделируйте прыгающий шарик в двухмерном пространстве, используя явления гравитации и упругого столкновения с поверхностью. Сам шарик можно представить в виде замкнутого многоугольника с достаточно большим количеством сторон, чтобы он выглядел на экране как гладкий. 2.18. В развитие предыдущего упражнения подумайте о том, как смоделировать игру в бильярд (это задача посложнее, чем простой "попрыгунчик"). Придется просчиты- просчитывать взаимодействие множества шаров со столом и друг с другом. Подсказка. Начните с двух шаров и подумайте над тем, как выяснить, столкнутся ли они. 2.19. В сообщениях о некоторых дисплеях на ЭЛТ анонсируется, что они способны вос- воспроизводить любые четыре из 64 цветов. Какую информацию о буфере кадра и ка- качестве дисплея несет такое сообщение? 2.20. Разработайте программу анализа выпуклости двухмерного многоугольника. 2.21. Для многих новичков, только приступивших к работе с OpenGL, большую слож- сложность представляет множество вариантов базовых функций, как, например, glVer- tex*(). В таких языках, как C++, можно использовать одно и то же имя функции при работе с разными типами аргументов — заботу о выборе подходящего варианта реализации функции берет на себя компилятор. Разработайте библиотеку функций C++, которые должны выполнять роль посредника между прикладной программой и OpenGL и таким образом облегчить жизнь прикладному программисту — позво- позволить ему пользоваться единственным именем функции и неявно специфицировать ее вариант через типы аргументов. Упражнения 101
ГЛАВА 3 Ввод и взаимодействие с пользователем В данной главе мы рассмотрим интерактивные графические программы. В этот класс входит огромное количество самых разнообразных приложений — от систем проекти- проектирования до программ управления большими системами, в которых используется графи- графический интерфейс, систем виртуальной реальности и компьютерных игр. Обсуждение этой темы я разделил на три части. Сначала я познакомлю читателей с устрой- устройствами ввода, которые используются одной из взаимодействующих "сторон", — пользователем графической программы. Устройства ввода мы рассмотрим с двух точек зрения: как способ описания физических устройств их реальными характеристиками и как способ представления этих устройств в прикладной программе. Затем мы рассмотрим сетевую среду и графические приложения типа "клиент/сервер", работающие в такой среде. Идеи организации подобных приложений мы будем использовать для создания подсистемы ввода информации, управляемой событиями. В последней части главы будет рассмотрена интерактивная графическая программа, изменяющая изображение в соответствии с командами пользователя. Эта программа продемон- продемонстрирует возможности организации эффективного взаимодействия человека и компьютера. 3.1. Интерактивная компьютерная графика Одно из основных достоинств компьютерной технологии состоит в том, что она позволя- позволяет пользователю оперативно взаимодействовать с компьютером в процессе решения опреде- определенной проблемы. При этом со стороны компьютера роль средства "общения" предоставля- предоставляется дисплею, на экране которого компьютер демонстрирует результат своей работы. Отсчет современной эры интерактивной компьютерной графики следует вести от проекта Sketch- Sketchpad, выполненного под руководством Апвена Сазерленда (Ivan Sutherland). В основе проекта лежала на удивление простая и эффективная идея — пользователь видит на экране дисплея изображение, сформированное компьютером, и реагирует на него, передавая компьютеру информацию через различные устройства ввода. В ответ на поступившую информацию — команды и данные — компьютер изменяет изображение, и цикл повторяется вновь. Эта идея используется повсеместно практически во всех современных программах— от программ,
создающих среду для разработки программ, до интерактивных экспонатов в музеях, отве- отвечающих на вопросы посетителей или демонстрирующих нечто в ответ на запрос посетителя. За те 35 лет, которые прошли со времени появления работы Сазерленда, и аппаратные, и программные средства подобных систем значительно усовершенствовались, но фундамен- фундаментальные идеи интерактивной компьютерной графики, введенные им в научный оборот, оста- остались прежними. Эти идеи охватывают широкий круг вопросов, начиная от концепции взаи- взаимодействия человека с компьютером и заканчивая методами структурирования графических данных, обеспечивающими эффективную реализацию таких систем. В этой главе я использую подход, слегка отличный от подхода к изложению материала в других главах. Хотя в большинстве современных графических API, a OpenGL в этом смысле не исключение, основное внимание уделяется методам реалистического тонирования изобра- изображения, возможность взаимодействия с пользователем является одной из важнейших функций большинства приложений. Но должен отметить, что OpenGL не располагает средствами не- непосредственной поддержки этого режима. Главная причина подобной "забывчивости" разра- разработчиков OpenGL кроется в том, что они стремились обеспечить максимальную "перено- "переносимость" API с одной платформы на другую, т.е. позволить системе работать в самых раз- различных операционных средах. Поэтому функции создания окон и ввода информации от поль- пользователя не включены в состав API, а отданы "на откуп" операционной системе. Хотя такое решение и обеспечило переносимость самых сложных алгоритмов формирования изображе- изображения с одной платформы на другую, оно в то же время затрудняет обсуждение вопросов взаи- взаимодействия с пользователем, так как при этом приходится учитывать специфику каждой от- отдельной операционной системы. Кроме того, поскольку большинство программ должно рас- располагать хотя бы минимальным интерфейсом с подсистемой окон, нам не удастся избежать обсуждения специфики операционных систем, если, конечно, наша цель — создание нетри- нетривиальных законченных программных продуктов. Если средства взаимодействия вынесены за пределы графического API, то прикладному программисту волей-неволей приходится само- самостоятельно постигать тайные пружины конкретной операционной системы. Этих потенциальных сложностей можно избежать, если использовать простую инструмен- инструментальную библиотеку, как мы делали это при обсуждении некоторых вопросов в главе 2. Такая инструментальная библиотека должна поддерживать выполнение базовых функций, которые существуют в том или ином виде в любой операционной системе, — открытие окна приложе- приложения, работа с клавиатурой и мышью, создание меню. В данной главе мы воспользуемся именно таким подходом, хотя он и не позволяет использовать с максимальной эффективностью весь спектр функциональных возможностей каждой отдельной операционной среды. Мы будем по-прежнему использовать термин система окон (windowing system) в том же смысле, что и в главе 2, подразумевая под ним какую-либо операционную среду, способную формировать окна приложений, например среду X Window, Microsoft Windows или операци- операционную систему компьютеров Macintosh. Те графические программы, которые будут рассмот- рассмотрены в этой главе, заполняют область окна, выделенного приложению, действуя в среде од- одной из перечисленных операционных систем. Та терминология, которая уже устоялась в ли- литературе по операционным системам, может иногда приводить к путанице, особенно при использовании термина "окно". Но вы всегда можете рассматривать окно OpenGL как част- частный случай окна операционной системы, например X Window. Использование инструмен- инструментальной библиотеки GLUT позволит нам избежать сложностей, связанных с взаимодействием системы окон, менеджера окон и графической системы. Как и те программы, которые мы рассматривали в главе 2, новые графические программы также не будут зависеть от конкрет- конкретной платформы, если только на ней функционирует своя версия библиотеки GLUT. Начнем изложение материала этой главы с описания нескольких типов устройств ввода и способов организации взаимодействия с пользователем на их базе. Затем будет рассказано о том, как эти устройства можно применять в сетевых системах класса "клиент/сервер". После 104 Глава 3. Ввод и взаимодействие с пользователем
этого будут описаны пакет API, обеспечивающий минимально необходимый набор функций взаимодействия, и простая графическая программа на его основе. 3.2. Устройства ввода Устройства ввода можно рассматривать с двух точек зрения. Прежде всего, их можно анализировать как физические устройства, например клавиатуру или мышь, и рассматривать физические принципы работы. Конечно же, программист должен иметь определенное пред- представление о том, как работают устройства, с которыми он сталкивается практически ежеми- ежеминутно. Однако с точки зрения прикладного программирования нежелательно использовать в программе конкретные характеристики физического устройства определенного типа. Следует скорее рассматривать такое устройство как логическое, свойства которого можно специфици- специфицировать в терминах функций, которые оно выполняет в прикладной программе. Логическое устройство характеризуется интерфейсом достаточно высокого уровня с программой поль- пользователя, а не физическими параметрами. Термин "логическое устройство" знаком всем про- программистам, работающим с языками высокого уровня. Например, в языке С ввод и вывод данных осуществляется с помощью функций printf (), scanf (), getchar() и putchar(), ар- аргументами которых являются переменные стандартных типов языка. При выводе строки с помощью функции printf () в качестве физического устройства используется дисплей тер- терминала, принтер или файл на диске. Выводимые данные могут быть использованы как вход- входные для другой программы. Нюансы форматирования, характерные для конкретного устрой- устройства, при этом никак не заботят прикладного программиста. В компьютерной графике работа с логическими устройствами ввода организуется не- несколько сложнее, поскольку формат входной информации может сильно отличаться от про- простой последовательности битов или символов, которой вполне достаточно для неграфических данных. Например, можно использовать мышь — физическое устройство — либо для указа- указания позиции на экране ЭЛТ, либо для выбора определенного пункта меню. .В первом случае в прикладную программу возвращается пара значений (х, у) в некоторой системе координат, а во втором — программа получает целое число, которое однозначно идентифицирует выбран- выбранный пункт меню. Такое отделение физического устройства от логического позволяет про- программе использовать одно и то же физическое устройство совершенно по-разному. Кроме то- того, такая программа способна нормально работать без всякой коррекции даже в том случае, когда мышь заменена другим физическим устройством, например планшетом или трекболом. 3.2.1. Физические устройства ввода Особенности конструкции каждого устройства позволяют ему специализироваться на вы- выполнении определенного круга задач. Мы будем придерживаться того же подхода, который использован в большинстве литературных источников по машинной графике, — разделение на две большие группы: устройства указания и клавиатуры. С помощью устройства указания {pointing device) пользователь может указать позицию на экране. Практически все устройства этой группы оснащены парой или несколькими кнопками, которые позволяют сформировать и передать в компьютер какие-либо сигналы или прерывания. Клавиатура (keyboard device) почти всегда представляет собой панель, на которой установлено множество (как правило, свыше сотни) кнопок. Различные варианты устройств этой группы объединяет то, что все они передают в компьютер коды символов1. 'Как правило, при этом используется формат кодов символов ASCII (Американский стандартный код для обмена информацией — American Standard Code for Information Interchange), рекомендованный Американ- Американским институтом стандартов. По ничто не мешает использовать и любую другую кодировку, если того требует специфика приложения. 3.2. Устройства ввода 105
Мышь (рис. 3.1) и трекбол (рис. 3.2) похожи не только по назначению, но часто и по кон- конструкции. Если перевернуть типичную механическую мышь кверху "брюшком", то она будет очень похожа на трекбол. В обоих устройствах вращение шарика преобразуется с помощью пары преобразователей в сигналы, передаваемые в компьютер. Преобразователи измеряют вращение относительно двух взаимно перпендикулярных осей. Рис. 3.1. Мышь Рис. 3.2. Трекбол Существует очень много модификаций устройств этих групп. В некоторых их них исполь- используются оптические, а не механические чувствительные элементы для измерения перемеще- перемещения. Оптическая мышь измеряет расстояние, подсчитывая штрихи на специальной подложке. Маленькие трекболы (trackball) широко используются в портативных компьютерах, где их встраивают прямо в клавиатуру. В некоторые клавиатуры встраиваются приборы, чувствительные к давлению, которые выполняют те же функции, что и мышь или трекбол, но при этом в них отсутствуют под- подвижные элементы. Преобразователи в таких устройствах измеряют величину давления на небольшой выпуклый набалдашник, размещенный между двумя кнопками в средней части клавиатуры. Выходные сигналы мыши или трекбола можно рассматривать как две независимые вели- величины и преобразовать их в координаты положения на двухмерной- плоскости экрана или в ка- какой-либо другой системе координат. Считанные с устройства значения можно сразу же ис- использовать для управления специальной отметкой (маркером, курсором) на экране, но в та- таком режиме подобные устройства используются очень редко. Совсем не обязательно, чтобы формируемые мышью или трекболом сигналы интерпре- интерпретировались как расстояния. Драйвер устройства и прикладная программа могут трактовать их и как значения двух независимых скоростей (см. упр. 3.4). Затем программа может ин- интегрировать последовательность этих значений и получить абсолютные координаты в двухмерной системе. Таким образом, по мере движения мыши по какой-либо поверхности интеграл от скорости дает значения (х, у), которые служат для отображения маркера на эк- экране (рис. 3.3). Интегрируя смещения трекбола (рассматривая их как значения скоростей), можно использовать его как устройство ввода с переменной чувствительностью. Неболь- Небольшие отклонения шара трекбола от заданного "нулевого" положения приводят к медленно- медленному смещению маркера на экране, а большие — к его быстрому движению. При использо- использовании мыши или трекбола в обычном режиме мы имеем дело фактически с относитель- относительным положением устройства. Если переместить указатель каким-либо способом в другое место, не вращая при этом шарик мыши или трекбола, то дальнейшие сигналы будут сме- смещать указатель относительно новой позиции. Абсолютные координаты устройства не счи- тываются прикладной программой. При решении некоторых задач, например при вводе в компьютер графиков, прикладная программа нуждается не в относительных, а в абсолютных координатах устройства ввода. Такую возможность обеспечивают разного рода планшеты (Data tablets). В типичном план- планшете применяется ортогональная сетка проводов, расположенных под его поверхностью 106 Глава 3. Ввод и взаимодействие с пользователем
(рис. 3.4). Положение щупа (stylus) определяется посредством электромагнитного взаимодей- взаимодействия сигналов, проходящих от проводов к щупу. Иногда в качестве планшета используются чувствительные к прикосновению прозрачные экраны, которые наносятся на поверхность ЭЛТ. Небольшие экраны такого типа размещаются иногда на клавиатуре портативных ком- компьютеров. Чувствительные к прикосновению панели можно использовать в режимах абсо- абсолютных и относительных отсчетов. Рис. 3.3. Управление положением указателя мыши Рис. 3.4. Планшет Пожалуй, самую длинную историю имеет использование в компьютерной графике устройст- устройства, получившего при рождении имя световое перо (lightpen). Впервые оно появилось еще в про- проекте Sketchpad. Световое перо содержит фоточувствительный элемент (рис. 3.5), который при приближении к экрану "чувствует" излучение, порождаемое при столкновении электронов с люминофорным покрытием экрана. Если мощность светового импульса превышает определен- определенный порог, фоточувствительный элемент формирует импульс, который передается в компьютер. Анализируя смещение этого импульса относительно начала цикла регенерации, компьютер мо- может четко определить координаты той точки экрана, возбуждение которой "засветило" фото- фотоэлемент (см упр. 3.19). Таким образом, в распоряжении пользователя оказывается устройство непосредственного указания, работающее напрямую с изображением на экране. В те далекие времена еще не была изобретена мышь, но сейчас эти простые и надежные устройства повсеме- повсеместно вытеснили экзотические световые перья. Кроме всего прочего, в графической системе, ра- работающей со световым пером, нужно предпринимать специальные меры, чтобы иметь возмож- возможность считывать координаты точек на темных участках экрана. Рис. 3.5. Световое перо Рис. 3.6. Джойстик Еще одно устройство, которое заслуживает упоминания, — это джойстик (Joystick) (рис. 3.6). Перемещение джойстика в двух взаимно перпендикулярных направлениях воспри- воспринимается преобразователями, интерпретируется как составляющие вектора скорости, интег- интегрируется, а полученные значения используются для управления положением маркера на экра- экране. Интегрирование выполняется таким образом, что неподвижный джойстик в каком-либо промежуточном положении не изменяет положение маркера, а чем дальше джойстик откло- отклонен от начального положения, тем быстрее маркер перемещается по экрану. Таким образом. 3.2. Устройства ввода 107
джойстик играет роль устройства ввода с переменной чувствительностью. Другое преимуще- преимущество джойстика — возможность имитации силовой обратной связи с помощью разного рода пружин. При этом пользователь чувствует, что чем дальше отклонен джойстик, тем большее усилие требуется для его дальнейшего движения. Это очень помогает в работе с разного рода симуляторами и играми. В трехмерной графической системе весьма соблазнительно использовать и трехмерные устройства ввода. Хотя и существуют различные конструкции таких устройств, они все еще не получили широкого распространения, поскольку проигрывают популярным двух- двухмерным устройствам как по стоимости, так и по техническим характеристикам. Пространственный шар (спейсбол — spaceball) очень похож на джойстик, но отличается от него тем, что на рукоятке закреплен шар (рис. 3.7), причем рукоятка в этой конструкции неподвижна. Шар имеет датчики давления, которые измеряют усилие, прикладываемое пользователем. Шар может измерять не только составляющие усилия в трех основных на- направлениях (сверху вниз, от себя или на себя, влево — вправо), но и вращение относитель- относительно трех осей. Таким образом, это устройство способно передавать в компьютер шесть не- независимых параметров (говорят, что устройство имеет шесть степеней свободы), характе- характеризующих как плоскопараллельное смещение, так и вращение. Такого рода устройство можно использовать, например, при настройке положения и ориентации камеры в нашей модели получения изображения. Существуют и другие трехмерные системы измерения и ввода, использующие самые современные технологии, на- например лазерные. В системах виртуальной реальности ис- используются еще более экзотические устройства, позволяю- позволяющие динамически отслеживать положение и ориентацию пользователя. Для приложений, связанных с современной ро- робототехникой и моделированием виртуальной реальности, иногда требуются устройства, обладающие еще большим Рис. 3.7. Пространственный числом степеней свободы, чем упомянутый спейсбол. В по- шар (спеис о.у следнее время появилось множество сообщений о новых раз- разработках в этом направлении, в частности об изобретении "очувствленных" перчаток, кото- которые способны улавливать движения отдельных органов подвижности человека. В тех программах, которые будут рассматриваться в этой книге, мы не будем использо- использовать никакие экзотические устройства, а ограничимся известными всем мышью и клавиату- клавиатурой. Но это отнюдь не значит, что пакет API ограничивает возможность ввода графической информации только двухмерными данными. 3.2.2. Логические устройства Теперь вернемся к рассмотрению устройств ввода с точки зрения возможности их связи с прикладной программой, т.е. будем рассматривать их как логические устройства. Функцио- Функционирование устройства ввода можно описать двумя характеристиками: ¦ какую измерительную информацию передает устройство в прикладную программу; ¦ когда устройство посылает эту информацию. В некоторых API, таких как PHIGS и GKS, рассматривается шесть классов логических устройств ввода. Поскольку в современных операционных системах функцию ввода можно полностью отделить от конструктивных особенностей того или иного прибора, в OpenGL такой подход не применяется. Тем не менее мы кратко познакомим читателей с этими ше- шестью классами, поскольку они демонстрируют, какой широкий спектр форм ввода инфор- информации имеется в распоряжении разработчика прикладной графической системы. Кроме то- 108 Глава 3. Ввод и взаимодействие с пользователем
го, этот перечень поможет нам в дальнейшем проанализировать, как аналогичные функции реализуются в OpenGL. 1. Строковое устройство. Это логическое устройство ввода, способное передавать в прикладную программу текстовую (символьную) информацию в виде строк (после- (последовательностей символов) в определенном формате — чаше всего в виде кодов ASCII. Обычно логические устройства этого типа конструктивно оформляются в виде клавиа- клавиатуры. В таком случае используемая терминология полностью совместима с применяе- применяемой в описании большинства существующих операционных систем. В OpenGL не де- делается различия между логическими строковыми устройствами и физическими уст- устройствами типа клавиатуры. 2. Локатор. Устройство типа локатор передает в прикладную программу информацию о положении в мировой системе координат. Обычно такое логическое устройство кон- конструктивно оформляется в виде устройства указания — мыши или трекбола. В OpenGL мы будем использовать устройства указания именно таким образом, хотя для этого и придется выполнять в прикладной программе промежуточные преобразования из системы координат экрана в мировую систему координат. 3. Указатель. Указатель возвращает в прикладную программу идентификатор указанно- указанного объекта. Обычно в качестве логического устройства этого типа используется та же самая мышь или трекбол, но оно имеет другой интерфейс с прикладной программой. При описании OpenGL будем именовать процессом выбора {selection) процедуру ука- указания какого-либо объекта на экране. 4. Устройство выбора. Устройство такого типа позволяет пользователю выбрать одну из дискретных (т.е. перечислимых) опций. В OpenGL в качестве логического устрой- устройства такого типа используются элементы графического интерфейса, поддерживаемые операционной системой, — меню, полосы прокрутки, экранные кнопки и т.д. Напри- Например, меню, включающее п пунктов, играет роль устройства выбора, позволяющего пользователю выбрать одну из п альтернатив. 5. Циферблат. Это устройство позволяет пользователю вводить значения непрерывных вели- величин. Роль циферблата опять же выполняют различные элементы графического интерфейса, поддерживаемые операционной системой, — шкалы, регуляторы с движками и т.п. 6. Росчерк. Логическое устройство, которое получило столь экзотическое название (в оригинале — stroke), возвращает массив позиций. Фактически его представляет неко- некоторая процедура— например, нажать кнопку мыши, переместить мышь, отпустить кнопку мыши. В результате в прикладную программу должен быть передан массив ко- координат траектории перемещения мыши. 3.2.3. Показания и синхронизация Информацию, передаваемую от физического или логического устройства в прикладную программу, можно условно разделить на две категории: показания устройства и сигнал син- синхронизации от устройства. Показание устройства — это то значение, которое устройство пе- передает в прикладную программу. Сигнал синхронизации — это физический сигнал от устрой- устройства, которым пользователь извещает программу о завершении некоторого этапа процесса ввода (или всего процесса). Например, показания клавиатуры — это строка символов, соот- соответствующая нажатым алфавитно-цифровым клавишам, а сигнал синхронизации формирует- формируется после нажатия клавиши <Enter> или <Retum>. Для устройства типа локатор показаниями являются данные о положении устройства, а сигнал синхронизации формируется после нажа- нажатия определенной кнопки на устройстве указания. 3.2. Устройства ввода 109
Помимо основной информации, которую пользователь вводит явно, показания могут со- содержать и дополнительную информацию, например о текущем состоянии устройства. Напри- Например, устройство типа указатель возвращает в качестве основного компонента показаний идентификатор того объекта, на который указывает пользователь. Если физическим устрой- устройством этого типа является мышь, то сигнал синхронизации вырабатывается после нажатия кнопки мыши (как правило, левой). При разработке прикладной программы следует преду- предусматривать и такой вариант, когда пользователь нажал кнопку мыши, не указывая при этом ни на какой объект. Если показания будут состоять только из идентификатора объекта, то программист в этом случае столкнется с довольно сложной проблемой анализа некорректно- некорректности показаний. Разрешить ее будет значительно проще, если в показания включить и еще один компонент, который будет нести информацию о том, указывает ли устройство в момент формирования сигнала синхронизации на какой-либо объект или нет, не вышел ли маркер устройства за пределы выделенного приложению окна и т.п. 3.2.4. Режимы ввода Помимо существующего множества типов логических устройств ввода, показания от этих устройств могут передаваться тремя разными способами. Эти способы (или режимы) отра- отражают характер взаимодействия процесса съема показаний (или применительно к работе поль- пользователя — ввода показаний) и формирования сигнала синхронизации. Обычно инициализа- инициализация устройства ввода запускает и процесс съема показаний. Для инициализации может по- потребоваться вызов в явном виде определенной функции API или она может выполняться автоматически. В любом случае, как только будет запущен процесс съема показаний, соот- соответствующая информация начинает поступать в буфер, хотя последний в это время и недос- недоступен для прикладной программы. Например, положение мыши отслеживается непрерывно соответствующими средствами операционной системы, независимо от того, нуждается ли прикладная программа в этой информации или нет. В режиме работы по запросу с синхронизацией от пользователя (request mode) показа- показания устройства ввода не передаются в прикладную программу до тех пор, пока устройство не сформирует сигнал синхронизации. Этот режим ввода является стандартным для неграфиче- неграфических приложений, например типичной программы на С, которая требует ввода символов. В таких приложениях, встретив вызов функции ввода символов (например, scanf ()), програм- программа приостанавливает выполнение и ожидает, пока пользователь не введет последователь- последовательность символов, пользуясь клавиатурой терминала. При этом в последовательности могут быть и символы забоя, а сама последовательность может иметь произвольную длину. По мере ввода данные помещаются в буфер клавиатуры, но содержимое последнего передается при- прикладной программе только после того, как будет нажата определенная клавиша (чаще всего <Enter>), которая сформирует сигнал синхронизации. Логическое устройство, например ло- локатор, можно перемещать в требуемую позицию, а затем, нажав кнопку на этом устройстве, сформировать сигнал синхронизации. В результате в прикладную программу поступят коор- координаты положения устройства именно в тот момент, когда была нажата кнопка. Схематиче- Схематически связь между сигналом синхронизации и показаниями в режиме работы по запросу пока- показана на рис. 3.8. Запрос Процесс * I ^^ Процесс съема 1^ ч синхронизации I ^*"u показаний 1 ^»'v Программа I Сигнал ¦ i ' • ' i \ъ mf Показания синхронизации Рис. 3.8. Режим работы по запросу с синхронизацией от пользователя 110 Глава 3. Ввод и взаимодействие с пользователем
В режиме выборок (sample-mode) ввод выполняется немедленно после поступления за- запроса со стороны прикладной программы. Как только в программе будет вызвана соответст- соответствующая функция, показания от устройства ввода передаются в программу. Таким образом, от устройства не требуется формирования какого-либо сигнала синхронизации (рис. 3.9). При работе в режиме выборок в программу поступают показания, введенные пользователем как раз перед , Выборка вызовом функции считывания показаний, — код пР°чвсс сь*м<3 г^ Поогоамма I ^J показаний I ^. npw'H°*rtMU t последней клавиши, нажатой на клавиатуре, ИЛИ .д | Показания „, ,,,„ , .„„,,. ,| положение указателя локатора. При работе через функции API с устройствами Рис. 3.9. Режим выборок ввода в обоих описанных режимах нужно заранее указать в программе, от какого именно устройства следует ввести информацию. Для работы с устройствами используются функции API, подобные приведенным ниже: request_locator(device_id, bmeasure); sample_locator(device_id, Smeasure); В качестве аргументов функций используются идентификатор конкретного устройства и адрес буфера приема информации. Совершенно очевидно, что в обоих режимах игнорируется информация от всех прочих устройств, кроме специфицированного в вызове функции. Такая идеология организации свойственна тем приложениям, в которых ведущую роль играет про- программа, — именно программа определяет, с каким устройством ввода на данном этапе дол- должен работать пользователь. Но если мы хотим отдать "пальму первенства" пользователю, оба эти режима ввода не подходят. Например, симулятор полета оснащен несколькими устройст- устройствами ввода—джойстиком, шкалами, кнопками и переключателями, причем именно пользо- пользователь-пилот выбирает, какое из устройств нужно использовать в данной ситуации. Разрабо- Разработать программу, которая обращалась бы ко всем этим устройствам в режиме запроса или вы- выборки, практически невозможно, поскольку заранее неизвестно, какое из них понадобится пилоту в той или иной ситуации. Рассуждая на обобщенном уровне, приходим к заключению, что эти режимы не удовлетворяют требованиям современных систем активного взаимодейст- взаимодействия пользователя с компьютером. Третий режим — режим ввода, синхронизируемый событиями (или короче, режим об- обработки событий — event mode), позволяет реализовать обслуживание множества одно- одновременно работающих устройств ввода. Описание этого режима я разбил на три стадии. Сначала я покажу, как режим обработки событий можно описать в терминах других режи- режимов в пределах парадигмы "показания-синхронизация". Затем будут рассмотрены основ- основные концепции архитектуры "клиент/сервер", в которой режим обработки событий играет главенствующую роль. Третья стадия — описание методики использования библиотеки GLUT при создании программ на OpenGL, активно взаимодействующих с пользователем. На этой стадии мы проанализируем демонстрационную программу, в которой применяется подобный интерфейс. Предположим, что наше приложение работает в операционной среде, располагающей множеством устройств ввода, каждое из которых имеет свои средства формирования сигна- сигналов синхронизации и съема показаний. Каждый раз, когда устройство формирует сигнал син- синхронизации, возникает (иногда говорят возбуждается) событие (event). При этом показания устройства вместе с его идентификатором помещаются в очередь событий (event queue). Очередь событий заполняется операционной системой без всякого вмешательства со стороны прикладных программ и независимо от того, как прикладная программа собирается реагиро- реагировать на возникшие события. Один из способов обработки событий в прикладной программе схематически показан на рис. 3.10. Прикладная программа может проанализировать инфор- информацию о событии, которое стоит в очереди первым на обработку, или, если очередь пуста, 3.2. Устройства ввода 111
перейти в состояние ожидания возникновения события. После извлечения из очереди инфор- информации о событии прикладная программа определяет его тип и решает, что делать дальше. Именно такой метод используется в API графических систем GKS и PHIGS. Ожидание Процесс 1 ^^ Процесс съема I Очередь Программа Событие синхронизации I показаний I событий тшя ятшш I Сигнал «„„„„ц,...!!,!,! т.,-! Показания я шт t синхронизации Рис. 3.10. Резким ввода с обработкой событий Другой подход— связать с каждым типом событий функцию с обратным вызовом {callback). Мы будем следовать именно такому подходу, поскольку он используется в подав- подавляющем большинстве современных операционных систем и доказал свою эффективность в системах с архитектурой "клиент/сервер". 3.3. Клиенты и серверы До сих пор, рассматривая процесс ввода информации, мы отстранялись от всех прочих про- процессов, происходящих в операционной среде. Графическая система представлялась этаким мо- монолитным блоком, который имеет ограниченные возможности коммуникации с внешним ми- миром, помимо тех устройств ввода, управление которыми тщательно спланировано в прикладной программе, и, конечно же, графического дисплея. Эта картина разительно изменилась с появле- появлением сетевых многопользовательских систем. Теперь даже автономная система, рассчитанная на работу с отдельным пользователем, организуется таким образом, что ее программное обеспе- обеспечение имеет ярко выраженное деление на компоненты клиентов и серверов, как в сетевой среде. Чтобы графические приложения могли получить широкое распространение в современ- современной компьютерной индустрии, они должны обладать способностью эффективно работать в распределенной вычислительной среде, организуемой на базе локальных (а теперь уже и гло- глобальных) вычислительных сетей. В такой среде основными структурными компонентами сис- системы — ее строительными блоками — являются серверы и клиенты. Серверы и клиенты мо- могут быть распределены между разными компонентами аппаратной части системы, как это по- показано схематически на рис. 3.11, а могут "сосуществовать" и на одном и том же компьютере. Многим хорошо знаком сервер печати, который позволяет всем пользователям сети пользо- пользоваться единственным устройством высококачественной печати. Другие, может быть, менее распространенные примеры — вычислительный сервер, реализованный на суперкомпьютере, доступный прикладным программам, выполняемым на отдельных, менее мощных компьюте- компьютерах рабочих станций, файл-сервер, позволяющий множеству пользователей совместно ис- использовать (а иногда и пополнять) информацию из определенных файлов, и терминальные серверы, которые заняты обслуживанием запросов, поступающих по телефонным каналам. Те пользователи и прикладные программы, которые пользуются услугами таких серверов, назы- называются клиентами или клиентскими программами. В этой ясной и четкой структуре рабочим станциям трудно сразу отвести определенную роль. В принципе, рабочая станция может быть и клиентом, и сервером, и даже более, парал- параллельно выполнять и клиентские программы, и серверные. Представленная здесь модель реализована операционной системой X Window. В даль- дальнейшем мы будем широко пользоваться терминологией, заимствованной из документации этой системы, которая сейчас повсеместно используется и в других операционных системах и хорошо согласуется с графическими приложениями. Рабочая станция, оснащенная растровым дисплеем, клавиатурой, устройством указа- указания, таким как мышь, является графическим сервером. Такой сервер может взять на себя 112 Глава 3. Ввод и взаимодействие с пользователем
оказание "услуг" по выводу информации на экран дисплея или по вводу данных с помо- помощью клавиатуры и устройства указания. На эти услуги может рассчитывать любой кли- клиент, подключенный к сети. Графический сервер Рабочая станция Рабочая станция Сервер-печати Вычислительный сервер Рис. 3.11. Сетевая вычислительная среда Графический сервер Те прикладные программы на OpenGL, которые мы рассматривали ранее (и будем рас- рассматривать впредь), являются, по сути, клиентскими программами, пользующимися "услуга- "услугами" графического сервера. Внутри автономно функционирующего компьютера это разделе- разделение между прикладной программой и специальными графическими средствами кажется не столь уж и существенным, но в сетевой среде прикладная клиентская программа (в том числе и графическая) может выполняться на одном компьютере, а пользователь будет сидеть за эк- экраном совершенно другого компьютера — графического сервера. 3.4. Дисплейный файл С помощью дисплейных файлов (или дисплейных списков) организуется эффективное взаимодействие между графическими клиентами и серверами в сетевой среде. История появ- появления дисплейных файлов уходит корнями в "седую древность" (конечно, по масштабам компьютерной эры). Как уже упоминалось в главе 1, в структуре ранних графических систем использовался компьютер общего назначения (сейчас для такого компьютера используется термин главный компьютер, или хост, а тогда подавляющее большинство компьютеров были "главными"), связанный через блоки цифро-аналоговых преобразователей с ЭЛТ (рис. 3.12). Компьютер передавал на преобразователи графические данные (координаты последователь- последовательных точек вычерчиваемых на экране линий) с частотой регенерации изображения2. В те вре- времена (начало 1960-х годов) вычислительная мощность компьютеров была невелика (по со- современным меркам), а сами компьютеры дороги, а потому использовать графические прило- приложения могли позволить себе только весьма состоятельные клиенты. 'Эта частота зависит от времени послесвечения люминофорного покрытия экрана ЭЛТ, и в ранних систе- системах ее стремились снизить, используя специальные трубки. В современных системах частота регенерации ле- лежит в пределах от 50 до 85 Гц или уменьшена наполовину, если исполыуется чересстрочная развертка. 3.4. Дисплейный файл 113
Главный компьютер Рис. 3.12. Простейший вариант архитектуры графической системы Решение этой проблемы нашли, воспользовавшись со- советом Юлия Цезаря, — "Разделяй и властвуй" (в этом ми- мире новое — это хорошо забытое старое). Были разработа- разработаны специализированные компьютеры — дисплейные про- процессоры, — организация которых схематически представ- представлена на рис. 3.13. Набор команд дисплейного процессора был ограничен, причем большинство команд было ориен- ориентировано на вычерчивание графических примитивов на экране ЭЛТ. Прикладная программа выполнялась на глав- главном компьютере и результатом ее выполнения был дис- дисплейный файл — программа построения изображения в терминах команд дисплейного про- процессора. Этот дисплейный файл сохранялся в дисплейной памяти (функционально— это аналог современного буфера кадра, хотя в нем хранились не коды засветки пикселей, а ко- команды, но описывали они единственный кадр изображения). Если в приложении не использо- использовался активный диалог с пользователем, то, сформировав дисплейный файл, главный компь- компьютер мог заняться чем-нибудь более важным (на то он и главный), а черную работу по реге- регенерации изображения брал на себя дисплейный процессор. Поначалу дисплейный процессор был довольно примитивным, но потом в его состав начали включать специализированные аппаратные блоки выполнения процедур, требовавших большого объема вычислений, напри- например тонирования. В результате через десяток лет вычислительная мощность специализиро- специализированного дисплейного процессора превысила вычислительную мощность того компьютера, который когда-то "гордился" тем, что он главный. Главный компьютер Рис. 3.13. Архитектура графической системы с ис- использованием дисплейного процессора Теперь то, что раньше именовалось дисплейным процессором, называется графически.» сервером, а прикладная программа, ранее выполнявшаяся на главном компьютере, стала кли- клиентской программой. Основной проблемой теперь является не обеспечение регенерации изо- изображения с необходимой частотой, а перегрузка сети при передаче информации между кли- клиентами и серверами. Попутно отмечу, что в высококачественных графических серверах ис- используются специализированные аппаратные средства (как для формирования изображения, так и для взаимодействия с пользователем). Организовать работу с дисплейным файлом можно двумя способами. Можно переслать на графический сервер полное описание изображения — координаты вершин, атрибуты, типы примитивов и дополнительную информацию о визуализации сцены. Напомню, что мы дого- договорились использовать в работе режим немедленного отображения {immediate mode), при котором как только программа выполняет оператор формирования примитива, последний пе- передается на сервер для отображения и в памяти о нем не остается никакой информации . Для ^Изображение примитива сохраняется в буфере кадра и используется для регенерации, но изображение и описание примитива — вещи разные. 114 Глава 3. Ввод и взаимодействие с пользователем
повторного отображения примитива (того же самого, в том же месте экрана) после очистки поля изображения или после внесения некоторых корректив в модель сцены придется заново его сформировать и переслать описание на сервер. Если приложение предназначено для ра- работы со сложными графическими объектами (например, моделью здания), причем пользова- пользователь активно "вмешивается" в процесс, объем информации, передаваемой от клиента серверу, может оказаться достаточно большим и привести к "заторам" в сети. Второй способ предполагает использование дисплейного файла (теперь чаще использует- используется термин дисплейный список, поскольку этот файл на диске не сохраняется). Такой способ часто называют режимом отображения с сохранением (retained-mode), поскольку при рабо- работе в этом режиме объект определяется однократно, а его описание помещается в дисплейный список, который сохраняется на сервере. Вывести его на экран можно вызовом специальной функции со стороны программы-клиента. В результате, во-первых, снижается объем инфор- информации, передаваемой от клиента серверу, во-вторых, в дисплейном списке описания объектов представлены таким образом, что появляется возможность использовать для их отображения специализированные аппаратные средства, которыми оснащен графический сервер. Оказыва- Оказывается, что оптимальная структура графической системы должна включать высокопроизводи- высокопроизводительный компьютер общего назначения, способный быстро выполнять обработку числовой информации (на него возлагается выполнение прикладной программы), и специализирован- специализированный графический компьютер, оснащенный аппаратными средствами формирования изобра- изображений, в том числе и средствами тонирования с учетом освещения и т.д. Короче говоря, от чего в начале 1970-х годов начали уходить, к тому сейчас опять вернулись, правда, в новом исполнении. В концепции использования дисплейного списка имеются и определенные недостатки. Для хранения списка требуется память на сервере и, кроме того, на его формирование и обработку тратится определенное время. Но эти дополнительные затраты окупаются высо- высокой эффективностью специализированной аппаратуры преобразования дисплейного списка в изображение. 3.4.1. Формирование дисплейного списка и преобразование его в изображение Для работы с дисплейными списками необходимо иметь в своем распоряжении средства создания списка и включения в него нужной информации. Механизм доступа к элементам списка должен быть достаточно гибким и обеспечивать прикладной программе значительную свободу. В составе OpenGL имеется небольшой набор функций манипулирования содержи- содержимым дисплейного списка, и ниже на примерах будет показано, как ими пользоваться4. Дисплейные списки формируются по той же схеме, что и графические примитивы. Спи- Список открывается функцией glNewList(), а закрывается функцией glEndList(). Операторы между этими двумя вызовами формируют содержимое списка. Каждый дисплейный список имеет уникальный идентификатор — целое число, которое определяется с помощью дирек- директивы макроопределения #def ine языка С. Например, приведенный ниже фрагмент програм- программы формирует дисплейный список, в котором имеется один объект — красный квадрат. Этот фрагмент практически идентичен фрагменту, приведенному в главе 2, но на сей раз информа- информация не отправляется сразу же на отображение, а помещается в список. tdefine BOX I /* Или любое другое ранее не использованное целое число */ 4 В пакете PHIGS имеются структуры ^structures,/, а в пакете GKS— сегменты ^segments/ причем и те и другие поддерживают многие из характеристик дисплейных файлов OpenGL. 3.4. Дисплейный файл 115
glNewList(BOX, GL_COMPILE); glBegin(GL_POLYGON); glColor3fA.0, 0.0, 0.0); glVertex2f(-1.0, -1.0); glVertex2f( 1.0, -1.0); glVertex2f( 1.0, 1.0); glVertex2f(-1.0, 1.0); glEnd(); glEndList(); Флаг GL_COMPILE настраивает такой режим работы графической системы, что список пе- пересылается на сервер, но его содержимое на экран не выводится. Если желательно сразу же вывести изображение на экран, то вторым аргументом функции glNewList() должен быть флаг GL_COMPILE_AND_EXECUTE. Как только возникнет необходимость вывести изображение этого квадрата на экран, нуж- нужно вызвать функцию glCallList(), передав ей в качестве аргумента идентификатор списка, в данном случае — BOX: glCallList(BOX); Как и по отношению к другим функциям построения примитивов на экране, к дисплейно- дисплейному списку применяются переменные текущего состояния исполнительной системы OpenGL, которые задают необходимые преобразования. Если,' например, изменить матрицы проектив- проективного преобразования и/или вида между двумя последовательными вызовами функции glCallList(), то изображение объекта появится в другом месте экрана (а может, и вообще исчезнет). Как выполняется управление матрицей проективного преобразования, показано в приведенном ниже фрагменте программы. glMatrixMode(GL_PROJECTION); for(i= I ; i<5; glLoadIdentity(); gluOrtho2D(-2.0*i, 2.0*i, -2.0*i, 2.0*i); glCallList(BOX); } При каждом вызове функции glCallList() квадрат будет повторно выводиться на экран с учетом новых параметров отсекающего прямоугольника. В последующих главах я познакомлю вас с матрицами преобразований, которые позволяют использовать дисплейный список для моделирования динамических сцен. Но учтите, поскольку имеется возможность манипулировать дисплейным списком, нужно быть предельно вниматель- внимательным при программировании такого рода процедур, иначе потом могут появиться совершенно нежелательные и часто непредсказуемые эффекты. Например, в приведенном выше дисплейном списке текущий цвет изменяется на красный. После этого все объекты будут выводиться на эк- экран красным цветом до тех пор, пока в каком-либо другом списке не встретится функция, уста- устанавливающая другой текущий цвет. Проще всего "предохраниться" от подобного эффекта с по- помощью стека матриц и атрибутов, который поддерживается пакетом OpenGL. Стек— это структура данных, в которой элемент, включенный в структуру последним, извлекается из нее первым. Таким образом, можно поместить в вершину соответствующего стека (иногда говорят "затолкать в стек") текущее значение используемого атрибута или матрицы, а затем восстано- восстановить его, извлекая из стека это значение (одновременно оно удаляется из стека). Поэтому стан- стандартная последовательность операций, обеспечивающая сохранение-восстановление текущих параметров состояния, предусматривает сначала сохранение их в соответствующих стеках, за- 116 Глава 3. Ввод и взаимодействие с пользователем
тем изменение текущего значения, заполнение дисплейного списка примитивами, а затем вос- восстановление значений параметров— извлечение их из стеков. Поэтому в начале процедуры формирования стека вы часто увидите вызовы функций glPushAttrib(GL_ALL_ATTRIB_BITS); glPushMatrix(); В конце процедуры в таком случае должны присутствовать вызовы функций glPopAttrib(); glPopMatrix(); При описании методов построения иерархических графических моделей в главе 8 мы бу- будем очень часто обращаться к стекам матриц и атрибутов. В составе OpenGL есть еще несколько функций, облегчающих работу с дисплейным спи- списком. Когда приходится работать со множеством дисплейных списков, то для формирования последовательных значений идентификаторов списков можно воспользоваться функцией glGenLists(<количество>), которая возвращает первое целочисленное значение в последо- последовательности из <количество>, еще не использованное в программе в качестве идентификато- идентификатора. Функция glCallLists() позволяет запустить процесс отображения сразу нескольких дис- дисплейных списков. Очень хорошим примером использования возможностей, которые откры- открывают дисплейные списки, является формирование текста. В следующем разделе будет подробно описана технология формирования текста с помощью средств OpenGL, но вы мо- можете при чтении этой главы и опустить этот материал, а вернуться к нему позже. Однако хочу обратить ваше внимание на то, что описанная методика демонстрирует, какими гибкими воз- возможностями обладает API и как это облегчает прикладному программисту решение множест- множества проблем. К теме дисплейных списков мы вернемся еще раз в главе 8, когда будем обсуж- обсуждать использование графических приложений в сети World Wide Web. 3.4.2. Дисплейные списки и формирование текста В главе 2 были описаны два способа формирования текстовых символов — растровый и штриховой. Независимо от того, какой из этих методов выбран, нам понадобится определен- определенная программа, описывающая весь набор символов определенного шрифта. Предположим, что мы остановились на растровом шрифте, в котором каждый символ должен быть описан матрицей битов размером 12x10. В результате для хранения каждого символа нужно выде- выделить память объемом 15 байт. Проще всего формировать изображение строки, отсылая на сервер матрицу битов каждого очередного символа в порядке их по- появления в строке. Таким образом, придется отсылать по 15 байт на Qi Input Output каждый символ строки. Если же сформировать штриховой шрифт, состоящий из отрезков, то на каждый символ "уйдет" разное коли- количество отрезков и соответственно потребуется разный объем памя- g. ти. Можно пойти дальше и сформировать шрифт из многоугольни- многоугольников с заливкой, как показано на рис. 3.14. Для буквы "I" много труда не потребуется, но чтобы получить плавное изображение буквы "О", РиСщ 3t4- Штриховые символы: а — мно- придется аппроксимировать ее контур множеством коротких отрез- r r r j г г г гоугольники с за- ков. В среднем на полный комплект оукв такого шрифта потребует- чивкой- б укруп- ся гораздо больше памяти, чем 15 байт на символ. Если приложение пенное изображе- должно выводить на экран достаточно длинный текст, то на сервер ние контуров придется пересылать очень много информации, что приведет к зна- значительной загрузке сети. Все, что говорилось выше, должно подвести вас к заключению о том, что такая техноло- технология вывода текста в приложении "клиент/сервер" неэффективна. Более рационально исполь- 3.4. Дисплейный файл 117
зовать дисплейные списки (по одному на каждый символ) и сохранить таким образом описа- описание шрифта на сервере. Это очень похоже на использование стандартных растровых матриц символов в алфавитно-цифровых терминалах. Весь набор символов сохраняется в ПЗУ тер- терминала, а затем каждый символ выводится на экран при получении его ASCII-кода— одного байта. Отличие заключается только в качестве получаемого изображения и количестве шриф- шрифтов. Ничто не мешает нам определить столько шрифтов, сколько сможет хранить память сер- сервера, а затем использовать описание каждого символа, составленного из отрезков, как любой другой графический объект, и применять по отношению к нему любые преобразования — масштабировать, поворачивать или смещать. Таким образом, основная идея — передать на сервер описание всех символов шрифта, а затем запрашивать отображение нужных символов, передавая всего один байт, — демон- демонстрирует, насколько эффективным может оказаться использование дисплейных списков в OpenGL. Процедура, по сути, та же, что и при работе с растровыми шрифтами. Сначала нужно сформировать множество из 96 воспроизводимых ASCII-символов или расширенный набор из 256 символов, если предполагается использовать приложение не только в англоязычных странах. Определим функцию OurFont(char с), которая будет вычерчивать любой ASCII-символ с. Эта функция в общих чертах имеет следующий вид: void OurFont(char с) { switch(c) { case 'a': break; case 'A': break; В каждом блоке case нужно позаботиться о размещении символа на экране — каждый очередной символ должен выводиться на определенном расстоянии справа (а иногда и слева) от предыдущего символа. Для перемещения изображения можно воспользоваться функцией glTranslate(). Пусть, например, у нас есть описание буквы "О" в виде дисплейного списка и желательно вписать ее изображение в единичный квадрат. Соответствующий фрагмент функ- функции OurFont() будет выглядеть следующим образом: case 'О': glTranslatef@.5, 0.5, 0.0); /* Передвинуть в центр */ glBegin(GL_QUAD_STRIP); for (i=0; i<=12; i++) /* 12 вершин */ { angle = 3.14159/6.0*i; /* 30 градусов в радианах */ glVertex2f@.4*cos(angle), 0.4*sin(angle)); glVertex2f@.5*cos(angle), 0.5*sin(angle)); } glEnd(); glTranslatef@.5, -0.5, 0.0); /* Передвинуть в правый нижний угол */ break; 118 Глава 3. Ввод и взаимодействие с пользователем
В этом фрагменте окружность (точнее, две концентрические окружности) аппроксимируется 12 четырехугольниками (полосой из 12 участков). Каждый прямоугольник будет залит в соответст- соответствии с текущими значениями атрибутов. Поскольку мы еще не рассматривали методы выполнения геометрических преобразований — это материал главы 4, — придется объяснить суть используе- используемых в этом фрагменте формул. Все преобразования выполняются с двухмерными объектами. Сле- Следовательно, каждый из них определен на плоскости z=0 и можно использовать любую систему ко- координат для его описания. Предполагается, что каждый объект (символ шрифта, включая и меж- межсимвольный интервал) вписан в прямоугольник5. Обычно отображение строки символов начинается с левого нижнего угла первого из них. Следующий символ размешается таким образом, что его левый нижний угол совпадает с правым нижним углом предыдущего символа. Первый вызов функции glTranslatef () в приведенном фрагменте задает смещение в центр прямоугольника, который в данном случае совпадает с центром окружности. Можно считать, что функция glTranslatef () сдвигает начало теку- текущей системы координат, т.е. системы, в которой будут отсчи- тываться координаты всех последующих графических объек- объектов. Затем формируются вершины, расположенные вдоль двух концентрических окружностей, одна из которых имеет радиус 0.5, а вторая — 0.4 (рис. 3.15). После того как таким спосо- способом будут сформированы 12 четырехугольников, начало сис- системы координат переносится в правый нижний угол. В резуль- результате все готово к вычерчиванию следующего символа, начи- начиная с правого нижнего угла только что вычерченного. Обратите внимание— в этом примере нам не пришлось об- обращаться к стекам матриц и атрибутов. Другие символы фор- формируются по этой же схеме. Хотя приведенный фрагмент программы выглядит и не очень элегантно, его эффективность не должна нас особенно заботить, поскольку символы формируются только один раз в течение сеанса работы приложения. Сформи- Сформированные символы пересылаются на сервер в виде скомпилированного дисплейного списка. Предположим, что нам необходимо сформировать расширенный шрифт из 256 символов. Соответствующая программа, в которой используется функция OurFont(), будет выглядеть следующим образом: base = glGenListsB56); /* Возвращает индекс первого из 256 доступных идентификаторов */ for(i=0; i<256; GL_COMPILE); Рис. 3.15. Вычерчивание бук- вы "® " glNewList(base OurFont(i); glEndList(); Когда понадобится использовать этот список для вычерчивания отдельных символов, можно будет не смещать каждый последующий вызов относительно base, а просто устано- установить базу смещения: glListBase(base); 5В принципе, каждый символ может иметь свое соотношение сторон и, следовательно, может быть вписан в прямоугольник своего уникального размера. Чтобы не усложнять задачу, будем считать наш шрифт моноширинным, т.е. таким, все символы которого имеют одинаковые размеры. Именно таким шриф- шрифтом набраны тексты программ в этой книге. 3.4. Дисплейный файл 119
Собственно вывод некоторой текстовой строки производится самим сервером при вызове функции glCallLists (): char *text string? glCallLists( (GLint) strlen(text_string), GL_BYTE, text_string); В этом вызове используется функция strlen() из стандартной библиотеки языка С, которая определяет длину строки text string. Первый аргумент функции glCallLists() задает коли- количество отображаемых дисплейных списков. Третий аргумент — указатель на массив, элементы которого имеют тип, заданный вторым аргументом. Идентификатор к-го дисплейного списка, который подлежит отображению, формируется как сумма базы списка, установленной функцией glListBase() и значением к-го символа в строке (массиве символов text string). 3.4.3. Шрифты библиотеки GLUT Конечно, любой программист предпочитает использовать уже разработанные ранее шрифты, а не заниматься созданием собственных, если в этом нет особой необходимости. В состав библиотеки GLUT входит несколько растровых и штриховых шрифтов6. Для того чтобы вывести с их помощью текст на экран, не обязательно использовать механизм рабо- работы с дисплейными списками, но в последнем примере этой главы мы все-таки создадим дисплейный список, который будет содержать один из шрифтов библиотеки GLUT. Для вывода на экран отдельного символа этого моноширинного шрифта нужно вызвать функ- функцию glutStrokeCharacter(): glutStrokeCharacter(GLUT_STROKE_MONO_ROMAN, int character); Константа GLUT_STROKE ROMAN задает режим вывода символов пропорционального шриф- шрифта. Процедуры манипулирования такими шрифтами нужно тщательно продумывать. Размер символов (не более 120 единиц) может не соответствовать размерам остальных элементов изображения, а потому при выводе текста нужно использовать масштабирование. Обычно управление положением символов выполняется смещением перед вызовом функции вывода символов на экран. Кроме того, каждый повторный вызов функции glutStrokeCharacter() приводит к дополнительному сдвигу по горизонтали на ширину символа и таким образом подготавливает позицию для вывода следующего символа надписи. Параметры масштабиро- масштабирования и смещения являются составляющими текущего состояния исполнительной системы OpenGL, поэтому при их изменении нужно не забывать о сохранении и восстановлении те- текущего состояния с помощью стека матриц (функции glPushMatrix() и glPopMatrix()). В противном случае могут возникнуть совершенно нежелательные побочные эффекты. Символы растровых шрифтов выводятся с помощью функций библиотеки GLUT анало- аналогичным способом. Например, для вывода на экран одиночного символа размером 8x13 нужно вызвать функцию glutBitmapCharacter(): glutBitmapCharacter(GLUT_BITMAP_8_BY_13, int character); Растровые символы позиционируются на экране значительно проще, чем штриховые, по- поскольку "образ" такого символа прямо переносится в буфер кадра и, в отличие от штрихового символа, не подвергается никаким геометрическим преобразованиям. Исполнительная систе- система OpenGL в качестве одного из параметров текущего состояния хранит и позицию растра (raster position). Этот параметр указывает позицию вывода образа очередного растрового примитива. Его значение устанавливается функцией glRasterPos*(). Как правило, после вы- 6 Можно использовать и шрифты, поддерживаемые операционной системой. 120 Глава 3. Ввод и взаимодействие с пользователем
вода образа очередного символа функцией glutBitmapCharacter() прикладная программа смещает позицию растра на один символ вправо. Это изменение не влияет на последующее тонирование геометрических примитивов. Если символы имеют разную ширину, нужно вос- воспользоваться функцией glutBitmapWidth(font, char), которая возвращает ширину заданно- заданного символа указанного шрифта. Таким образом, фрагмент прикладной программы, который организует вывод символа некоторой надписи, обычно имеет вид glRasterPos2i(rx, ry); glutBitmapCharacter(GLUT BITMAP_8_BY_13, k); rx+=glutBitmapWidth(GLUT~BITMAP_8_BY_13, k); Далее в этой главе будет рассмотрена программа, в которой надпись выводится растро- растровым шрифтом с помощью дисплейного списка. Организация прикладной программы на базе использования дисплейных списков требу- требует особого внимания при программировании, и мы еще вернемся к этому вопросу в главе 8, когда будем рассматривать иерархические графические модели. Но сейчас нас больше ин- интересует ясность и наглядность программы, хотя это и может пойти в ущерб ее производи- производительности. Поэтому в большинстве примеров в последующих главах дисплейные списки применяться не будут. В принципе, большую часть приведенных текстов программ доста- достаточно легко использовать и применительно к дисплейным спискам. Для этого требуется заключить фрагменты, формирующие изображение, в "процедурные скобки"— вызовы функций glNewList() и glEndList(). 3.5. Программирование ввода, управляемого событиями В этом разделе на множестве несложных примеров мы рассмотрим механизм обработки событий при вводе информации пользователем в прикладную программу. В основу про- программной реализации этого механизма положены функции с обратным вызовом, упомянутые в разделе 3.2. Будут рассмотрены типы событий, распознаваемые операционной системой, и для тех из них, которые представляют интерес для графических приложений, разработаны функции с обратным вызовом, отвечающие за реакцию приложения на эти события. 3.5.1. Использование устройств указания Начнем обсуждение механизма обработки событий с модификации функции main() в программе построения узора Серпинского, рассмотренной в главе 2. В первой ее версии для вывода окна приложения на экран были использованы функции из библиотеки GLUT, а затем с помощью функции glutMainLoop() организован цикл ожидания событий. В теле цикла не было запрограммировано никаких операций. Даже прекращение работы программы выпол- выполнялось внешним по отношению к этому циклу механизмом. Этот недостаток первой версии программы был преодолен за счет использования устройства указания, которое позволяло прекратить выполнение программы, вызвав стандартную функцию прерывания выполнения exit() при нажатии определенной кнопки мыши. Мы будем рассматривать только те события, которые распознаются библиотекой GLUT, хотя операционные системы, подобные X Window, распознают гораздо более широкий класс событий. Но в библиотеку GLUT включены средства распознавания только тех событий, ко- которые являются общими для большинства существующих операционных систем и представ- представляют интерес для разработчиков графических приложений. С устройством указания, в каче- качестве которого чаще всего используется мышь, ассоциируются события двух типов. Событие типа перемещение возбуждается в том случае, если мышь перемещается при нажатой кнопке (любой из имеющихся на устройстве). Если мышь перемещается, но при этом никакая кнопка 3.5. Программирование ввода, управляемого событиями 121
не удерживается в нажатом состоянии, это событие рассматривается как пассивное переме- перемещение. Положение мыши — показание этого устройства ввода — становится доступным при- прикладной программе после возникновения события типа перемещение. Другой тип события — событие мыши — возбуждается в случае, если пользователь нажал или отпустил любую из кнопок. Пока кнопка удерживается нажатой, никакие события не возбуждаются (если, конеч- конечно, мышь не перемешается) до тех пор, пока кнопка не будет отпущена7. Передаваемая при этом информация — показания устройства — включает идентификатор кнопки, послужив- послужившей причиной возникновения события, ее состояние после возникновения события (нажата или отпущена) и положение маркера мыши в координатах экрана. Регистрация в операцион- операционной системе функции мыши с обратным вызовом обычно выполняется в теле функции main() через вызов функции библиотеки GLUT: glutMouseFunc(mouse_callback_func) Функция с обратным вызовом для мыши должна иметь формат void mouse_callback_func(int button, int state, int x, int y); В теле функции с обратным вызовом должны быть запрограммированы операции, кото- которые необходимо выполнить в ответ на возникшее событие. Вариантов таких операций может быть достаточно много, в зависимости от того, какая кнопка вызвала появление события и в каком состоянии она оказалась после этого (нажата или отпущена). В данном простом при- примере нас интересует нажатие левой кнопки мыши, которое должно привести к прекращению выполнения прикладной программы. Таким образом, тело функции с обратным вызовом бу- будет включать всего один оператор: void mouse_callback_function(int button, int state, int x, int y) { if(button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) exit(); } Любое другое сочетание показаний события — отпускание левой кнопки или любые мани- манипуляции с правой кнопкой — останутся без внимания со стороны прикладной программы, по- поскольку в функции с обратным вызовом никаких действий на этот случай не предусмотрено. Следующий пример продемонстрирует достоинство той структуры программы, которая была описана в предыдущей главе. Разработаем программу, которая будет вычерчивать небольшой квадратик на экране в том месте, где установлен маркер мыши в момент нажатия левой кнопки. Нажатие средней кнопки мыши должно привести к завершению работы выполнения программы. Функция main() новой программы будет выглядеть практически так же, как и аналогич- аналогичная функция в предыдущем примере8. int main(int argc, char **argv) glutlnit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutCreateWindow("square"); myinit(); 7 В некоторых операционных системах нажатие и отпускание кнопки рассматривается как единое событие. 8 Мы будем использовать соглашение об именовании функций с обратным вызовом, регламентированное в руководстве OpenGL Programmer's Guide [Ope97,a]. 122 Глава 3. Ввод и взаимодействие с пользователем
glutReshapeFunc(myReshape); glutMouseFunc(mouse); glutDisplayFunc(display); glutMainLoop(); } Событие перерисовки возбуждается операционной системой при любом изменении раз- размеров или положения окна приложения, которое может возникнуть по требованию пользова- пользователя. Реакцию прикладной программы на это событие мы рассмотрим ниже. В этом примере нет нужды использовать функцию отображения с обратным вызовом, поскольку графические примитивы формируются только в ответ на щелчок кнопкой мыши. Но библиотека GLUT требует, чтобы каждое приложение содержало функцию отображения с обратным вызовом, а потому нам придется ее определить как пустую. void display(){ } На события мыши будет реагировать функция с обратным вызовом mouse (): void mouse(int btn, int state, int x, int y) { if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN) drawSquare(x,y); if(btn==GLUT_MIDDLE_BUTTON && state==GLUT_DOWN) exit(); } Поскольку графические примитивы формируются только в функции drawSquare(), значе- значения атрибутов можно устанавливать в любом месте программы. Мы для этого будем исполь- использовать функцию myinit(). Нам потребуются три глобальные переменные. Размеры окна могут изменяться в процессе работы программы, а потому текущие значения соответствующих параметров должны быть доступны всем компонентам программы. В частности, они понадобятся функции перерисовки и функции вычерчивания квадратиков drav;Square(). Если бы нужно было вычерчивать квад- квадратики переменного размера, то соответствующий параметр также следовало бы сделать гло- глобально доступным. Программа инициализации устанавливает размеры отсекающего прямо- прямоугольника равными размерам окна приложения, которое будет сформировано в main(), a размеры видового окна задает таким образом, чтобы оно занимало всю область окна прило- приложения. Цвет фона окна — черный. Обращаю ваше внимание на то, что установку параметров окна приложения и видового окна в данном случае можно и не включать в программу, по- поскольку мы фактически дублируем установки по умолчанию. Но я специально включаю в программу соответствующие операторы, чтобы потом можно было сравнить этот вариант программы с тем, который будет создан в следующем разделе. /* Глобальные переменные */ GLsizei wh = 500, ww = 500; /* Исходные размеры окна */ GLfloat size =3.0; /* Половина длины стороны квадратика */ void myinit(void) { /* Установка параметров визуализации */ glviewport@,0,ww,wh); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D@.0, (GLdouble)ww, 0.0, (GLdouble)wh); 3.5. Программирование ввода, управляемого событиями 123
glMatrixMode(GL_MODELVIEW); /* Очистить окно, цвет фона - черный */ glClearColor @.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); } При разработке программы вычерчивания квадратиков нужно учитывать, что те коорди- координаты положения маркера мыши, которые передаются операционной системой при возбужде- возбуждении события мыши, определены в системе координат окна приложения, начало которой на- находится в его верхнем левом углу. Следовательно, придется сначала скорректировать значе- значение координаты у, используя для этого текущее значение высоты окна (глобальную переменную wh). Для вычерчивания будем использовать случайно выбранный цвет, восполь- воспользовавшись для этого стандартным генератором случайных чисел — функцией rand(). void drawSquare(int x, int у) { y=wh-y; glColor3ub((char)rand()%256, (char)rand()%256, (char)rand()%256); glBegin(GL_POLYGON); glVertex2f(x+size, y+size); glVertex2f(x-size, y+size); glVertex2f(x-size, y-size); glVertex2f(x+size, y-size); glEnd(); glFlush(); } Остается только вставить в текст программы необходимые директивы # include, и полу- получим программу, которая будет вполне удовлетворительно работать до тех пор, пока пользова- пользователь не попытается изменить размеры окна приложения. 3.5.2. События окна Большинство современных операционных систем позволяет пользователю изменять раз- размеры окон приложений и перемещать их по поверхности экрана. Эта процедура, как правило, выполняется с помощью мыши путем перетаскивания какого-либо из углов окна. При этом операционная система возбуждает событие, которое относится к группе события окна (window event). В прикладной программе должна присутствовать функция, которая будет реа- реагировать на это событие9. Продумывая реакцию приложения на изменение размеров окна, следует обратить внимание на следующие вопросы. 1. Следует ли перерисовать все объекты, которые были вычерчены в области окна перед тем, как его размеры изменились? 2. Как следует отреагировать на изменение соотношения сторон окна приложения? 3. Нужно ли после этого изменять размеры или атрибуты новых примитивов? 9 Существует функция перерисовки с обратным вызовом, которая вызывается по умолчанию, но она может выполнять совсем не те операции, которые желательны для данного приложения. 124 Глава 3. Ввод и взаимодействие с пользователем
Однозначных ответов на эти вопросы не существует — здесь все зависит от специфики кон- конкретного приложения. Если на экране изображена реальная сцена, то, скорее всего, следует сохра- сохранить нормальное соотношение сторон, чтобы изображение не оказалось искаженным. Но при этом может оказаться так, что часть области окна будет не использована или часть сцены не "впишется" в окно измененного размера. Если при изменении размеров окна нужно воспроизвести в нем все ранее вычерченные объекты, то необходим какой-то механизм сохранения информации об этих объектах и их повторной перерисовки. Чаще всего это выполняется в теле единственной функции, например функции display (), которую мы уже использовали в программе из главы 2. Эта функция регистрируется в качестве функции отображения с обратным вызовом. Но в данном примере этот механизм не подходит, поскольку было решено, что программа будет вычерчивать квадратики только в ответ на действия пользователя в интерактивном режиме. В этой программе все квадратики должны иметь один и тот же размер, который не зави- зависит от размеров и формы (соотношения сторон) окна приложения. При каждом изменении размеров окна приложения оно очищается, и мы фактически начинаем "с чистого листа". В качестве показаний событие перерисовки передает в прикладную программу высоту и шири- ширину нового окна. Эти параметры используются для переопределения прямоугольника отсече- отсечения с помощью функции gluOrtho2D() и нового видового окна с тем же соотношением сто- сторон. Затем окно очищается, причем в качестве цвета фона устанавливается черный цвет. Та- Таким образом, функция перерисовки с обратным вызовом будет иметь вид void myReshape(GLsizei w, GLsizei h) /* Настроить прямоугольник отсечения */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D@.0, (GLdouble)w, 0.0, (GLdouble)h); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Настроить параметры видового окна и очистить его */ glViewport@,0,w,h); glClearColor @.0, 0.0, 0.0, 0.0); glClear(GL_COLOR BUFFER_BIT); glFlush(); } Полный текст программы читатель найдет в приложении А. Но предлагаемый вариант функции перерисовки не единственно возможный. Можно, на- например, изменять размеры вычерчиваемых в дальнейшем квадратиков в соответствии с новыми размерами окна приложения. Помимо события изменения размеров окна приложения, сущест- существуют и другие, связанные с перекомпоновкой окон на экране, — перемещение окна без измене- изменения его размеров, перевод окна "на передний план" и т.д. Для всех таких событий, если в при- приложении планируется реагировать на них каким-то образом, нужно предусмотреть свои функ- функции обработки с обратным вызовом, аналогичные myReshape(), или можно "положиться" на те функции по умолчанию, которыми обеспечивает приложение библиотека GLUT. В разработанную программу можно ввести и еще одно усовершенствование — пусть она вычерчивает квадратики, пока пользователь будет перемещать мышь, удерживая кноп- кнопку нажатой. В этом случае необходимо определить в программе соответствующую функ- 3.5. Программирование ввода, управляемого событиями 125
цию с обратным вызовом и зарегистрировать ее в операционной системе с помощью функ- функции glutMotionFunc(). В нашей программе уже есть такая функция — drawSquare(), а по- потому остается просто зарегистрировать ее в качестве функции с обратным вызовом: glutMotionFunc(drawSquare); Теперь можно отслеживать на экране траекторию движения мыши, которая будет обрисо- обрисовываться прямоугольной кистью. 3.5.3. События клавиатуры В качестве одного из устройств ввода очень часто в графических приложениях использу- используется и клавиатура. События клавиатуры возбуждаются в том случае, если на клавиатуре на- нажата одна из клавиш в тот момент, когда маркер мыши на экране находится в области окна приложения. В некоторых операционных системах событие клавиатуры возбуждается и при отпускании клавиши, но в библиотеке GLUT на этот случай функция регистрации обратного вызова не предусмотрена. При возбуждении события операционная система передает в при- прикладную программу ASCII-код нажатой клавиши и координату положения маркера мыши в окне приложения. Регистрация функции обработки события клавиатуры выполняется функ- функцией glutKeyboardFunc(): glutKeyboardFunc(keyboard); Если, например, в приложении с помощью клавиатуры планируется только сигнализиро- сигнализировать программе о завершении работы, то функция обработки события клавиатуры будет иметь вид void keyboard(unsigned char key, int x, int y) { if(key=='q' || key == 'Q') exit(); 3.5.4. Функции отображения и простоя Из еще не рассмотренных функций с обратным вызовом две заслуживают особого внимания — это функция отображения и функция простоя {idle). Функция отображения упоминалась в главе 2. Она регистрируется в операционной системе с помощью функции glutDisplayFunc() из библиотеки типов GLUT: glutDisplayFunc(display); Функция, указанная в glutDisplayFunc(), будет вызываться каждый раз, когда потребу- потребуется перерисовка изображения. Одна из таких ситуаций— инициализация окна приложения. Поскольку заранее известно, что событие отображения возбуждается, когда открывается окно приложения, то в эту функцию желательно включить все операции вывода, не связанные с взаимодействием с пользователем. Исполнительная система GLUT требует, чтобы в каждом графическом приложении была определена функция отображения с обратным вызовом. Функцию отображения можно использовать и в другом контексте, например при анима- анимации, когда некоторые параметры, определяемые в приложении, изменяются. Библиотеку GLUT можно использовать для того, чтобы открыть несколько окон. В таком случае в число компонентов текущего состояния приложения включается и идентификатор текущего окна, и можно выводить объекты в разные окна, изменяя этот параметр. Можно также "свернуть" окно в пиктограмму {iconify). Следовательно, интерактивные приложения и программы ани- анимации могут вызывать функцию отображения в разных ситуациях. Удобно выполнять эту процедуру косвенно через функцию glutPostDisplay() из библиотеки GLUT. Эта функция 126 Глава 3. Ввод и взаимодействие с пользователем
включает определенную логику и предотвращает попытку вывода на экран, который не мо- может быть реализован, например, если окно свернуто в пиктограмму. Функция простоя с обратным вызовом (idle callback) вызывается в том случае, если про- программа не получает сообщений о каких-либо событиях. По умолчанию эта функция ничего не выполняет— ее тело не содержит никаких операторов, но программист может реализовать в этой функции какие-либо полезные для приложения операции. Типичный пример использова- использования такой функции — формирование графических примитивов, которые выводятся на экран, пока в окружающем мире ничего не меняется (см. упр. 3.2). Функции отображения и простоя будут использованы в программе, которую мы рассмотрим в последних разделах этой главы. В процессе выполнения можно переназначать функции с обратным выводом для обработ- обработки определенных событий. Можно вообще заблокировать обработку какого-либо события, передав в качестве аргумента соответствующей функции регистрации NULL. 3.5.5. Управление окнами Библиотека GLUT поддерживает как работу с несколькими окнами в одном приложении, так и с дочерними окнами в пределах одного главного окна приложения. В приложении можно соз- создать второе окно верхнего уровня (с заголовком second window) следующим оператором: id=glutCreateWindow("second window"); Значение переменной id можно в дальнейшем использовать для установки этого окна в каче- качестве текущего и перенаправления в него всех последующих операторов графического вывода: glutSetWindow(id); Во втором окне можно установить другие настройки параметров, обратившись к функции glutInitDisplayMode() перед вызовом glutCreateWindow(). Более того, каждое окно может иметь свой набор функций с обратным вызовом, поскольку функции регистрации имеют силу только по отношению к текущему окну. 3.6. Меню В прикладной программе с помощью графических примитивов и функций обработки со- событий мыши можно создать собственные средства графического интерфейса. Например, можно сформировать ползунковый регулятор, изображенный на рис. 3.16. Для этого потребу- потребуется сформировать на экране изображения прямоугольников и вывести текст надписей шка- шкалы, а затем, обрабатывая события мыши, получать информацию о желаемом перемещении ползунка регулятора. Конечно, программирование такого элемента управления — задача до- довольно утомительная, особенно если попытаться воспроизводить разного рода объемные эф- эффекты изображения отдельных деталей механизма (если хочется, чтобы регулятор выглядел на экране, "как живой"). В большинстве операционных сис- систем имеется свой набор элементов управления для графиче- графического интерфейса, но поскольку в этой книге мы не "привязываемся" к определенной операционной системе, то обсуждать специфику их использования не будем. Но, к сча- счастью, в библиотеке GLUT имеются функции поддержки ра- Рис. 3.16. Ползунковый регу- боты с всплывающими меню (pop-up menus), которые можно ля тор использовать в приложениях, нуждающихся в интенсивном диалоге с пользователем. Работа с меню в прикладной программе включает несколько достаточно простых опера- операций. Сначала нужно определить в программе пункты меню. Нужно связать меню с опреде- определенной кнопкой мыши. И последнее — нужно определить функцию с обратным вызовом для 3.6. Меню 127
обработки выбора каждого пункта меню. Ниже мы продемонстрируем, как это делается, на примере меню из трех пунктов. Выбор первого пункта будет завершать выполнение прило- приложения, в выбор второго и третьего — изменять размеры квадратиков, вычерчиваемых функ- функцией drawSquare(). Функцию обработки меню с обратным вызовом назовем demo_menu(). В функцию main() поместим вызовы функций, которые формируют меню и связывают его с правой кнопкой мыши: glutCreateMenu(demojnenu); glutAddMenuEntry("quit",1); glutAddMenuEntry("increase square size", 2); glutAddMenuEntry("decrease square size", 3); glutAttachMenu(GLUT_RIGHT_BUTTON); Функция glutAddMenuEntry() формирует очередной пункт меню и ее второй аргумент — идентификатор, который будет передаваться функции обработки с обратным вызовом при выборе этого пункта. Текст функции обработки меню с обратным вызовом приведен ниже. void demojnenu(int id) { if(id == 1) exit( ); else if (id == 2) size = 2 * size; else if (size > 1) size = size/2; glutPostRedisplay( ); } Обращение к функции glutPostResdisplay() запрашивает перерисовку изображения, кото- которое выполняется той функцией отображения, которая зарегистрирована функцией glutDis- playFunc(). В результате после завершения выполнения demojnenu () на экране восстановит- восстановится картинка без изображения меню. Библиотека GLUT поддерживает работу и с иерархическими меню (рис. 3.17). Пусть, на- например, главное меню состоит только из двух пунктов. При выборе первого пункта выполне- выполнение программы завершается, а при выборе второго на экран выводится подменю. Это подме- подменю содержит два пункта, позволяющих увеличивать или уменьшать размеры квадратиков, формируемых функцией drawSquare(). Для организации такого иерархического меню в функцию main() следует включить следующий фрагмент: subjnenu = glutCreateMenu(sizejnenu); glutAddMenuEntry("increase square size", 2); glutAddMenuEntry("decrease square size", 3); glutCreateMenu(topjnenu); glutAddMenuEntry("quit",1); glutAddSubMenu("Resize", subjnenu); glutAttachMenu(GLUT_RIGHT_BUTTON); Функции обработки size_menu() и top_menu() я предлагаю читателям разработать самостоятельно (см. упр. 3.5). quit resize \ increase square size decrease square size Рис. 3.17. Иерархическое меню 128 Глава 3. Ввод и взаимодействие с пользователем
3.7. Указание объектов Указание объектов {picking) — это одна из операций ввода, которая позволяет пользо- пользователю передать прикладной программе информацию о выборе того или иного из изобра- изображенных на экране объектов. Хотя при реализации этой операции и используется то же са- самое физическое устройство, что и для операции локализации позиции на экране, в инфор- информационном плане они значительно отличаются. В графических системах с растровым способом создания отображения реализация операции указания объектов связана со значи- значительными сложностями. Но так было не всегда. В системах с векторным способом вычерчивания дисплейный про- процессор получал сигнал указания от светового пера в тот момент, когда на экране физически вычерчивался выбранный объект, а потому селекция выполнялась по времени — достаточно было зафиксировать адрес объекта в дисплейном файле в момент получения прерывания от светового пера. Одна из причин, усложняющих выполнение операции указания в современных систе- системах, — конвейерный принцип обработки информации. Примитивы, определенные в пользо- пользовательской программе, затем проходят через несколько стадий преобразования, пока не будет сформировано их растровое разложение в буфере кадра. Хотя практически все преобразова- преобразования обратимы в математическом смысле, эту обратимость сложно реализовать, поскольку преобразования выполняются аппаратно. Поэтому преобразование информации о положении на экране в информацию о графическом примитиве не может быть сведена к простейшим вы- вычислениям. Кроме того, существует и проблема потенциальной неоднозначности такого пре- преобразования (см. упр. 3.11 и 3.12). Существуют два главных способа преодоления этих сложностей. Первый способ — се- лектирование объектов— предполагает настройку прямоугольника отсечения небольшого размера и видового окна так, что с их помощью можно отобрать то подмножество объектов, которые попадают в окрестность маркера устройства указания. Эти отобранные примитивы включаются в специальный список селекции (hit list), который далее анализируется приклад- прикладной программой. Альтернативный подход — использовать при анализе прямоугольные оболочки объектов (bounding rectangles или extents). Прямоугольная оболочка (или просто оболочка) — это наи- наименьший прямоугольник со сторонами, параллельными осям координат, который может быть описан вокруг объекта. Отобрать среди оболочек всех объектов те, которые содержат точку, указанную пользователем, не так сложно. Если в прикладной программе используется доста- достаточно простая структура данных для сопоставления объектов в локальных или мировых сис- системах координат с их оболочками в системе координат окна, то выбор соответствующих объ- объектов можно возложить на прикладную программу. Мы продемонстрируем этот подход на практике в программе, которая будет рассмотрена в следующем разделе. Значительно сложнее реализовать операцию указания объектов в том случае, если в гра- графической системе используется иерархическая организация отображаемых объектов. В этом случае каждый отдельный объект является членом какого-либо подмножества объектов. Ко- Когда такой объект "захватывается" устройством, в прикладную программу нужно переслать список всех объектов этого подмножества. Как правило, в графических API не специфицируется, как близко должна находиться ука- указанная на экране точка к объекту, чтобы его можно было считать выбранным. Одна из при- причин такого "упущения" — стремление предоставить программистам самостоятельно реализо- реализовать подходящий способ отбора. Если, например, использовать подход, основанный на при- применении оболочек, то можно указывать любую точку на поле объекта. Следует учитывать, что и пользователи при выполнении операции указания также далеко не всегда располагают маркер устройства точно на объекте, особенно если это контурный объект. 3.7. Указание объектов 129
3.8. Простая программа рисования Все, что было сказано выше о функциях обработки с обратным вызовом, дисплейных спи- списках и разработке интерактивных графических программ, мы продемонстрируем теперь на примере реальной программы. Программы рисования обладают множеством функциональ- функциональных возможностей развитых программ автоматизированного проектирования. Практически всякая программа рисования должна иметь следующий набор функций. ¦ Работа с геометрическими объектами, такими как отрезки прямых и многоугольники. Полагая, что объекты описываются множеством вершин, программа должна позволять вводить эти вершины в интерактивном режиме. ¦ Возможность манипулировать пикселями и таким образом формировать изображение непосредственно в буфере кадра. ¦ Управление атрибутами — цветом, типом линий и образцом заполнения. ¦ Поддержка операций с меню для управления режимом работы приложения. ¦ Корректное отслеживание перемещения окна приложения и изменение его размеров. Всеми этими возможностями обладает программа, которую мы сейчас рассмотрим. На рис. 3.18 показано, как выглядит окно программы сразу после начала сеанса. Пять прямоугольников в верхней части окна — это кнопки выбора одного из режимов рисования: Отрезок, Прямоугольник, Треугольник, Пиксель и Текст. Режим выбирается щелчком на поле соответствующего прямоугольника левой кнопкой мыши. После того как будет выбран режим Отрезок, следующими двумя щелчками левой кнопкой мыши пользователь может указать положение конечных точек отрезка. При этом указатель мыши должен находиться в рабочей области окна — вне зоны кнопок режима. После задания второй крайней точки про- программа выводит соответствующее изображение отрезка, причем он вычерчивается текущим цветом. По этой же схеме формируются изображения прямоугольников и треугольников. При вычерчивании прямоугольника задаются две точки на противоположных углах по диагонали, а при вычерчивании треугольника — три точки вершин. Новый режим можно выбрать после вычерчивания любого очередного объекта. В режиме Пиксель последовательные щелчки ле- левой кнопкой мыши задают положение малень- маленьких квадратиков, размер которых регулируется, вс : , , начиная с двух пикселей, причем квадратики ,ч ,,.-, , , ч> заливаются случайно выбранным цветом. В ре- , . . ', . , J-,'. >Л-,*:\.". жиме Текст пользователь вводит текст с кла- ,../;., виатуры, который отображается, начиная с по- последней указанной в рабочем поле точки. Два меню вызываются на экран после : ,, щелчка средней и правой кнопками мыши , . . ,. | (рис. 3.19). То меню, которое вызывается пра- , . , .ч ,&. вой кнопкой, позволяет пользователю очи- _ у. , ,,, ', стить экран (команда Clear) или прекратить .; '* выполнение программы (команда Exit). Меню, .,,.,." 1 . С,, которое вызывается средней кнопкой, имеет >,•."• •• >\- • v,V.. иерархическую структуру и включает три .., ; ,,yi . .. ,, , ,, ин »- г . /™. подменю. При выборе пункта Color в подме- Iw^- чиЧ^-"--' t,v > ¦«-,-• (• . . ню основного меню можно указать новый те- 4isV%t s / ?"> I'. . - ,, кущий цвет. Выбор пункта Fill в основном ме- меню позволяет задать в подменю режим запол- Рис. 3.18. Окно программы рисования в начале цешя ( Fj|| } или контурного изоб. сеанса v J ' JV сеанса 130 Глава 3. Ввод и взаимодействие с пользователем
ражения (пункт Fill off) прямоугольников и треугольников. Последний пункт основного ме- меню, Pixel size, позволяет указать в подменю увеличение размеров формируемых квадратиков (пункт increase pixel size) или их уменьшение (пункт decrease pixel size). Правая кнопка tm Средняя кнопка ¦»> quit clear Colors Pixel size Fill Red Green Blue Cyan Magenta Yellow White Black increase pix decrease pi> Fill on Fill off si size eel size Рис. 3.19. Структура меню программы рисования Самое сложное при разработке программы — решить, как распределить задачи между отдельными компонентами (функциями). То решение, которое нашло отражение в приве- приведенном ниже программном коде, согласуется с ранее рассмотренными примерами. Многие из функций дублируют рассмотренные ранее примеры. В программе определены следую- следующие функции: /* Обработка событий мыши */ void mouse(int btn, int state, int x, int y); /* Обработка событий клавиатуры */ void key( unsigned char c, int x, int y); void display(void); /* Отображение */ /* Формирование г«ацрата со случайным цветом заливки */ void drawSquare(int х, int у); /* Обработка изменения размеров окна */ void myReshape(GLsizei, GLsizei); void myinit(void); /* Инициализация */ /* Построение квадрата заданного размера в системе координат окна */ void screen_box(int х, int у, int s); /* Обработка команд меню */ void right menu(int id); void middle menu(int id); 3.8. Простая программа рисования 131
void colorjnenu(int id); void pixeljnenu(int id); void fill_menu(int id); /* Выбор режима рисования */ int pick(int x, int y); Функция main() в этой программе также аналогична функциям main() в рассмотренных ранее примерах: int main(int argc, char **argv) { int cjnenu, pjnenu, fjnenu; glutlnit(&argc,argv); glutlnitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutCreateWindow("paint"); glutDisplayFunc(display); c_menu = glutCreateMenu(colorjnenu); glutAddMenuEntry("Red",1); glutAddMenuEntry("Green", 2); glutAddMenuEntry("Blue",3); glutAddMenuEntry("Cyan",4); glutAddMenuEntry("Magenta",5); glutAddMenuEntry("Yellow",6); glutAddMenuEntry("White",7); glutAddMenuEntry("Black",8); pjnenu = glutCreateMenu(pixel jnenu); glutAddMenuEntry("increase pixel size", 1); glutAddMenuEntry("decrease pixel size", 2); fjnenu = glutCreateMenu(filljnenu); glutAddMenuEntry("fill on", 1); glutAddMenuEntry("fill off", 2); glutCreateMenu(right menu); glutAddMenuEntry("quit",1); glutAddMenuEntry("clear",2); glutAttachMenu(GLUT_RIGHT_BUTTON); glutCreateMenu(middle jnenu); glutAddSubMenu("Colors", cjnenu); glutAddSubMenu("Pixel Size", pjnenu); glutAddSubMenu("Fill", f menu); glutAttachMenu(GLUT_MIDDLE_BUTTON); myinit (); glutReshapeFunc (myReshape); glutMouseFunc (mouse); glutKeyboardFunc(key); glutMainLoop(); Функция myinit () очищает экран, устанавливает значения глобальных переменных и формирует дисплейных список для отображения 128 символов. 132 Глава 3. Ввод и взаимодействие с пользователем
void myinit(void) { /* Установка шрифта в дисплейном списке */ int i; base = glGenListsA28); for(i=0;i<128;i++) { glNewList(base+i, GL_COMPILE); glutBitmapCharacter(GLUT_BITMAP_9_BY_15, i); /* Для установки штрихового шрифта раскомментируйте следующий оператор */ /*glutStrokeCharacter(GLUT_STROKE_ROMAN, i); */ glEndList(); } glListBase(base); glViewport@,0, ww, wh); /* Настройка параметров отсекающего прямоугольника в соответствии с размерами окна операционной системы X window. Такая настройка позволяет избежать масштабирования координат объектов при изменении размеров окна приложения. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho@.0, (GLdouble)ww , 0.0, (GLdouble)wh , -1.0, 1.0); /* Настройка цвета фона окна и очистка окна приложения */ glClearColor @.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); } Функция display() очищает окно, устанавливает цвет фона и выводит изображения кно- кнопок. Кнопки представляют собой прямоугольники со сторонами, соответствующими пример- примерно 10% высоты и ширины окна. Такой способ вычерчивания обеспечивает изменение разме- размеров кнопок при изменении размеров окна. На поле каждого прямоугольника затем вычерчи- вычерчиваются условные обозначения соответствующих функций в виде простейших фигур. Функция myReShape() в этом примере дублирует рассмотренную в разделе 3.5.2 одно- одноименную функцию с обратным вызовом. Функция pick() выясняет, какой объект на экране выбран с помощью мыши, и работает в сочетании с функцией обработки события мыши mouse (). В OpenGL поддерживается метод выбора обобщенных объектов, который довольно сложен в реализации. Но если в некоторой задаче нас интересует только определенная прямоугольная область экрана, например занятая кнопкой, то проще разработать собственный метод выбора. Та процедура, которая представ- представлена в данной программе, работает следующим образом. Как только возбуждается событие мыши, функция mouse () вызывает функцию pick() и с помощью последней выясняет, в ка- какой части экрана расположен маркер мыши. Поскольку кнопки занимают на экране прямо- прямоугольные области, то не представляет особого труда выяснить, где находится маркер мы- 3.8. Простая программа рисован ия 133
ши — на одной из кнопок или на рабочем поле окна. Эта информация возвращается функции mouse(), которая и берет на себя остальную работу. Поскольку область экрана, на которой выполнен щелчок мышью, определяется функцией pick(), то mouse(), анализируя текущий режим, либо выводит на экран соответствующий примитив, либо запоминает текущие коор- координаты маркера мыши. Например, если пользователь выбрал режим построения треугольни- треугольников, щелкнув перед этим на кнопке с изображением треугольника, программа должна запом- запомнить положение первых двух вершин, а после указания третьей вершины построить тре- треугольник. Соответствующий фрагмент текста функции mouse () представлен ниже. case (TRIANGLE): /* Функция pick() определила ранее, что был выполнен щелчок на кнопке с изображением треугольника */ switch(count) /* Счетчик количества вершин */ { case(O): /* Сохранить координаты первой вершины */ count++; хр[0] = х; ур[0] = у; break; case(l): /* Сохранить координаты второй вершины */ count++; хр[1] = х; УР[1] = У? break; caseB): /* Третья вершина. Вычертить треугольник */ if(fill) glBegin(GL_POLYGON); else glBegin(GL LINE LOOP); glVertex2i(xp[0],wh-yp[0]); glVertex2i(xp[1],wh-yp[1]); glVertex2i(x,wh-y); glEnd(); draw_mode=0; /* Сбросить переменную режима */ count=0; Обратите внимание на то, что первым щелчком кнопкой мыши был выбран режим вычер- вычерчивания треугольников, а последующие щелчки подсчитываются и соответствующие коорди- координаты вершин сохраняются. Если же пользователь щелкнет на другой кнопке до того, как вве- введет положение третьей вершины треугольника, выбирается новый режим и данные о преды- предыдущих вершинах утрачиваются. Функция key () заполняет надпись символами, которые пользователь вводит с клавиату- клавиатуры, и выводит эту надпись на экран. void key(unsigned char k, int xx, int yy) { if(draw_mode!=TEXT) return; /* Символы выводятся на экран до тех пор, пока не изменится режим. */ glRasterPos2i(rx,ry); glCallList(k); /* Вывести на экран дисплейный список, соответствующий коду введенного символа к. */ rx+=glutBitmapWidth (GLUT_BITMAP J_BY_15, к); 134 Глава 3. Ввод и взаимодействие с пользователем
Типичный результат работы программы представлен на рис. 3.20. Хотя тот вариант программы рисования, который мы рассмот- рассмотрели выше, достаточно прост и его возможно- возможности далеки от совершенства, функции про- программы при желании можно расширить, при- причем это выполняется без особого труда. Например, можно заменить растровый шрифт на штриховой, добавить возможность выбора шрифтов, настройку атрибутов (ширины и ти- типа), предоставить пользователю возможность выбирать режим вычерчивания фигур (с за- заливкой или контурный), включить другие ти- типы фигур— произвольные многоугольники, аппроксимированные круги, кривые и т.д. Од- Однако обеспечить построение гладких кривых в программе рисования, подобно тому, как это делается в коммерческих программных про- продуктах, довольно сложно. Почему это так, вы узнаете в следующем разделе. Полный текст рассмотренной программы представлен в Приложении А. Рис. 3.20. Результат работы программы рисо- рисования 3.9. Интерактивные программы анимации Программы, которые мы рассматривали до сих пор, формировали статическое изображе- изображение, т.е. выведенные на экран объекты нельзя было изменять. Но для пользователя гораздо больший интерес представляют динамические картинки, в которых отдельные объекты могут двигаться или изменяться в ответ на команды пользователя (или по ходу какого-нибудь внешнего по отношению к программе процесса). Например, можно "оживить" символы над- надписи и заставить их двигаться по экрану или показать, как меняется изображение некоторой сцены при перемещении наблюдателя. Ниже мы рассмотрим, как организовать в программе анимацию на примере простого вращающегося объекта. 3.9.1. Вращающийся квадрат Рассмотрим двухмерную точку, описываемую парой координат: х = cos9, .у = sinG. Эта точка при изменении значения угла 9 будет перемещаться вдоль окружности единичного ра- радиуса. Три точки (—sinG, cos9), (-cosG, -sinG) и (sinG, -cosG) также лежат на окружности единично- единичного радиуса и вместе с первой образуют вершины квадрата, вписанного в эту окружность (рис. 3.21). Стороны этого квадрата имеют длину V2 . Полагая, что значение 9 является глобальной переменной, можно вычертить этот квадрат с по- помощью следующей функции: (-sin в, cos в) (-cos в, sin 6») (cosfl, sin (sin в, —cos в) Рис. 3.21. Квадрат, вписанный в окруж- окружность единичного радиуса 3.9. Интерактивные программы анимации 135
void display() { glClear(); glBegin(GL_POLYGON); /* Преобразование градусов в радианы */ thetar = theta/(B*3.14159)/360.0); glVertex2f(cos(thetar), sin(thetar)); glVertex2f(-sin(thetar), cos(thetar)); glVertex2f(-cos(thetar), -sin(thetar)); glVertex2f(sin(thetar), -cos(thetar)); glEnd(); } Приведенная функция display () будет формировать изображение квадрата при любом заданном значении 9, но при этом предполагается, что значение 9 изменяется по ходу выпол- выполнения программы и в результате на экране появляется изображение вращающегося квадрата. Для этого нужно время от времени вызывать с помощью функции glutPostRedisplay(), ко- которая входит в библиотеку GLUT, функцию display (). Теперь организуем приращение угла 9 на фиксированное значение в то время, когда программа не получает сообщения ни о каких других событиях (находится в состоянии "простоя"). Это выполняется с помощью функции обработки простоя, которая регистриру- регистрируется функцией glutIdleFunc() из библиотеки GLUT: glutldleFunc(idle); Функция idle () имеет следующий вид: void idle() { theta+=2; /* или какое-либо другое приращение */ if(theta >= 360.0) theta-=360.0; glutPostRedisplay(); } Такая программа будет работать, но желательно было бы хоть как-то управлять этим про- процессом. Самый простой вариант— предоставить пользователю возможность "включать" и "выключать" вращение. Для этого потребуется включить в программу функцию обработки события мыши, в которой будет выполняться изменение режима работы функции обработки простоя. Функция обработки события мыши регистрируется следующим образом: glutMouseFunc(mouse); Текст самой функции mouse () представлен ниже: void mouse(int button, int state, int x, int y) { if(button==GLUT_LEFT && state==GLUT_DOWN) glutldleFunc(idle); if(button==GLUT_MIDDLE && state=GLUT_DOWN) glutldleFunc(NULL)? } Таким образом, щелчок левой кнопкой мыши запускает вращение квадрата, а щелчок средней кнопкой — прекращает его. Если запустить программу на выполнение, то, скорее всего, выводимое изображение не создаст у вас иллюзию вращающегося квадрата, а вы уви- 136 Глава 3. Ввод и взаимодействие с пользователем
дите фрагменты квадрата, изменяющиеся с течением времени. Что же в этой программе сде- сделано неправильно? Этим вопросом мы и займемся в дальнейшем. 3.9.2. Двойная буферизация При выводе изображения нужно, чтобы частота обновления была достаточно высокой и зритель не смог заметить, как экран очищается и как на нем формируется новое изображение. Эта частота должна превышать 50 Гц, тогда зритель не заметит мелькания изображения на экране. В графических компьютерных системах это требование касается прежде всего часто- частоты регенерации изображения, т.е. быстродействия буфера кадра. Если содержимое буфера не изменяется и обеспечена частота регенерации свыше 50 Гц (а лучше где-то около 85 Гц), для зрителя будут созданы необходимые комфортные условия восприятия изображения10. Но если содержимое буфера изменяется, то зритель может увидеть совершенно нежелательные эф- эффекты, например заметить "дерганье" последовательных фаз изменяющейся картинки. Это явление наблюдается особенно отчетливо, если изображение достаточно сложное и оче- очередную его фазу не удается сформировать за один период регенерации. В этом случае зритель увидит половину кадра с новой фазой движения, а половину — с прежней. Изображение пере- перемещающегося объекта будет при этом искажено на экране. То же самое происходит и с после- последовательными процессами очистки экрана и обрисовки нового изображения, как это имеет ме- место в нашем примере с вращающимся квадратом. Хотя квадрат — это довольно простой объект, который формируется за время, меньшее периода регенерации, в нашей программе отсутствует связь между процессом формирования объекта в буфере и выводом содержимого буфера на эк- экран аппаратными средствами. Таким образом, вполне вероятно, что на экран будет отправлено содержимое буфера, в котором новый квадрат сформирован только частично. Эту проблему можно решить с помощью двойной буферизации (double buffering). Пред- Предположим, что в нашем распоряжении есть два буфера кадра, которые принято называть рабочим (front buffer) и фоновым (back buffer). Рабочий буфер — это тот, из которого выполняется реге- регенерация изображения на экране, а в фоновом изображение формируется программой. По коман- командам от прикладной программы можно переключать функции буферов: сделать рабочим тот, ко- который ранее был фоновым, а фоновым — тот, который ранее был рабочим. При переключении вызывается функция display (), в результате чего на экран начинает автоматически выводиться изображение, подготовленное ранее в фоновом буфере, а новое изображение можно формиро- формировать в том буфере, который теперь стал фоновым. Механизм двойной буферизации устанавли- устанавливается в режиме инициализации аргументом функции glutInitDisplayMode(). Вместо констан- константы GLUTSINGLE нужно задать в дизъюнкции флагов константу GLUT_DOUBLE. Переключение функций буферов выполняется функцией glutSwapBuf fers(), которая входит в состав библио- библиотеки GLUT. Если формируется довольно сложное изображение, требующее много времени, программа может спокойно работать с фоновым буфером и это никак не скажется на регене- регенерации ранее созданного изображения из рабочего буфера. После завершения формирования очередной фазы динамической картинки выполняется переключение буферов. Двойная буферизация — это стандартная технология организации компьютерной ани- анимации. Все операторы формирования изображения включаются в функцию display(), но при использовании двойной буферизации в этой функции нужно сначала очистить рабочий буфер, вызвав glClear(), а последним оператором вызвать функцию переключения буфе- буферов glutSwapBuffers(). 10Если используется чересстрочная развертка, четные и нечетные строки кадра выводятся поперемен- попеременно и, таким образом, действительная частота регенерации равна половинной частоте кадровой развертки. При этом зритель может заметить легкое дрожание изображения по вертикали, особенно когда сидит до- довольно близко к экрану. 3.9. Интерактивные программы анимации 137
Теперь вернемся к нашему вращающемуся квадрату. В рассмотренную ранее программу нужно внести два изменения. Во-первых, в функции main() нужно заказать режим двойной буферизации: glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); Во-вторых, нужно скорректировать функцию display(), добавив в самый ее конец оператор glutSwapBuffers(); Учтите, что двойная буферизация не решает проблемы скорости формирования изобра- изображения. Фактически эта технология позволяет только избежать наложения отображения и формирования новой фазы картинки — новое изображение выводится на экран только после завершения его формирования. Но возможно получить вполне приемлемое качество динами- динамического изображения при частоте формирования новых фаз порядка 10-20 Гц. 3.9.3. Проблемы с буферизацией При создании интерактивных графических программ формирования динамического изо- изображения программисту приходится сталкиваться с целым рядом проблем, которые не могут быть решены только за счет двойной буферизации. Возвращаясь к рассмотренному выше примеру — программе рисования по командам пользователя, — предположим, что нам пона- понадобилось добавить в картинку изображение часов, которые показывали бы текущее время выполнения программы (рис. 3.22). В большинстве операционных систем суще- существуют функции, возвращающие текущее ас- астрономическое время в виде строки символов в коде ASCII, либо функции, возвращающие це- целое число, равное времени, прошедшему после некоторого события (в секундах). Периодически вызывая эти функции (например, в программе обработки простоя), можно затем преобразо- преобразовать полученное значение в приемлемый фор- формат, например часы „минуты: секунды. Послед- Последняя стадия процесса — вывести эти меняющие- меняющиеся данные на экран в виде текста. Посмотрим, что случится, если добавить со- соответствующий фрагмент в первый вариант программы построения вращающегося квадра- квадрата, в котором не использовалась двойная буфе- буферизация. Вы, скорее всего, сразу же заметите, что показания часов мигают при выполнении очистки экрана функцией display!). Попробу- Попробуем справиться с этой проблемой, воспользо- воспользовавшись технологией двойной буферизации. При переходе в режим двойной буфериза- буферизации мигание исчезнет, но вместо этой проблемы появится другая, еще более серьезная, — станут мерцать основные объекты на рабочем поле окна. Причина этого неприятного эффекта заключается в том, что используется режим немедленного вывода изображения. Объекты, ко- которые формируются программой по командам пользователя, передаются в буфер кадра един- единственный раз. Поэтому каждый объект оказывается только в одном из двух буферов и не мо- может присутствовать одновременно в двух. Теперь, справившись с миганием показаний часов, мы видим, что начали мерцать объекты в графической зоне экрана. Рис. 3.22. Окно программы рисования, в ко- которой есть часы текущего времени вы- выполнения 138 Глава 3. Ввод и взаимодействие с пользователем
Решение этой проблемы тоже можно отыскать. В нашей программе рисования для вывода объектов на экран можно использовать дисплейный список, в который следует вносить объ- объекты по мере обработки команд пользователя. После этого можно при каждом переключении очищать рабочий буфер и формировать в нем изображения всех объектов, уже включенных к этому времени в дисплейный список. Правда, для этого программу придется значительно ус- усложнить, особенно если для вывода символов также используются свои дисплейные списки. Но можно поступить и проще, сделав так, чтобы изображение каждого объекта оказалось сразу в обоих буферах— и в экранном, и в рабочем. OpenGL и некоторые другие графиче- графические системы позволяют таким образом настроить режим работы системы, чтобы можно бы- было одновременно формировать изображение во множестве буферов. Мы рассмотрим подроб- подробно эту технологию позже, в главе 9, когда будем обсуждать и другие проблемы использова- использования буферов. 3.10. Разработка интерактивных графических программ Формально определить перечень характеристик хорошей интерактивной графической программы довольно сложно, но отличить хорошую программу этого типа от плохой, пора- поработав с обеими, можно достаточно быстро. Хорошая программа, как правило, располагает следующими функциональными возможностями. 1. Плавное изображение динамических сцен без видимого '"дерганья" объектов на экране. 2. Наличие нескольких устройств ввода, с помощью которых можно управлять характе- характеристиками объектов на экране, в том числе и их динамикой. 3. Разнообразие методов ввода и отображения информации. 4. Доступный и интуитивно понятный интерфейс пользователя. 5. Обратная связь с пользователем. 6. "Лояльное" отношение к возможным ошибкам пользователя. 7. В программе должны учитываться как зрительные, так и моторные свойства человека- оператора, т.е. его способность воспринимать зрительную информацию и реагировать на нее, выполняя какие-либо действия с устройствами ввода. Важность перечисленных функциональных возможностей графической программы и сложность их реализации на практике нельзя недооценивать. Тема взаимодействия человека с машиной (HCI— human-computer interaction)— одна из тех, которые привлекают внима- внимание множества исследователей во всем мире, и ее нельзя изложить на нескольких страницах. В данной книге нас в этой теме больше всего будет интересовать "компьютерная" сторона, которая обеспечивает формирование графического изображения в такой комплексной систе- системе. Но есть несколько вопросов, общих для компьютерной графики и теории человеко- машинных систем, которые нужно учитывать при разработке интерактивных программ. 3.10.1. Инструментальные средства, экранные элементы управления и буфер кадра В рассмотренной выше программе рисования мы использовали графические средства ин- интерфейса, в частности всплывающие меню, которые поддерживаются библиотекой GLUT, и графические кнопки, которые разработали самостоятельно. Спектр экранных элементов ин- интерфейса, доступных в современных программах, значительно шире. Он включает и различ- различные линейные регуляторы, шкалы, "горячие" области экрана, звук, пиктограммы и т.п. Обыч- Обычно прикладной программист рассматривает эти элементы как стандартные и рассчитывает в 3.10. Разработка интерактивных графических программ 139
этом деле на библиотеки инструментальных программ, хотя некоторые из специалистов счи- считают, что свой продукт всегда лучше, чем заимствованный. В общем случае, почти все такие инструментальные пакеты используют для связи с прикладной программой механизм функ- функций с обратным вызовом. Значительно облегчит разработку собственных средств графиче- графического интерфейса описание методики работы с внутренними буферами графической системы, которое представлено в главе 9. Ниже на двух примерах будет показано, что использование графических построений на геометрическом уровне недостаточно эффективно при работе с элементами экранного ин- интерфейса. Гораздо эффективнее в этом случае работать напрямую с буфером кадра. Сначала рассмотрим, как выполняются операции построения всплывающего меню. При обращении к функции работы с меню на экране должно появиться его изображение, причем примерно в том месте, где находится указатель мыши в момент, когда пользователь щелкнул соответст- соответствующей кнопкой. После того как будет выбран один из пунктов меню, его изображение должно исчезнуть с экрана, а на его месте восстановлены ранее вычерченные объекты. Если использовать только операции геометрических построений, описанные ранее, выполнить та- такую операцию быстро не удастся. Наиболее целесообразно запомнить изображение, "перекрытое" меню, а затем, после завершения работы с меню, вернуть его на экран. Таким образом, нам придется работать на уровне пикселей поля экрана, а не геометрических объек- объектов и, следовательно, иметь дело с буфером кадра. При этом интенсивно используются опе- операции пересылки битового блока памяти (операции группы bitbli). Второй пример— построение "резиновой" линии (rubberbanding)— метод формирова- формирования графических изображений в интерактивном режиме. В рассмотренной выше программе рисования для задания вершин формируемого графического объекта пользователь должен был дважды (или трижды) ука- указать позиции в графической зоне окна, щелкнув кнопкой мы- мыши. При этом он только мысленно мог себе представить, как именно пройдет формируемый отрезок и как он соотносится с ранее вычерченными объектами. Новый метод позволяет пользователю постоянной видеть "эскиз" формируемого от- отрезка, который будет следить за текущим положением мыши, связывая его с ранее введенной точкой. Поведение этого "эскиза" нового отрезка очень напоминает поведение резино- резиновой нити, закрепленной одним концом на каком-либо гвозди- гвоздике, вбитом в чертежную доску, а другим концом на игле цир- циркуля или "чертилки", — куда бы ни двигалась "чертилка", между нею и вбитым гвоздиком всегда видна прямая линия, образованная натянутой нитью. Точно такую же роль играет в самом примитивном виде и искусственная "резиновая" нить в графической системе. Обычно изображение нити возникает, когда пользователь нажимает кнопку мыши, и отслеживает текущее положение указателя мыши до тех пор, пока пользо- пользователь не отпустит кнопку (рис. 3.23). Но в некоторых графи- графических системах применяется и другая последовательность операций— нить возникает после первого щелчка, а после второго и всех последующих "перепрыгивает" в новую позицию, пока не будет сформирована команда прекращения по- последовательности отрезков. При формировании изображения "резиновой" нити на каждом цикле прежнее изображение отрезка (т.е. отрезка, связывавшего предыдущее положение ука- указателя мыши с начальной точкой) стирается, а вычерчивается новое изображение, связанное с текущим положением указателя мыши. Рис. 3.23. Построение "рези- "резиновой "линии 140 Глава 3. Ввод и взаимодействие с пользователем
При построении изображения "резиновой" нити не обойтись без операций на уровне бу- буфера кадра или без специальных аппаратных средств. Эта проблема во многом сходна с про- проблемой построения изображения всплывающего меню. В каждом цикле отслеживания теку- текущего положения мыши нужно сначала вернуть изображению "первозданный" вид, а потом вычертить новый отрезок. С помощью аналогичной технологии вычерчиваются не только "резиновые" отрезки, но и "резиновые" прямоугольники и окружности. Такие инструмен- инструментальные пакеты, как GLUT, предоставляют программисту широкие возможности для выпол- выполнения операций пересылки блоков буфера кадра. В состав пакетов практически всегда вклю- включаются средства для формирования всплывающих меню и построения изображения "рези- "резиновой" нити, что избавляет прикладного программиста от необходимости самостоятельно разрабатывать их, если в приложении нужно реализовать подобные функции. 3.11. Резюме В этой главе мы лишь слегка "прикоснулись" ко многим темам, связанным с взаимодейст- взаимодействием пользователя и графической программы. Организация в программе реакции на действия пользователя с выводом динамического изображения на экран придает приложению совер- совершенно новые возможности. Мы показали, что хотя состав функций API OpenGL и не зависит от конкретной операционной системы, в любой графической программе не обойтись без ми- минимального набора средств взаимодействия с операционной системой. Роль переходного элемента между независящим от платформы API и операционной системой играет набор ин- инструментальных программ, который, с одной стороны (со стороны графической системы), имеет единообразный интерфейс, а с другой стороны, имеет множество вариантов реализа- реализации, специфических для каждой из имеющихся операционных систем. При работе с OpenGL роль такого инструментального пакета отводится библиотеке GLUT. Было рассмотрено, как отражается парадигма "клиент/сервер" на структуре графической системы и характеристиках ее отдельных компонентов. Эта парадигма не только способству- способствует рациональной организации графических приложений в сетевой среде, но позволяет созда- создавать графические программы, переносимые с одной аппаратной или программной платформы на другую. Эта концепция нашла свое воплощение в объектно-ориентированном подходе к программированию задач компьютерной графики, а также во внедрении графических прило- приложений в сетевую среду World Wide Web (эти темы будут подробно рассмотрены в главе 8). С точки зрения прикладного программиста идеология создания интерактивных графических приложений практически единая во всех системах. Графическая часть сервера включает растро- растровый дисплей, клавиатуру и устройство указания. Почти на всех рабочих станциях пользователь работает в сетевой операционной среде, поддерживающей многооконный интерфейс. Как пра- правило, в такой среде параллельно решается множество задач, но операционная система создает у пользователя иллюзию, что он является безраздельным хозяином компьютера. Избыточность в программе, настроенной на такой режим работы, минимальна, а исполь- использование логических внешних устройств избавляет прикладного программиста от заботы об организации обслуживания конкретной модели физического устройства. Стандартным методом взаимодействия с внешними устройствами в такой многопользова- многопользовательской операционной среде является механизм реакции на события, хотя при желании можно использовать и другие методы. Например, ввод-вывод по запросу и обработка событий предос- предоставляют наиболее широкие возможности в организации гибкой реакции программы на все раз- разнообразие ситуаций, возникающих во внешнем по отношению к программе мире. Интерактивная компьютерная графика представляет собой удивительно мощный инстру- инструмент с практически неограниченными возможностями. Пользуясь различными методиками организации взаимодействия с пользователем, программист может создавать программные продукты, рассчитанные на любую категорию пользователей. Возможно, вы убедитесь в этом на собственном опыте, выполнив упражнения, представленные в конце главы. 3.11. Резюме 141
3.12. Рекомендуемая литература Проект Sketchpad Сазерленда (Sutherland) описан в работе [Sut63]. Большинство концепций, положенных в основу оконного графического интерфейса, раз- разработано в научно-исследовательском центре фирмы Xerox в Пало-Альто (Xerox Palo Alto Research Center— PARC) в 1970-х годах (см. [Sch87J). Здесь же появилась на свет и мышь [Eng68]. Разработки PARC лежат и в основе популярных сегодня операционных систем — Macintosh Operating System, X Window и Microsoft Windows. Книга Фоли (Foley) и его соавторов [Fol94] содержит подробное описание процесса раз- разработки интерфейса пользователя с акцентом на аспекты использования графических средств. Книги Шнайдермана (Schneiderman) [Sch97] и Нильсона (Nielson) [Nie94] могут по- послужить хорошим вводным курсом в проблематику человеко-машинных систем. Операционная система X Window [Sch88J разработана в Массачусетсском технологиче- технологическом институте (Massachusetts Institute of Technology) и в настоящее время стала стандартом de facto для всех приверженцев UNIX. Завоевывающая все большую популярность среди пользователей ПК версия UNIX под названием Linux также поддерживает работу X Window. Режимы ввода информации и взаимодействия с пользователем, которые мы обсуждали на страницах этой книги, основываются на стандартах, реализованных в GKS [ANSI85] и PHIGS [ANSI88J. Эти стандарты разрабатывались как универсальные, т.е. пригодные и для вектор- векторных, и для растровых дисплеев, а потому в них не учитывались уникальные возможности рас- растровых графических систем (см. [Pik84, Gol83J). Хотя при изложении материала в этой главе мы опирались на инструментальную библио- библиотеку GLUT [Kil94,b], программисты на практике используют и непосредственный доступ к операционной системе X Window, и различные версии других инструментальных библиотек, позволяющих работать как с X Window [Kil94,a, OSF89], так и с Microsoft Windows. Интерес представляет и использование при разработке пользовательского интерфейса в OpenGL- программах языков написания сценариев, таких как tel/tk [Ous94]. Упражнения 3.1. Объясните, в чем суть проблем, с которыми сталкивается программист при опреде- определении штриховых шрифтов. Разработайте простую структуру данных, которая по- позволила бы определить набор символов штрихового шрифта, оградившись только отрезками прямых. 3.2. Разработайте новую версию программы отображения узора Серпинского из гла- главы 2, в которой щелчок левой кнопкой мыши запускал бы процесс формирования точек узора на экране, щелчок правой кнопкой мыши останавливал бы процесс формирования новых точек, а щелчок средней кнопкой прекращал выполнение приложения и закрывал его окно. Включите в программу обработку события изме- изменения размеров окна приложения. 3.3. Разработайте линейный регулятор, который позволял бы пользователю задавать цвет вычерчивания фигур в программе рисования. С помощью этого интерфейса пользователь сможет сначала визуально оценить новый цвет, а уже затем назначить его для формирования очередных фигур. 3.4. Виртуальный трекбол можно разработать на основе обычной мыши, "отобразив" ее на поверхность шара. Основное назначение такого устройства — позволить пользо- пользователю задавать с помощью мыши скорость, вращая шар вокруг вертикальной оси (имитируя "спин"). Разработайте такое виртуальное устройство и графическую про- программу, которая будет демонстрировать его возможности. 142 Глава 3. Ввод и взаимодействие с пользователем
3.5. Разработайте новую версию программы вычерчивания квадратов (раздел 3.5), в кото- которой использовалось бы всплывающее меню, аналогичное описанному в разделе 3.6. 3.6. Добавьте в программу рисования (раздел 3.8) индикатор времени работы, используя собственную программу считывания показаний системных часов. 3.7. Хорошую практику при освоении методов программирования взаимодействия ком- компьютера и пользователя дает разработка игровых программ. Разработайте програм- программу игры в шашки. Каждую клетку на доске следует рассматривать как объект, кото- который может выбрать пользователь. Чтобы не связывать себя тайнами шашечной стратегии, разработайте программу, в которой оба участника — люди. 3.8. Разработайте простую программу раскладки пасьянса. Сначала разработайте про- простую колоду карт, используя только те примитивы, которые мы уже рассмотрели в этой книге. Программу следует свести к выбору прямоугольных объектов на экране. 3.9. Моделирование разных видов игры в бильярд — это тоже довольно интересная за- задача для тех, кто увлекается игровыми программами. В такой программе, как и в упр. 2.18, придется вычислять траектории и определять столкновение шаров. Инте- Интерактивный аспект новой программы — задание направления движения шаров с по- помощью этакого компьютерного кия и обеспечение достаточно плавной имитации движения шара на экране. Разработайте программу для двух игроков. 3.10. Вместо того чтобы использовать для ввода команд меню или кнопки, можно просто разбить все поле графического окна на зоны и анализировать, в какой из них поль- пользователь щелкнул кнопкой мыши. Используйте этот принцип в программе рисова- рисования (раздел 3.8). 3.11. Пересчет координат точек из мировой системы координат в систему координат экрана обеспечивает однозначное соответствие, но оно не является обратимым, поскольку происходит переход от трехмерной системы координат к двухмерной. Предположим, однако, что мы имеем дело с двухмерным приложением. Является ли в нем такой пе- пересчет обратимым? Какие проблемы возникают при определении значения координат в мировой системе по положению точки в системе координат экрана? 3.12. Как применить результат упр. 3.11 к организации процедуры указания в программе? 3.13. При разработке прикладной программы программисту приходится решать, следу- следует ли использовать в ней дисплейные списки. Рассмотрите хотя бы два разных практических приложения и составьте для каждого из них список доводов за и против применения дисплейных списков. Используя шрифты, имеющиеся в со- составе библиотеки GLUT, проверьте на практике, правильно ли были сформулиро- сформулированные доводы. 3.14. Разработайте интерактивную программу, которая позволит провести "виртуальную" крысу через лабиринт, который был сформирован при выполнении упр. 2.7. Можно для управления движением крысы использовать кнопки мыши: правую и левую — для поворота соответственно вправо и влево, а среднюю — для движения вперед. 3.15. В разделе 3.9 было показано, что даже двойная буферизация в программе рисования не гарантирует прекращение мигания изображения. Можно ли справиться с ним, используя только те программы формирования изображения, которые вам известны на данной стадии изучения? Обоснуйте свой ответ. 3.16. Недорогие устройства ввода типа джойстик, как те, что часто используются в иг- игровых приставках, не имеют преобразователей, вместо которых используются трехпозиционные переключатели. Как, по-вашему, такие устройства "общаются" с программой? Упражнения 143
Тангаж 3.17. Ориентация летательного аппарата описывается ориентацией осей системы координат, показанной на рис. 3.24. Движение джойстика вперед-назад контролирует поворот вверх-вниз продольной оси аппарата {тангаж аппарата— pitch). Движение джойстика влево-вправо контролирует вращение вокруг этой оси {крен — roll). Разработайте про- программу, которая будет использовать в качестве уст- устройства ввода мышь и по ее смещению управлять аппаратом по тангажу и крену, изменяя параметры визуализации для пилота, который сидит в кабине такого аппарата. Это упражнение можно выполнить для двухмерного случая, рассматривая набор объ- объектов, видимых из кабины аппарата и пренебрегая эффектами перспективы. 3.18. Рассмотрим стол, на котором смонтирован плоский рычажный механизм (манипулятор), на конце которо- которого имеется сенсор (рис. 3.25). Предположим, что дли- длины обоих звеньев манипулятора фиксированы, а зве- звенья связаны с основанием и друг с другом простыми поворотными сочленениями (сочленениями с одной степенью свободы). Найдите соотношения, связы- связывающие углы поворота в сочленениях 9 и ф и положе- положение сенсора, закрепленного на последнем звене. 3.19. Пусть имеется ЭЛТ-монитор с размерами экрана 40x40 сантиметров. В мониторе используется про- прогрессивная развертка частотой 60 Гц, причем 10% периода кадровой развертки уходит на обратный ход луча из правого нижнего угла экрана в левый верхний. Будем считать, что разрешение монитора 1024x1024 пикселей. Найдите соотношение между временем появления сигнала на выходе светового пера и положением светового пера на поле экрана. Представьте результат в метрических единицах из- измерения и в относительных (пикселях). 3.20. Программа вычерчивания электронных схем является одним из вариантов программы рисования, рассмот- рассмотренной в этой главе. Будем считать, что схема состо- состоит из логических элементов типа И (AND), ИЛИ (OR) и НЕ (NOT), символические обозначения которых представлены на рис. 3.26. Разработайте программу, которая позволяет пользователю вычерчивать схему, пользуясь меню для выбора типа элемента и задавая его положе- положение на экране с помощью мыши. Подумайте над тем, как автоматически совмещать выход одного элемента с входом другого. 3.21. Дополните программу, разработанную в предыдущем упражнении, возможностью графически формировать последовательность входных сигналов для разработанной схемы. Программа должна в ответ выводить графики сигналов в указанных пользо- пользователем узлах схемы. Рис. 3.24. Система координат летательного аппарата Рис. 3.25. Плоский манипуля- манипулятор 144 Глава 3. Ввод и взаимодействие с пользователем
+ b OR AND NOT Рис. 3.26. Символы логических элементов элек- электронной схемы 3.22. Дополните программу, разработанную в упр. 3.20, таким образом, чтобы у пользо- пользователя была возможность вводить логические выражения. В ответ программа должна формировать электронную схему, реализующую введенное выражение. 3.23. Распространите методы, положенные в основу разработки программы упр. 3.20, на формирование схем алгоритмов программ или отображение графов, которые изу- изучаются в курсах структур данных. 3.24. Программные пакеты вычерчивания графических документов, как правило, предос- предоставляют в распоряжение пользователей множество методов формирования изобра- изображения. Разработайте интерактивную программу вычерчивания двухмерных кривых. Это приложение должно предоставлять пользователю возможность выбрать режим отображения графиков (в виде ломаной линии, столбиковой или сегментной диа- диаграммы), цвета и типа линии. 3.25. Требования к частоте регенерации дисплеев на ЭЛТ E0-85 Гц) сформулированы применительно к люминофорам с малым послесвечением. Но существуют и люми- люминофоры с длительным послесвечением. Почему, по вашему мнению, ЭЛТ с такими люминофорами не используются в большинстве дисплеев компьютерных рабочих станций? Где имеет смысл использовать подобные дисплеи? Упражнения 145
ГЛАВА 4 Объекты и геометрические преобразования Итак, мы готовы обстоятельно рассмотреть трехмерную графику. Большая часть этой главы посвящена описанию различных способов представления базовых геометриче- геометрических типов, преобразований одного представления в другое и свойств геометрических объектов, независимых от конкретного представления. Начнем с анализа математических соотношений, положенных в основу компьютерной графики. Такой подход поможет избежать в дальнейшем путаницы, которая порождается из- за отсутствия четкого представления об отличиях между геометрическими сущностями, спо- способами их описания в конкретной системе координат и математическими абстракциями. Мы будем использовать понятия аффинного и Евклидова векторных пространств, кото- которые являются математическим фундаментом для дальнейшей работы. Одна из основных целей, которую я преследую в этой главе, познакомить читателя с методами решения гео- геометрических проблем, не зависящими от выбранных систем координат. Такой подход яв- является достаточно надежной основой для всего последующего анализа, распространяется на любые частные виды представления геометрических объектов и логически приводит к идее использования обобщенных однородных координат, которая не только помогает по- понять суть проблемы, но и является основополагающей для эффективной реализации мето- методов ее решения на практике. Мы используем терминологию абстрактных типов данных для того, чтобы подчеркнуть отличие между объектом как таковым и его частным представлением. Дальнейшее обсужде- обсуждение покажет, что математические соотношения естественным образом вытекают из нашего желания манипулировать немногими базовыми геометрическими типами данных. Математи- Математический аппарат, который будет представлен в этой главе, относится к таким областям мате- математики, как векторные пространства, геометрия и линейная алгебра. Формальное изложение математики векторных пространств и матричной алгебры читатель найдет в приложениях Б и В соответственно. Следуя подходу, декларированному в начале этой книги и уже реализованному в преды- предыдущих главах, для иллюстрации основных теоретических положений будет разработана не- несложная программа, в которой мы постараемся продемонстрировать методику реализации
средствами графического API основных концепций геометрических преобразований. Пример, предложенный вашему вниманию в этой главе, имеет дело с представлением и преобразова- преобразованиями простого геометрического объекта — куба. Мы также продемонстрируем, как задавать параметры преобразований в интерактивном режиме. 4.1. Скаляры, точки и векторы Компьютерная графика имеет дело со множеством геометрических объектов, таких как точки, многоугольники и многогранники. Как уже было показано в предыдущих главах на примере двухмерных приложений, все разнообразие геометрических объектов можно свести к ограниченному множеству простейших сущностей. Нам понадобятся для этого три базовых типа — скаляры, точки и векторы. Существуют, по крайней мере, три варианта определения этих сущностей, в зависимости от того, с какой точки зрения их рассматривать — чисто ма- математической (формальной), геометрической или с точки зрения программной реализации. В конечном счете нам придется иметь дело со всеми тремя вариантами, хотя на первый взгляд и кажется, что они мало связаны друг с другом. Несмотря на то что существует огромное множество примеров каждого из трех базовых типов, в этой главе мы рассмотрим только по одному представителю каждого типа. Как бы там ни было, но для нас наибольшую важность представляет вопрос об отличиях между ма- математическим определением каждого типа и его частной реализацией, что требует основа- основательного знакомства с математикой, лежащей в основе дальнейших рассуждений. 4.1.1. Геометрическое определение базовых типов Роль фундаментального геометрического объекта в компьютерной графике играет точка. В трехмерной геометрической системе точка— это положение в трехмерном пространстве. Единственным атрибутом точки является ее положение. Математическая точка даже не имеет размера. Точки существуют в пространстве независимо от какой-либо системы коор- координат. Конечно, это может показаться неудобным, поскольку на определенную точку прихо- приходится ссылаться как "точка вот там" или "синяя точка рядом с красной". Проблема ссылок решается с помощью систем координат или фреймов (раздел 4.3), но сейчас нас интересует, как далеко можно продвинуться, не обращаясь к конкретной системе отсчета. Скаляры — это всегда действительные числа. Хотя скаляры и не имеют геометрических свойств, они понадобятся нам в качестве единиц измерения. Например, длина отрезка есть скаляр. Скаляром является и угол вращения объекта. В компьютерной графике точки часто связываются направленными отрезками, как по- показано на рис. 4.1. Такие отрезки прямых и есть векторы. В более общем смысле физики используют термин вектор для обозначения любой величины, характеризуемой направле- направлением и значением. Физические величины, такие как скорость и сила, являются векторами. Однако вектор в этом смысле не имеет фиксированной точки приложения (позиции). Сле- Следовательно, направленные отрезки, показанные на рис. 4.2, являются одинаковыми векто- векторами, поскольку имеют одно и то же направление и одинаковую длину, хотя и разные точ- точки приложения. Мы часто будем использовать в этой книге термины вектор и направлен- направленный отрезок как синонимы. Длина и направление вектора характеризуются вещественными числами. Вектор А на рис. 4.3,а имеет то же направление, что и вектор В, но В имеет длину в два раза большую, чем А, в потому можно записать В = 2А. Вектор С имеет ту же длину, что и А, но противопо- противоположное направление, а потому соотношение между ними выразится формулой С = -А. Объединять векторы можно с помощью правила сочленения начала с концом (head-to-tail rule), схематически представленного на рис. 4.3,6. На этом рисунке конец вектора А сочленен 148 Глава 4. Объекты и геометрические преобразования
с началом вектора В и сформирован новый вектор С, величина и направление которого опре- определяются отрезком, соединяющим начало вектора А и конец вектора В. Этот новый вектор будем называть суммой векторов А и В и записывать соотношение между ними в виде С=А+В. Обратите внимание на то, что поскольку векторы не имеют фиксированной точки приложе- приложения, то для геометрического сложения векторов их можно переносить параллельно самим се- себе куда угодно. Рис. 4.1. Соединение двух точек направленным отрезком Рис. 4.2. Одинаковые векторы Рис. 4.3. Соотношение между векторами: а — выраженное с помо- помощью вещественных чисел; б — правило сочленения начала с концом Точки и векторы — это разные геометрические типы. у Геометрическое представление точки в виде направленного * •р = (х, у, отрезка, соединяющего некоторую опорную точку с задан- заданной (рис. 4.4), следует рассматривать как сомнительное. Од- Однако существует операция, в которой связываются точки и направленный отрезок (см. рис. 4.1.) Можно использовать направленный отрезок для перемещения от одной точки к другой. Точно так же две точки определяют отрезок, прове- проведенный между ними, а точка и вектор определяют вторую точку. Таким образом, правильная интерпретация рис. 4.4 состоит в том, что данный вектор можно определить как ис- исходящий из фиксированной опорной точки (начала системы координат) и приходящий в определенную точку простран- Рис- 4-4- Сомнительное ства. Обратите внимание на то, что вектор, как и точка, су- представление вектора шествует независимо от опорной системы, но, как будет по- показано дальше, мы должны будем работать с их представлениями в определенной систе- системе координат. 4.1. Скаляры, точки и векторы 149
4.1.2. Математическое определение: векторное и аффинное пространства Скаляры, точки и векторы можно рассматривать как элементы математических множеств. Поэтому мы будем использовать разного рода абстрактные пространства для представления и манипулирования этими множествами объектов. В математике принято использовать такие абстрактные пространства для решения разного рода прикладных проблем — от решения дифференциальных уравнений до аппроксимации математических функций. Формальное оп- определение тех пространств, которые представляют для нас интерес — векторного, аффинно- аффинного и Евклидова, — приведено в приложении Б. Нас интересуют только такие пространства, элементами которых являются геометрические типы. Возможно, наиболее важным математическим пространством является векторное (линейное) пространство. Векторное пространство содержит две различные сущности — векто- векторы и скаляры. В этом пространстве существуют правила объединения скаляров с помощью двух операций (сложения и перемножения), и, таким образом, определено скалярное поле. Приме- Примерами скаляров могут быть вещественные и комплексные числа, а также рациональные функции. В векторном пространстве можно комбинировать скаляры и векторы и создавать новые векторы с помощью операции умножения скаляра на вектор. Можно также комбинировать и векторы с помощью операции сложения векторов. Примерами математических векторных пространств являются «-группы действительных чисел, решение однородных линейных дифференциальных уравнений и геометрические операции над направленными линейными отрезками. Аффинное пространство — это расширение векторного пространства, в которое включен дополнительный тип объектов — точка. В аффинном пространстве определена операция сложения точки и вектора, результатом которой является новая точка. Обратной ей является операция вычитания двух точек, результатом которой будет вектор. В линейном векторном пространстве отсутствует способ измерения скалярных величин. Евклидово пространство — это расширение векторного пространства, в которое введена мера для измерения расстояния (размера). В этих абстрактных пространствах объекты могут быть определены независимо от кон- конкретного представления — они просто являются элементами некоторых множеств. Одна из основных концепций векторного пространства состоит в представлении любого вектора в терминах одного или нескольких множеств базисных векторов. Представление обеспечивает связь между абстрактными объектами и их реализацией, а взаимные преобразования одних представлений в другие приводят нас к геометрическим преобразованиям. 4.1.3. Информационное определение Хотя математики предпочитают иметь дело со скалярами, точками и векторами как чле- членами множеств, которые можно комбинировать в соответствии с определенными аксиомами, для специалистов по информатике привычнее и ближе рассматривать их как абстрактные типы данных — АТД {ADT — abstract data types). Абстрактный тип данных — есть множест- множество операций над некоторыми данными, причем операции определяются независимо от внут- внутреннего представления данных или от способа их реализации в конкретной системе. Понятие абстрагирование от данных {data abstraction) является фундаментальным в современной науке о компьютерной обработке информации. Например, операция включения нового эле- элемента в список или перемножения двух многочленов может быть определена независимо от того, как именно хранится список или как представлены действительные числа в данном компьютере. Те, для кого эта концепция стала привычной, безо всякого труда воспринимают разницу между понятием объекта (или операции над объектами) и представлением объектов (или их реализацией) в конкретной системе. С точки зрения информатики нам требуется 150 Глава 4. Объекты и геометрические преобразования
средство, позволяющее объявлять геометрические объекты в тексте программы независимо от их представления или реализации в данном компьютере. Выглядеть такое объявление должно примерно таким образом: vector u,v; point p,q; scalar a,b; Для определения абстрактных типов в объектно-ориентированных языках программиро- программирования, подобных C++, можно применить такие средства, как классы и перегрузку операторов. Это позволяет записать в программе операцию над данными некоторого геометрического ти- типа в следующем виде1: q = p+a*v; Но сначала нужно определить функции, которые будут выполнять операции над данными новых типов, а для этого понадобится рассмотреть те математические соотношения, которые требуется реализовать в этих функциях. 4.1.4. Геометрические абстрактные типы данных Рассмотрев понятия скаляра, точки и вектора с трех точек зрения, мы получили матема- математическую и информационную "оболочку", в которой будем в дальнейшем работать с геомет- геометрическими объектами. Скаляры— это действительные числа, по отношению к которым можно применять обычные операции сложения и умножения. Геометрические точки — это положения в пространстве, а векторы — направленные отрезки в пространстве. На эти объек- объекты распространяются правила, определенные в абстрактном аффинном пространстве. В про- программе можно определить абстрактные типы данных для представления базовых типов объ- объектов и специфицировать операции, подчиняющиеся этим правилам. Теперь рассмотрим, как можно использовать базовые типы для выполнения геометриче- геометрических операций и формирования новых объектов. Будем обозначать в дальнейшем скаляры бу- буквами греческого алфавита а, Р, у,..., точки— прописными буквами латинского алфавита P,Q,R,..., а векторы— строчными буквами латинского алфавита u,v,w, .... Позже, в разделе 4.3, мы введем в рассмотрение и другие абстрактные математические объекты, кото- которые будем обозначать полужирными буквами латинского алфавита. До сих пор мы никак не затрагивали вопрос о системе отсчета, например системе координат, а потому векторы и точ- точки являются для нас пока что абстрактными объектами, а не объектами, представленными в определенной системе отсчета. Модуль вектора v (или его длина, абсолютная величина — magnitude) — это действительное число, которое обозначим ]v|. Операция умножения скаляра на вектор обладает следующим свойством (см. приложение Б): |а v| = |ct||v|, причем при положительном а направление вектора av то же, что и направление вектора v. Обращаю ваше внимание на то, что в дальнейшем нам понадобится специальный вектор ну- нулевой длины (нуль-вектор — zero vector), который будем обозначать полужирной цифрой 0. Существуют две эквивалентные операции, связывающие две точки и вектор. Первая — это вычитание точек, которая формирует из двух точек Р и Q вектор v (рис. 4.5). Эту опера- операцию запишем следующим образом: v = P-Q. 'Поскольку в этой книге в основном используется язык С, а не С+ -*- и поскольку OpenGL не является объ- объектно-ориентированным пакетом, нам приходится пользоваться в примерах несколько устаревшими конст- конструкциями вроде typedefiau struct. 4.1. Скаляры, точки и векторы 151
Следствием такого определения операции вычитания точек является то, что для заданной точки Q и вектора v существует единственная точка Р, которая удовлетворяет приведенному выше соотношению. Это утверждение можно формально выразить следующим образом: пусть заданы точка Q и вектор v, тогда существует точка Р, такая, что Р = v+Q. Таким образом, Р формируется с помощью операции сложения вектора с точкой. Пра- Правило сочленения начала вектора с концом другого вектора позволяет очень наглядно пред- представить операцию сложения векторов (рис. 4.6,а). Из правила сложения векторов следует и отношение, справедливое для любых трех точек P,Q и R (рис. 4.6,6): (P-Q) + (Q-R) = P-R. P-R Рис. 4.5. Вычитание точек Q Рис. 4.6. Правило сочленения начала одного вектора с концом другого: а — сложение векторов; б — соотношения между точками P-Q 4.1.5. Прямые Логическим следствием операции сложения точки и вектора (или, если вам это нравится больше, операции вычитания точек) является понятие прямой в аффинном пространстве. Рас- Рассмотрим все точки, удовлетворяющие соотношению Р(а) = P0 + ad, где Ро— произвольная точка, d— произвольный вектор, а а— некоторый скаляр. В соот- соответствии с правилами объединения точек, векторов и скаляров, определенными в аффинном пространстве, Р(а) является точкой при любом заданном значении а. Все эти точки будут лежать на прямой, как показано на рис. 4.7. Такую форму представления прямой часто назы- называют параметрической, поскольку совокупность точек, представ- представляющих прямую, формируется в результате изменения значения параметра а. При а = 0 точка на прямой совпадает с Ро, и по мере увеличения а точки располагаются все дальше от исходной в на- направлении векторам/. Если ограничить значения параметра а только неотрицательными числами, то получим луч, исходящий из точки Ро в направлении вектора d. Рис. 4.7. Определение прямой в аффинном пространстве 152 Глава 4. Объекты и геометрические преобразования
4.1.6. Аффинное сложение В аффинном пространстве определены операции сложения двух векторов, умножения вектора на скаляр и сложения вектора и точки, а операции сложения произвольных точек и умножения точки на скаляр отсутствуют. Но в этом пространстве существует также операция аффинного сложения {affine addition), в которой имеются некоторые элементы двух послед- последних операций. Для любой точки Q, вектора v и положительного скаляра а результат операции Р = Q+av описывает все точки на прямой, проходящей через точку Q в направлении вектора v (рис. 4.8). Всегда можно найти такую точку R, что v = R-Q. Следовательно, Р = Q + a(R-Q) = aR Эта операция выглядит так же, как и сложение двух точек, и, следовательно, имеет эквива- эквивалентную форму: /> = a,fi + a2Q, где а, + а2 = 1. 4.1.7. Выпуклость Выпуклым (convex) является такой объект, для которого соблюдается следующее правило: все точки, лежащие на отрезке, соединяющем две любые точки, принадлежащие объекту, также принадлежат этому объекту. Мы уже видели, на примере многоугольников (см. гла- главу 2), насколько важна выпуклость объекта. Для более формального анализа выпуклости мы теперь можем воспользоваться операцией аффинного сложения. При 0<а<1 операция аф- аффинного сложения определяет отрезок, связывающий точки R и Q, как показано на рис. 4.9. Следовательно, отрезок прямой является выпуклым объектом. Расширим операцию аффин- аффинного сложения и на объекты, заданные п точками Ри Р2, ..., Р„. Рассмотрим выражение Рис. 4.8. Аффинное сложе- Рис 4.9. Отрезок прямой, со- ние единяющий две точки Можно доказать по индукции, что эта сумма определена тогда и только тогда, когда а,+ сх2 + ... +а„=1. 4.1. Скаляры, точки и векторы 153
Множество точек, образованных аффинным сложением п точек, на которое распространяется дополнительное ограничение а,>0, /= 1,2,..., я, называется выпуклой оболочкой {convex hull) исходного набора точек (рис. 4.10). Несложно доказать по индукции, что выпуклая оболочка включает все отрезки прямых, связывающих любую пару точек из набора {Ри Р2,..., Р,,}. В геометрической трактовке выпуклая оболочка— это множество точек, при- принадлежащих поверхности минимальной площади, "натяну- "натянутой" на заданное множество исходных точек. Понятие выпук- выпуклости играет особенно важную роль при конструировании кривых и поверхностей, и мы еще не раз вернемся к нему, в частности в главе 10. Рис. 4.10. Выпуклая оболочка 4.1.8. Скалярное и векторное произведение Многие геометрические процедуры, связанные с определением углов между векторами, реализуются с помощью элементарных операций скалярного (dot, inner) и векторного (cross) произведений двух векторов. Скалярное произведение векторов и и v обозначается и • v. Если и • v = 0, то и и v являются ортогональными. В Евклидовом пространстве квадрат модуля век- вектора есть скалярное произведение вектора на самого себя: \и\2= и ¦ и. Угол между двумя векторами определяется по формуле Кроме того, выражение описывает ортогональную проекцию вектора v на и, как показано на рис. 4.11. Если в трехмерном пространстве заданы три линейно-независимых вектора, то можно с помо- помощью операции скалярного произведения построить три вектора, каждый из которых будет ортого- ортогонален двум другим. Этот процесс рассмотрен в приложении Б. Можно также использовать два не- непараллельных вектора, и и v, для того чтобы определить третий вектор п, ортогональный по отно- отношению к первым двум (рис. 4.12). Этот вектор задается с помощью операции векторного произведения (crossproduct), формальное определение которой приведено в Приложении В: п = и х v. |и| cos в Рис 4.11. Скалярное произведение и ортогональная проекция и Xv Рис. 4.12. Векторное произве- произведение 154 Глава 4. Объекты и геометрические преобразования
Модуль векторного произведения позволяет определить синус угла 6 между векторами- сомножителями и и v. Обратите внимание на то, что векторы и, v и п образуют правостороннюю систему коорди- координат {right-handed coordinate system), т.е. если вектор и направлен в ту же сторону, что и большой палец правой руки, а вектор v будет направлен вдоль указательного пальца, то век- вектор п будет параллелен среднему пальцу. 4.1.9. Плоскости Плоскость {plane) в аффинном пространстве определяется прямым расширением пара- параметрического представления прямой. Из курса элементарной геометрии вам должно быть из- известно, что три точки, не лежащие на одной прямой, однозначно определяют плоскость. Предположим, что P,QwR — это именно такие три точки в аффинном пространстве. Отре- Отрезок, который соединяет PwQ, есть множество точек, описываемое соотношением Предположим, что на этом отрезке выбрана произвольная точка и из нее проведен отрезок до точки R, как показано на рис. 4.13. Точки, расположенные на этом отрезке, можно описать, воспользовавшись вторым параметром, Р: р У'Ца R Л \ S(a) Q Эти точки определяются уже парой параметров — а и Р, а их совокупность образует плоскость, заданную точками Р, Q и R. Объединив два предыдущих уравнения, получим уравне- уравнение плоскости в параметрическом виде: Да, Р) = Р[а/> + A-аH] + A-р)Л. Полученное уравнение можно переписать в другом виде: Да, 3) = Р + Kl-aXfr-fl + О-т-Р). Учитывая, что Q-P и R-P представляют собой векторы, мож- можно показать, что плоскость также определяется точкой Ро и парой непараллельных векторов и и v следующим образом: Да, р) = Ро + аи + Pv. Несложно прийти к заключению, что при 0 < а, Р < 1 все точки Да, Р) лежат внутри тре- треугольника, образованного вершинами P,QwR. Если точка Р принадлежит плоскости, то Р-Ро = аи + pv. Можно определить вектор п, который будет ортогонален и и, и v. Воспользовавшись вектор- векторным произведением п = и х v, получим уравнение плоскости в таком виде: п-(Р-Р0) = 0. Вектор п перпендикулярен (ортогонален) плоскости; его принято называть нормалью к плос- плоскости. Уравнения прямых, в левой части которых стоит член Р(а), и плоскостей, в левой час- части которых стоит член Да, Р), принято называть уравнениями в параметрической форме, поскольку они определяют точки на прямой или плоскости как функции параметров а и р. 4.1. Скаляры, точки и векторы 155
4.2. Трехмерные примитивы В трехмерном пространстве значительно возрастает разнообразие геометрических объектов, доступных компьютерной графике. При работе на двухмерной плоскости в примерах, рассмот- рассмотренных в главе 2, мы имели дело только с отрезками, плоскими кривыми и объектами, которые имели внутреннюю область, в частности многоугольниками. В трехмерном пространстве все эти типы объектов сохраняются, но, во-первых, они могут лежать в разных плоскостях, а во-вторых, к ним добавляются пространственные кривые (рис. 4.14). Помимо плоских объектов с внутрен- внутренней областью, существуют и аналогичные пространственные объекты — участки криволиней- криволинейных поверхностей (рис. 4.15). В трехмерном пространстве появляется совершенно новый класс объектов — объемные тела, такие как параллелепипеды и эллипсоиды (рис. 4.16). Рис. 4.14. Трехмерные ' Рис 4.15. Трехмерные Рис. 4.16. Объемные кривые поверхности тела При создании графической компьютерной системы, способной работать со всем разнооб- разнообразием трехмерных объектов, программист неизбежно сталкивается со множеством проблем, из которых наиболее существенны две. Во-первых, математическое описание пространствен- пространственных объектов оказывается значительно более сложным, чем их двухмерных аналогов (если таковые вообще существуют). Во-вторых, из всего разнообразия трехмерных объектов при- приходится отбирать такие, которые могут быть эффективно реализованы методами компьютер- компьютерной графики (имеется в виду как процесс отображения, так и манипулирование объектами). Те типы объектов, которые не попали в это множество, нужно аппроксимировать — прибли- приближенно представить объектами отобранных типов. Трехмерные объекты, работа с которыми может быть реализована существующими аппа- аппаратными и программными средствами компьютерной графики, должны обладать следующи- следующими свойствами. 1. Объекты описываются поверхностями и могут рассматриваться как полые. 2. Количественно объекты полностью характеризуются множеством трехмерных вершин. 3. Объекты либо состоят из плоских выпуклых многоугольников, либо могут быть ап- аппроксимированы плоскими выпуклыми многоугольниками. Понять, почему мы ограничили множество реализуемых в графических системах объектов таким образом, можно, если принять во внимание, что эффективнее всего в современных графических системах выполняется отображение треугольников, т.е. простейших плоских многогранников. Графические рабочие станции высокого класса способны выводить на экран до 5 миллионов маленьких тонированных треугольников в секунду. Специализированные платы управления монитором для персональных компьютеров обеспечивают вывод до 1 миллиона тонированных треугольников в секунду2. Из первого условия следует, что для 2Показателем производительности графической системы обычно является количество треугольников, объединенных в полосы. Кроме того, указывается, поддерживается ли на аппаратном уровне затенение или засветка этих треугольников без снижения скорости вывода их на экран. 156 Глава 4. Объекты и геометрические преобразования
моделирования трехмерных объектов вполне достаточно двухмерных примитивов (плоский многоугольник, даже расположенный в плоскости, произвольно ориентированной в трехмер- трехмерном пространстве, является все же двухмерным примитивом). Второе условие является, по сути, расширением выводов, сделанных в главах 1 и 2. Если объект количественно полностью характеризуется множеством вершин, он наилучшим образом подходит для реализации в сис- системе с конвейерной архитектурой. Последнее из сформулированных условий следует из об- обсуждения свойств двухмерных многоугольников. Большинство графических систем оптими- оптимизировано с учетом потребностей обработки и отображения точек и многоугольников. В трех- трехмерном пространстве многоугольник определяется упорядоченным множеством вершин, но если таких вершин больше трех, то многоугольник не обязательно является плоским, т.е. все его вершины не всегда лежат на одной плоскости, а следовательно, значительно сложнее оп- определить внутреннюю область этого объекта. Поэтому в подавляющем большинстве графи- графических систем на прикладную программу возлагается ответственность за то, что отображае- отображаемые многоугольники являются плоскими. В противном случае не гарантируется корректный результат растрового преобразования. Поскольку треугольник по самой своей природе явля- является плоским многоугольником, то все поверхности в графической системе желательно пред- представлять множеством таких треугольников, причем это может выполняться как на стадии мо- моделирования объектов отображаемой сцены, так и на стадии подготовки объектов к отобра- отображению. В последнем случае именно графическая система производит разбиение {tessellation) произвольных многоугольников на треугольные компоненты. Эти же аргументы сохраняются и в отношении криволинейных поверхностей, например сферических. Такие криволинейные поверхности нужно аппроксимировать плоскими многоугольниками, причем проще всего выполняется аппроксимация треугольниками. Следовательно, даже если система моделиро- моделирования и использует при построении модели криволинейные поверхности, подразумевается, что на одном из следующих этапов обработки выполняется их полигональная аппроксимация. Исключением из этого подхода является новое направление в компьютерной графике, ко- которое получило название конструктивная геометрия тел — KIT (CSG — constructive solid geometry). В таких системах объект строится из небольшого множества монолитных (объемных) примитивов с помощью операций теории множеств, таких как объединение и пересечение. Мы вернемся к моделированию на базе конструктивной геометрии тел в главе 8. Хотя для решения задач моделирования использование концепции КГТ очень эффективно, отображать созданные модели значительно труднее, чем модели, построенные на базе поли- полигонального разбиения поверхностей. Вполне возможно, что в будущем ситуация изменится, но сейчас мы будем уделять основное внимание методам отображения полигональных моде- моделей объектов. Все примитивы, с которыми приходится работать в таких моделях, описываются множе- множествами вершин. Если перейти от абстрактных объектов к реальным, нужно сначала опреде- определиться с методами представления в графической системе вершин — точек в пространстве. 4.3. Системы координат и фреймы В предыдущих разделах мы рассматривали точки и векторы как абстрактные объекты, не привязываясь ни к какой системе отсчета. В трехмерном векторном пространстве можно од- однозначно представить любой вектор ir в виде линейной комбинации любых трех линейно- независимых векторов v,, v2 и v3 (см. приложение Б): \v = a,v, + a:v2 + (X3V3. Скаляры 0(|, а2 и а3— это компоненты (или координаты) вектора и- в базисе vb v2, v3. Это отношение показано на рис. 4.17. Можно записать представление вектора ir в некотором базисе в виде матрицы, состоящей из одного столбца: 4.3. Системы координат и фреймы 157
а = а, а, а, Здесь и далее буквы, набранные полужирным шрифтом, обозначают представление в опреде- определенном базисе, в отличие от абстрактного вектора w, который обозначается обычным шриф- шрифтом. Это же соотношение можно записать и в виде Векторы V|, v2, v3, образующие базис, определяют конкретную систему координат. Но при рассмотрении проблем, в которых принимают участие точки, векторы и скаляры, нам потребуется более общий метод определения системы координат. Суть проблемы поясняет рис. 4.18. Систему координат, представлен- представленную на рис. 4.18,а, образуют три вектора, причем все они исходят из одной и той же точки. Именно так мы и при- привыкли рисовать систему координат на бумаге. Эти три вектора можно использовать в качестве базиса для пред- представления любого вектора в трехмерном пространстве. Однако выше уже отмечалось, что векторы как геометри- геометрические объекты не имеют точки приложения, а характе- характеризуются только направлением и модулем. Следователь- Следовательно, векторы на рис. 4.18,а,б эквивалентны. Большинство студентов весьма смущает базис, представленный на пра- правой картинке, хотя с точки зрения чистой математики обе картинки имеют один и тот же смысл. Но у нас по- прежнему остается нерешенной проблема представления точки — геометрического объекта, который имеет фикси- фиксированное положение в пространстве. Рис. 4.17. Разложение вектора по векторам базиса / 1 о) б) Рис. 4.18. Системы координат: а — векторы, исходящие из од- одной и той же позиции; 6 — свободные векторы Поскольку аффинное пространство включает и точки, то, как только мы зафиксируем оп- определенную точку отсчета — начало координат, в нем можно будет однозначно предста- 158 Глава 4. Объекты и геометрические преобразования
вить любую точку. Общепринято изображать оси системы координат как исходящие из точ- точки начала координат (см. рис. 4.18,а). Это имеет смысл в аффинном пространстве, в котором можно представлять как векторы, так и точки. Однако такое представление требует исполь- использования точки отсчета и векторов базиса, которые в совокупности называются фреймом {frame). Если не требовать особой строгости в формулировках, то можно считать, что базис- базисные векторы исходят из некоторой точки Ро. В конкретном фрейме любой вектор можно од- однозначно записать в виде w = a,v, +CUV,+a3v3, точно так же, как и в векторном пространстве. Кроме того, для любой точки можно написать соотношение TI2V2 + TI3V3. Таким образом, любой вектор описывается в фрейме тремя скалярами, а описание точки включает три скаляра и данные о точке отсчета. Как будет показано в разделе 4.3.3, отказ от знакомого со школьной скамьи понятия системы координат в пользу менее привычного поня- понятия фрейма позволит избежать трудностей при работе с векторами, которые имеют направле- направление и величину, но не имеют фиксированной точки приложения. Кроме того, концепция фрейма позволяет представлять точки и векторы таким образом, что для манипуляций с ними можно использовать единый математический аппарат матричной алгебры, сохраняя, тем не менее, различие между этими двумя типами геометрических объектов. 4.3.1. Замена систем координат Очень часто требуется выяснить, как изменится представление вектора при изменении ба- базиса. Предположим, что {vb v2, v3} и {ии и2, м3} — два базиса. Каждый вектор второго базиса можно представить в первом базисе (и наоборот). Следовательно, существует девять скаляр- скалярных компонентов {у/у}, таких, что Эти скаляры можно "уложить" в матрицу 3x3: ii 112 113 М = Y 21 Y22 Y23 _Y 31 Y32 Y33. а соотношение между компонентами представить в матричной форме: Матрица М содержит информацию, необходимую для преобразования представления вектора в одном базисе в представление в другом. Обращение матрицы М дает матрицу, которая по- позволяет преобразовать представление в базисе {иь и2, м3} в представление в базисе {v,, v2, v3}. Пусть вектор w имеет в базисе {vb v2, v3} представление {a,, a2, a3}, т.е. W = + (X2V2 + CC3V3. 4.3. Системы координат и фреймы 159
Это же соотношение можно записать и в матричной форме: где а = Предположим, что b является представлением w в базисе {иии2,щ}: или и. ul где "А" Л. Таким образом, используя представление во втором базисе в терминах первого, получим: V, «2 = ьгм Следовательно, Матрица (М7) ' позволяет перейти от а к Ь: b = Аа = (Мг)"'а. Важность этого результата заключается в том, что он позволяет перейти от рассмотрения абстрактных векторов к операциям с представлениями векторов— матрицами-столбцами, состоящими из скаляров. Но при работе с такими матрицами всегда нужно помнить о том, что за ними "скрывается" определенный базис, иначе мы рискуем оказаться совсем в другой системе координат. Изменение базиса не затрагивает положение точки начала координат. Такое изменение мож- можно использовать для представления поворота или масштаба набора векторов базиса (рис. 4.19). Однако плоскопараллельное смещение фрейма или перенос начала координат (рис. 4.20) таким способом представить нельзя. После того как мы рассмотрим простой пример, будет введено понятие однородных координат, пользуясь которыми можно с помощью операций над матри- матрицами выполнять комплексное преобразование фреймов. 160 Глава 4. Объекты и геометрические преобразования
, V V2 1 Рис. 4.19. Поворот и масштаби- масштабирование базиса Рис 4.20. Плоскопараллельное смещение базиса 4.3.2. Пример изменения представления Предположим, что имеется вектор w, представление которого в некотором базисе имеет вид а = Обозначим три вектора базиса соответственно vu v2, v3. Тогда It' = V! + 2v2 + 3v3. Теперь предположим, что возникла необходимость сформировать новый базис на основе прежнего: Ml = V,, 1/2 = V, + V2, Щ ~ vl + V2 + V3- Матрица М в этом случае имеет следующий вид: М = Матрица преобразования представления из базиса {v,, v2, v3} в базис {м,, м2, м3} имеет вид 1 1 1 0 1 1 0 0 1 3 Если мы работаем в метрическом трехмерном пространстве (/?'| а не в абстрактном, то молено ас- ассоциировать базис V/. v^, \з с единичным базисом в Я4: е. = 1 0 0 . е2 = 0 1 0 0 0 1 4.3. Системы координат и фреймы 161
1 1 1 Г1 -1 1 А=(МГ)"'= Oil =0 1 -1 [0 0 lj [О 0 1 В новой системе координат представление w будет иметь вид "-Г = Аа = -1 Отсюда следует, что w =-М| - и2 + Зм3. 4.3.3. Однородные координаты Когда речь заходит о представлении точки Р, имеющей положение (х, у, z), с помощью трехмерного фрейма с началом в точке Ро и базисом {vh v2, v3}, то первой приходит в голову мысль использовать матрицу-столбец Р = где x,y,z — компоненты базисных векторов в этой точке, т.е. коэффициенты в соотношении Р = Ро + XV\ + yv2 + ZVj. Но в этом случае точка будет представлена так же, как и вектор w = 5|V| + 52v2 + 53v3, поскольку w представляется столбцом X S, А. В литературе очень часто точка (x,y,z) ассоциируется с вектором, проведенным в эту точку из начала координат. Эта ассоциация может привести к недоразумениям. Например, вектор из точки A, 1, 1) в точку B, 3,4) равен вектору, проведенному из точки @, 0, 0) в точ- точку A, 2, 3), поскольку оба вектора имеют одинаковый модуль и направление. Но первый век- вектор нельзя ассоциировать с точкой A, 2, 3) до тех пор, пока его точка приложения не будет совмещена с началом координат — точкой @, 0,0). Пренебрежение различием в представлении точек и векторов может серьезно усложнить реализацию системы, поскольку после этого не удастся представить смещение фрейма как все другие виды трансформаций — с помощью операции перемножения матриц. Поэтому пред- предпочтительнее выбрать такой способ представления, который позволит легко отличать точки от векторов. Для этого мы воспользуемся так называемыми однородными координатами (homogeneous coordinates). В системе однородных координат для представления точек и векторов трехмерного пространства используются четырехмерные матрицы-столбцы. Во фрейме, заданном набором параметров (уг, v2, v3, Л,), любую точку Р можно однозначно предста- представить соотношением Р = PQ+ <x,v, + cc2v2 + <x3v3. 162 Глава 4. Объекты и геометрические преобразования
Если на множестве точек операцию умножения на скаляры 0 и 1 определить следующим образом: 0/> = 0, 1 Р = Р, то приведенное выше соотношение можно записать в матричной форме с помощью пере- перемножения матриц: а, а, Строго говоря, это выражение не является скалярным произведением, поскольку элемен- элементы матрицы разнородны, но в компьютере такое выражение реализуется той же подпрограм- подпрограммой, что и скалярное произведение. Четырехмерная матрица-строка в правой части уравне- уравнения — это представление в однородных координатах точки Р во фрейме, определенном па- параметрами (vb v2, v3, Ро). Точку Р можно представить и матрицей-столбцом Р = В том же самом фрейме любой вектор w можно представить в виде V, = 51vl+61v1+51v1=[81 52 5, О]7 Следовательно, вектор w можно представить матрицей-столбцом О Существует множество способов геометрической интерпретации этих формул. Для нас же самое важное состоит в том, что, пользуясь формальным аппаратом однородных координат, можно выполнять операции над точками и векторами с помощью обычных операций матрич- матричной алгебры. Рассмотрим, например, изменение параметров фрейма — проблему, которую не удается просто решить при использовании обычного трехмерного представления. Если ( V|, v2, vifPQ) и ( u\, и2, «з, Q) — два фрейма, то векторы базиса второго фрейма и его начало координат можно выразить в терминах первого фрейма следующим образом: Щ = 4.3. Системы координат и фреймы 163
Эти уравнения можно записать и в матричной форме: и,' м, 2 Щ а = м V, 2 V3 где на сей раз М обозначает матрицу размером 4x4: *Yn Y.2 Y.3 О" М = Y2| Y3I Y4I Y22 Y32 Y42 Y23 Y33 Y43 0 0 1 Матрица М называется матрицей представления изменения фреймов. Матрицу М можно использовать и для непосредственного вычисления изменения пред- представления. Пусть а и b — представления в однородных координатах двух точек (или двух векторов) в разных фреймах. Тогда и. ": "з .а_ = Ь7М V, V2 v3 А. = а7 V, V2 v3 /о. Следовательно, а = М7Ь. Использование аппарата однородных координат имеет и множество других достоинств, кото- которые мы будем повсеместно использовать по ходу изложения всего материала этой книги. Возможно, наиболее важным из них является то, что все аффинные преобразования (т.е. со- сохраняющие линейные свойства геометрических объектов) при использовании представления в однородных координатах выполняются единообразно — с помощью перемножения матриц. Хотя для решения задач трехмерного пространства при этом используется четыре измерения, количество арифметических операций в результате даже снижается. Единообразное пред- представление всех аффинных преобразований позволяет, во-первых, довольно просто организо- организовать последовательное выполнение сложных преобразований (конкатенацию преобразова- преобразований), а во-вторых, реализовать большинство этих операций аппаратно, что значительно по- повышает скорость обработки. 4.3.4. Пример перехода из одного фрейма в другой Вернемся к примеру из раздела 4.3.2. Опять начинаем с базиса {vb v2, v3} и преобразуем его в базис {ии и2, щ), три уравнения для векторов которого имеют вид Щ = V|, и2 ~ vl + V2» Щ = V, + V2 + V3. Точка отсчета фрейма не меняет своего положения, а потому к этим трем уравнениям доба- добавим четвертое: 164 Глава 4. Объекты и геометрические преобразования
Нас интересуют матрица преобразования М, обратная ей матрица М ' и транспонированная матрица Мт. Матрица М имеет вид М = О О Предположим, что, помимо изменения базиса, требуется изменить и точку отсчета в новом фрейме. Ее положение в прежнем фрейме имеет представление A, 2, 3, 1). Вектор смещения v=V|+2v2+3v3 сместит Ро в Qo. Ненулевой четвертый компонент означает, что объект является точкой. Таким образом, к трем уравнениям предыдущего примера добавляется четвертое: Qo = Л>+ v,+ 2v2+ 3v3, а матрица М7 примет вид 1111 1 о о о о о Обратная матрица имеет вид 1-10 1 -1 2 1 -3 0 1 Эти матрицы позволяют преобразовывать представления в разных фреймах в обе стороны. Обратите внимание на то, что матрица А преобразует точку, имеющую в исходном фрейме представление A, 2, 3) или в однородных координатах представление Г р = в начало координат нового фрейма: " 0 0 1 Но вектор A,2,3), который имел в однородных координатах исходного фрейма представление я = 4.3. Системы координат и фреймы 165
преобразуется в новом фрейме в '-Г ь="з' О Этот результат полностью согласуется с тем, который мы получили в примере с изменением систем координат. Он также демонстрирует, как важно отличать точки от векторов при вы- выполнении операций. 4.3.5. Фреймы и абстрактные типы данных До сих пор мы рассматривали сугубо математические вопросы представления геометри- геометрических объектов. Теперь наступила пора "спуститься на фешную землю" и задуматься над тем, как вся эта математика может быть реализована в компьютерной программе. Начнем с понятия об абстрактных типах данных. В предыдущих разделах мы рассмотрели несколько типов геометрических объектов, в част- частности точки, прямые и плоскости, исполняющих в фафических системах роль примитивов — "кирпичиков", из которых строятся другие объекты. Поэтому желательно, чтобы фафическая система позволяла включить в профамму определение переменных в таком, например, виде: point3 p,q; vector3 v,u; а затем разрабатывать программу в терминах этих типов данных. В языках программирова- программирования, подобных C++, которые поддерживают переопределение (перегрузку) операторов, мож- можно использовать такие выражения: q=p+v; u=p-q; Но включение в профамму выражений q=v; u=p+q; приведет к появлению сообщения об ошибке. Приведенные выше типы можно реализовать в профамме разными способами. В C++ и других объектно-ориентированных языках профамми- рования можно использовать такие средства, как конструкторы, деструкторы и перегрузку опе- операторов, что позволяет скрыть детали реализации от прикладного профаммиста. Те же, кто ра- работает на языке С, могут определить в профамме тип данных point3 следующим образом: typedef float point3[4]; Таким образом, с помощью четырехмерного массива можно ввести в профамму представле- представление в однородных координатах. Другой тип typedef float point2[4]; позволяет рассматривать двухмерные фафические объекты как частный случай трехмерных. Стандартные операции над геометрическими примитивами можно реализовать в виде набора функций. Такой подход используется в большинстве фафических систем (в частности, GKS, PHIGS и OpenGL), однако он не позволяет избежать и определенных трудностей. Рассмот- Рассмотрим, например, проблему инициализации точки. В стандартной профамме на языке С напра- напрашивается воспользоваться для этого таким фрагментом кода: point3 p*{1.0,2.0,3.0}; 166 Глава 4. Объекты и геометрические преобразования
Но это повлечет за собой два нежелательных следствия. Во-первых, это приведет к смешива- смешиванию конкретной реализации и абстрактного типа данных. В результате программный код не будет переносим с одной графической системы на другую, т.е. будет зависеть от конкретной реализации. Во-вторых, при такой инициализации "за кадром" остается вопрос, представле- представление в какой системе координат или в каком фрейме формируется этим кодом. Первую проблему можно решить, разработав спецификацию набора функций, реализую- реализующих стандартные абстрактные операции, что избавит прикладного программиста от необхо- необходимости разбираться в деталях конкретной реализации. Например, инициализация точки мо- может выполняться вызовом функции р = new_point3A.0, 2.0, 3.0); и все операции также выполняются через вызовы специальных функций, как в приведенном ниже фрагменте: point3 p,q; vector3 v; v = point_sub(p,q); Вторая проблема на практике решается значительно сложнее, чем в теории. Как было показано раньше, представление некоторой точки имеет смысл только в том случае, если оно привязано к спецификации фрейма. Следовательно, один из возможных подходов — создать специальный тип данных frame. Графическая система должна позволить программисту либо использовать при определении точки фрейм, специфицированный по умолчанию, либо создать свой и указать, что новая точка "привязывается" к этому фрейму. Функции выполнения стандартных операций могут включать в качестве аргумента явную ссылку на определенный фрейм: point3 p,q; vector3 v; frame f; v = point_sub(p,q,f); или использовать текущий фрейм, который становится в этом случае таким же параметром текущего состояния системы, как и прочие атрибуты (тип линии, цвет и т.п.). 4.3.6. Фреймы в OpenGL При работе с OpenGL мы пользуемся двумя фреймами— фреймом камеры и мировым фреймом. Фрейм камеры можно рассматривать как фиксированный. Матрица вида задает по- положение мирового фрейма относительно фрейма камеры. Таким образом, матрица вида {model- view matrix) преобразует представление точек и векторов в однородных координатах в мировом фрейме в представление в фрейме камеры4. Поскольку матрица вида является одним из компо- компонентов текущего состояния графической системы, то, следовательно, в любой момент в системе специфицированы фреймы камеры и мировой. OpenGL поддерживает стек матриц, в котором можно сохранять текущую матрицу вида, или, что то же самое, два фрейма. Как мы уже говорили в главе 2, камера всегда размещается в точке начала координат своего фрейма. Векторы базиса этого фрейма направлены следующим образом: один, у, вверх по от- отношению к камере, второй, z, в направлении, обратном направлению визирования камеры, а третий, х, таким образом, чтобы вместе с двумя первыми образовать правостороннюю ортого- ортогональную систему координат. Другие фреймы, которые понадобятся нам в процессе размещения объектов сцены, формируются с помощью однородных преобразований относительно фрейма камеры. В разделе 4.5 будет описано, каким образом можно задавать такие преобразования. В главе 5 мы рассмотрим использование этих преобразований при размещении камеры относи- 4Можно считать и наоборот — мировой фрейм фиксирован, а фрейм камеры подвижен. Какой точки зрения вы будете придерживаться — дело вкуса. 4.3. Системы координат и фреймы 167
тельно объектов сцены. Поскольку изменение фрейма представляется матрицей вида и система позволяет сохранять текущее значение матрицы, у прикладного программиста имеется возмож- возможность переключаться между фреймами, изменяя текущее значение матрицы вида. Прежде чем перейти к подробному анализу методов выполнения преобразований и реализа- реализации этих методов в OpenGL, рассмотрим еще один простой пример. По умолчанию фрейм ка- камеры совмещен с мировым фреймом, причем ось визирования камеры направлена вдоль оси :, но в обратную сторону (рис. 4.21,а). В большинстве приложений объекты сцены, как правило, размещаются вблизи начала координат. Например, квадрат строится относительно своего цен- центра, а фуппа объектов — вокруг центра масс. Естественно таким же образом настраивать пара- параметры визуализации, чтобы в поле зрения камеры попадали только объекты, расположенные пе- перед ней. Следовательно, для того чтобы сформировать изображение, в котором будут присутст- присутствовать все сформированные объекты, нужно либо отодвинуть камеру от объектов, либо отод- отодвинуть объекты от камеры. Это все равно, что сместить фрейм камеры относительно мирового фрейма. Если же рассматривать фрейм камеры как фиксированный, а матрицу вида — как пред- представляющую положение мирового фрейма относительно фрейма камеры, то матрица вида А = 10 0 0 0 10 0 0 0 1 -d 0 0 0 1 преобразует точку, имеющую в мировом фрейме координаты (дг, у, z), в точку, которая во фрейме камеры имеет представление (дг, у, z-d). Если задать d достаточно большое положи- положительное значение, то можно "передвинуть" объекты, чтобы они оказались перед камерой (рис. 4.21,6). Учтите, что пользователь— а именно он имеет дело с мировой системой коор- координат — размещает объекты, как и раньше, недалеко от начала координат. Матрица вида оп- определяет только взаимное положение фреймов. Использовать такой подход гораздо удобнее и логичнее, чем изменять параметры вершин объектов, чтобы разместить их перед камерой. б) Рис. 4.21. Фреймы камеры и мировой: а — исходное положение по умолчанию; б—размещение по- после задания матрицы вида 168 Глава 4. Объекты и геометрические преобразования
В OpenGL матрицу вида можно задать поэлементно, передав 16 чисел в качестве аргумен- аргументов функции glLoadMatrix(). Однако не ясно, как определить нужные значения элементов. При решении геометрических задач мы всегда мыслим категориями последовательных пре- преобразований (элементарных переходов), таких как плоскопараллельные смещения, повороты и изменения масштаба. Этому подходу мы и последуем в дальнейшем. 4.4. Модель разноцветного куба Выше вы познакомились с инструментарием — математическим аппаратом и средствами его программной реализации, — необходимым для создания трехмерных графических при- приложений. Как и в рассмотренных примерах двухмерных приложений, мы будем следовать подходу, ориентированному на конвейерную архитектуру системы. Объекты снова опреде- определяются набором вершин, которые подвергаются последовательным преобразованиям до того, как соответствующие примитивы будут преобразованы в растр в буфере кадра. В основе всех преобразований лежит математический аппарат однородных координат, который позволяет эффективно реализовать весь процесс обработки графической модели. В этом разделе будет рассмотрена задача формирования на экране изображения вращаю- вращающегося куба. (Один кадр такого "мультфильма" представлен на рис. 4.22.) Всю задачу можно разбить на ряд подзадач: ¦ моделирование; ¦ преобразование во фрейм камеры; ¦ отсечение; ¦ проецирование; ¦ удаление невидимых поверхностей; ¦ преобразование в растр. Ниже мы по очереди рассмотрим каждую из подзадач. Начнем с того, каким образом можно представить трехмерный объект, опираясь на множество его вершин, аналогично то- тому, как это делалось с двухмерными объектами. Для этого нам потребуется разработать структуру данных, учитывающую связи между вершинами, реб- ребрами и гранями геометрического трехмерного объекта. Такая структура дан- данных в OpenGL поддерживается массивами вершин (vertex arrays), о которых речь пойдет в конце данного раздела5. Рис. 4.22. Один кадр анимации вращающегося куба Разобравшись с методами моделирования куба, мы перейдем к его анимации, используя для этого математический аппарат аффинных преобразований в однородных координатах (подробно они будут рассмотрены в разделе 4.5). С помощью этих преобразований будет из- изменяться матрица вида OpenGL. В главе 5 мы опять будем использовать эти преобразования в качестве одного из этапов конвейерного процесса визуализации. Описания вершин будут подвергаться последовательным преобразованиям по мере "перемещения по конвейеру", причем на каждой стадии будет применяться все то же представление вершин в однородных координатах. На выходе конвейера описания объектов, опирающиеся на множество транс- трансформированных вершин, подвергаются растровому преобразованию. На этой стадии можно смело полагать, что все будет выполнено корректно, если предыдущие преобразования были сделаны правильно. Средства поддержки операций с массивами вершин включены в версию OpenGL 1.1. 4.4. Модель разноцветного куба 169
4.4.1. Моделирование куба Пожалуй, трудно подобрать для первых экспериментов более простой трехмерный объ- объект, чем куб. Но даже такой простой объект можно моделировать в компьютере по- разному. В системе CSG он рассматривается как единый примитив. Но, с другой стороны, куб обрабатывается аппаратными средствами как объект, состоящий из восьми вершин. Поскольку мы повсеместно будем использовать модели, основанные на ограничивающих поверхностях, то куб следует рассматривать либо как совокупность шести пересекающих- пересекающихся плоских поверхностей, либо как шесть многоугольников, которые определяют его грани (facets). Начнем с определения массива вершин, который должен быть доступен всем ком- компонентам программы: GLfloat vertices[8][3] = {{-1.0,-1.0,-1.0}, {1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; Если следовать объектно-ориентированному подходу, то нужно ввести новый тип данных: typedef GLfloat point3[3]; В таком случае вершины куба будут определены в программе следующим образом: point3 vertices[8] ={{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; Использование typedef — это вопрос стиля программирования. OpenGL в любом случае реа- реализует каждую вершину в виде четырехмерного массива однородных координат. При вызове функций, для которых задан трехмерный тип данных (таких как glVertex3fv()), значения преобразуются в четырехмерную форму внутри графической системы. Для определения граней куба можно использовать список точек— элементов массива вершин. Например, одна грань в тексте программы определяется следующим образом: glBegin{GL_POLYGON}; glVertexIfv(vertices[0]); glVertex3fv(vertices[3]); glVertex3fv(vertices!2]); glVertex3fv(vertices[1]); glEnd(); Другие пять граней определяются аналогично. Обратите внимание на то, что трехмерный многоугольник в программе определяется точно так же, как и двухмерные в рассмотренных выше примерах. 4.4.2. Внешние и внутренние грани При определении трехмерных многогранников порядок перечисления вершин имеет очень существенное значение. Во фрагменте программы, приведенном выше, мы перечисли- перечислили вершины первой грани в таком порядке: 0, 3, 2, 1. Перечисление в порядке 1, 0, 3, 2 приве- приведет к тому же результату, поскольку последняя вершина в списке автоматически соединяется с первой. Однако порядок 0, 1, 2, 3 даст совсем иной результат. Хотя таким образом форми- формируется тот же самый контур многоугольника, ребра будут "обходиться" в обратном направле- направлении, как показано на рис. 4.23. Следует учитывать, что многоугольник (точнее, его внутрен- внутренняя область) имеет две стороны — внешнюю и внутреннюю. Можно отображать любую из них или обе, но в любом случае нужно располагать каким-то методом идентификации сторон. 170 Глава 4. Объекты и геометрические преобразования
Будем называть грань смотрящей наружу, или внешней {outward facing), если при взгляде с внешней стороны объекта на эту грань ее вершины, расположенные в том порядке, который задан при определе- определении грани, "обходятся" против часовой стрелки. Этот метод определе- определения порядка вершин некоторого контура известен как правило правой руки {right-hand rule), поскольку, если расположить четыре согнутых пальца правой руки вдоль направления обхода контура, большой палец будет указывать наружную сторону грани. В нашем примере очень важно перечислить при определении первой грани вершины именно в порядке 0, 3, 2, 1, а не 0, 1,2,3, поскольку в Рис 4.23. Обход этом случае внешняя сторона многоугольника будет и внешней по от- вершин много- к б угольника ношению к кубу . J 4.4.3. Структура донных для представления объектов Теперь рассмотрим, каким образом описать с помощью множества вершин объемный объект — куб. Можно, например, вызывать функцию формирования многогранника glBegin(GL_POLYGON) шесть раз, передавая ей каждый раз свой набор из четырех вершин (посредством glVertex()) и завершая список вызовом glEnd(). Можно использовать другой способ — вызвать glBegin(GL QUADS), а затем передать список из 24 вершин, который завершается glEnd(). Оба эти способа будут восприняты графической системой, но при этом, хотя гео- геометрия куба и будет воспроизведена, информация о его топологии как составного объекта будет утеряна. Если рассматривать куб как многогранник, то он представляет собой сово- совокупность шести граней, которые являются многоугольниками с совпадающими вершина- вершинами, — каждая вершина принадлежит трем граням. Каждая пара соседних вершин опреде- определяет ребро, которое принадлежит двум граням. Приведенные утверждения и описывают топологию шестигранника. Они правомерны независимо от положения конкретных вер- вершин, т.е. от геометрии объекта7. В дальнейшем мы увидим, насколько существенно формировать структуру данных описания объекта таким образом, чтобы геометрия объекта в ней была отделена от его топологии. В данном примере для описания геометрии объекта используется список, или массив вер- вершин— vertices!8]. Объектом верхнего уровня иерархии является куб. Мы рассматриваем его как совокупность шести граней, а каждая грань, в свою очередь, описана четырьмя упо- упорядоченными вершинами. Доступ к определенной вершине из списка вершин осуществляется по ее индексу в этом списке. Такая структура данных схематически представлена на рис. 4.24. Одно из ее достоинств состоит в том, что каждая геометрическая позиция определяется толь- только в одном месте и не повторяется в спецификации каждой из трех граней, пересекающихся в соответствующей точке. Таким образом, если по ходу выполнения программы потребуется изменить значения координат точки, это придется сделать только в одном месте, не отслежи- отслеживая все грани, пересекающиеся в ней. 6Термины вперед и назад в этой книге рассматриваются по отношению к положительному направлению базисного вектора г. 7Мы не будем принимать во внимание особые случаи (их принято называть вырожденными случаями — singularities/ которые проявляются, если три или более вершин лежат на одной прямой или если вершины расположены таким образом, что грани не пересекаются. 4.4. Модель разноцветного куба 171
Многогранник Грани Списки вершин Вершины , А В с D Е F: ^- 0 2 1 3 7:. 6 2 J —> „^ х4,У4; Рис 4.24. Структура данных описания куба 4.4.4. Цвет куба Список вершин можно использовать и для того, чтобы хранить информацию, необходи- необходимую для раскрашивания куба. С вершинами в данном примере будут ассоциированы чистые цвета вершин цветового куба, о котором шла речь в главе 2 (черный, белый, красный, зеле- зеленый, синий, голубой, фиолетовый, желтый). Функция quad() вычерчивает четырехугольник, заданный точками в его списке вершин, а функция colorcube () задает шесть граней таким образом, чтобы все они были внешними, typedef GLfloat point3[3); point3 vertices[8] «{{-1.0,-1.0,1.0),{1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}, {-1.0,-1.0,-1.0}, {1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}}; GLfloat colors[8][3) = {{0.0,0.0,0.0},{1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}}; void quad(int a, int b, int с , int d) { glBegin(GL_QUADS); glColor3fv(colors[a]); glVertex3fv(vertices[a]); glColor3fv(colors[b]); glVertex3fv(vertices[b]); glColor3fv(colors[c]); glVertex3fv(vertices[c]); glColor3fv(colors[d]); glVertex3fv(vertices[d]); glEnd(); void colorcube( 172 Глава 4. Объекты и геометрические преобразования
quad(O,3,2,1); quadB,3,7,6); quad@,4,7,3); quad(l,2,6,5); quadD,5,6,7); quad@,l,5,4); 4.4.5. Билинейная интерполяция Недостаточно только указать, каким цветом выводить на экран вершины объекта. Графи- Графическая система должна каким-то образом по этим данным раскрасить области, занимаемые на поле изображения гранями объекта, — внутренние области многоугольников. Существует довольно много способов, как "распределить" некоторый атрибут (в данном случае цвет), значение которого фиксировано в вершинах, по внутренней области, ограниченной контуром, построенным на этих вершинах, — интерполировать {interpolate) атрибут. Возможно, наи- наиболее распространенным является метод билинейной интерполяции {bilinear interpolation). Рассмотрим, как этот метод прилагается к задаче интерполяции цвета. Пусть задан четы- четырехугольник, с каждой из вершин которого связан определенный цвет, — Со, Сь С2, С3 (рис. 4.25). Для определения распределения цвета вдоль любого ребра (линии, связывающей соседние вершины) можно воспользоваться линейной интерполяцией : Согласно этим уравнениям, при изменении а от 0 до 1 формируются значения цвета С0)(о:) и С2з(сх) точек, расположенных на ребрах, соединяющих вершины 0 и 1 и 2 и 3 соответственно. При данном значении а получаем два значения цвета, С4 и С5, для соответствующих точек на двух этих ребрах. По этим значениям можно выполнить еще одну процедуру интерполя- интерполяции — на сей раз вдоль линии, соединяющей соответствующие точки на двух противолежа- противолежащих ребрах: Если четырехугольник плоский, то каждое значение цвета, сформированное по этим уравне- уравнениям, будет соответствовать определенной точке внутренней области многоугольника. Но ес- если четырехугольник не плоский, т.е. четыре его вершины не принадлежат одной плоскости, то, хотя значения цвета и сфор- сформируются, положение соответствующих точек на криволиней- криволинейной поверхности определить так просто не удастся. Существует другой алгоритм, интерполяции вдоль строк растра {scan-line interpolation), который позволяет обойти сложности, связанные с формой поверхности, "натянутой" на контур четырехугольника. Этот алгоритм можно включить в процесс растрового преобразования. Многоугольник заполня- Рис 425 Билинейная ин- ется цветом только при выводе на экран. Но еще до выполне- терполяция ния растрового преобразования многоугольник проецируется на двухмерную картинную плоскость (рис. 4.26). Если затем многоугольник закрашивается по строкам растра, строка за строкой, как показано на рис. 4.27, то для распределения цвета нПредполагается, что для работы с цветом используется RGB-система, а интерполяция выполняется раздельно по каждому из первичных цветов. 4.4. Модель разноцветного куба 173
вдоль строки растра используются только два противолежащих ребра изображения. OpenGL поддерживает этот метод не только для цветов, но и для других атрибутов, которые можно ассоциировать с вершинами. Подробнее об этом — в главе 6. Центр проецирования Рис. 4.26. Проецирование много- многоугольника Рис. 4.27. Интерполяция вдоль строк растра Итак, на этой стадии у нас есть объект, который можно отобразить на экране тем же спо- способом, который был использован для вывода трехмерного узора Серпинского в главе 2. Как вы, надеюсь, помните, тогда использовалась ортографическая проекция, которая формирует- формируется функцией glOrtho(). В разделе4.5 будут рассмотрены геометрические преобразования, после знакомства с которыми можно будет "оживить" куб на экране, а также создавать более сложные объекты. Но сначала я познакомлю вас с новым средством поддержки операций с вершинами, которое появилось в версии OpenGL 1.1. Оно позволяет не только сократить объем программного кода, но и предоставляет в распоряжение прикладного программиста методы высокого уровня для работы с многогранниками. 4.4.6. Массивы вершин При вычерчивании куба, в модели которого используются списки вершин, приходится до- довольно много раз вызывать различные функции OpenGL. Если с каждой вершиной ассоции- ассоциируется определенный цвет, то нужно выполнить 60 вызовов функций OpenGL: 6 граней, фор- формирование каждой из которых требует вызовов glBegin() и glEnd(), 4 вызовов glColor() и 4 вызовов glVertex (). Каждый вызов функции влечет за собой накладные расходы и пере- пересылку данных. Как будет показано в главе 6, при вычерчивании объекта также необходимо задавать векторы нормалей для каждой вершины, и в результате количество вызовов при формировании изображения куба даже превышает указанную цифру. Следовательно, хотя описанная структура данных и позволяет инкапсулировать информацию о геометрии и топо- топологии куба как трехмерного объекта, соответствующий программный код будет выполняться не так быстро, как нам хотелось бы. Массивы вершин {vertex arrays) в OpenGL обеспечивают значительное повышение скорости вычерчивания трехмерных объектов, состоящих из многоугольников, сохраняя при этом воз- возможность инкапсуляции их внутренней топологической структуры. Использование такой струк- структуры данных предусматривает три этапа. Первый — разрешение работы с механизмом обслу- обслуживания массивов векторов. Второй — передача исполнительной системе OpenGL информации о том, где и в каком формате содержатся в программе массивы вершин. Третий — формирова- формирование объектов с использованием этих массивов. Две первые операции выполняются в процессе инициализации программы, а третья, как правило, реализуется с помощью функций с обратным вызовом. Ниже будет показано, как это делается на примере формирования куба. 174 Глава 4. Объекты и геометрические преобразования
Пакет OpenGL позволяет работать с шестью специальными видами массивов — вершин, цветов, индексов цветов, координат текстур и флагов ребер, — соответствующих шести опе- операторам между glBegin() и glEnd(). Но, как правило, в отдельном приложении все эти типы не используются. В программе формирования цветового куба нам потребуются только масси- массивы цветов и вершин. Механизм поддержки работы с этими типами массивов инициализирует- инициализируется следующими вызовами функций OpenGL: glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); Сами по себе массивы те же, что и раньше, причем объявляются они в статусе глобальных: GLfloat vertices[] = {{-1.0,-1.0,-1.0}, {1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat colors[] = {{0.0,0.0,0.0}, {1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}}; Следующая операция — передать OpenGL информацию о том, где находятся массивы. Это выполняется следующим образом: glVertexPointerC, GL_FLOAT, 0, vertices); glColorPointerC, GL_FLOAT, 0, colors); Первые три аргумента несут информацию о том, что элементы массивов являются соответ- соответственно трехмерными цветами и вершинами, которые сохраняются в формате float, и что элементы образуют непрерывный массив. Четвертый аргумент — указатель на определен- определенный массив. Теперь нужно включить в структуру данных информацию об отношениях между верши- вершинами и гранями в кубе. Для этого организуется массив из 24 упорядоченных индексов вершин для шести граней: GLubyte cubeIndices[24]={0,3,2,l, 2,3,7,6, 0,4,7,3, 1,2,6,5, 4,5,6,7, 0,1,5,4}; Первая грань имеет вершины с индексами @, 3, 2,1), вторая — B, 3, 7,6) и т.д. Обратите внимание на то, что при упорядочении индексов вершин нужно учитывать порядок их обхода в многоугольнике при взгляде с внешней стороны куба. Массив индексов вершин также дол- должен иметь статус глобального. Теперь можно приступить к формированию трехмерного объекта — в данном случае ку- куба — на основе информации из этих массивов. При формировании изображения обрабатыва- обрабатываются все элементы объявленных массивов (в данном случае — цветов и вершин). Имеется не- несколько опций настройки, касающихся режима обработки массивов при отображении. Для настройки используется функция glDrawElements(type, n, format, pointer) Аргументы функции имеют следующий смысл: 4.4. Модель разноцветного куба 175
¦ type — тип элемента, например линия или многоугольник, который определяется массивом; ¦ п — количество элементов, которые планируется отобразить; ¦ format — описывает формат данных в индексном массиве; ¦ pointer — указатель на первый используемый индекс. При формировании изображения куба в функции отображения display () нужно шесть раз вызвать функцию glDrawelements(), по одному разу на каждую грань: for(i=0;i<6;i++) glDrawElements(GL_POLYGON; 4, GL_UNSIGNED_BYTE, &cubelndex[4*i]); Таким образом, после формирования и инициализации массивов в процессе отображения придется выполнить только шесть вызовов функций OpenGL вместо пятидесяти с лишним. Поворачивать изображение куба можно тем же способом, что и раньше, поскольку функция glDrawElementsO использует при формировании изображения параметры текущего состоя- состояния, в том числе и матрицу вида. Можно еще больше упростить процесс отображения, если учесть, что каждая грань куба является именно четырехугольником, а не многоугольником общего вида. Тогда можно ис- использовать в функции glDrawelements ()тип GL_QUADS вместо GL_POLYGON и вызывать эту функцию в display () только один раз вместо шести вызовов при вычерчивании многоуголь- многоугольников общего вида: glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, cubelndices); Если в glDrawelements () используется тип GL_QUADS, то вычерчивание следующего четырех- четырехугольника начинается автоматически после завершения обработки каждой очередной четвер- четверки вершин. 4.5. Аффинные преобразования Под преобразованием {трансформацией — transformation) в дальнейшем будем понимать функцию, которая принимает (точку или вектор) и отображает ее на другую точку (или век- вектор). Графически смысл преобразования представлен на рис. 4.28. Для точек преобразование формально записывается в виде Q-ПП а для векторов — в виде v = /?(м). При использовании однородных координат и векторы, и точки представляются в виде четы- четырехмерных матриц-столбцов. В таком случае преобразование имеет единообразный вид и для точек, и для векторов одного и того же фрейма: v=/u). Эта формулировка носит слишком общий характер, чтобы ее можно было использовать на практике, поскольку она распространяется на любые однозначные отображения точек и векторов. На практике, даже располагая удобным описанием функции J0, нужно применять преобразование ко всем точкам кривой. Если, например, трансформируется отрезок прямой, обобщенное преобразование придется применить к каждой точке между двумя заданными конечными. 176 Глава 4. Объекты и геометрические преобразования
Мы будем рассматривать ограниченный класс преобразо- преобразований в четырехмерном пространстве однородных координат. В этом пространстве и точки, и векторы представляются как вершины, т.е. как наборы из четырех чисел9. Этот класс можно определить формально, наложив определенные ограничения на вид функции ft). Наиболее важное ограничение — линей- линейность преобразования. Функция/) является линейной тогда и только тогда, когда для любых скаляров а и C и для любых вершин р и q выполняется соотношение Рис. 4.28. Преобразование Особое значение такого свойства преобразований состоит в том, что если известно преоб- преобразование вершин, то всегда можно получить и преобразование линейной комбинации вер- вершин, используя для этого линейную комбинацию преобразований. В результате отпадает не- необходимость в повторном вычислении преобразований вершин при определении преобразо- преобразования их линейной комбинации. В четырехмерном пространстве мы работаем с представлениями точек и векторов. Ли- Линейное преобразование, которое трансформирует представление точки (или вектора) в другое представление, всегда может быть записано в терминах двух представлений и и v с помощью перемножения матриц: v = Аи, где А — квадратная матрица. Сравнивая это выражение с полученным в разделе 4.3 для операции изменения фрейма, приходим к выводу, что, если А невырождена, любое линей- линейное преобразование соответствует изменению фрейма. Следовательно, линейное преобра- преобразование можно рассматривать с двух точек зрения: как изменение представления или фрейма, которое приводит к новому представлению вершин, или как преобразование вер- вершин в рамках того же фрейма. При выполнении операций в однородных координатах А представляет собой матрицу 4x4, которая оставляет неизменным четвертый компонент представления (м). Матрица А имеет вид А = а п а, а 13 а «2 12 21 "-22 3 «3. «32 «33 0 0 0 1 Двенадцать элементов матрицы могут изменяться произвольным образом, и мы говорим о та- таком преобразовании, что оно имеет 12 степеней свободы {degrees of freedom). Но точки и векторы в аффинном пространстве имеют немного другое представление. Любой вектор можно представить в виде и = 9Рассматриваются только такие функции преобразования, которые отображают одни вершины на другие вершины и которые подчиняются правилам манипулирования с точками и векторами, описанными ранее в этой главе и в приложении Б. 4.5. Аффинные преобразования 177
а любую точку — в виде "Р, 1 Если рассмотреть применение произвольного преобразования А по отношению к вектору v = Аи, то придем к выводу, что на результат влияют только 9 элементов матрицы А, т.е. преобразо- преобразование векторов обладает только девятью степенями свободы. Аффинные преобразования то- точек обладают всеми двенадцатью степенями свободы. Несложно показать, что аффинное преобразование преобразует прямую линию в прямую линию. Предположим, что прямая линия записана в виде Да) = P0 + ad, где Ро — точка, ас/— вектор. В некотором фрейме прямая выражается с помощью представ- представления ро и d соответственно, точки Ро и вектора d в этом фрейме: р(а) = ро + ad. Для любой матрицы аффинного преобразования А справедливо соотношение Ар(а) = Ар0 + aAd. Таким образом, можно построить преобразование прямой, выполнив сначала преобразование Ро и d, а затем воспользоваться любым алгоритмом формирования прямой. Если используется форма представления отрезка двумя точками р(а) = аро + A-<х)рь получим аналогичный результат— нужно сначала выполнить преобразование р0 и р,, а затем по ним построить преобразованный отрезок. Поскольку в матрице А имеется 12 элементов, которые можно выбирать произвольно, то аффинное преобразование прямой или прямоли- прямолинейного отрезка имеет 12 степеней свободы. До сих пор мы рассуждали в терминах абстрактного математического пространства. Од- Однако эти рассуждения имеют совершенно конкретное приложение к практическим проблемам компьютерной графики. Из них следует, что для построения в системе компьютерной графи- графики изображения преобразованного отрезка необходимо выполнить только преобразование представлений в однородных координатах конечных точек этого отрезка, а, следовательно, графическую систему можно реализовать в виде конвейера аффинных преобразований пред- представлений отдельных точек в однородных координатах. К счастью, подавляющее большинство преобразований, которые используются в компью- компьютерной графике, можно свести в конечному множеству аффинных преобразований. Это мно- множество включает вращение, плоскопараллельное смещение (сдвиг) и масштабирование. Вне- Внеся небольшие изменения, таким же образом можно выразить и проективные преобразова- преобразования — параллельное и перспективное. 4.6. Поворот, сдвиг и масштабирование Рассматривая преобразования, нам придется все время переключаться от геометрических объектов как абстрактных сущностей к их конкретным представлениям в определенных фреймах. В прикладной программе мы имеем дело с представлениями объектов. В этом раз- 178 Глава 4. Объекты и геометрические преобразования
деле сначала будет показано, что наиболее важные аффинные преобразования можно сфор- сформулировать независимо от конкретного представления. Затем будут выведены матрицы этих преобразований, которые позволяют работать с представлениями точек и векторов. О том, как эти преобразования реализованы в OpenGL, рассказывается в разделе 4.8. Мы рассматриваем преобразования как способ перемещения группы точек, описывающих один или несколько геометрических объектов, в новую позицию. Хотя существует множество способов выполнить перемещение отдельной точки, оказывается, что имеется только один способ однообразно выполнить такое преобразование по отношению к множеству точек, со- сохранив при этом отношения между вершинами в объекте. 4.6.1. Плоскопараллельное смещение Плоскопараллельное смещение, или сдвиг {translation),— это операция, которая смещает точки на фиксированное расстояние вдоль заданного направления (рис. 4.29). Сдвиг задается только вектором смещения d, поскольку для всех точек Р на объекте справедливо соотношение P'=P + d. Обратите внимание на то, что такое определение операции никак не связано с системой от- отсчета или фреймом представления. Сдвиг имеет 3 степени свободы, поскольку можно произ- произвольно задать три компонента вектора смещения. о) Рис 4.29. Сдвиг: а — объект в исходной позиции; б — сдвинутый объект 4.6.2. Поворот Задать поворот {rotation) несколько сложнее, чем сдвиг, так как требуется специфициро- специфицировать больше параметров. Начнем с простого примера поворота точкой вокруг начала коорди- координат в двухмерной плоскости, как показано на рис. 4.30. Поскольку за- задана определенная точка — начало координат, — мы имеем дело с оп- определенным фреймом. Двухмерная точка (х, у) в этом фрейме повора- поворачивается вокруг начала координат на угол 6 и занимает после поворота позицию (х' у'). Формулы описания поворота несложно получить, вос- воспользовавшись полярным представлением точек (х, у) и (х1, уу. х = р cos0, у = р sin<)>, x'=pcos(9 + ф), у' = р sin(9 + ф). Воспользовавшись формулами приведения синуса и косинуса суммы углов, получим х' = р cos0 cos0 - р siru|> sin8 = x cos6 -у sin9, у' = р созф sinG + р siiu|> cos9 = х sin9 + у cos9. Рис. 4.30. Поворот в двухмерной системе коор- координат 4.6. Поворот, сдвиг и масштабирование 179
Эти же уравнения можно записать и в матричной форме: (Vlfcose -sin el [у'\ [sine cosG О том, как можно расширить эти формулы на трехмерный случай, вы узнаете в разделе 4.7. Обратите внимание на три свойства этого преобразования, которые сохраняются и в дру- других вариантах операции поворота. 1. Существует некоторая точка, она называется фиксированной точкой (fixed point) пре- преобразования (в приведенном выше примере— начало координат), которая остается неподвижной при применении к ней этого преобразования. На рис. 4.31 показано, ка- какой результат получится при повороте вокруг фиксированной точки, отличной от на- начала координат. 2. Учитывая, что двухмерное пространство является частным случаем трехмерного, можно распространить сделанные выводы и на три измерения. В правосторонней сис- системе координат, когда оси х и у выглядят привычным для всех нас образом, положи- положительное направление оси z трехмерной системы координат будет направлено на на- наблюдателя (т.е. ось z "исходит" из листа бумаг, на котором начерчены оси * и у). По- Положительное направление поворота — это направление против часовой стрелки, если смотреть со стороны положительной полуоси г, исходящей из фиксированной точки. Это же определение положительного направления поворота мы будем использовать и для трехмерного случая при повороте вокруг произвольной оси. 3. Двухмерное вращение на плоскости эквивалентно трехмерному вращению вокруг оси z. Все точки на плоскости, параллельной ху (т.е. имеющие одинаковое значение компоненты г), поворачиваются одинаково, сохраняя значение компонентам. Рис 4.31. Поворот вокруг фиксированной точки Эти свойства мы используем при выводе обобщенного преобразования поворота в трех- трехмерном пространстве, не зависящего от конкретного фрейма. Для такого преобразования (рис. 4.32) нужно специфицировать три параметра — фиксированную точку (Pf), угол пово- поворота (9) и прямую или вектор, вокруг которого выполняется поворот (ось вращения). Если фиксированная точка задана, то в нашем распоряжении имеются 3 степени свободы: два угла необходимы для задания ориентации оси вращения и один угол задает сам поворот. Поворот и сдвиг относятся к группе так называемых изометрических преобразований (или преобразований твердого тела — rigid-body transformations). Комбинация этих преоб- преобразований не может изменить формы объекта, а изменяет только его положение в простран- пространстве — позицию и ориентацию. Следовательно, сами по себе повороты и смещения не дают 180 Глава 4. Объекты и геометрические преобразования
полного набора аффинных преобразований. Преобразования, представленные на рис. 4.33, также являются аффинными, но не относятся к группе изометрических преобразований. Рис. 4.32. Трехмерный поворот Рис. 4.33. Анизометрические преобразования 4.6.3. Масштабирование Масштабирование (scaling) представляет собой анизометрическое аффинное преобразо- преобразование. Практически любое аффинное преобразование можно свести к суперпозиции преобра- преобразований масштабирования, поворота и сдвига. Преобразование масштабирования увеличивает или уменьшает размеры объекта. На рис. 4.34 показаны два варианта масштабирования — пропор- пропорциональное (равномерное во всех направлениях или преобразо- преобразование подобия) и растяжение только в одном направлении. По- Последний вариант также понадобится включить в набор элемен- элементарных преобразований, которыми мы будем пользоваться в дальнейшем при моделировании и визуализации объектов. Как видно на рис. 4.35, преобразование масштабирования также имеет фиксированную точку. Таким образом, набор параметров этого преобразования включает фиксированную точку, направле- направление, вдоль которого изменяется масштаб, и масштабный множи- множитель (а). При а > 1 объект растягивается в заданном направлении, а при 0<сх <1 объект сжимается. При отрицательном значении ос происходит отражение (reflection) объекта относительно заданной фиксированной точки в указанном направлении (рис. 4.36). Рис. 4.34. Пропорциональ- Пропорциональное и неравномерное масштабирование Рис 4.35. Параметры масшта- масштабирования Рис. 4.36. Отражение 4.6. Поворот, сдвиг и масштабирование 181
4.7. Преобразования в однородных координатах В большинстве графических API приходится работать в определенной системе отсчета (системе координат). Хотя программист и может изменить эту систему координат (как прави- правило, фрейм), он не может работать с представлениями на высоком уровне, например с выра- выражениями вида Q = Р + av. Вместо этого программист имеет дело с представлениями в однородных координатах и вы- выражениями вида q = р + O.V. В некотором фрейме любое аффинное преобразование может быть представлено матрицей 4x4 вида А = ос,, ее, а а, 13 "-14 «23 «24 «31 «32 «33 0 0 0 4.7.1. Сдвиг X У z 1 . р' = ~х'~ у' z' 1 d- «2 «3 0 Преобразование сдвига смещает точки в новые позиции в соответствии с заданным векто- вектором смещения. Если точка р сдвигается в р' на расстояние d, то такое преобразование можно записать в виде р1 = р + d. В однородных координатах р, р' и d выражаются следующим образом: Р = Тогда эти уравнения можно записать в виде отдельных выражений для каждого компонента: х' = х+ о^, У'=У + Оу, z' = z + ou. Метод представления преобразования сдвига посредством суммирования матриц-столбцов плохо сочетается с другими аффинными преобразованиями. Существует и другой метод представления сдвига — с помощью перемножения матриц: Р' = Тр, где 10 0а. Т = 0 1 0 av 0 0 1 аг 0 0 0 1 182 Глава 4. Объекты и геометрические преобразования
Матрица Т называется матрицей сдвига (translation matrix). Иногда ее записывают в виде T(Oj., a,, ex.), чтобы подчеркнуть три независимых параметра преобразования. На первый взгляд может показаться, что четвертый элемент в матрицах-столбцах из- излишний, но оказывается, что при использовании трехмерной версии выражения р1 = Тр тот же результат не получается. Поэтому иногда применение однородных координат для выполнения преобразования сдвига рассматривается как своего рода трюк, позволяю- позволяющий заменить суммирование трехмерных матриц-столбцов перемножением четырехмер- четырехмерных матриц. Матрицу обратного преобразования можно сформировать либо с помощью алгоритма об- обращения матриц, либо приняв во внимание, что обратное преобразование — это сдвиг на рас- расстояние ~d. В любом случае получим 1 0 0 0 0 1 0 0 0 0 1 0 -а, -ау -а. 1 4.7.2. Масштабирование Как уже говорилось выше, преобразования поворота и масштабирования характеризу- характеризуются фиксированной точкой, которая остается неподвижной при выполнении этих преоб- преобразований. Сейчас будем считать, что фиксированной точкой является начало координат, а в дальнейшем покажем, как с помощью суперпозиции базовых преобразований можно сформировать преобразование масштабирования относительно произвольной фиксирован- фиксированной точки. Матрица преобразования масштабирования, имеющего фиксированную точку в начале координат, позволяет задавать масштабные коэффициенты по каждой из координатных осей независимо друг от друга. Имеем три уравнения: которые можно выразить в однородных координатах матричным уравнением P=Sp, где (Рд'Ру'Рс]- "Р, 0 0 0 0 р, 0 0 0 0 р. 0 0" 0 0 1 В этой матрице, а также в матрице сдвига и других матрицах преобразований однородных координат четвертая строка не зависит от характера преобразования, а служит для того, что- чтобы после выполнения перемножения сохранялось значение 1 в четвертом компоненте точки. Для обращения матрицы масштабирования необходимо использовать обратные значения масштабных коэффициентов по осям: s-(p,.p,.p,).8[jL.J-.JL 4.7. Преобразования в однородных координатах 183
4.7.3. Поворот Сначала рассмотрим поворот вокруг начала координат, т.е. будем считать, что фиксиро- фиксированная точка преобразования поворота находится в начале координат. Поскольку поворот можно выполнять независимо вокруг каждой из осей координат, это преобразование имеет три степени свободы. Но нужно учитывать, что перемножение матриц не является коммута- коммутативной операцией (см. приложение В). Если за поворотом вокруг оси х на угол 0 следует по- поворот вокруг оси у на угол ф, то результат будет отличаться от полученного при выполнении этих же преобразований в другой последовательности. Чтобы найти матрицу поворота вокруг отдельной оси системы координат, можно исполь- использовать результат, полученный при анализе поворота в двухмерной системе (см. раздел 4.6). Мы уже видели, что поворот в двухмерной системе фактически является поворотом вокруг оси z трехмерной системы координат и что после выполнения такого преобразования точ- точки остаются на той же плоскости, т.е. компонент - точки не изменяется. Следовательно, уравнения поворота вокруг оси г на угол 0 в трехмерном пространстве можно записать следующим образом: х' = xcosQ - ys\nQ, y' = xs\r\Q +>>cos0, г' = г, или в матричном виде: где cose -sine О О' sine cosO О О О 0 10 0 0 0 1 Рассуждая аналогично, можно сформировать и матрицы поворота вокруг осей jc и у. При по- повороте вокруг оси х значения компонентов х точек остаются неизменными, и получаем двух- двухмерный поворот в плоскости х = const. При повороте вокруг оси у сохраняются значения компоненту точек. Матрицы этих поворотов имеют вид 10 0 0' 0 0 0 cose sinO 0 cose -: 0 sine 0 0 1 0 0 -sine cose 0 sine 0 COS 6 0 0 0 1 0 0 0 1 Знак элементов матриц, содержащих sin, соответствует принятому определению положитель- положительного направления поворота в правосторонней системе координат. Обозначим через R любую из трех приведенных выше матриц поворотов вокруг осей сис- системы координат. После поворота на угол 0 преобразованные точки всегда можно вернуть в исходное состояние, выполнив поворот вокруг той же оси на угол -0. Следовательно, 184 Глава 4. Объекты и геометрические преобразования
R '@) Обратите внимание и на то, что элементы, содержащие cos, всегда размещаются на глав- главной диагонали матрицы, а пара элементов, содержащих sin, — по разные стороны от глав- главной диагонали. Воспользовавшись тригонометрическими тождествами cos(-e) = cos0 и sin(-6) = -sine, получим, что R '@) = R7@). В разделе 4.8 будет показано, как сформировать матрицу поворота вокруг произвольной оси с фиксированной точкой в начале координат, используя перемножение матриц поворота вокруг отдельных осей системы координат: R = R,R, R.. Учитывая, что преобразование произведения есть произведение преобразований в обрат- обратном порядке, можно показать, что для любой матрицы поворота выполняется соотношение Матрицы, для которых операция транспонирования дает тот же результат, что и обраще- обращение, называются ортогональными {orthogonal matrix); любая ортогональная матрица описы- описывает некоторое преобразование поворота с фиксированной точкой в начале координат. 4.7.4. Скос Хотя любое аффинное преобразование можно свести к последовательности поворо- поворотов, сдвигов и изменений масштаба, существует один вид преобразования, который на- настолько важен в компьютерной графике, что мы будем рассматривать его также как ба- базовый тип. Это преобразование скоса {shear). Рассмотрим куб, центр которого находит- находится в начале системы координат, а ребра параллельны осям координат (рис. 4.37). Если сместить верхнюю грань куба вправо, а нижнюю — влево, объект будет скошен в на- направлении оси д:. Учтите, что при этом компоненты у и z всех точек на объекте остаются неизменными. Мы будем называть показанное на рисунке преобразование дг-скосом, чтобы отличать его от скоса в другом направлении. Воспользовавшись простыми триго- тригонометрическими соотношениями, которые поясняются на рис. 4.38, можно охарактери- охарактеризовать преобразование такого типа единственным параметром — углом скоса 0. Уравне- Уравнения преобразования имеют вид х' = х + yctgQ, откуда следует вид матрицы скоса:  ctgG О О' 0 10 0 0 0 10 0 0 0 1 н,(е) = Матрицу обратного преобразования несложно получить, если учесть, что следует выполнить скос в обратном направлении; следовательно, Н,'@) = Н/Н>). 4.7. Преобразования в однородных координатах 185
z z Рис. 4.37. Преобразование скоса (х, у) (х, /) Рис 4.38. К определению матрицы скоса 4.8. Суперпозиция преобразований В этом разделе продемонстрировано, как формировать матрицу сложного аффинного преобразования, перемножая матрицы отдельных базовых (канонических) преобразований. Выражаясь языком математики, можно сказать, что сложное преобразование есть суперпози- суперпозиция базовых преобразований (concatenating), которые были рассмотрены в предыдущем раз- разделе. Использованию такой стратегии формирования матрицы сложного преобразования сле- следует отдать предпочтение перед непосредственным вычислением элементов матрицы. Этот подход хорошо "вписывается" в конвейерную архитектуру графической системы. Предположим, что выполняются три последовательных преобразования точки р и при этом формируется новая точка q. Поскольку произведение матриц ассоциативно, можно за- записать такую последовательность преобразований в виде q = CBAp, без использования скобок. Однако порядок выполнения операций существенно влияет на производительность процесса вычисления. Например, можно сначала умножить матрицу А, затем на полученный результат умножить матрицу В, а потом умножить матрицу С (этот по- порядок соответствует группировке, представленной на рис. 4.39): q = (C(B(Ap))). |—> Щ, в у';;;||—> с |—> < Рис. 4.39. Последовательное выполнение преобразований Если выполняется преобразование единственной точки, то этот порядок наиболее эффек- эффективен, поскольку на каждом шаге квадратная матрица умножается на матрицу-столбец. Но если необходимо преобразовать множество точек, то операцию следует реализовать в два этапа. Сначала вычислять произведение квадратных матриц отдельных преобразований: М = СВА, а затем использовать полученную матрицу для обработки всего множества точек: q = Mp. Этот порядок реализуется в графическом конвейере, как показано на рис. 4.40. Сначала вычисляется матрица произведения М, затем она загружается в ту секцию конвейера, ко- 186 Глава 4. Объекты и геометрические преобразования
торая выполняет преобразование. Если посчитать количест- количество операций, то окажется, что вычисление матрицы М тре- требует несколько большего числа операций, чем (С(В(Ар))), но затем при обработке каждой из сотен или тысяч точек требуется выполнить только одну операцию умножения квадратной матрицы на матрицу-столбец. Ниже на примерах будет показано, как вычислять матрицу М. СВА м Рис. 4.40. Выполнение преобра- преобразований в графическом конвейере 4.8.1. Поворот вокруг произвольной фиксированной точки В первом примере будет показано, как формировать матрицу преобразований поворота вокруг произвольной фиксированной точки, используя канонические матрицы поворота во- вокруг начала координат. Направление оси поворота в примере совпадает с направлением ко- координатной оси 2, но этот же метод можно использовать и при другом направлении оси. Рассмотрим куб, центр которого находится в точке р/, а ребра параллельны осям коор- координат. Требуется повернуть куб вокруг оси z, но сохранить неизменным положение центра (рис. 4.41). Если бы точка р/совпадала с началом координат, мы могли бы воспользоваться уже сформированной матрицей R.F). Отсюда следует, что сначала нужно сдвинуть куб та- таким образом, чтобы его центр оказался в начале координат, а затем воспользоваться мат- матрицей R:(Q). Последняя операция — сдвинуть куб так, чтобы его центр занял прежнее по- положение р/. Эта последовательность преобразований представлена на рис. 4.42. Используя введенные раньше обозначения, нужно сначала выполнить преобразование Т(-ру), далее — R.F), а последним выполнить Т(ру). В результате суперпозиции этих преобразований по- получим матрицу а) 6) Рис. 4.41. Поворот куба вокруг собственного центра После перемножения матриц получим: cosG -sinG О X; -A^cosG + ^sinG cosG 0 yf -.xysinG-^cosG M.= sinG О О 4.8. Суперпозиция преобразований 187
Рис. 4.42. Последовательность канонических преобразований 4.8.2. Поворот вокруг произвольной оси Теперь покажем, как можно представить поворот вокруг произвольной оси в виде су- суперпозиции трех поворотов вокруг осей координат. Порядок выполнения канонических преобразований в этом случае не однозначен (см. упр. 4.10), хотя результат будет одно- однозначным. В примере начало координат выбрано в качестве фиксированной точки преобра- преобразования. Первой операцией будет поворот вокруг оси z, затем поворот вокруг оси у и по- последним — поворот вокруг оси х. Рассмотрим куб, центр которого находится в начале координат, а ребра параллельны осям системы координат, как показано на рис. 4.43,а. Сначала повернем его вокруг оси z на угол а таким образом, чтобы ориентация граней куба стала такой, как на рис. 4.43,6. Затем повернем куб на угол 3 вокруг оси у, как показано на рис. 4.44. Последний поворот — на угол у вокруг оси х, как показано на рис. 4.45. б) Рис. 4.43. Поворот куба вокруг оси г: а — до поворота; б — после поворота Таким образом, матрица поворота вокруг произвольной оси получается в результате пе- перемножения трех матриц: Несложный эксперимент должен убедить вас в том, что правильно подобрав значения уг- углов а, Р и у, можно таким способом обеспечить поворот вокруг оси с любой ориентацией, хо- хотя выбор углов — задача непростая, о чем будет рассказано в разделе 4.8.4. 188 Глава 4. Объекты и геометрические преобразования
а) 6) Рис. 4.44. Поворот куба вокруг оси у У а) Рис. 4.45. Поворот куба вокруг оси х 6) 4.8.3. Преобразование экземпляра Рассмотренный пример с кубом, который можно ориентировать как угодно, подводит нас к обобщенному подходу, который можно использовать при моделировании объектов. Рассмотрим сцену, состоящую из множества простых объектов (рис. 4.46). Один из возможных вариантов формирования описания такой сцены— определить каждый объект сцены множеством вер- вершин, учитывая его положение, размеры и ориентацию. Другой подход — сначала определить набор прототипов объектов сце- сцены определенного фиксированного размера, положения и ори- ориентации, причем прототипами могут быть как стандартные объ- объекты (параллелепипед или сфера), так и более сложные — стол, стул и т.п. Каждый конкретный объект сцены — это экземпляр прототипа (instance) со своими размерами, положением и ори- ориентацией в пространстве сцены. Размеры, положение и ориента- ориентация экземпляра задаются каноническими преобразованиями, ко- которые в данном случае будем называть преобразованиями эк- земпляра {instance transformation). В результате описание сцены ™с' примет вид базы данных, опирающейся на список идентифика- торов прототипов (например, 1 — куб, 2 — сфера и т.д.). Преобразование экземпляра применяется в порядке, представленном на рис. 4.47. Объект обычно определяется в собственном фрейме, начало координат которого находится в центре масс объекта, а оси координат параллельны характерным ребрам объекта или его оси сим- симметрии. Сначала устанавливаются размеры экземпляра, затем его сдвигают и поворачивают. Следовательно, преобразование экземпляра имеет вид М = TRS. °' ^ена из простых ооъектов 4.8. Суперпозиция преобразований 189
Моделирование на основе преобразований экземпляров эффективно реализуется не толь- только в системах с конвейерной архитектурой, но и в системах, работающих с дисплейными списками, о которых шла речь в главе 3. Описания сложных прототипов, которые исполь- используются в модели многократно, передаются на сервер только один раз в виде дисплейного списка. Отображение каждого экземпляра потребует только пересылки на сервер соответст- соответствующей матрицы преобразования экземпляра. 4.8.4. Поворот вокруг произвольной оси Пример выполнения поворота, который мы рассмотрим в данном разделе, продемонстри- продемонстрирует, как использовать углы поворотов вокруг осей координат для задания поворота вокруг произвольного вектора. Рассмотрим поворот куба, показанного на рис. 4.48. Для определения произвольного поворота в пространстве требуются три параметра: фиксированная точка пре- преобразования (в качестве таковой выберем центр куба — точку р0), вектор, вокруг которого выполняется поворот, и угол поворота. Обратите внимание на то, что ни один из них не зави- зависит от конкретного фрейма, а следовательно, мы специфицируем преобразование, не связы- связываясь ни с какой системой координат. Но при формировании матриц аффинных преобразова- преобразований нам придется привязаться к определенному фрейму. I Рис. 4.47. Преобразование экземпляра Рис 4.48. Поворот куба вокруг произвольной оси Вектор, вокруг которого поворачивается куб, можно специфицировать разными способа- способами, например, можно задать его двумя точками Pi и р2: и = р2-р,. Обратите внимание на то, что порядок использования точек задает положительное направле- направление вращения, и хотя на рисунке вектор и проходит через точку р0, имеет значение только его направление. Выполнение дальнейших операций облегчит нормализация вектора оси поворо- поворота — замена и вектором единичной длины, имеющим то же направление: аг а, ос. 190 Глава 4. Объекты и геометрические преобразования
P2-Pl Выше уже было показано, что преобразование упрощается, если перенести фиксированную точку в начало координат. Таким образом, первый сдвиг — это Т(-р0), а последний — Т(ро). После начального сдвига задача поворота приобретает вид, как на рис. 4.49. В предыдущем примере (см. раздел 4.8.2) было показано, что поворот вокруг произвольной оси можно свести к по- последовательности поворотов вокруг отдельных осей коор- координат. Основная загвоздка при этом — определить, на какой угол нужно повернуть вокруг каждой оси. Поэтому приме- применим другую стратегию — выполним два первых поворота та- таким образом, чтобы ось поворота v совместилась с коорди- координатной осью г. Затем повернем вокруг z на заданный угол 0, после чего выполним первые два поворота в обратном поряд- порядке и обратном направлении. Таким образом, матрица ком- комплексного преобразования имеет вид произведения: r = R,(-ex) R,(-e,) R.(er) R/e,) R^e,). Эта последовательность поворотов показана на рис. 4.50. Наиболее сложная часть проце- процедуры — определение Qx и 6Г Рис. 4.49. Перенос фиксиро- фиксированной точки в начало координат z z z 'У z Рис. 4.50. Последовательность вращений вокруг координатных осей Рассмотрим компоненты вектора v. Поскольку v является вектором единичной длины, то Проведем отрезок от начала координат в точку (а*, Оу, а.). Этот отрезок имеет единичную длину и ориентирован в направлении вектора v. Опустим перпендикуляры из точки (а,, а,, а.) на каждую координатную ось, как показано на рис. 4.51. Три направляющих угла {direction angles) — ф„ фя ф. — это углы между отрезком (или вектором v) и координатными осями. Между направляющими косинусами {direction cosines) и компонентами v существует очевид- очевидное соотношение: CLy, соэф. = a... Независимы только два направляющих угла, поскольку 4.8. Суперпозиция преобразований 191
Зная значения направляющих косинусов, можно вычислить значения углов Qx и ву. Рассмот- Рассмотрим рис. 4.52. На нем видно, что поворот точки (о^, о^,, а.) приводит к такому повороту от- отрезка, что он оказывается в плоскости у = 0. Длина проекции отрезка (до выполнения поворо- поворота) на плоскость х = 0 равна d. Можно и по-другому трактовать этот рисунок. Считайте, что плоскость х = 0 — это стена, и рассмотрите удаленный источник света, расположенный где- то на положительной полуоси х. Отрезок, который вы увидите на стене, — это тень, отбрасы- отбрасываемая отрезком, проведенным из начала координат в точку (с^, а,,, а.). Учтите, что длина те- тени меньше длины отрезка. Можно сказать, что отрезок сжат до величины d = Ja\ + a) . Ис- Искомый угол поворота— это угол между этой тенью и осью z. Однако в матрице поворота присутствуют не углы, а их синусы и косинусы, следовательно, нам не потребуется вычислять само значение 9„ а только 1 0 0 0 0 ajd ay/d 0 0 -ay/d a. Id 0 0 0 0 1 Рис. 4.57. Направляющие углы оси поворота Рис. 4.52. Вычисление угла по- поворота вокруг оси х Элементы матрицы R,, вычисляются исходя из аналогичных рассуждений (рис. 4.53). Обрати- Обратите внимание на то, что поворот вокруг оси у выполняется по часовой стрелке, поэтому нужно внимательно следить за знаками тех элементов матрицы, кото- которые содержат функцию синуса. Матрица R>. будет иметь вид у «,(«,)¦ d 0 tx 0 0 1 0 0 -ax 0 d 0 0 0 0 1 Последний шаг— перемножить матрицы отдельных преобра- преобразований: м = т(ро) R,(-e,) |ц-е,) R.Fr) вд) кх(ех) т(-Ро). Теперь рассмотрим численный пример. Пусть требуется по- повернуть объект на 45° вокруг прямой, которая проходит через начало координат и точку A,2,3). Оставим фиксированную точку преобразования в начале координат. Первый шаг — оп- Рис 4.53. Вычисление угла поворота вокруг оси у 192 Глава 4. Объекты и геометрические преобразования
ределение точки на прямой, которая отстоит от начала координат на единичное расстояние. Эту точку получим после нормализации тройки A,2, 3). Получим Шу/ы, 2/>/l4, 3/VT4] или в однородных координатах — (l/VU, 2/-УГ4, 3/-v/l4, l). После выполнения первой фазы поворота эта точка перейдет в положение @, 0, 1, 1). Сначала будет выполнен поворот вокруг оси х на угол, равный arccos-T= . В результате точка vl3 (\/\fl4, 2/VU, 3/>/l4, \\ преобразуется в точку (\/у/\4, 0. Vl3/14, l), которая принадлежит плоскости у = 0. Вокруг оси у объект поворачивается на угол, равный arccosVl3/14 . В результате прямая окажется вытянутой вдоль оси г, и можно будет выполнить поворот вокруг этой оси на 45°. Последний этап — выполнить первые два поворота в обратном порядке и в обратном направлении. Перемножив матрицы этих преобразований, получим матрицу R: R -arccosj— R D5) r[ arccosj— R | arccos ¦'[ V14 J л >[ Vl4j \ 13V2 2-V2-3V7 6-зУ2+4>/7 28 14 28 2-V2+3V7 4 + 5^2 6-3V2-V7 14 14 14 6-3V2-W7 6-3>/2+>/7 18 + 5V2 28 14 28 0 0 0 Эта матрица не изменяет положение точек, которые находятся на прямой, проходящей через начало координат и точку A,2, 3). Если потребуется выполнить такой же поворот, но фикси- фиксированная точка располагается не в начале координат, а в некоторой позиции р/, то нужно скорректировать матрицу преобразования: M = T(py)RT(-P/). Этот пример показывает, как использовать множество простейших преобразований для выполнения сложного преобразования. Задача поворота вокруг произвольной оси и произ- произвольной фиксированной точки возникает в множестве приложений. Варианты ее решения за- зависят в основном от того, каким образом задана ось поворота. Однако почти всегда можно воспользоваться описанной выше методикой для определения направляющих углов или на- направляющих косинусов. 4.9. Матрицы преобразований в OpenGL В этом разделе речь пойдет о реализации механизма преобразований в однородных коор- координатах в виде программного пакета и об интерфейсе между этим пакетом и прикладной про- программой. В OpenGL существуют три матрицы, которые входят в состав параметров, характе- характеризующих текущее состояние графической системы. В этой главе мы рассмотрим только матрицу вида (model-view matrix). Всеми тремя матрицами можно манипулировать с помо- помощью одного и того же набора функций, а для выбора, с какой именно матрицей выполняются операции, используется функция glMatrixMode(). 4.9. Матрицы преобразований в OpenGL 193
4.9.1. Текущая матрица преобразования В большинстве графических систем используется текущая матрица преобразования — ТМП (СТМ— current transformation matrix). Эта матрица применяется для преобразования всех вершин. Если изменяется ТМП, изменяется текущее состояние системы. Умножение на ТМП является одной из стадий конвейерного процесса обработки информации в графи- графической системе (рис. 4.54). Обозначим матрицу ТМП через С. Тогда, если р — это верши- вершина, то при "перемещении" ее по конвейеру формируется произведение Ср. Матрица ТМП имеет размер 4x4 и может быть изменена функциями, которые входят в состав графиче- графического пакета10. Вершины тмп ¦ Вершины Рис. 4.54. Текущая матрица преобразования В исходном состоянии ТМП является единичной матрицей размера 4x4; при необходимо- необходимости в любой момент прикладная программа может ее реинициализировать. Будем использо- использовать символ <— для обозначения процедуры замены содержимого матрицы. Операция ини- инициализации в нашей системе обозначений будет выражаться следующим образом: Функции изменения С разделены на две группы: функции присвоения новых значений эле- элементам матрицы и функции преобразования матрицы умножением ее на другую матрицу справа или слева". В подавляющем большинстве графических систем поддерживаются три вида преобразований: сдвиг, масштабирование с фиксированной точкой в начале координат и поворот с фиксированной точкой в начале координат. В принятой системе символических обозначений операции умножения матрицы ТМП справа на матрицы преобразований выра- выражаются следующим образом: с<-ст, c<-cs, C«-CR. Операции установки элементов ТМП обозначаются так: С«-Т, c<-s, Cf-R. В большинстве графических систем можно присвоить элементам ТМП значения из любой другой матрицы или умножить ее справа на произвольную матрицу М: С<-М, с<-см. 10 В OpenGL матрица вида представляет собой матрицу аффинного преобразования, которая имеет только 12 степеней свободы, как об этом уже упоминалось в разделе 4.5. Матрица проецирования, ко- которая будет рассмотрена в главе 5, также имеет размер 4x4, но не является матрицей аффинного пре- преобразования. " В OpenGL используется только умножение справа. PHIGS допускает умножение как справа, так и слева. 194 Глава 4. Объекты и геометрические преобразования
4.9.2. Поворот, сдвиг и масштабирование В OpenGL матрица ТМП, которая преобразует координатные описания всех примитивов, является произведением двух матриц— матрицы вида GL_MODELVIEW и матрицы проецирова- проецирования GLJPROJECTION (рис. 4.55). Программист имеет возможность манипулировать каждой из них по отдельности, выбирая нужную с помощью функции glMatrixMode(). Выбранная мат- матрица загружается вызовом функции glLoadMatrixf(<указатель_на_матрицу>); или приравнивается к единичной матрице (инициализируется) вызовом функции glLoadIdentity(); Вершины ; '' '• ¦ :: ¦ -i ¦• - •"'¦' '" I Вершины > Вид I > Проецирование! ^ ТМП Рис. 4.55. Матрицы вида и проецирования Произвольную матрицу размера 4x4 можно задать указателем на массив из 16 чисел, в кото- котором элементы матрицы перечислены по столбцам. Изменить выбранную матрицу можно с помощью функции умножения справа glMultMatrixf (<указатель_на_матрицу>). Для выпол- выполнения поворота, сдвига и масштабирования служат три специальные функции: glRotatef(angle, vx, vy, vz); glTranslatef(dx, dy, d2); glScalef(sx, sy, sz); Каждая из этих функций изменяет выбранную с помощью glMatrixMode() матрицу текущего состояния, домножая ее справа. В функции поворота glRotatef (angle, vx, vy, vz) первый аргумент angle задает угол поворота в градусах, а три последующих — vx, vy и vz — компо- компоненты вектора оси поворота. Аргументы функции сдвига glTranslatef ()— компоненты вектора смещения. Аргументы функции масштабирования glScalef () — масштабные коэф- коэффициенты по координатным осям. 4.9.3. Поворот вокруг фиксированной точки средствами OpenGL В разделе 4.8 было показано, что для поворота вокруг произвольной фиксированной точ- точки (т.е. точки, отличной от начала координат) сначала нужно выполнить преобразование сдвига, совмещающее заданную фиксированную точку с началом координат, потом преобра- преобразование поворота вокруг начала координат, а затем обратное преобразование сдвига, "возвращающее" фиксированную точку в прежнюю позицию. В приведенной ниже последо- последовательности вызовов функций OpenGL сначала устанавливается режим работы с матрицей вида, затем выполняются описанные выше преобразования для следующих исходных данных: угол поворота 45°, ось поворота параллельна прямой, проведенной через начало координат и точку A, 2 , 3), а фиксированная точка имеет координаты D, 5, 6): glMatrixMode(GL_MODELVIEW) glLoadIdentityG; glTranslatefD.О, 5.0, 6.0); 4.9. Матрицы преобразований в OpenGL 195
glRotatefD5.0, 1.0, 2.0, 3.0); glTranslatef(-4.0, -5.0, -6.0); Обратите внимание на то, что работая с пакетом OpenGL, не нужно формировать матрицу поворота вокруг произвольной оси так, как мы это делали в примере из раздела 4.8.4, хотя в качестве упражнения попробуйте запрограммировать этот метод с помощью имеющихся в составе OpenGL функций умножения матриц. 4.9.4. Последовательность выполнения преобразований В приведенной выше последовательности обращения к функциям OpenGL внимательный читатель наверняка отметил, что преобразования задаются "в обратном порядке", т.е. первое преобразование сдвига задается пятым оператором, а последнее — третьим. Дело в том, что в OpenGL действует следующее правило: Преобразование, которое должно быть в цепочке последним, передается первым вы- вызовом соответствующей функции. Несложный анализ убедит вас в справедливости этого правила, которое учитывает особенно- особенности выполнения операций над текущей матрицей преобразования в исполнительной системе OpenGL. Дело в том, что ТМП всегда домножается справа на новую матрицу преобразования. Поэтому приведенную выше последовательность вызовов функций можно выразить с помо- помощью введенной раньше системы обозначений следующим образом: С<-1, С <- СТD.0, 5.0, 6.0), C<-CRD5.0, 1.0,2.0,3.0), С<-СТ(-4.0, -5.0, -6.0). На каждом шаге домножаем ТМП справа на новую матрицу преобразования, в результате чего вся процедура будет иметь результат С = ТD.0, 5.0, 6.0) RD5.0, 1.0, 2.0, 3.0) Т(-4.0, -5.0, -6.0), который полностью соответствует формуле, приведенной в разделе 4.8.1. Каждая вершина р, которая задается после того, как будет сформирована такая матрица вида, умножается справа на матрицу С, в результате чего формируется новая вершина q = Cp. Можно и по-другому рассматривать такой порядок операций в OpenOL, например вос- воспользовавшись концепцией стека. Изменение матрицы ТМП имеет аналогию с занесением матриц в стек. После завершения цепочки матрицы извлекаются из стека в обратном порядке и образуют представленную выше последовательность сомножителей. Аналогия эта только концептуальная, поскольку в исполнительной системе OpenGL стек используется совсем ина- иначе, а матрица, сформированная функцией преобразования, немедленно умножается справа на матрицу ТМП. Что касается стека матриц, то он нам часто понадобится при выполнении мо- моделирования, о чем будет подробно рассказано в главе 8. В составе OpenGL имеются функ- функции записи матрицы в стек glPushMatrix() и извлечения матрицы из стека glPopMatrix(), которые будут использоваться при построении иерархии объектов сцены. 4.9.5. Вращение куба В описанной ниже программе мы воспользуемся кубом, определенным в разделе 4.4, и будем вращать его по командам, формируемым при нажатии кнопок мыши. Нам потребуются три функции с обратным вызовом, которые регистрируются следующим образом: 196 Глава 4. Объекты и геометрические преобразования
glutDisplayFunc(display); glutldleFunc(spincube); glutMouseFunc(mouse); В функции отображения display() сначала устанавливается матрица вида, для чего исполь- используются три угла, заданных в функции обработки событий мыши mouse(). Затем с помощью функции colorcube(), которую мы рассматривали в разделе 4.4, вычерчивается куб. В этом примере будет использоваться двойная буферизация. При каждом вызове display() очища- очищаются буфер кадра и буфер глубины. Последний используется для удаления невидимых по- поверхностей. Выполнение display () завершается процедурой переключения буферов: void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glRotatef(theta[0], glRotatef(theta[l], glRotatef(theta[2]f colorcube(); glutSwapBuffers(); 1 0 0 • o, • o, .0, 0 1 0 • o, • o, .0, 0 0 1 .0); .0); .0); В функции обработки событий мыши mouse () выбирается ось вращения: void mouse(int btn, int state, int x, int y) { if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; } В функции обработки простоя выполняется прирашение на 2° угла поворота, связанного с те- текущей осью: void spinCube() { theta[axis] += 2.0; if( theta[axis] > 360.0 ) theta[axis] -= 360.0; display(); } Завершается выполнение программы нажатием клавиши на клавиатуре. Это событие обраба- обрабатывается функцией mykey (): void mykey (char key, int mousex, int mousey) { if(key=='q'||key=='Q') exit(); } Обсуждение алгоритмов удаления невидимых поверхностей мы отложим до главы 5, но сейчас нужно отметить, что с точки зрения прикладного программиста эта задача решается в программах OpenGL очень просто. От него требуется только очистить буфер глубины {depth buffer) и разрешить работу алгоритма вызовом glEnable(GL_DEPTH_TEST). Полный текст про- программы вы найдете в приложении А. 4.9. Матрицы преобразований в OpenGL 197
4.9.6. Загрузка матриц и использование стека матриц Хотя в большинстве случаев для формирования матрицы преобразования вполне доста- достаточно функций поворота, сдвига и масштабирования, в некоторых ситуациях приходится формировать матрицу поэлементно. В частности, это приходится делать при формировании матрицы скоса. Точно так же, как матрицу преобразования можно приравнять к единичной матрице, в нее можно загрузить и произвольную 4х4-матрицу однородных координат. Для этого в составе OpenGL имеется функция glLoadMatrixf(myarray) Аргументом функции glLoadMatrixf () является указатель на загружаемый массив. Теку- Текущую матрицу, выбранную с помощью glMatrixMode(), можно домножить справа на про- произвольную матрицу, определенную в прикладной программе. Это выполняется с помощью функции gMultMatrixf (myarray). Аргумент myarray представляет собой одномерный мас- массив из 16 чисел, в котором элементы матрицы представлены по столбцам. Так, если необ- необходимо сформировать в массиве myarray элементы матрицы М (речь идет только о матри- матрицах размером 4x4), то это делается следующим образом: GLfloat myarray[16]; for(i=0;i<3;I++) for(j=0;j=3;j++) myarray[4*j+i]= m[i][j]; Иногда в программе нужно выполнить некоторое преобразование, а затем восстановить предыдущее состояние. В частности, это необходимо делать при организации преобразова- преобразований экземпляров. Такие преобразования не должны распространяться на все объекты, а толь- только на выбранные. Сохранение-восстановление матриц текущего состояния выполняется с по- помощью стека матриц. Для записи матрицы в стек служит функция glPushMatrix(), а для из- извлечения из стека — функция glPopMatrix(). Поэтому в графических программах вы можете часто встретить примерно такой фрагмент: glPushMatrix(); glTranslatef(....); glRotate?( ); glScalef ( ); /* Здесь вычерчиваются объекты */ glPopMatrix(); 4.10. Взаимодействие пользователя с трехмерными графическими приложениями В рассмотренном выше примере для управления направлением вращения куба на экране использовалась трехкнопочная мышь. Но возможности такого интерфейса пользователя с приложением слишком ограничены. Вместо того чтобы три кнопки мыши использовать для задания ориентации, их можно было бы применить для управления программой, например ее завершением. Как уже отмечалось в разделе 4.8, существует множество способов выполнения поворота вокруг произвольной оси. Мы использовали последовательное вращение вокруг осей коорди- 198 Глава 4. Объекты и геометрические преобразования
нат х,у hz, но можно организовать этот процесс и по-другому: сначала повернуть вокруг оси *, затем вокруг оси у, а закончить опять поворотом вокруг оси х. В таком случае можно до- добиться любой ориентации объекта, используя только две кнопки мыши. Однако по-прежнему остается проблема с направлением вращения — в нашей программе поворот всегда выполня- выполняется только в одном направлении. Для пользователя же желательно иметь возможность пово- поворачивать в обе стороны — и вперед, и назад — и останавливать процесс, когда объект будет сориентирован, как задумано. Библиотека GLUT позволяет использовать для управления процессом кнопки мыши в со- сочетании с управляющими клавишами клавиатуры. Например, левой кнопкой мыши можно задать вращение вокруг оси х по часовой стрелке, а нажав одновременно еще и клавишу <Ctrl> — против часовой стрелки. Но есть и другие способы, которые позволяют организовать более гибкий и удобный ин- интерфейс пользователя с программой. Ниже мы рассмотрим два таких способа. 4.10.1. Использование областей экрана Предположим, что решено использовать одну кнопку мыши для управления ориентацией объекта, другую — для сдвига его ближе к наблюдателю или дальше от него, а третью — для смещения объекта вправо-влево или вверх-вниз. Для организации таких процедур можно воспользоваться функцией с обратным вызовом motion (), которая возвращает информацию о том, какая кнопка мыши нажата и где находится указатель мыши на экране. Информацию о положении указателя мыши можно использовать для управления скоростью вращения или смещения и их направлением. Как уже отмечалось, можно добиться произвольной ориентации, выполняя поворот только во- вокруг двух осей координат. Следовательно, для управления ориентацией нам достаточно одной кнопки мыши и информации о текущем положении указателя мыши на экране. Если при нажатии левой кнопки мыши указатель находится в центре экрана, поворот не выполняется. Если в этот момент указатель находится вверху экрана, то объект поворачивается вокруг оси х по часовой стрелке, а если внизу экрана — против часовой стрелки. Аналогично организуется и вращение во- вокруг оси у, но указатель должен находиться справа или слева от центра. Когда указатель находится в углу экрана, можно одновременно поворачивать объект и вокруг оси х, и вокруг оси у. Сдвиг объекта вправо-влево или вверх-вниз можно выполнять по этой же схеме, но ис- использовать правую кнопку мыши. Для управления движением объекта к наблюдателю или от него (вдоль оси z) будем использовать среднюю кнопку. При этом можно принимать во вни- внимание только положение указателя по вертикали относительно центра экрана. Мы предоставляем читателям возможность самостоятельно реализовать описанный алго- алгоритм в виде программы (см. упр. 4.19). 4.10.2. Виртуальный трекбол Идея использовать текущее положение указателя мыши для управления поворотом вокруг двух осей координат очень близка к тем методам, которые используются при работе с трек- трекболом (см. главе 3). Ниже мы рассмотрим, как развить эти идеи и создать виртуальный трек- трекбол, используя только мышь. Одно из достоинств этого "устройства" состоит в том, что задав с его помощью параметры, можно организовать непрерывное вращение, которое прекратится только по команде пользователя. Кроме того, оно позволяет оперативно, "на ходу", изменять скорость вращения и его направление. Этот же принцип можно использовать и для управле- управления смещением объекта. Начнем с того, что определим соответствие между положением точки на поверхности ша- шарика трекбола и позицией указателя мыши. Будем рассматривать трекбол, представленный на рис. 4.56, и предположим, что радиус его шарика равен одной единице. Тогда можно поста- 4.10. Взаимодействие пользователя с трехмерными графическими приложениями 199
вить в соответствие точкам поверхности шарика точки на плоскости у = О (рис. 4.57). Точка (.х, у, z) на поверхности шарика проецируется в точку (х, О, z) на плоскости. Такая проекция обратима, поскольку известно, что трехмерная точка, спроецированная в определенную точку на плоскости, должна удовлетворять соотношению, описывающему сферу: x2+y2+z2 = 1. Следовательно, данной точке (х, О, z) на плоскости должна соответствовать точка (дг, у, z) на верхней полусфере, где v = - v2 --2 Рис. 4.56. Фрейм трекбола Рис. 4.57. Проекция на плоскость точки на по- поверхности шарика трекбола Таким образом, по положению указателя мыши на экране можно восстанавливать трех- трехмерную точку на поверхности шарика трекбола. Рассмотрим теперь две точки, р, и р2, на верхней полусфере шарика. Векторы, проведенные из центра шарика к этим точкам, опреде- определяют ориентацию плоскости, нормаль к которой можно вычислить как векторное произведе- произведение этих векторов (рис. 4.58): п = р, х р2. Поворачивая трекбол вокруг оси п, можно совместить вектор pi с р2. Угол по- поворота— это угол между векторами pi и р;, который можно вычислить по значению модуля векторного произведения. По- Поскольку р, и р2 являются единичными век- векторами, угол определяется соотношением |sin9| = |п|. Если с большой скоростью отслежи- отслеживать траекторию указателя мыши, так что каждое элементарное смещение ее пози- позиции на экране будет достаточно мало, то можно воспользоваться аппроксимацией угла 0, не прибегая к обратным тригонометрическим функциям: sine « 0. Рис. 4.58. Определение плоскости поворота 200 Глава 4. Объекты и геометрические преобразования
Реализовать обработку положения трекбола можно с помощью функций обработки собы- событий простоя, движения мыши и нажатия кнопок мыши из библиотеки GLUT. Этот процесс можно рассматривать в терминах трех логических переменных или флагов, которые контро- контролируют движение мыши и перерисовку изображения. При инициализации программы этим логическим переменным присваиваются такие значения12: bool trackingMouse = false; bool trackballMove = false; bool redrawContinue = false; Если значение флага redrawContinue равно true, то функция обработки простоя вы- вызывает функцию перерисовки изображения. Если значение флага trackingMouse равно true, то обновляется позиция трекбола в функции обработки движения. Если значение флага trackballMove равно true, то обновляется матрица вращения, которая использу- используется в функции display (). Значения флагов изменяются в функции обработки событий мыши mouse (). Когда поль- пользователь нажимает кнопку мыши (определенную или любую — это определяет разработчик программы), запускается процесс обновления позиции трекбола в функции обработки собы- события движения мыши, причем, если пользователь передвигает мышь, вызывается функция пе- перерисовки объектов на экране. Когда пользователь отпускает кнопку мыши, отслеживание прекращается. Два последних положения мыши используются для определения вектора ско- скорости, что позволяет организовать непрерывное обновление матрицы поворота и таким обра- образом — непрерывное вращение объектов на экране. В этом случае объект будет вращаться и после того, как пользователь отпустит кнопку мыши. Текст программы, реализующей описанный алгоритм, находится в файле cube2.c. Учти- Учтите, что в этой программе используется некоторое упрощение при определении скорости вра- вращения, но в ней также обрабатываются ситуации, не рассмотренные в данном описании. На- Например, что делать, если указатель мыши находится в углу экрана и соответствующая точка на плоскости не может быть спроектирована на верхнюю полусферу поверхности шарика трекбола. 4.10.3. Плавное вращение Описанный пример демонстрирует, с какими сложностями встречается программист при использовании рассмотренных выше методов определения параметров поворота. Этот под- подход основан на .углах Эйлера, которые описывают повороты вокруг осей системы координат. Он позволяет сформировать матрицу произвольного поворота, используя суперпозицию про- простых поворотов вокруг осей системы координат х,у и z. Хотя OpenGL и позволяет самостоя- самостоятельно сформировать любую матрицу поворота, но удобнее использовать принцип суперпо- суперпозиции для определения как оси вращения, так и соответствующих углов. Рассмотрим, что получится в программе анимации, если попытаться организовать движе- движение объекта при переходе от одной ориентации к другой. Подходящую матрицу поворота можно определить в виде произведения матриц поворота вокруг трех осей: R(9) = Rr(er) НДв,.) R,F..). Если цель разработчика— организовать на экране плавное вращение объекта между за- заданными крайними положениями, то нужно на каждом шаге использовать малые приращения углов. Но такая последовательность не будет восприниматься наблюдателем как плавное вращение — он будет видеть вращения вокруг каждой из трех осей. Если компилятор не поддерживает тип Boolean, можно воспользоваться директивой tdefine bool int и затем определить константы true и false как 1 и 0 соответственно 4.10. Взаимодействие пользователя с трехмерными графическими приложениями 201
При описании работы с трекболом было показано, как можно достаточно плавно повора- поворачивать куб из одного положения в другое. Для этого использовалось соответствие между двумя ориентациями и двумя точками на окружности единичного радиуса. Плавный переход от одной ориентации к другой соответствует движению по большой окружности на поверх- поверхности сферы. При этом ось вращения — это нормаль к плоскости окружности, которая опре- определяется двумя точками на окружности и центром сферы. Если плавно увеличивать этот угол, наблюдатель увидит плавное вращение. Сложности при реализации этого метода проистекают из использованного нами матема- математического аппарата, который "привязан" к некоторой системе координат. Кватернионы {quaternions) — это математический аппарат, связанный с комплексными числами, который позволяет использовать альтернативные методы описания вращения. Хотя этот аппарат и не так интуитивно понятен, как тот, который использовали мы, он имеет существенные пре- преимущества в приложениях, где требуется анимация изображения. Другие примеры организа- организации плавного вращения будут рассмотрены в главе 8. 4.11. Резюме В этой главе мы представили две различные, но дополняющие друг друга точки зрения на математический аппарат, пригодный для решения задач компьютерной графики. Одна из них состоит в том, что для глубокого понимания сути выполняемых в графической системе опе- операций необходимо использовать математические абстракции объектов. Другая же состоит в том, что базовым для реализации графической системы должен быть аппарат преобразова- преобразований, в частности преобразований в однородных координатах. Используемый математический аппарат заимствован из векторного анализа и линейной алгебры. Однако последовательность описания этого аппарата, которая определяется задача- задачами компьютерной графики, весьма непривычна для большинства студентов. Как правило, сначала студенты знакомятся с линейной алгеброй, а затем с векторными пространствами (как приложение к изучению я-мерных пространств R"). Мы же, наоборот, сначала рассмот- рассмотрели представление объектов в математических пространствах, которое привело нас к ис- использованию линейной алгебры в качестве инструмента для реализации абстрактных типов. При изложении мы старались следовать подходу1, независящему от координат. Сделано это было по двум причинам. Во-первых, желательно показать, что все базовые концепции геометрических объектов и преобразований не зависят от способа их представления. Во- вторых, все более популярными становятся языки объектно-ориентированного программиро- программирования, и большинство прикладных программистов будут работать непосредственно с объек- объектами, а не с их представлениями. В литературе, которая упоминается в следующем разделе, имеется множество примеров геометрических систем программирования, демонстрирующих потенциальные возможности такого подхода. Однородные координаты — это один из прекрасных примеров огромных возможностей математической абстракции. Используя абстрактное математическое пространство — аф- аффинное пространство, — оказалось возможным создать исключительно эффективный про- программный инструмент для манипулирования графическими объектами. В последних разделах главы описаны те преобразования, которые поддерживаются сред- средствами OpenGL, и обсуждается методика их суперпозиции для получения произвольных аф- аффинных преобразований. Принцип комбинированного использования небольшого набора ба- базовых преобразований для определения преобразования любой сложности лежит в основе подавляющего большинства графических систем. На его использование ориентированы все упражнения в конце настоящей главы. В главе 5, взяв за основу эту же методику, мы рас- рассмотрим процесс визуализации трехмерных сцен, а в главе 8 покажем, как она используется при иерархическом моделировании объектов. 202 Глава 4. Объекты и геометрические преобразования
4.12. Рекомендуемая литература Векторному анализу и линейной алгебре посвящено огромное множество книг, хотя прак- практически во всех эти два вопроса рассматриваются по отдельности. В среде тех, кто занимает- занимается компьютерной графикой, подход к описанию кривых и поверхностей, основанный на ис- использовании векторных пространств, довольно популярен и представлен, например, в книге Фокса (Faux) и Пратта (Pratt) [Fau80]. Введению в геометрическое программирование по- посвящены книги Де Розе (DeRose) [DeR89, DeR90]. Однородные координаты были придуманы геометрами [MaxSIJ и только значительно позже их открыли для себя специалисты по ком- компьютерной графике [Rie81]. Аппаратно методы преобразований в однородных координатах впервые были реализованы в Silicon Graphics Geometry Engine [Cla82]. В современных гра- графических станциях используются специализированные БИС (ASIC — Application Specific Integrated Circuits), которые среди прочих функций реализуют и преобразования в однород- однородных координатах. Использование кватернионов в графических системах при анимации изображения описа- описано Шомейкером (Shoemaker) в работе [Sho85]. Для изучения методов работы с матрицами геометрических преобразований прекрасно подходят современные программные продукты Mathematica [Wol91] и Matlab [Mat95]. Упражнения 4.1. Рассмотрите решение любого линейно-дифференциального уравнения с постоян- постоянными коэффициентами или аналогичного разностного уравнения. Докажите, что решения однородных уравнений образуют векторное пространство. Покажите связь между решением определенного неоднородного уравнения и аффинным пространством. 4.2. Покажите, что следующие последовательности коммутативны: • поворот и равномерное масштабирование; • два поворота вокруг одной и той же оси; • два смещения. 4.3. Разработайте библиотеку функций, которая позволит реализовать геометрическое программирование. Библиотека должна содержать функции манипулирования ба- базовыми геометрическими типами (точками, прямыми, векторами) и операциями над этими типами, включая скалярное и векторное произведение. Функции долж- должны допускать изменение фрейма. Включите в библиотеку и функции взаимодей- взаимодействия с OpenGL, что позволит просматривать на экране результаты геометриче- геометрических вычислений. 4.4. При работе исключительно с двухмерными объектами можно использовать трех- трехмерные однородные координаты для представления точки в виде р = [ху I]7 и век- векторов в виде v = [а Ъ О]7. Какой вид будут иметь в этом случае ЗхЗ-матрицы преоб- преобразований поворота, сдвига, масштабирования и скоса. Сколько степеней свободы у каждого из этих преобразований? 4.5. Параметры аффинного преобразования можно однозначно определить, сравнивая положение нескольких точек до и после выполнения преобразования. Сколько та- таких точек нужно использовать для однозначного описания аффинного преобразова- преобразования в трехмерном пространстве? Сколько их требуется для решения этой же задачи в двухмерном пространстве? Упражнения 203
4.6. Какой вид примет матрица поворота при работе в левосторонней системе коорди- координат и как нужно переопределить положительное направление вращения? 4.7. Покажите, что любая последовательность поворотов и сдвигов может быть заме- заменена единственным поворотом с фиксированной точкой в начале координат и сдвигом. 4.8. Выведите матрицу преобразования скоса в виде суперпозиции матриц поворота, сдвига и масштабирования. 4.9. В двухмерном пространстве прямую можно задать уравнением у = mx+h. Найдите аффинное преобразование, которое сформирует отражение точки относительно этой прямой. Обобщите полученный результат на отражение точки относительно плоскости в трехмерном пространстве. 4.10. В разделе 4.8 было показано, что матрица поворота вокруг произвольной оси может быть получена в результате суперпозиции трех матриц поворотов вокруг коорди- координатных осей. Сколько способов подобного разложения существует? Всегда ли нуж- нужно использовать все три оси координат? 4.11. Добавьте в набор преобразований экземпляра преобразование скоса. Покажите, как использовать такой расширенный набор для формирования параллелепипеда из единичного куба. 4.12. Отыщите представление плоскости в однородных координатах. 4.13. Какой вид имеет матрица поворота, сформированная функцией glRotate(). Эле- Элементы матрицы должны зависеть от аргументов вызова glRotate(). 4.14. Напишите программу, которая будет формировать узор Серпинского следующим образом. Сначала строится белый треугольник. На каждом очередном шаге исполь- используются преобразования, формирующие три подобных треугольника поверх исход- исходного, которые вычерчиваются черным цветом. 4.15. Пусть в исходном состоянии имеется куб, центр которого совмещен с началом ко- координат, а ребра ориентированы вдоль осей системы координат. Какой вид имеет матрица поворота, которая придаст кубу симметричную ориентацию, как показано на рис. 4.59. 4.16. Для описания таких объектов, как трехмерные многоугольники, используются трехмерные вершины. По заданному множеству вершин определите, является ли определенный ими много- многоугольник плоским. 4.17. Три вершины определяют треугольник в том случае, если они не лежат на одной прямой (неколлинеарны). Разработайте тест коллинеарности трех точек. 4.18. Мы определили преобразование экземпляра как суперпозицию сдвига, поворота и масштабирования. Можно ли получить тот рца 4.59. Симмет- же самый объект, если применить преобразования этих типов в ричная ори- другом порядке? ентация куба 4.19. Напишите программу, которая позволит ориентировать куб по командам от одной кнопки мыши, сдвигать по командам от второй кнопки, а растя- растягивать-сжимать — по командам от третьей. 4.20. Заданы два непараллельных трехмерных вектора и и v. Как сформировать ортого- ортогональную систему координат, в которой вектор и будет одним из векторов базиса? 204 Глава 4. Объекты и геометрические преобразования
ГЛАВА 5 Визуализация В предыдущей главе мы рассмотрели одну часть модели синтезированной камеры — ме- методику описания трехмерных объектов и манипулирования ими. В этой главе будет рас- рассмотрена другая часть — способы описания виртуальной камеры. В процессе обсужде- обсуждения мы затронем и темы, тесно связанные с этими способами, в частности сопоставим клас- классические методы визуализации с теми, которые используются в компьютерной графике. Обсуждение условно делится на три части. Сначала мы обсудим, какие типы видов можно формировать в графической системе и почему недостаточно располагать видами только од- одного типа. Затем рассмотрим, какие средства предоставляет OpenGL в распоряжение при- прикладного программиста для формирования конкретного вида. Будет показано, что процесс визуализации распадается на две стадии. На первой стадии используется матрица вида, кото- которая задает преобразование между мировым фреймом и фреймом камеры. Напомним, что мо- модели объектов сцены представляются в мировом фрейме. Представление объектов во фрейме камеры позволяет на второй стадии использовать канонические процедуры визуализации. Вторая часть процесса имеет отношение к преобразованию проецирования. Прикладной про- программист, а значит, и пользователь могут выбирать тип проекции (параллельная или перспек- перспективная) и ту часть мирового пространства, которая преобразуется в изображение. По этим параметрам формируется матрица проецирования (или проективного преобразования), кото- которая объединяется с матрицей вида. Весь процесс визуализации будет проиллюстрирован на примере с использованием соответствующих функций OpenGL. Третья часть обсуждения в этой главе — вывод матриц проецирования для наиболее распространенных параллельных и перспективных видов. 5.1. Классическая и компьютерная визуализация Прежде чем рассматривать интерфейс между системой компьютерной графики и при- прикладной программой, необходимый для визуализации трехмерных объектов, совершим не- небольшой экскурс в область классических методов визуализации. Это нужно сделать по двум причинам. Во-первых, множество видов чертежных работ, выполнявшихся раньше вруч- вручную — в архитектурном проектировании, конструировании и мультипликации, — сейчас пе- перекладывается на системы компьютерной графики. При этом с помощью компьютерной сис- системы нужно воспроизводить привычные для специалистов виды — изометрические, в плане и
Объект Проецирующий луч\^ Картинная плоскость разного рода перспективные. Во-вторых, сравнение классических методов визуализации и тех, которые доступны системам компьютерной графики, позволит выяснить достоинства и недостатки подходов, использованных в конкретных графических API. При описании модели синтезированной камеры (см. главу 1) мы уже обращали ваше вни- внимание на сходство классических и компьютерных методов визуализации. Базовые элементы в обоих случаях одни и те же: объекты, которые мы "просматриваем", проецирующие лучи и картинная плоскость (плоскость проекции) (рис. 5.1). Про- Проецирующие лучи пересекаются в точке, которая на- называется центром проецирования (ЦП). Точка ЦП соответствует фокусу объектива или глаза и в сис- системе компьютерной графики выбирается в качестве центра фрейма камеры (camera frame). Описанная в главе 1 модель синтезированной камеры, которая, в свою очередь, базируется на геометрической опти- оптике, положена в основу всех стандартных графиче- графических систем. Но следует учитывать, что использо- использование в качестве поверхности, на которую проеци- проецируется изображение, плоскости, не является единственно возможным вариантом. Иногда изображение проецируется на цилиндрическую или сферическую поверхность, но в стандартных графических системах такие варианты не используются, по крайней мере в настоящее время. И в классической, и в компьютерной графике ЦП может быть расположен как угодно да- далеко от картинной плоскости и от объектов, что делает проецирующие лучи параллельными. В результате центр проецирования в модели заменяется направлением проецирования (DOP— direction of projection), как показано на рис. 5.2. Виды, которые создаются при рас- расходящихся проецирующих лучах, т.е. когда ЦП находится не слишком далеко от картинной плоскости, называются перспективными {perspective views), а при параллельных проецирую- проецирующих лучах — параллельными видами {parallel views). Объект Рис. 5.1. Визуализация Л Направление проецирования Проецирующий луч Картинная плоскость Рис 5.2. Удаление ЦП в бесконечность На ил. 3 и 4 цветной вклейки показаны соответственно параллельный и перспективный виды спроектированного здания, созданные с помощью САПР. Эти иллюстрации показыва- показывают, насколько важно иметь в составе графической системы средства для формирования видов обоих типов. В большинстве современных графических API такие средства имеются. Класс проекций, формируемых такими системами, называется плоскими геометрическими проек- проекциями {planar geometric projections), поскольку поверхность, на которую проецируется изо- изображение, является плоскостью, а линии проецирования — лучами. И перспективная, и па- параллельная проекции преобразуют прямую в прямую, но, в общем случае, не сохраняют углы 206 Глава 5. Визуализация
между прямыми. Хотя параллельные виды можно считать частным случаем перспективных, и в классической, и в компьютерной графике их рассматривают как отдельные классы видов. В классической (ручной) графике для построения параллельных и перспективных видов ис- используются совершенно различные методики, что известно всем, кто профессионально зани- занимался рисованием или техническим черчением. С точки зрения компьютерных графических систем они отличаются набором характеристических параметров. Кроме того, до последнего времени средства для формирования параллельных видов реализовать было значительно проще, чем средства построения перспективных изображений. В современных системах с конвейером, реализованным аппаратно, это различие практически исчезло, но при программ- программной реализации построение параллельных проекций все же требует меньших вычислитель- вычислительных затрат, чем построение перспективных. В классической графике, помимо рассмотренных параллельной и перспективной про- проекций, используется, хотя и значительно реже, и множество других способов проецирова- проецирования, начиная от классической системы трех ортогональных видов и заканчивая двух- и трехточечными перспективными видами. Такие виды позволяют передать отношения меж- между объектом, наблюдателем и картинной плоскостью, что противоречит принятому в ком- компьютерной графике подходу, ориентированному на независимость этих трех "субъектов" процесса визуализации. 5.1.1. Визуализация в классической графике Когда архитектор вычерчивает на листе ватмана вид проектируемого здания, он заранее планирует, какая сторона здания должна быть изображена, и соответственно "размещает" мнимого наблюдателя по отношению к зданию. Каждый классический вид определяется спе- специфическими отношениями между объектами и наблюдателем. В классической графике существует понятие главной грани {principal face). Чертежи ре- реальных объектов, например зданий в архитектуре или машин в технике, компонуются из ви- видов на картинные плоскости, параллельные главным граням. Для объектов, имеющих прямо- прямоугольную форму, выбор главных граней очевиден — это фасад, тыльная сторона, верхняя и нижняя грани и две боковые. На рис. 5.3 показаны основные типы видов, используемых в архитектурном черчении. Мы начнем их обсуждение с наиболее простых, а затем перейдем к тем, которые характеризуются меньшими ограничениями. Фронтальный Фронтальная косоугольная проекция Верхняя косоугольная проекция Изометрия Одноточечная перспектива Рис. 5.3. Виды в архитектурном черчении Трехточечная перспектива 5.7. Классическая и компьютерная визуализация 207
5.1.2. Ортографические проекции Первый классический вид — это ортогональная, или ортографическая проекция {orthographic projection), представленная на рис. 5.4. При построении всех ортогональных видов проецирующие лучи перпендикулярны картинной плоскости. В техническом и архи- архитектурном черчении часто на одном чертеже совмещают три ортогональные проекции — фронтальную, в плане и боковую, для каждой из которых картинная плоскость расположена параллельно соответствующей главной грани объекта (или, точнее, прямоугольной оболочки объекта) (рис. 5.5). Но использование только ортогональных проекций требует от наблюдате- наблюдателя определенного искусства, умения восстановить пространственную форму объекта по этим проекциям. В технике даже имеется для этого специальный термин — "умение читать черте- чертежи". Такие проекции обладают весьма важным свойством — они сохраняют как пропорции размеров элементов, так и углы между ними. Рис. 5.4. Ортографическая проекция Рис. 5.5. Здание и три его ортогональных вида 5.1.3. Аксонометрические проекции Если желательно иметь на чертеже изображение не одной главной грани, а двух-трех, нужно устранить одно из ограничений, характеризующих ортографические проекции. В ак- аксонометрических (axonometric) проекциях проецирующие лучи по-прежнему ортогональны картинной плоскости, как показано на рис. 5.6, но сама картинная плоскость может иметь любую ориентацию относительно объекта. Если картинная плоскость ориентирована сим- симметрично по отношению к трем главным граням, пересекающимся в одном углу прямоуголь- прямоугольного объекта, то образуется изометрическая (isometric) проекция. Если картинная плоскость ориентирована симметрично по отношению к двум главным граням, то образуется диметри- ческая (dimetric) проекция. Общий случай — триметрическая (trimetric) проекция. Все три вида аксонометрических проекций показаны на рис. 5.7. Обратите внимание на то, что на изометрическом виде длины отрезков меньше, чем на исходном объекте. В изометрической проекции коэффициент искажения (foreshortening) длин одинаков по всем трем главным осям, а потому такой вид можно использовать для измерения длин. В диметрической проек- проекции коэффициент искажения длин по двум осям одинаков, а по третьей отличается, т.е. мы имеем дело с парой коэффициентов. В триметрической проекции коэффициенты искажения по всем трем осям различны. В аксонометрических проекциях сохраняется параллельность прямых, но углы между ними искажаются. Окружность в аксонометрической проекции пре- 208 Глава 5. Визуализация
образуется в эллипс. Эти искажения — плата за возможность видеть на одной плоской про- проекции больше, чем одну главную грань объекта, при том что методы построения аксономет- аксонометрических проекций достаточно просты и давно освоены в техническом черчении. Аксоно- Аксонометрические проекции широко применяются в архитектурном планировании и техническом конструировании. Картинная плоскость' Картинная плоскость 6) Картинная плоскость Рис. 5.6. Аксонометрическое проецирование: а — формирование триметрической про- проекции; 6 — вид сверху; в — вид сбоку Диметрическая Триметрическая Рис. 5.7. Виды аксонометрических проекций Изометрическая 5.1.4. Косоугольные проекции Косоугольная (oblique) проекция является параллельной проекцией общего вида. При по- построении косоугольной проекции не накладываются никакие ограничения на угол между про- проецирующими лучами и картинной плоскостью (рис. 5.8). В косоугольной проекции сохраня- сохраняются углы между прямыми на объекте, расположенными в плоскости, параллельной картин- картинной. Окружность в такой плоскости также проецируется в окружность и при этом на изображении присутствует более чем одна главная грань объекта. Строить косоугольные про- проекции вручную — задача довольно сложная. Кроме того, они выглядят все-таки как-то нена- ненатурально. В большинстве физических систем формирования изображения — в фотоаппаратах или в глазах человека— плоскость объектива параллельна картинной плоскости. Хотя в та- таких приборах фактически формируется перспективная проекция, но при разглядывании дос- достаточно удаленных объектов получается почти параллельная проекция, но именно ортого- ортогонально параллельная, поскольку картинная плоскость параллельна плоскости объектива. Не- 5.1. Классическая и компьютерная визуализация 209
которые фотокамеры с растягивающимся мехом (их теперь можно увидеть только в музее или лавке антиквара) оснащены механизмом, который позволяет поворачивать объектив, со- сохраняя неизменной ориентацию фотопластинки. Это один из немногих физических приборов, в котором можно получить проекцию, близкую к параллельно косоугольной. Картинная плоскость ¦ Картинная плоскость' Картинная плоскость а) б) в) Рис. 5.8. Косоугольная проекция: а — формирование; б — вид сверху; в — вид сбоку С точки зрения прикладного программиста особой разницы между разными видами па- параллельных проекций нет. От него требуется только указать тип проекции — параллельная или перспективная — и набор параметров, специфицирующих положение и характеристи- характеристики камеры. Единственная проблема — как задать эти параметры, если требуется получить определенный "классический" вид или наилучшим образом представить определенный объект наблюдателю. 5.1.5. Визуализация с учетом перспективы Все перспективные виды характеризуются наличием перспективных сокращений {diminution). Чем дальше отстоит объект от наблюдателя, тем меньше размер его изображе- изображения. Это искажение размеров и придает перспективному изображению натуральность, но оно же не позволяет использовать такое изображение для оценки действительных размеров изо- изображенных объектов. Поэтому перспективные проекции применяются в основном в тех об- областях приложения компьютерной графики, где важна именно натуральность изображения с точки зрения наблюдателя, — в архитектуре и кинематографии. При построении перспективной проекции классическими методами предполагается, что наблюдатель расположен симметрично относительно картинной плоскости (точнее, сторон ограничивающего прямоугольника в картинной плоскости), как показано на рис. 5.9. Таким образом, пирамида, основанием которой является ограничивающий прямоугольник в картин- картинной плоскости, а вершиной — центр проецирования, является симметричной, или правильной пирамидой. Эта симметрия порождается геометрией конструкции большинства оптических приборов, в том числе и глаза человека. Но в некоторых фотокамерах, в частности фотокаме- фотокамерах с мехом, плоскость фотопластины можно поворачивать относительно плоскости объек- объектива. В результате можно формировать перспективную проекцию общего вида. Те модели построения перспективы, которые используются в компьютерной графике, распространяются и на такой случай. 210 Глава 5. Визуализация
В классической графике известны одно-, двух- и трехточечная перспективные проекции. Разница между этими тремя видами заключает- заключается в том, сколько направлений сохраняет па- параллельность при проецировании. Рассмотрим три разные перспективные проекции здания на рис. 5.10. К каждому углу здания подходят ли- линии, параллельные его главным осям. В наибо- наиболее общем случае — трехточечной перспектив- перспективной проекции — линии, направленные вдоль всех трех осей, которые на объекте параллель- параллельны, становятся на проекции непараллельными и сходятся в тех разных точках схода {vanishing points)(pnc. 5.10,a). При двухточечной перспек- перспективе линии, проходящие вдоль одной из главных осей, сохраняют параллельность и на изображе- изображении (рис. 5.10,6), а при одноточечной перспективе сохраняют параллельность линии, прохо- проходящие вдоль двух главных осей (рис. 5.10,в). В двухточечной перспективе имеются две точки схода, а в одноточечной — одна. С точки зрения прикладного программиста все описанные варианты являются частным случаем обобщенной перспективной проекции, математическое описание которой будет детально рассмотрено в разделе 5.3. Рис. 5.9. Перспективная проекция о) б) в) Рис. 5.10. Классические перспективные проекции: а— трех- трехточечная; б — двухточечная; в — одноточечная 5.2. Размещение камеры Теперь вернемся к трехмерной системе компьютерной графики. Рассмотрим, какими функциями располагает OpenGL для поддержки отображения трехмерных объектов. Мы так- также увидим, насколько эти средства отличаются от тех, которые имеются в других графиче- графических системах, — GKS-3D и PHIGS. В этом разделе речь пойдет о средствах моделирования положения камеры, а в разделе 5.4 — о проективных преобразованиях. В OpenGL матрицы вида и проецирования перемножаются, и формируется единая мат- матрица преобразования, которая применяется ко всем вершинам всех геометрических объек- объектов. Мы уже рассматривали использование матрицы вида для размещения объектов в про- пространстве. Другой способ ее применения — преобразование из опорного фрейма модели во фрейм камеры. 5.2. Размещение камеры 211
5.2.1. Настройка положения фрейма камеры Как было показано в главе 4, координаты вершин можно задавать в любых приемлемых единицах, а матрицу вида определять с помощью суперпозиции аффинных преобразований, которые задают изменение положения этих вершин. Операции формирования матрицы вида можно рассматривать и как изменение фрейма, а результат применения преобразования ви- вида— как задание положения вершин в другом фрейме. Другими словами, мы определяем объекты в некотором подходящем локальном фрейме, а затем с помощью матрицы вида пе- переводим их из локального фрейма в мировой или фрейм пользователя. В некоторых API этот процесс трактуется таким образом: объекты моделируются в собственной системе координат модели, а преобразование экземпляра переносит их в мировой фрейм. Объекты моделируются независимо от того, как они потом будут отображаться, а следо- следовательно, и независимо от положения камеры, через которую на них будет "смотреть" поль- пользователь. По умолчанию OpenGL размещает камеру в начале координат мирового фрейма, причем ось проецирования направляется в сторону, противоположную оси z мирового фрей- фрейма (рис. 5.11). Следовательно, если матрица вида единичная, то фрейм камеры и мировой фрейм совпадают. В большинстве приложений в ходе моделирования объекты размещаются в окрестности начала координат, а значит, камера при ее расположении по умолчанию не видит всех объектов сце- сцены. Таким образом, либо нужно сместить камеру так, чтобы в поле ее зрения попали все необходимые объекты, либо нужно "оттащить" объекты так, чтобы они оказались перед камерой. В программном смысле это эквивалентные операции. Можно считать, что сцена, сформированная в процессе моде- моделирования, имеет единичную матрицу вида, и задавать все верши- вершины с помощью функции glVertex(). Последующие изменения матрицы вида сместят мировой фрейм по отношению к камере и таким образом повлияют на то, как они, в конце концов, будут видны в камере, поскольку вершины заданы относительно преоб- преобразованного мирового фрейма. В терминах процесса выполнения программы матрицы проецирования и вида являются компонента- компонентами текущего состояния. При формировании примитивов система использует текущее состояние и именно его применяет к вершинам, заданным в программе. Рассмотрим последовательность операций, представленную на рис. 5.12. Исходная кон- конфигурация показана на рис. 5.12,а. Вершина, заданная набором координат р, имеет одинако- одинаковое представление в обоих фреймах. На рис. 5.12,6 показана измененная конфигурация фрей- фреймов — матрица вида в результате последовательности преобразований получила значение С. Теперь два фрейма не совпадают, а матрица С содержит информацию о переходе от фрейма камеры к мировому фрейму, или, что то же самое, содержит информацию о том, как камера была сдвинута из исходной позиции, совпадавшей с началом координат мирового фрейма. Вершина, которая была сформирована в q с помощью glVertex(), после изменения матрицы вида будет по-прежнему занимать позицию q в мировом фрейме. Однако ее позиция во фрейме камеры теперь будет Cq, и это будет известно исполняющей системе OpenGL. OpenGL преобразует координаты позиций в систему координат камеры в процессе выполне- выполнения операций конвейера визуализации. Можно и по-иному трактовать этот процесс: камера по-прежнему остается в начале координат собственного фрейма, а матрица вида применяется ко всем примитивам, описанным в данной системе. Вы можете использовать ту трактовку, которая для вас более понятна. Главное — внимательно следить за тем, каково состояние матрицы вида в момент формирования примитивов в программе. Рис. 5.11. Исходное по- положение камеры 212 Глава 5. Визуализация
Z-Zc x, xc a) 6) Рис. 5.12. Взаимное перемещение фрейма камеры и мирового фрейма: а — исходная конфигурация; б — конфигурация после изменения матрицы вида В любой текущий момент состояние матрицы вида задает отношение между фреймом ка- камеры и мировым фреймом. Хотя объединение в одной матрице преобразований моделирова- моделирования и формирования вида поначалу может сбивать с толку, но после внимательного анализа оказывается, что такой подход довольно эффективен. Если считать камеру объектом с опре- определенными геометрическими свойствами, то преобразования, которые изменяют положение и ориентацию каких-то объектов, будут также изменять и положение, и ориентацию камеры относительно этих объектов. После знакомства с концепцией взаимного размещения фреймов невольно возникает во- вопрос, а как все это реализовано в OpenGL. В дальнейшем для определенности будем считать, что камера перемещается относительно мирового фрейма. В общих чертах мы рассмотрим три подхода: один — в этом разделе, а два других — в разделе 5.2.2. Еще два подхода я пред- предлагаю читателям проанализировать самостоятельно при выполнении упр. 5.2 и 5.3. Сначала зададим положение камеры косвенно посредством последовательности пово- поворотов и сдвигов в матрице вида. Этот подход является, по сути, реализацией преобразова- преобразований экземпляра, описанных в главе 4. При этом нужно обратить особое внимание на два обстоятельства. Во-первых, как правило, в программе стремятся определиться с положени- положением камеры еще до того, как будут созданы отображаемые объекты1. Во-вторых, примене- применение преобразований по отношению к камере может привести к эффекту, противоположно- противоположному ожидаемому. Рассмотрим объект, центр которого находится в начале координат. Камера находится в начальном положении — в начале координат, а ее оптическая ось направлена вдоль отрица- отрицательной полуоси г. Пусть нам требуется "посмотреть" на ту грань объекта, которая видна со стороны положительной полуоси л:. Следовательно, нужно отодвинуть камеру от начала ко- координат. Если при этом не изменять ориентацию камеры, то смещать ее нужно вдоль поло- положительной полуоси г. Соответствующее преобразование реализуется функцией ' В графических программах анимации используется другой прием — положение камеры часто меняется в зависимости от творческих планов режиссера. Камера то фиксируется и наблюдает за динамически из- изменяющейся сценой, то "закрепляется" на движущемся объекте (см. упр. 5.3). 5.2. Размещение камеры 213
glTranslatef@.0, 0.0, -d); где d — положительное число. Для многих удобнее интерпретировать эту операцию как сдвиг фрейма камеры относи- относительно мирового фрейма. Такая точка зрения базируется на классической графике. В компью- компьютерной графике распространен другой подход — считается, что объект установлен в фикси- фиксированном фрейме, а наблюдатель "бегает" вокруг него в поисках подходящего ракурса.В классической графике наблюдатель занимает доминирующую позицию. Концептуально про- процесс визуализации выполняется таким образом: мы берем объект, ориентируем его так, как считаем нужным, и переносим в нужное место. Одно из следствий классического подхода со- состоит в том, что расстояние измеряется от наблюдателя до объекта, а не от объекта до на- наблюдателя, как это делается в большинстве физических приборов. В результате классический подход приводит к левостороннему фрейму камеры. Во многих графических системах, в том числе и OpenGL, следуют этому подходу, моделируя в правосторонней системе координат, а визуализируя — в левосторонней. Это решение, хотя и корректное с технической точки зре- зрения, кажется весьма странным для большинства пользователей. В OpenGL расстояния, харак- характеризующие параллелепипед видимости (те, которые используются в качестве аргументов вызова glOrtho()), отсчитываются от камеры, что эквивалентно использованию левосто- левостороннего фрейма камеры. Теперь рассмотрим, как посмотреть на этот объект со стороны положительной полуоси х. Для этого потребуется не только отодвинуться от объекта, но и повернуть камеру вокруг оси у, как показано на рис. 5.13. Сдвиг нужно выполнить после поворота на 90° вокруг оси у. В тексте программы вызовы соответствующих функций должны идти в обратном порядке, как на том акцентировалось ваше внимание в главе 4. Поэтому соответствующий фрагмент про- программы будет выглядеть следующим образом: glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef@.0, 0.0, -d); glRotatef(-90.0, 0.0, 1.0, 0.0); В терминах двух фреймов мировой фрейм сначала поворачивается относительно фрейма камеры, а затем фреймы отодвигаются друг от друга. В некоторых примерах в главах 2 и 4 была использована устанавливаемая по умолчанию матрица проецирования, под- подробнее о ней мы поговорим в разделе 5.3. Сейчас же восполь- воспользуемся тем, что по умолчанию она формирует ортогональную проекцию, причем камера размещается в начале координат мирового фрейма, а ее ось проецирования направляется в сто- сторону, противоположную оси z мирового фрейма. В примере с вращением куба (см. главу 4) мы поворачивали куб, чтобы увидеть нужную грань. Но как уже отмечалось, тот же эффект дает и поворот фрейма куба относительно фрейма камеры (такое же изображение можно получить, поворачивая камеру Рис. 5.13. Позиционирование относительно куба). камеры Рассмотрим, как можно сформировать изометрический вид куба. Предположим, что в исходном положении центр ку- куба находится в начале координат, а его ребра ориентированы вдоль осей координат. Так как камера оказывается при этом внутри куба, нам нужно отодвинуть ее с помощью преобразова- преобразования сдвига. Изометрический вид получится в том случае, если камера будет установлена симметрично трем граням, пересекающимся в некоторой вершине куба. Поэтому сначала нужно соответствующим образом повернуть куб, а потом отодвинуть от него камеру. 214 Глава 5. Визуализация
Хотя нам еще предстоит отодвинуть камеру, предположим, что мы смотрим на куб отку- откуда-то со стороны положительной полуоси z. Можно будет получить один из восьми изомет- изометрических видов (соответствующих восьми вершинам куба), повернув его сначала вокруг оси у, пока не увидим симметричные изображения двух граней (рис. 5.14,а). Очевидно, что для этого нужно повернуть куб на 45°. Второй поворот— вокруг оси х. Будем поворачивать куб вниз, пока не получим требуемый изометрический вид. Для этого потребуется повернуть его вокруг оси х на -35.26°. Вычислить значение второго угла можно следующим образом. Рассмотрим, какое положение занял куб после первого поворота. Поскольку мы смотрим на него со стороны положительной полуоси г, то куб будет выглядеть, как на рис. 5.14,а. Вершина (-1, 1, 1) теперь займет положение @, 1, V2 ). Если теперь посмотреть на куб со стороны оси х, как на рис. 5.14,6, то очевидно, что нужно поворачивать его до тех пор, пока правая верхняя вершина не совместится с осью у. Прямоугольный треугольник, который со- содержит этот угол, имеет катеты 1 и \2 , что соответствует углу 35.26°. Поворот должен быть выполнен по часовой стрелке, а значит, нужно взять этот угол со знаком "минус". Последний шаг — отодвинуть камеру от начала координат. Следовательно, наша стратегия — сначала повернуть фрейм камеры относительно фрейма объекта, а затем разделить два фрейма. Соот- Соответствующая матрица вида определяется следующим образом: М = TIMV 35.26е а) б) Рис. 5.14. Куб после поворота вокруг оси у: а — вид со стороны положи- положительной полуоси г; б — вид со стороны положительной полуоси х Элементы этой матрицы вычисляются суперпозицией матриц элементарных преобразова- преобразований в однородных координатах. Перемножение матриц поворотов дает следующее: 1 0 0 л/б/3 0 л/3/3 0 0 л/2/2 л/б/6 -л/3/3 0 0 -S/ л/б/: 0 0 л/б/3 -Уз/з 0 0" 3 0 5 0 1 л/2/2 -ч/б/6 л/3/3 0 л/2/2 0 -л/2/2 0 о" 0 0 1 0 1 0 0 л/2/2 0 л/2/2 0 0 0 0 1 5.2. Размещение камеры 215
Несложно проверить, что исходная вершина (-1, 1,1) корректно преобразуется этой мат- матрицей в (О, О, V3 ). Теперь добавим к повороту преобразование сдвига на @, 0, -d). Получим: TR = V2/2 О V2/2 О л/б/6 V6/3 -Тб/6 О -л/3/3 л/з/3 VJ/3 -d 0 0 0 1 Фрагмент программы, в котором формируется эта матрица вида, имеет следующий вид: glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef@.0, 0.0, -d); glRotatef(-35.26, 1.0, 0.0, 0.0); glRotatefD5.0, 0.0, 1.0, 0.0); Обратите внимание на то, что функция glOrtho () устанавливает параллелепипед видимо- видимости относительно фрейма камеры. Таким образом, при ортогональном проецировании сдвиг камеры не влияет на размер изображения, но определяет, попадет ли объект "в кадр" или нет. 5.2.2. Задание ориентации камеры Я подозреваю, что предыдущий пример с вычислением параметров преобразования ви- вида, необходимых для получения изометрии объекта, вас, мягко говоря, озадачил. Неужели придется вручную вычислять эти параметры, а затем задавать их в программе? Должен вас успокоить — продемонстрированный подход далеко не единственный. Ниже мы рассмот- рассмотрим другой подход, который реализован в PHIGS и GKS-3D. Как и раньше, начнем с миро- мирового фрейма. Опишем в нем положение и ориентацию камеры. Тип проецирования — па- параллельное или перспективное — определяется отдельно. Эта процедура эквивалентна спецификации матрицы проецирования в OpenGL. Первую часть процесса визуализации часто называют нормализацией {normalization transformation). Как и раньше, в исходном состоянии камера находится в начале координат мирового фрейма, и ее оптическая ось на- направлена вдоль отрицательной полуоси z мирового фрейма. Требуется установить камеру таким образом, чтобы она находилась в точке, которую будем называть точкой привязки вида — ТПВ {view-reference point) (рис. 5.15). Положение ТПВ задается в мировом фрейме. Для установки камеры в эту точку в прикладной программе вызывается функция set_view_reference_point(x, у, z); Далее нужно задать ориентацию камеры. Эту задачу можно разделить на две подзадачи: задание ориентации картинной плоскости посредством вектора нормали к картинной плоско- плоскости {view-plane normal) и задание вектора вертикали вида {view-up vector). Вектор нормали к картинной плоскости (п на рис. 5.15) задает ориентацию картинной плоскости или "фотопластинки" в камере. Ориентация любой плоскости все- всегда определяется вектором нормали к ней, соответствующая функция входит в состав API: set_view_plane_normal(nx, ny, nz); ТПВ Рис. 5.15. Фрейм камеры Но для однозначного определения ориентации камеры недостаточно знать ориентацию кар- картинной плоскости, поскольку остается еще одна степень свободы — поворот камеры вокруг 216 Глава 5. Визуализация
нормали к картинной плоскости. Поэтому потребуется определить еще и положение вектора вертикали вида (w на рис. 5.15), что зафиксирует однозначную ориентацию камеры. Это вы- выполняется вызовом еще одной функции API: set_view_up(w_x, w_y, w_z); Проекция вектора вертикали вида w на картинную плоскость дает вектор вертикали картинной плоскости v (рис. 5.16). Таким образом, в качестве вектора вертикали вида w можно задать любой вектор, непарал- непараллельный п, что избавляет пользователя от необходимости вычислять век- вектор, лежащий обязательно в картинной плоскости. Вектор v ортогонален вектору п. Третий вектор, и, образующий с первыми двумя правосторон- правостороннюю тройку, есть векторное произведение v x п. Полученную таким об- образом новую систему координат принято называть либо системой коор- координат вида (vieiving-coordinate system), либо u-v-n системой. Вместе с ТПВ эта тройка векторов образует фрейм камеры. Матрица, которая вы- выполняет соответствующее преобразование фрейма, называется матрицей ориентации вида (view-orientation matrix). Эта матрица формируется в результате суперпозиции матриц поворо- поворота и сдвига в однородных координатах. Начнем с определения точки привязки вида Рис. 5.16. Опреде- Определение вектора вертикали картинной плоскости Р = У нормали к картинной плоскости п_ п = п. О и вектора вертикали вида w = О Начало координат нового фрейма устанавливается в точке привязки вида, вектор нормали к картинной плоскости задает направление одной координатной оси, а две другие задаются векторами и и v. По умолчанию считаем, что тройке осей х, у, z соответствуют и, v, п. Такой выбор используется и в установленной по умолчанию матрице вида в OpenGL. Перенос нача- начала фрейма в точку привязки вида выполняется посредством матрицы сдвига 1(-х, -у, -z). Да- Далее выполняется поворот, и матрица вида V образуется как произведение V = TR. Вектор v должен быть ортогонален вектору п, следовательно, п • v = 0. 5.2. Размещение камеры 217
На рис. 5.16 видно, что v является проекцией w на картинную плоскость, заданную своей нормалью п. Следовательно, v есть линейная комбинация п и w: v = an + Cw. Если пренебречь длинами векторов, то можно положить C=1 и получить W П а = — п п wn V = W П . п п Теперь можно определить третий вектор правой тройки как векторное произведение: U = V X П. В общем случае эти векторы не являются единичными, поэтому их нужно нормализовать и получить три единичных вектора (орта) осей координат фрейма камеры u1, v1 и п1. Матрица поворота м = их и' ¦ 0 vx V V 0 п> п п 0 ' 0 ' 0 ' 0 1 позволяет получить ориентацию вектора, заданного в системе u1, v', n1, по отношению к осям координат исходной системы. Но нам нужно решать обратную задачу — получить представ- представление вектора, заданного в исходной системе координат,, в системе u1, v1, n1. Поэтому нужно обратить матрицу М, но поскольку это матрица "чистого" поворота, то обращение можно за- заменить транспонированием: R = M М' Последняя операция — умножение на матрицу сдвига Т: = RT = ux uv и. -xux-yuv-zuz v» v.' v' -xv'x -yv'y -zv'2 n'x n'v n'i -xn'x -yn[ -zn'2 0 0 0 1 Обратите внимание на то, что в данном случае матрица сдвига стоит в произведении справа, в то время как раньше мы умножали ее слева. Интерпретировать это отличие можно следую- следующим образом: в первом случае мы сначала поворачивали фрейм, а затем отодвигали его в на- направлении, которое было представлено в координатах фрейма камеры. В данном же случае позиция камеры (а следовательно, и требуемое ее смещение из исходного положения) пред- представлена в координатах мирового фрейма. Можно и по-другому разобраться в этих отличиях. Обратите внимание на то, что матрицы RT и TR имеют аналогичную структуру. Ориентирующие части обоих произведений мат- матриц — верхние левые подматрицы 3x3 — идентичны, а три первых элемента четвертого столбца отличаются по той простой причине, что компоненты смещения выражаются в них в разных фреймах. В произведении TR — это компоненты в неповернутом фрейме, а в произ- 218 Глава 5. Визуализация
ведении RT — в повернутом. Для примера с получением изометрического вида, который был рассмотрен в предыдущем разделе, имеем: 'О' 1 О -1 1 1 О w = Положение камеры следует выбрать на диагонали исходного фрейма, т.е. d d 1 В результате получим ту же матрицу вида, что и в разделе 5.2.1. Описанный выше метод задания положения камеры является только одним из многих. В некоторых ситуациях удобнее задавать ориентацию камеры косвенно с помощью точки визи- визирования на рассматриваемом объекте. Рассмотрим компоновку камеры и объекта, представ- представленную на рис. 5.17. Камера находится в точке е, которая задана в координатах мирового фрейма, а ее оптическая ось направлена таким образом, что проходит через вторую точку а, которая называется точкой визирования {at point). Этими двумя точками определяется вектор нормали к картинной плоскости: п = е-а. После этого остается только определиться с направлением вектора вертикали картинной плоскости, который, как и раньше, можно задать вектором вертикали вида. В составе OpenGL имеется функция gluLookAt(), которая позволяет изменить матрицу вида в соответствии с заданной точкой визирования и заданным вектором вертикали вида: gluLookAt(eyex, eyey, eyez, atx, aty, atx, upx, upy, upz); Аргументы функции имеют следующий смысл: ¦ eyex, eyey, eyez — компоненты точки привязки вида; ¦ atx, aty, atx — компоненты точки визирования; ¦ upx, upy, upz—компоненты точки, ко- которая задает вектор вертикали вида. В приложениях, имеющих дело с различными тренажерами, ни один из описанных способов задания положения камеры не подходит. В симу- ляторе полета пилот управляет программой ото- отображения, задавая три угла ориентации вообра- воображаемого летательного аппарата: крена {roll), тангажа {pitch) и рыскания (yaw). Эти углы за- задаются относительно осей системы координат аппарата, центр которой находится в его центре масс (рис. 5.18). Следовательно, и изображение, (<*, (ирх, up upz) которое видит пилот, должно формироваться в Рис, 5.17. Задание положения камеры с по- зависимости от этих углов и расстояния между мощью точки визирования 5.2. Размещение камеры 219
аппаратом и объектами. Исходя из этого, матрица вида может формироваться из матрицы сдвига и трех канонических матриц поворота вокруг осей координат (см. упр. 5.2). Крен Тангаж Рис. 5.18. Углы ориентации летательного аппарата Рыскание В некоторых других приложениях ориентацию камеры удобно описывать в полярных, а не прямоугольных координатах. В частности, это приходится делать в приложениях, где одни объекты вращаются вокруг других. Рассмотрим, например, каким образом задается положение светила на небосводе. Направ- Направление взгляда наблюдателя на светило задается углами возвышения {elevation) и азимута (azimuth) (рис. 5.19). Угол возвышения отсчи- тывается от горизонтальной плоскости. Азимут— это угол, кото- который отсчитывается от координатной оси, лежащей в горизонталь- горизонтальной плоскости, до проекции линии визирования на эту плоскость. При таком способе задания ориентации камеры, ось проекции ко- которой совмещается с линией визирования, остается еще одна сте- степень свободы — возможность поворачивать камеру вокруг оси проекции, задавая угол кручения (twist angle). Возвышение Азимут Рис. 5.19. Возвышение и азимут 5.3. Проецирование Те, кто увлекаются фотографией, знают, что после установки камеры нужно выбрать под- подходящий объектив или настроить фокусное расстояние трансфокатора. Как было показано в главе 1, зона захвата камеры зависит от размеров пленки и характеристик объектива. В ком- компьютерной графике имеет место аналогичная ситуация — какие объекты будут включены в изображение, зависит от выбора типа проецирования и параметров визуализации. В фотографии широкоугольный объектив дает наиболее ощутимую перспективу — объекты, расположенные близко к камере, имеют на отпечатке гораздо большие размеры, чем объекты, несколько отстоящие от камеры. И наоборот, длиннофокусные объективы (их часто называют телеобъективами) скрадывают перспективу, уравнивая размеры объектов. Говорят, что такое изображение "плоское", приближающееся по характеру к тому, что создается при параллельном проецировании. В большинстве графических API параллельное и перспективное проецирования рассматриваются как два разных типа, причем каждому из них соответствует свой набор функ- функций обработки. Такой же подход реализован и в OpenGL, хотя и для того, и для другого исполь- используется тот же конвейер обработки, как мы увидим в разделах 5.8 и 5.9. При работе с OpenGL матрица проецирования может быть загружена с помощью функции glLoadMatrix(), как и матрица вида. Для большинства стандартных условий визуализации в составе OpenGL имеются соответствующие функции. Но сначала мы рассмотрим математи- математический аппарат описания проективных преобразований на основе однородных координат. Этот аппарат позволяет описать практически любое проективное преобразование с помощью матриц размера 4x4. 220 Глава 5. Визуализация
цп 5.3.1. Перспективные проекции Будем считать, что камера находится в начале координат и ее ось направлена вдоль от- отрицательной полуоси z. На рис. 5.20 показаны два варианта компоновки объектива и кар- картинной плоскости камеры. На рис. 5.20,а картинная плоскость ортогональна оптической оси объектива. Этот вариант характерен для боль- большинства реальных оптических приборов, в том чис- числе и для глаза человека. На рис. 5.20,6 показан бо- более общий случай, когда ориентация картинной плоскости не совпадает с оптической осью объек- объектива. Мы детально рассмотрим первый вариант, по- поскольку он проще. Однако вывод соотношений для общего случая выполняется по той же методике, и мы предоставляем читателям сделать его самостоя- самостоятельно (см. упр. 5.6). Как было сказано в главе 2, плоскость проек- проекции можно поместить перед центром проецирова- проецирования. Если картинная плоскость перпендикулярна оси проекции, то сформируются виды, схематически представленные на рис. 5.21. Точ- Точка, имеющая в мировом пространстве координаты (х, у, :), проецируется в точку (хр, у,„ zp). Все проецирующие лучи сходятся в начале координат, и, поскольку картинная плоскость перпендикулярна оси г, zp = d. Так как камера "смотрит" в направлении отрицательной по- полуоси г. картинная плоскость находится в отрицательном полупространстве г < 0 и значе- значение d также отрицательно. Рис 5.20. Две камеры (x,z) (х, у, z) (x.-d) а) б) в) Рис. 5.21. Перспективная проекция: а — вид в пространстве; б — вид сверху; в — вид сбоку На рис. 5.21,6 показаны два подобных треугольника, для которых справедливы соот- соотношения Xх х — = —- и л: = . z d p z/d Анализируя вид сбоку, получим аналогичный результат для ур: v =^- ' z/d' Эти уравнения нелинейны. Деление на г реализует перспективное сокращение — чем дальше отстоит объект от центра проецирования, тем меньше размер его изображения. Процесс проецирования можно рассматривать как преобразование точки {х, у, z) в другую точку (хр, ур, zp). Это перспективное преобразование {perspective transformation) 5.3. Проецирование 221
сохраняет линейность, но не является аффинным. Кроме того, оно необратимо. По- Поскольку все точки, расположенные на проецирующем луче, преобразуются в одну и ту же точку, восстановить их исходное положение нельзя2. Для работы с проективными преобразованиями нам придется слегка модифицировать используемый аппарат одно- однородных координат. При описании однородных координат мы представляли трехмерную точку (х, у, г) в виде четырехмерной (дг, у, z, 1). Вместо этого будем представлять точку (х, у, z) в таком виде: Р = WX wy WZ W Если w * 0, то трехмерное представление точки можно однозначно восстановить из четырех- четырехмерного, разделив три первых компонента на четвертый w. В этой более общей форме одно- однородных координат трехмерные точки отображаются в прямые четырехмерного пространства. Преобразования по-прежнему выражаются матрицами размера 4x4, но теперь элементы по- последней строки можно изменять, и, таким образом, преобразование распространяется и на четвертый компонент w. Естественно, мы будем стараться работать си' = 1, поскольку это позволяет избежать до- дополнительных операций деления при восстановлении трехмерных координат. Однако, изме- изменяя w, можно представить в матричном виде более широкий класс преобразований, в том числе и перспективное. Рассмотрим матрицу м = 1 0 0 ( ) 0 10 0 0 0 10 0 0 \/d 0 Матрица М преобразует точку Р = X У z 1 в точку q = X У Z z/d На первый взгляд в q мало что изменилось по сравнению с р, но не нужно забывать, что при переходе в трехмерное пространство первые три компонента необходимо разделить на четвертый. Следовательно, получится результат z/d Ур z/d Приведенные уравнения описывают простое перспективное преобразование. В однородных координатах деление компонентов точки q на четвертый компонент w заменяет ее эквива- эквивалентной точкой: 2В разделах 5.7 и 5.8 будет рассказано об обратимом варианте проецирующего преобразования, кото- который реализован в OpenGL. 222 Глава 5. Визуализация
X z/d У z/d d 1 V Ур zp i Итак, вы убедились, что, по крайней мере, простые перспективные преобразования можно реализовать в матричной форме, определив соответствующую 4х4-матрицу. Это преобразо- преобразование выполняется после преобразования вида, заданного матрицей вида. Но в конце процес- процесса нужно выполнить перспективное деление (perspective division), которое можно также реа- реализовать в виде одной из стадий конвейерного процесса обработки (рис. 5.22). Вид I ^ „ I ^ Перспективное! ^ I >Проецирование! > Рделение > Рис. 5.22. Конвейерная организация визуализации 5.3.2. Ортогональная проекция *. У. Ортогональная проекция — это частный случай параллельной проекции, при которой проецирующие лучи ортогональны картинной плоскости. Можно с определенной точностью считать, что ортогональная проекция получается в камере, имеющей объектив с очень большим фокусным расстоянием, причем картинная плоскость камеры ортогональна оптической оси ее объектива. Однако уравнения ортогонального проециро- проецирования можно вывести и непосредственно, не прибегая к "пере- "переносу" в бесконечность центра проецирования математической модели. На рис. 5.23 показана ортогональная проекция на кар- картинную плоскость z = 0. При проецировании точки на эту плос- плоскость сохраняются значения ее компонентов д: и у, а уравнения проецирования будут иметь вид хР=х, ур=у, zp=0. Рис. 5.23. Ортогональное проецирование Запишем результат в виде матрицы преобразования однородных координат: 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 X У Z 1 1 При выполнении ортогонального проективного преобразования отпадает необходимость в делении, хотя для обоих видов проективного преобразования можно использовать одну и ту же аппаратную реализацию конвейерной обработки. Результаты, полученные для простых случаев, можно расширить на перспективное и па- параллельное проецирование общего вида, дополнив их последовательностью преобразований сдвига и поворота. Но сначала рассмотрим, как реализуется описанный математический ап- аппарат в OpenGL. 5.3. Проецирование 223
5.4. Проективные преобразования в OpenGL Рассматривая в разделе 5.3 процесс проецирования, мы не принимали во внимание харак- характеристики камеры — фокусное расстояние объектива и размеры пленки (окна на картинной плоскости). На рис. 5.24 показан геометрический смыслило зрения {angle of view) для каме- камеры-обскуры, которую мы рассматривали в главе 1. На изображении появляются только те объекты, которые попали внутрь угла зрения. Если окно на картинной плоскости имеет вид прямоугольника, то на изобра- изображении появятся только те объекты, которые окажут- окажутся внутри бесконечной пирамиды с вершиной в цен- центре проецирования. Эта пирамида ограничивает зону видимости (view volume). Объекты, не попавшие в эту зону, отсекаются (clipped) и не включаются в отображаемую сцену. Таким образом, приведенное выше математическое описание процесса визуализа- визуализации является неполным, поскольку мы не включили в него отсечение. В большинстве графических API параметры отсе- отсечения определяются вместе с параметрами проеци- проецирования. В системах компьютерной графики зона ви- видимости ограничивается не только с боков, но и вдоль оси проецирования — так называемыми передней и задней отсекающими плоскостями (рис. 5.25). В результате зона приобретает вид усеченной пирамиды видимости (frustum). В ней есть только один жестко зафиксированный параметр — центр проецирования, который находится в начале координат фрейма камеры. С помощью остальных шести параметров можно, в принципе, добиться любой ориентации и формы пирамиды, но такая гибкость ис- используется в очень редких случаях. Ниже мы рассмотрим, как задаются параметры проециро- проецирования и пирамиды видимости в OpenGL. В других графических системах набор функций мо- может отличаться, но сохраняются практически те же ограничения. Пирамида видимости Рис. 5.24. Геометрический смысл угла зрения Задняя _ отсекающая Передняя плоскость Картинная ^екающая плоскость плоскость •цп" Рис. 5.25. Передняя и задняя отсекающие плоскости 5.4.1. Перспективные преобразования в OpenGL В составе OpenGL имеются две функции для задания перспективных проекций и одна — для задания параллельных проекций. Но, кроме того, можно формировать матрицы проек- проективного преобразования и поэлементно или использовать преобразования поворота, сдвига и 224 Глава 5. Визуализация
масштабирования исходной единичной матрицы. Параметры пирамиды видимости задаются функцией, смысл аргументов которой поясняет рис. 5.26: glFrustum(xmin, xmax, ymin, ymax, near, far) Значения аргументов near и far, задающих положения передней и задней отсекающих плос- плоскостей, должны быть положительными и отсчитываться от центра проецирования вдоль оси проецирования. Плоскости отсечения параллельны плоскости г = 0 фрейма камеры. Обратите внимание на то, что поскольку камера "смотрит" в сторону отрицательной полуоси г, то пе- передняя отсекающая плоскость описывается уравнением z --^ rmm = ¦-/„, где /„ — значение аргу- аргумента near, а задняя (дальняя — far) отсекающая плоскость описывается уравнением ~ ~=та\ = -1/, гДе h— значение аргумента far3. Поскольку матрица проецирования, опреде- определенная таким набором параметров, умножается на текущую матрицу, сначала нужно задать режим работы с этой матрицей. Типичная последовательность операций представлена ниже: glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(xmin, xmax, ymin, ymax, near, far) Рис. 5.26. Параметры пирамиды видимости Совсем не обязательно, чтобы параметры, задающие левую (xmin), правую (xmax), верхнюю (ymax) и нижнюю (ymin) грани пирамиды были симметричны относительно оси г. Точно так- также не требуется, чтобы формируемая в результате пирамида была симметричной (правильной) усеченной пирамидой. В разделе 5.8 будет показано, как в этом случае из мат- матрицы простого перспективного проецирования, выведенной в разделе 5.3, выводится матрица проективного преобразования. Во многих приложениях предпочтительнее задавать не линейные параметры, характери- характеризующие положение углов усеченной пирамиды видимости, а угол или поле зрения. Однако если окно картинной плоскости является прямоугольником, а не квадратом, то нужно зада- задавать пару углов зрения: один— в вертикальной плоскости, а другой— в горизонтальной (рис. 5.27). Угол в вертикальной плоскости fovy измеряется между плоскостями верхней и нижней граней пирамиды, а угол в горизонтальной плоскости — между правой и левой гра- гранями. Эти параметры задаются в OpenGL функцией gluPerspective(fovy, aspect, near, far). Аргументы этой функции имеют следующий геометрический смысл: ¦ fovy — угол зрения в вертикальной плоскости; ¦ aspect — отношение ширины окна картинной плоскости к его высоте; 3Измерение расстояний от камеры эквивалентно переходу от правосторонней системы координат ми- мирового фрейма, в котором задаются объекты сцены, к левосторонней системе координат фрейма камеры 5.4. Проективные преобразования в OpenGL 225
near и far — расстояния от центра проецирования до передней и задней отсекающих плоскостей. Параметры near и far задаются исходя из тех же соображений, что и при вызове функции glFrustum(). Сформированная по этим параметрам матрица заменяет текущую, поэтому перед вызо- вызовом функции следует выбрать режим работы с мат- матрицами (вызвать glMatrixMode(GL PROJECTION)) и загрузить сначала единичную матрицу, а уже потом вызывать gluPerspective(). Рис. 5.27. Задание пирамиды видимости с помощью углов зрения 5.4.2. Параллельное проецирование в OpenGL В составе OpenGL имеется только одна функция для задания параметров параллельного проецирования, которая формирует ортогональную проекцию: glOrtho(xmin, xmax, ymin, ymax, near, far) Аргументы вызова этой функции имеют тот же геометрический смысл, что и одноимен- одноименные аргументы функции glFrustum(). Зона видимости при этом превращается в параллеле- параллелепипед видимости (рис. 5.28.). Как и в случае перспективной проекции, передняя отсекающая плоскость описывается уравнением г = zmin = -/,„ где /„ — значение аргумента near, а задняя отсекающая плоскость — уравнением z = rmax = -//, где //— значение аргумента far. X V —Z I mox' 'max' max' Параллелепипед видимости Рис. 5.28. Параметры ортогональной проекции При формировании перспективной проекции расстояния до передней и задней плоскостей отсечения должны были быть положительными, поскольку все проецирующие лучи должны были пересекаться в центре проецирования. Поэтому объекты, расположенные "за" центром проецирования, будут изображаться "вверх ногами". Точки на плоскости z = 0 вообще не мо- могут быть спроецированы, поскольку это приведет к делению на нуль. При параллельном про- проецировании все эти проблемы отпадают, а потому ограничение на знаки расстояний до плос- плоскостей отсечения при вызове функции glOrtho() снимаются. Остается единственное ограни- ограничение —far > near. 226 Глава 5. Визуализация
5.5. Удаление невидимых поверхностей Теперь можно вернуться к программе вращения куба, которую мы рассматривали в главе 4, и добавить в нее перспективную визуализацию и перемещение камеры. Но сначала выясним, как вы- выполняется удаление невидимых поверхностей, — этот процесс мы использовали в первой версии программы без объяснения. Когда мы смотрим на объект, в данном случае куб, грани которого не- непрозрачны, то видим только три ближайшие к нам грани. Принимая во внимание используемую модель визуализации, это можно объяснить тем, что проецирующие лучи, исходящие из центра проецирования, блокируются этими гранями и не могут достичь точек на других гранях. Но в системе компьютерной графики описания всех граней, т.е. все вершины модели, "проходят" через единую систему конвейерной обработки, следовательно, графическая сис- система каким-то образом должна определить, какие же грани, в конце концов, выводить на эк- экран. Таким образом, нужно изобрести алгоритм, который либо удалил бы из процесса обра- обработки те грани, которые закрываются другими, — алгоритм удаления невидимых поверхно- поверхностей (hidden-surface—removal algorithm), либо выделил те грани, которые видимы,— алгоритм выделения видимых поверхностей (visible-surface algorithm). Существует множест- множество методов решения этой проблемы, некоторые из которых будут подробно рассмотрены в главе 7. В OpenGL используется один из них — алгоритм z-буфера (z-buffer algorithm). Для обращения к этому алгоритму в OpenGL требуется вызвать три функции. Ниже мы кратко опишем суть этого алгоритма, а к его подробному анализу вернемся в главе 7. Все алгоритмы удаления невидимых поверхностей можно разделить на два больших клас- класса. В алгоритмах объектного пространства (object-space algorithms) сначала выполняется упорядочение поверхностей объектов сцены. Объекты сортируются таким образом, что более дальние стоят в списке раньше тех, что расположены ближе к наблюдателю. Если затем вы- выводить на экран поверхности в таком порядке, то в результате сформируется корректное изображе- изображение сцены. Применительно к нашему кубу процесс формирования изображения будет выглядеть так: сначала на экран выводятся изображения граней, ориентированных прочь от наблюдателя, а затем их перекрывают изображения граней, смотрящих на наблюдателя. В итоге на экране появляется корректное изображение куба4. Алгоритмы пространства изображения (image-space algorithms) включаются в процесс проективного прес^тювания и анализируют от- отношения между всеми точками объектов сцены, лежащими на одном и том же луче проецирова- проецирования. Алгоритм z-буфера относится именно к этому классу и очень хорошо сочетается с кон- конвейерной архитектурой формирования изобра- изображения в большинстве графических систем. Основная идея этого алгоритма поясняется на рис. 5.29. Проецирующий луч, исходящий из центра проецирования ЦП, пересекает две поверхности. Если в процессе растрового преобразо- преобразования многоугольников мы будем отслеживать расстояние от ЦП до ближайшего многоуголь- многоугольника вдоль каждого луча проецирования, то сможем обновлять эту информацию по мере проек- проективного преобразования всех многоугольников, описывающих объекты сцены. В изображение от каждого луча проецирования включается только точка многоугольника, ближайшего к ЦП. Картинная плоскость - ЦП* Рис. 5.29. Алгоритм z-буфера 4Если речь идет о выпуклых объектах, таких как куб, то грани, ориентированные прочь от наблюдате- наблюдателя, можно просто исключить из дальнейшего процесса. Частные случаи будут рассмотрены в главе 7. 5.5. Удаление невидимых поверхностей 227
Основное достоинство этого алгоритма заключается в том, что его сложность в худшем случае пропорциональна количеству многоугольников и что для его реализации требуется очень мало вычислительных операций в дополнение к тем, которые в любом случае требуется выполнять при формировании изображения. Для реализации алгоритма в системе должен быть организован буфер глубины (или z-буфер), в котором по мере выполнения обработки очередных многоугольников сохраняется информация о расстоянии между ЦП и точками на объекте. В некоторых графических системах такой буфер организуется в основной памяти компьютера, а в некоторых добавляется специальный модуль памяти, с которым работает ап- паратно реализованный конвейер обработки. От прикладного программиста при работе с этим алгоритмом требуется выполнить только две операции: инициализировать буфер глубины и разрешить режим удаления невидимых по- поверхностей. В OpenGL это выполняется следующим образом: glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glEnable(GL_DEPTH_TEST); В этом фрагменте для инициализации используется функция из библиотеки GLUT, в ко- которой флаг работы с буфером глубины добавляется к уже известным вам флагам работы с RGB-цветом и флагом двойной буферизации. Перед формированием нового изображения программист может очистить буфер глубины, вызвав функцию glClear(GL_DEPTH_BUFFER_BIT); 5.6. Путешествие с камерой по сцене В программе, рассмотренной в главе 4, куб вращался относительно начала координат. Ис- Использовалось ортогональное проецирование, и нам не нужно было думать о том, как разме- размещается камера, — положение и ориентация, устанавливаемые системой по умолчанию, нас вполне устраивали. В новой версии программы будет использовано перспективное проецирование, и мы по- позволим наблюдателю "ходить" с камерой по сцене, управляя его движением с помощью кла- клавиш <х>, <Х>, <у>, <Y>, <z> и <Z>. Для задания положения и ориентации камеры восполь- воспользуемся функцией gluLookAt(). Для того чтобы разработанная раньше программа соответствовала новой спецификации, в нее придется внести минимум изменений. Определим массив viewer[3], в котором будем хранить информацию о положении камеры. Содержимое этого массива будет изменяться программой обработки событий клавиатуры keys(): void keys(unsigned char key, int x, int y) { if(key == 'x') viewer[0]-= 1.0; if(key == 'X') viewer[0]+= 1.0; if(key == 'y') viewer[l]-= 1.0; if(key == 'Y') viewer[l]+= 1.0; if(key == 'z') viewer[2]-= 1.0; if(key == 'Z') viewer[2]+= 1.0; display(); } В функции отображения display() вызывается функция LookAt(), которая использует массив viewer для задания параметров камеры, а в качестве точки визирования используется начало коор- координат. Тот фрагмент программы, который организует вращение куба, остается в новой версии без 228 Глава 5. Визуализация
изменений. Управление вращением по-прежнему реализуется с помощью мыши. Обратите внима- внимание на очередность вызовов функций, изменяющих матрицу вида в функции display (): void display(void) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); gluLookAt(viewer[0],viewer[I],viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[l], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); colorcube(); glFlush(); glutSwapBuffers(); } Функция glFrustum() вызывается из функции перерисовки myReshape () и задает положение и ориентацию камеры: void myReshape(int w, int h) { glViewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if(w<=h) glFrustum(-2.0, 2.0, -2.0 *(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w, 2.0, 20.0); else glFrustum(-2.0, 2.0, -2.0*(GLfloat)w/(GLfloat)h, 2.0* (GLfloat)w/(GLfloat)h, 2.0, 20.0); glMatrixMode(GL_MODELVIEW); } Обращаю ваше внимание на то, что аргументы функции glFrustun() задаются с помо- помощью соотношения сторон окна приложения w и h. Теперь остается только добавить в про- программу main() регистрацию функции с обратным вызовом для обработки событий клавиату- клавиатуры keys(), и новая версия программы будет готова. Полный текст программы читатель най- найдет в приложении А. После запуска программы на выполнение вы, несомненно, отметите про себя эффект камеры, движущейся под "чутким руководством" пользователя, более близкое к фотографическому перспективное изображение куба и эффект отсечения, который вносится заданием передней и задней плоскостей отсечения. Обратите внимание на то, что как бы вы ни "ходили" по сцене, куб всегда оказывается в центре кадра. Существует множество способов организовать движение камеры в пространстве сцены с помощью различных устройств ввода. Если в программе мышь не используется для управле- управления объектами сцены (в нашем примере — вращением куба), то, манипулируя кнопками мы- мыши, можно выполнять, как говорят кинематографисты, "наезд" камеры, поворачивать ее вправо или влево. Для "привязки" камеры обычно используется функция gluLookAt(), но этот эффект можно организовать и другими способами. Один из них — воспользоваться мат- матрицами поворота и сдвига и с их помощью менять в цикле матрицу вида. Если в процессе пе- перемещения камеры не нужно держать в поле зрения определенный объект, этот способ может 5.6. Путешествие с камерой по сцене 229
оказаться более предпочтительным. Можно также организовать в программе специальную переменную для хранения положения камеры и изменять ее по командам пользователя. В та- таком случае матрицу вида нужно будет вычислять в каждом цикле, начиная с единичной, а не накапливать приращения. В конце концов, способ, который выбирает программист, зависит от специфики приложения. Иногда приходится обращать внимание на эффект накопления ошибки в вычислении матрицы вида при длительном цикле работы с приращениями. 5.7. Матрицы параллельного проецирования Те матрицы проективного преобразования, которые формируются и используются в OpenGL, не так просты, как рассмотренные в разделе 5.3. В этом и следующем разделах мы подробно рассмотрим особенности формирования матриц проективного преобразования в OpenGL. Поскольку моделирование проецирования является ключевой проблемой в трехмер- трехмерной компьютерной графике, без глубокого понимания используемых при этом математиче- математических методов нельзя ни написать хорошую прикладную программу, ни разработать новую графическую систему. Более того, хотя функции работы с проективными преобразованиями, которые имеются в OpenGL, и дают вполне удовлетворительные результаты в большинстве ситуаций, возникающих в практике разработки графических приложений, некоторые виды проекций, в частности косоугольные, непосредственно в OpenGL не поддерживаются. Если в приложении нужно организовать именно такую проекцию, приходится прибегать к косвен- косвенным методам, а для этого необходимо глубокое понимание механизмов работы системы. Ни- Ниже будет показано, как это знание может быть использовано на практике. 5.7.1. Нормализация проецирования Используемый в OpenGL подход базируется на методике нормализации проецирования {projection normalization), которая предусматривает сведение всех типов проекций к ортогональ- ортогональной, для чего выполняется предварительное искажение исходных объектов (рис. 5.30). Поскольку такое искажение может бьпъ представлено в виде преобразования в однородных координатах, можно не выполнять фактическое искажение формы объектов, а добавить соответствующие мат- матрицы преобразований к матрице обычного ортогонального проецирования и получить матрицу требуемого проективного преобразования, как показано схематически на рис. 5.31. I [ к тРД1 7 4 а) б) Рис 5.30. Предварительное искажение объекта: а — перспективная проекция; б — ортогональ- ортогональная проекция искаженного объекта 230 Глава 5. Визуализация
Предыскажение 1 fc Ортогональное I fc (нормализация) I проецирование I Рис 5.31. Нормализация преобразования 5.7*2. Матрицы ортогонального проективного преобразования Хотя параллельная проекция является частным случаем перспективной проекции, мы начнем с параллельной ортогональной проекции, а затем распространим методику нормали- нормализации на перспективную. Выше было показано, что проективное преобразование отображает точки трехмерного пространства на точки картинной плоскости и что такое преобразование является вырожденным (т.е. определитель матрицы преобразования равен 0). Все точки, рас- расположенные на одном проецирующем луче, преобразуются в единственную точку на картин- картинной плоскости. Анализ процесса проецирования разделим на две части. Сначала с помощью невырож- невырожденного преобразования в однородных координатах преобразуем заданную зону видимости в стандартную. Будем использовать преобразование, которое выполняет эту операцию по от- отношению ко всем объектам, комбинируя матрицу преобразования с матрицей вида. Объекты искажаются таким образом, что после выполнения на втором этапе проективного преобразо- преобразования выбранного типа (мы сейчас выбрали параллельное ортогональное проективное преоб- преобразование) по отношению к этим объектам и зоне видимости получим х,,=х, ур=у, :,, = 0. Обратите внимание на то, что, по сути, ортогональное проективное преобразование сводится только к приравниванию компонента г к 0 или его простому игнорированию, поскольку при отображении он нам просто не нужен. Таким образом, вся вычислительная нагрузка перено- переносится на первую стадию процесса. Разделение процесса проецирования на две стадии в зна- значительной мере связано с выполнением других задач в ходе конвейерной обработки. В част- частности, в главе 7 будет показано, что отсечение должно выполняться при работе с трехмерным представлением объектов и что использование невырожденных матриц преобразования по- позволяет сохранять информацию о глубине расположения объектов вдоль проецирующего лу- луча, которая необходима для удаления невидимых поверхностей и закрашивания (глава 6). На первой стадии процесса формируется матрица проецирования {projection matrix) (в боль- большинстве графических систем, в том числе и в OpenGL, она называется именно так). В OpenGL также выполняется разделение между координатами экрана (screen coordinates), которые являются двухмерными и не содержат информации о глубине, и координатами окна (window coordinates), которые являются трехмерными и сохраняют информацию о глубине. Матрица проецирования и последующее перспективное деление преобразуют вершины в координаты окна. При ортогональном проецировании простейшей зоной видимости является куб, центр ко- которого находится в начале координат, а грани образуются плоскостямих = ±\,у = ±\,z = ±\. В OpenGL такой куб видимости устанавливается системой по умолчанию. Для его прину- принудительной установки нужно включить в программу такую последовательность операторов: glMatrixMode(GLJPROJECTION); glLoadldentityO; glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); В дальнейшем этот куб будем называть канонической зоной видимости (canonical view vol- volume). Два последних аргумента в вызове glOrtho() — это расстояния до передней и задней отсекающих плоскостей, измеренные от позиции камеры в направлении отрицательной полу- 5.7. Матрицы параллельного проецирования 231
оси z. Передняя отсекающая плоскость г = 1.0 находится за камерой, задняя отсекающая плоскость г = -1.0 — перед камерой. Хотя проецирующие лучи параллельны и ортогональная проекция по своим свойствам аналогична использованию телеобъектива с очень большим фокусным расстоянием, задание расстояний до передней и задней отсекающих плоскостей позволяет селектировать отображаемые объекты сцены. Предположим, что при вызове функции glOrtho() заданы произвольные значения аргументов: glOrtho(xmin, xmax, ymax, ymin, lnear, lfar); Такой набор аргументов задает правильный параллелепипед видимости, правая сторона которого, если смотреть от камеры, — это плоскость х = дгП1ах, левая сторона — плоскость х - xmin, верхняя грань— плоскость^ ~-= утах, а нижняя грань— плоскость у = ут\п- Передняя и задняя отсекающие плоскости — это z = -/„ и: = -lf. Матрица проецирования, которая уста- устанавливается при этом в OpenGL, преобразует этот параллелепипед в куб, центр которого рас- расположен в начале координат, а ребра имеют длину 2 единицы (рис. 5.32). На этом рисунке -max — это взятое с обратным знаком значение аргумента lfar, a zmm — взятое с обратным знаком значение аргумента lnear вызова функции glOrtho(). Полученная таким способом матрица преобразует координаты вершин, описывающих объекты, которые были заданы при вызове glVertex(), в вершины внутри канонической зо- зоны видимости, для чего выполняются преобразования масштабирования и сдвига. В резуль- результате те вершины, которые находились внутри заданного параллелепипеда видимости, преоб- преобразуются в вершины, находящиеся внутри канонической зоны видимости, а те вершины, ко- которые были снаружи от заданного параллелепипеда видимости, окажутся и снаружи канонической зоны видимости. Итак, мы видим, что матрица проецирования определяется заданным типом проекции и параметрами зоны видимости, переданными в качестве аргумен- аргументов функции glOrtho(), причем они специфицируются относительно камеры. Положение и ориентация камеры задаются матрицей вида. Эти две матрицы — вида и проецирования — перемножаются, и координаты объектов умножаются справа на результирующую матрицу. IX V Т. 1 * max' 'max' min' Рис. 5.32. Соответствие между заданным па- параллелепипедом видимости и канонической зоной видимости Теперь, используя наши познания в аффинных преобразованиях, выведем уравнение мат- матрицы проецирования, формируемой в OpenGL. Нам предстоит для этого решить две задачи. Сначала с помощью преобразования сдвига передвинем центр заданного параллелепипеда в центр канонической зоны видимости — начало координат. Затем изменим масштабные ко- коэффициенты таким образом, чтобы каждое ребро параллелепипеда имело длину 2 единицы (рис. 5.33). Следовательно, имеем два преобразования: SB/(xmax - *min), 2/(ymax -ymin), 2/(zmax - zmin)), 232 Глава 5. Визуализация
после суперпозиции которых сформируется матрица проецирования: = ST = 2 X-nux — Xnun 0 n и 0 л и 2 — v пах п и 0 п и 0 2 "" niax ~ nun 0 X X У _ г шх + Л"пип lux ¦ nun n.,x + >'nun — v ш\ У nun 1:14 "" nun z 1 Сдвиг Масштабирование г Рис. 5.33. Аффинные преобразования в процессе нор- нормализации Поскольку ось проецирования камеры направлена вдоль отрицательной полуоси г, проеци- проецирующие лучи направлены из бесконечности отрицательного полупространства в начало коорди- координат. В терминах расстояния до передней и задней отсекающих плоскостей — параметрах lnear и If аг вызова функции giOrtho() — матрица проецирования примет следующий вид: 2 Р = А"|Ш\ ~ -Vmu п и 0 0 \i 2 0 0 Л'тш /, Л У) 2 -/„ 0 х|Шч л-пих + хтп1 - х1шп + .v1IUIl - >'„„,, + I, -, 5.7.3. Косоугольная проекция Использование функции glOrtho() позволяет создавать в OpenGL ограниченный класс параллельных проекций — только такие, в которых проецирующие лучи ортогональны картинной плоскости. Но во многих приложениях желатель- желательно иметь и косоугольные проекции5. Можно, конечно же, са- самостоятельно сформировать соответствующую матрицу по- поэлементно, но попробуем воспользоваться той же идеей, ко- которую разработчики OpenGL применили при формировании матрицы ортогонального проецирования, — найдем преобра- преобразование желаемой проекции в каноническую проекцию пред- предварительно искаженных объектов. Косоугольная проекция характеризуется углом между проецирующими лучами и картинной плоскостью (рис. 5.34). В графической системе, которая поддерживает параллельное проецирование в общем виде, зона видимости для косоуголь- Рис. 5.34. Косоугольная проек- проекция точки 'Обращаю ваше внимание на то, что без косоугольного проецирования не удалось бы вычерчивать оси координат в рисунках этой книги (см. упр. 5.15). 5.7. Матрицы параллельного проецирования 233
ной проекции имеет переднюю и заднюю грани, параллельные картинной плоскости, а боко- боковые— верхнюю и нижнюю, параллельные проецирующим лучам (рис. 5.35). Анализируя вид сверху и сбоку на рис. 5.36, выведем уравнения косоугольной проекции. Проекция характеризуется углами в и ф. В техническом черчении широко использу- используются частные виды косоугольных проекций — кавальерная и кабинетная, — которые характеризуются конкретными значениями этих углов. Однако эти углы не являются единственно возможным набором параметров, характеризующих косоугольную проек- проекцию (см. упр. 5.9 и 5.10). Задняя отсекающая плоскость Передняя отсекающая плоскость Картинная плоскость Направление проецирования Рис. 5.35. Зона видимости при косоугольном проецировании U у) а) б) Рис. 5.36. Косоугольное проецирование: а — вид сверху; б — вид сбоку Из анализа вида сверху очевидно следует, что хр находится из соотношения tge=—*--. х-хр Следовательно,хр = х-rctg0. Аналогично получим и соотношениеур= у-^ctgcj). Используя уравнение картинной плоскости zp = 0, запишем результат в виде матрицы преобразования однородных координат: 234 Глава 5. Визуализация
р = 1 0 -ctgG О О 1 -ctg<|) О ОООО 0 0 0 1 Следуя принятой стратегии, представим матрицу Р в виде произведения: = Мог1Н@,ф) = 1 0 0 0 0 1 0 0 0 0 0 0 0] 0 0 1 1 0 -ctg9 О О 1 -ctg0 0 0 0 10 0 0 0 1 где НF, ф) является матрицей преобразования скоса. Таким образом, косоугольное проециро- проецирование можно представить как первоначальный скос объектов посредством Н@, ф), а затем выполнение обычного ортогонального проецирования. На рис. 5.37 показано, как матрица Н@, ф) преобразует объект (куб) внутри зоны видимости. Боковые грани зоны видимости становятся перпендикулярными картинной плоскости, а боковые грани куба—наклонными по отношению к ней, поскольку на оба объекта воздействует то же самое преобразование скоса. Следовательно, ортогональная проекция искаженного куба будет выглядеть точно так же, как и косоугольная проекция неискаженного. Рис. 5.37. Эффект от преобразования скоса Но мы еще не закончили, поскольку созданная после скоса зона видимости еще не являет- является канонической. Теперь воспользуемся преобразованиями масштабирования и сдвига, выве- выведенными в предыдущем разделе: ST = 2 0 0 0 0 2 У max У min 0 0 0 0 2 *" max ~ min 0 x + x x — x Л чих Л min ynm +>•„„„ v — v s max • min *" nu\ ~ min max min 1 5.7. Матрицы параллельного проецирования 235
Матрицу этого преобразования нужно вставить между матрицами скоса и конечного ортого- ортогонального преобразования: Р = MortSTH. Значения лтах, xmin, _утах, >min — это параметры правильного параллелепипеда видимости, который получится после преобразования скоса. Эти значения должна вычислить прикладная программа, взяв за основу исходную зону видимости — косоугольную призму. 5.8. Матрицы перспективного проецирования Принцип, положенный в основу реализации перспективного проецирования в OpenGL, тот же, что и в организации параллельного, — формируется преобразование, которое приво- приводит к искажению объектов, такому, что после их проецирования стандартным способом соз- создается нужное изображение. Для вывода вида такого преобразования сначала нужно решить, какую форму в случае перспективного проецирования должна иметь каноническая зона ви- видимости. Затем следует сформировать преобразование перспективной нормализации (perspective-normalization transformation), которое преобразует перспективную проекцию в ортогональную. Последний этап — вывод матрицы перспективного проецирования, которая и используется в OpenGL. 5.8.1. Перспективная нормализация В разделе 5.3 была выведена матрица простого перспективного преобразования. Если уравнение картинной плоскости г = -1, а центр проецирования размещен в начале координат, то эта матрица имеет вид М = Но для формирования изображения нам нужно знать параметры зоны видимости. Пред- Предположим, что выбран угол зрения 90° и, таким образом, боковые грани зоны видимости на- наклонены по отношению к картинной плоскости на 45°. Следовательно, зона видимости имеет вид пирамиды бесконечной в одном направлении, грани которой — плоскости х = ±z, у = ±г (рис. 5.38). 1 0 0 0 0 1 0 0 0 0 1 _ I 0 0 0 0 (-1.-1.-1) Рис. 5.38. Простая перспективная проекция Эту зону можно ограничить, задав переднюю и заднюю отсекающие плоскости z = rmin и - = "та« где оба значения отрицательны, причем гтах > rmin. 236 Глава 5. Визуализация
Рассмотрим матрицу 0 0 0 0 10 0 0 0 а 3 0 0-10 которая аналогична М, но не является вырожденной. Пока что не будем рассматривать, како- каковы должны быть значения аиC, — главное, что они должны быть отличны от 0. Если при- применить преобразование N к точке в однородных координатах р = [ху z \]', то получим новую точку q -- [х' у' г' II']7, где х' = х, у' = у, z' = схг + Р, м'1 -¦ -~. После деления на м' получим координаты точки в трехмерном пространстве Если применить по отношению к N преобразование ортогонального проецирования вдоль оси z, получим в результате матрицу М которая и является матрицей простого перспективного преобразования. Проекция произволь- произвольной точки р имеет вид 1 0 0 0 0 1 0 0 0 0 0 -1 0 0 0 0 ' = MonNp = После выполнения перспективного деления получим искомые значения л/; и у,,: х v -*¦„ = --, >',=-7- Итак, мы показали, что если применить к произвольной точке преобразование N, то ее орто- ортогональная проекция будет иметь такой же вид, как и перспективная проекция исходной точ- точки. Здесь есть аналогия с применением преобразования скоса на первом этапе приведения ко- косоугольной проекции к ортогональной. Матрица N является невырожденной и преобразует исходную зону видимости в новую. Параметры а и Р можно выбрать таким образом, чтобы новая зона имела вид канонической. Рассмотрим уравнения ограничивающих плоскостей х = ±z. Эти плоскости преобразуются посредством х" = -х/z в лг" = ±1. Аналогичным образом плоскости y--±z преобразуются в у" - ±1. Передняя отсекающая плоскость зоны видимости г = rmin преобразуется в плоскость 5.8. Матрицы перспективного проецирования 237
И, наконец, задняя отсекающая плоскость зоны видимости г = zmax преобразуется в плоскость Если выбрать значения этих параметров в соответствии с соотношениями а = — + 7 1- ~ ^'min Я _ ^-•mux'"min 7 — 7 «-max *тап то отображением плоскости г = zmm будет плоскость г" = -1, плоскости z - гтах — плоскость :" ~ 1, т.е. мы получим искомую каноническую зону видимости. На рис. 5.39 показано, как выглядит это преобразование и искажение куба внутри зоны видимости. Таким образом, N трансформирует усеченную пирамиду видимости в правильный параллелепипед, а ортого- ортогональное преобразование трансформирует параллелепипед в канонический куб видимости. В результате ортогональная проекция искаженного объекта будет иметь тот же вид, что и пер- перспективная проекция исходного. Матрицу N называют матрицей перспективной нормализа- нормализации {perspective-normalization matrix). Отображение г =- сс + - является нелинейным, но сохраняет порядок расположения точек по глубине. Так, если Z\ и z2 — значения глубины расположения двух точек до преобразования и Г| > г2, то после преоб- преобразования выполняется соотношение г", >г. Следовательно, алгоритм удаления невидимых поверхностей, основанный на анализе глу- глубины, будет успешно работать и после выполнения перспективной нормализации, хотя нели- нелинейность и может привести к некоторым проблемам с точностью вычислений, так как буфер глубины имеет ограниченную разрядность (обычно — 24 или 32 бита). ЦП Рис. 5.39. Перспективная нормализация зоны видимости Итак, мы показали, как с помощью предварительного искажения можно свести и перспек- перспективное, и параллельное проективные преобразования к ортогональному. Такая замена позво- позволяет включить любой тип проективного преобразования в стандартный процесс конвейерной обработки. Все детали реализации конвейера мы рассмотрим в главе 7, где будет показано, что возможность привести зону видимости любого типа к параллелепипеду также упрощает и выполнение отсечения, и удаление невидимых поверхностей. 5.8.2. Перспективное преобразование в OpenGL При использовании функции glFrustum() зона видимости не обязательно должна иметь форму симметричной (правильной) усеченной пирамиды. Геометрический смысл аргументов вызова этой функции поясняет рис. 5.40. Матрица перспективного преобразования OpenGL формируется таким образом, что сначала заданная усеченная пирамида трансформируется в симметричную с углом на- 238 Глава 5. Визуализация
клона боковых граней 45° (см. рис. 5.38). Этот процесс аналогичен трансформации косоугольной призмы видимости в ортогональный параллелепипед. Первый этап трансформации — преобра- преобразование скоса, превращающее асимметричную пирамиду в симметричную. Угол скоса должен быть таким, чтобы точка ((х^„ + *таху2, (у,™ + у^пу2, z^) заняла положение @,0, z^n)- Таким образом, матрица скоса определяется в соответствии с соотношением Н@,ф) = Н arcctg - Larcctg Полученная в результате усеченная пирамида види- видимости имеет следующие плоскости граней: } Z = -1 -\ Z •7 X ,у max max min 2-^.х max •min 2Zn»s > у г \ 'max' min' Puc. 5.40. Перспективное преобразова- преобразование в OpenGL Следующий этап — масштабирование боковых граней пирамиды таким образом, чтобы вы- выполнялись соотношения X = ±2, у = ±Г. При этом положение передней и задней отсекающих плоскостей не изменяется. Такое преоб- преобразование выполняется матрицей масштабирования SBrmax/(.xmax - дгтш), 22таДутах-.ут1п), 1). Обратите внимание на то, что матрица масштабирования не зависит от положения задней от- отсекающей плоскости - = rmin, поскольку в трехмерном пространстве аффинное преобразова- преобразование однозначно определяется соответствием положения четырех точек. В данном случае та- таковыми являются четыре вершины, в которых боковые грани пирамиды пересекаются с пе- передней отсекающей плоскостью. Для того чтобы после выполнения нормализации проецирования задняя плоскость отсе- отсечения стала плоскостью г = -1, а передняя— плоскостью г= 1, матрица нормализации N должна иметь вид 10 0 0 0 10 0 0 0 а р 0 0-10 причем значения а и Р выбираются в соответствии с результатами, полученными в разде- разделе 5.8.1. С учетом всего сказанного, получим вид матрицы проективного преобразования: 2г Р = NSH = х — х max nun 0 0 0 2 .'max "min .'min 0 0 .'max .'max *max + ymn +yc nun -1 0 2max- 0 < min -Zmin 5.5. Матрицы перспективного проецирования 239
В терминах расстояний по передней (/„) и задней (lf) отсекающих плоскостей получим матри- матрицу Р в виде = NSH = о 2-.... X — У л inav л nun /,-/„ о о 2/,/„ lf-ln о (*/, У/, 5.9. Проецирование и формирование теней Одна из интересных задач, при решении которой используются матрицы проективного пре- преобразования, — формирование теней в изображении. Хотя в структурном смысле тени и не яв- являются геометрическими объектами, без них невозможно представить себе, как передать в плоском изображении трехмерную форму объектов и отношения между ними в пространстве сцены. Появляются тени только в том случае, если имеется хотя бы один источник света. Неко- Некоторая точка на объекте оказывается в тени, если на нее не попадает свет ни от одного из имею- имеющихся источников света. Но если источник находится в центре проецирования, то мы не увидим теней, поскольку все точки, находящиеся в тени, закрыты видимыми поверхностями объектов. Такая модель освещения называется моделью '"плоской засветки" ("flashlight in the eye" model), и именно ее мы фактически использовали до сих пор, не акцентируя на этом ваше внимание. Для того чтобы сформировать в изображении тени (или, как сказал бы художник, нало- наложить тени), нужно представлять себе физику процесса взаимодействия света и материалов реальных объектов. Об этом мы подробно поговорим в главе 6. А в этом разделе только покажем, насколько сложна подобная задача из-за объема вычислений, ко- которые при отображении динамических объектов долж- должны выполняться в реальном масштабе времени. Но как бы там ни было, без формирования теней нельзя обойтись при воссоздании в компьютере изо- изображения, близкого к реальному, а потому в тех систе- системах, где требуется еще и высокая динамика изображе- изображения, приходится идти на различные хитрости. Рас- Рассмотрим, как образуется тень при использовании единственного точечного источника света (рис. 5.41). Для простоты предположим, что тень падает на пло- плоскую поверхность у = 0. При этом на поверхности об- образуется многоугольник тени (shadow polygon), кото- который является ничем иным, как проекцией объекта- многоугольника на заданную поверхность, причем центр проецирования располагается в той точке, где находится источник света. Таким образом, если удаст- удастся сформировать проекцию исходного объекта- многоугольника во фрейме, начало координат которо- которого совпадает с источником света, то получим вершины многоугольника тени. Далее нужно сформировать представление полученных вершин в мировой системе координат. Эту задачу Рис 5.41. Тень от отдельного много- многоугольника 240 Глава 5. Визуализация
можно решать в прикладной программе, но гораздо рациональнее подобрать подходящую матрицу проективного преобразования и передать дальнейшие заботы о формировании мно- многоугольника тени графической системе, в данном случае —OpenGL. Предположим, что ис- источник света находится в точке (х/, у,, zj), как показано на рис. 5.41. Если с помощью матрицы сдвига T(-xt, -yt, -zj) изменить положение объектов на рисунке таким образом, чтобы источник света оказался в центре координат (рис. 5.42,6), то получим обычную перспективную проекцию с центром проецирования в начале координат. Матрица проективного преобразования при этом имеет вид М = 1 0 0 0 0 1 0 1 1 0 0 1 0 0 0 0 0 -у, Последний этап — сдвинуть все обратно с помощью матрицы сдвига Т(х/, yh zj). Суперпо- Суперпозиция двух сдвигов и проективного преобразования проецирует вершину (х, у, z) в точку (Хр. у р. =Р), где х — х. УР=0, -/> */ (у-у,)/ у, а) 6) Рис. 5.42. Проекция многоугольника тени: а — при взгляде от источника света; б — источник сдвинут в начало координат 5.9. Проецирование и формирование теней 241
Для формирования такого многоугольника в программе OpenGL можно изменить матрицу вида: GLfloat m[16]; /* Матрица проективного преобразования тени */ for(i=0;i<15;i++) m[i]=0.0; m[0]=m[5]=m[10]=1.0; m[7]= -1.0/yl; glColor3fv(polygon_color) glBegin(GL_POLYGON} /* Вычертить многоугольник как обычно. */ glEnd() glMatrixMode(GL_MODELVIEW); glPushMatrix() /* сохранить состояние */ glTranslatef(xl,yl,zl); /* сдвинуть */ glMultMatrixf(m); /* проекция */ glTranslate(-xl,-yl,-zl); /* перенести источник света в начало координат */ glColor3fv(shadow_color); glBegin(GL_POLYGON); /* Снова вычертить многоугольник. */ glEnd(); glPopMatrix(); /* восстановить состояние */ Обратите внимание на то, что, хотя выполняется проективное преобразование относи- относительно точки положения источника света, матрица, которую мы при этом используем, явля- является "системной" матрицей вида. Тот же самый многоугольник вычерчивается дважды: пер- первый раз — как обычно, а второй раз — с использованием измененной матрицы вида, которая преобразует вершины многоугольника. И для исходного многоугольника, и для его тени ис- используются одни и те же параметры визуализации. Результат вычисления многоугольника те- тени для изображения цветового куба показан на ил. 5 цветной вклейки, а программу формиро- формирования тени вы найдете в файле cubes.с. В простых программах, например в программе, отображающей тень, которую отбрасыва- отбрасывает на землю летящий самолет, описанный прием работает достаточно эффективно. Его мож- можно использовать и при работе не с точечным, а с удаленным (параллельным) источником све- света (см. упр. 5.17). Но если объект отбрасывает тень на поверхность другого объекта, то такая методика не годится. В главе 9 будет описан другой, более общий метод формирования те- теней, который, правда, требует гораздо более сложных вычислений. 5.10. Резюме Итак, подведем первые итоги. Закончив изучение материала этой главы, вы уже способны самостоятельно полностью разработать нетривиальную программу формирования изображе- изображений трехмерных объектов. Возможно, самое полезное, что следует сейчас предпринять, — это попробовать написать одну-две такие программы. Работая над программой и отлаживая 242 Глава 5. Визуализация
ее, вы приобретете определенные практические навыки работы с матрицами вида и проек- проективного преобразования, "прочувствуете", как они влияют на формируемое изображение. В этой главе вы познакомились с математическим описанием стандартных проективных преобразований. Хотя большинство графических API избавляет прикладного программиста от забот, связанных с созданием собственных средств выполнения проективных преобразова- преобразований, знание основ этого процесса помогает понять, как в графической системе работает кон- конвейер обработки информации, базирующийся на операциях с 4х4-матрицами. В современных графических программах организация проективных преобразований и подготовка всех необ- необходимых исходных данных для них возлагается на прикладную программу. В оставшейся части этой книги мы рассмотрим три большие темы. Во-первых, мы про- проанализируем возможности моделирования сложных объектов из примитивов. В главе 8 бу- будет представлена методика иерархического моделирования сложных объектов. В главе 10 будут рассмотрены криволинейные графические объекты и методы их аппроксимации пло- плоскими объектами. Такая аппроксимация позволяет использовать для отображения криво- криволинейных объектов уже знакомые вам конвейерные средства. В главе 11 будет описан объ- объектно-ориентированный подход к моделированию. Этот подход позволяет моделировать реальные объекты с той степенью детализации, которая диктуется спецификой конкретно- конкретного приложения, включать в модель физические законы, лежащие в основе тех или иных моделируемых явлений. Будет показано, как в результате такого подхода удается визуали- визуализировать реальные физические процессы, которые невозможно описать, ограничиваясь только многоугольниками. Вторая большая тема — формирование изображения, близкого к реалистическому, т.е. не уступающему по качеству тому, которое формируется современными приборами оптического наблюдения и регистрации. В главе 6 мы рассмотрим физику взаимодействия света с мате- материалом поверхности реальных объектов; алгоритмы удаления невидимых поверхностей; мо- модели закрашивания объектов с учетом формы поверхности, а в главе 9 сконцентрируемся на современной технологии наложения текстур, которая находит все более широкое применение при формировании сложных тонированных изображений. Детальное знакомство с реализацией современных графических систем — третья тема, которую мы обсудим в главе 7. Будут рассмотрены все компоненты графического конвейера и алгоритмы выполнения операций на каждом из этапов. Мы представим вам также дополни- дополнительную информацию о работе напрямую с буфером кадра. Закончив изучение материала, изложенного в главе 6, остальные главы можно читать в произвольном порядке. 5.11. Рекомендуемая литература В книге Карлбома (Carlbom) и Пасьорека (Paciorek) [Car78] обсуждаются сходства и раз- различия классических и компьютерных методов визуализации. Роджерс (Rogers) и Адаме (Adams) [Rog90] приводят множество примеров матриц проективного преобразования при- применительно к стандартным видам, используемым в техническом черчении. В книгах Фоли [Fol90], Уатта (Watt) flVa(93J, Херна (Hearn) и Бейкера (Baker) [Hea94] выведены канониче- канонические проективные преобразования. Все эти книги ориентированы на пользователей графиче- графической системы PH1GS, API которой несколько отличается от API системы OpenGL. Но следует отметить, что в книге Фоли рассматривается наиболее общий случай. В разных книгах выве- выведенные соотношения не совпадают, поскольку одни авторы используют для представления точки центра проецирования матрицу-строку, а другие — матрицу-столбец. Кроме того, по- разному определено положительное направление оси г. Подробную информацию о матрицах вида и проективного преобразования в OpenGL читатель сможет найти в руководстве про- программиста OpenGL Programmer's Guide [Ope97,a]. 5.11. Рекомендуемая литература 243
Упражнения 5.1. Не все проекции относятся к классу плоских геометрических. Приведите примеры проекций, где поверхность, на которую выполняется проецирование, является кри- криволинейной. Придумайте примеры, в которых линии проецирования не являются прямыми. 5.2. Представьте себе летательный аппарат, состояние которого в пространстве задается углами крена, тангажа и рыскания и расстоянием от некоторого другого объекта. Какова будет матрица вида, выраженная этими параметрами. 5.3. Пусть имеется спутник, вращающийся вокруг Земли. Его положение относительно Земли задается в полярных координатах. Какова будет матрица вида для наблюда- наблюдателя, который, сидя в движущемся спутнике, пытается все время держать Землю в поле зрения. 5.4. Покажите, как вычислить с помощью операции векторного произведения направле- направления и и v по заданным вектору нормали к картинной плоскости, точке привязки ви- вида и вектору вертикали вида. 5.5. Можно ли сформировать изометрический вид куба, используя только один поворот вокруг некоторой оси? Поясните свой ответ. 5.6. Выведите матрицу перспективного проективного преобразования для случая, когда центр проецирования является произвольной точкой в пространстве, а картинная плоскость имеет произвольную ориентацию. 5.7. Докажите, что перспективное проективное преобразование сохраняет прямолиней- прямолинейность линий. 5.8. Любая попытка проецировать точку, которая принадлежит плоскости, параллель- параллельной картинной и проходящей через центр проецирования, приведет к делению на нуль. Какой вид будет иметь проекция прямолинейного отрезка, одна из конечных точек которого лежит на такой плоскости? 5.9. Разработайте один или несколько вариантов API для задания косоугольной проек- проекции. Выполнение этого упражнения не потребует от вас разработки соответствую- соответствующих функций — нужно только определить набор параметров, передаваемых этим функциям. 5.10. Выведите матрицу косоугольного проективного преобразования, для которой ис- исходными являются параметры передней и задней отсекающих плоскостей, правой верхней и левой нижней точек пересечения боковых граней зоны видимости с пе- передней отсекающей плоскостью. 5.11. Подход, основанный на выполнении нормализации для любого типа проецирова- проецирования, предполагает предварительное искажение всех объектов отображаемой сцены и выполнение для любого типа проекции на втором этапе только ортогонального проецирования. Какие проблемы, по-вашему, могут возникнуть при использовании этого подхода для создания графической системы? 5.12. Как изменится матрица проективного преобразования OpenGL, если центр проеци- проецирования не совпадает с началом координат? Положите, что центр проецирования находится в точке @, 0, d), а картинная плоскость описана уравнением г = 0. 5.13. Один из классов трехмерных объектов формируется вытягиванием в направлении третьей оси соответствующих двухмерных объектов. Например, в результате такой операции из окружности формируется цилиндр, а из плоского четырехугольника — параллелепипед. С помощью этой методики преобразуйте программу формирова- 244 Глава 5. Визуализация
ния двухмерного лабиринта (см. упр. 2.7) таким образом, чтобы с ее помощью можно было сформировать трехмерный лабиринт. 5.14. Расширьте возможности программы, разработанной при выполнении предыдущего упражнения, таким образом, чтобы пользователь мог "пройтись" по сформирован- сформированному трехмерному лабиринту. Щелчок средней кнопкой мыши должен задавать движение вперед, а щелчки правой и левой кнопками — поворачивать направление движения на 90° вправо или влево. 5.15. Если для вычерчивания осей использовать ортогональное проецирование, то оси х иу будут лежать в плоскости чертежа, а ось г преобразуется в точку. Можно так организовать ортогональное проецирование, что проекции осей х и у будут по- прежнему пересекаться под углом 90°, а проекция оси г будет проходить под уг- углом -135° по отношению к оси д:. Отыщите соответствующую матрицу проектив- проективного преобразования. 5.16. Напишите программу отображения вращающегося куба внутри ящика с тремя ис- источниками света. Тень от куба, создаваемая каждым источником, должна быть вид- видна на одной из видимых граней этого ящика. 5.17. Отыщите тень от точки на плоскость ах + by + cz + d = 0. Тень должна создаваться в результате освещения удаленным источником света, лучи которого направлены вдоль вектора (dx, dp d:). Упражнения 245
ГЛАВА 6 Закрашивание Изучив материал предыдущих глав, вы уже можете формировать графические модели трехмерных объектов и выводить на экран их изображения. Но вряд ли вас устроит качество этого изображения — все объекты на нем кажутся плоскими, картинка не пе- передает пространственного характера смоделированных объектов. Такое качество изображе- изображения объясняется тем, что при его формировании мы заливали каждую поверхность равномер- равномерно одним и тем же цветом. В результате ортогональная проекция сферы выглядит на экране как равномерно закрашенный круг, а куб — как плоский шестиугольник. Фотография той же сферы выглядит совсем по-другому — на ней внутренняя область окружности, в которую проектировалась сфера, окрашена неравномерно, и в результате явно чувствуется выпуклая поверхность криволинейного геометрического тела. О таком изображении говорят, что цвет в нем имеет множество градаций или оттенков (shades). Именно таким способом передается объемность, пространственность объекта на плоском изображении. До сих пор в книге не затрагивался вопрос о том, как отражается реально существующее в природе взаимодействие света и материала на моделях, сформированных компьютером. В этой главе мы попытаемся восполнить этот пробел в ваших познациах. Будут рассмотрены по отдельности модели разных источников света и модели наиболее распространенных физиче- физических явлений, возникающих при взаимодействии света и материальной среды. Нашей целью является показать, как в уже описанную выше конвейерную архитектуру графической систе- системы включить еще одну очень важную функцию — закрашивание отображаемой сцены с уче- учетом характеристик источников света и материала моделируемых объектов. Поэтому мы со- сосредоточим внимание только на локальных моделях распределения света. Такие модели, в противоположность глобальным моделям освещения, позволяют вычислить оттенок отдель- отдельной точки на некоторой поверхности, независимо от других поверхностей как этого, так и других объектов сцены. В процессе вычислений учитываются только характеристики мате- материала, ассоциированного с данной поверхностью, локальная геометрия этой поверхности, расположение и свойства источников света. В предыдущей главе говорилось о том, как определить форму тени, отбрасываемой мно- многоугольником. Теперь мы разовьем эту идею и выясним, как в общем случае реализовать на- наложение теней на изображение, созданное на основе полигональной модели. В качестве тес- тестового примера рассмотрим программу рекурсивной аппроксимации сферы. Затем перейдем
к практическим вопросам реализации средств закрашивания в OpenGL— спецификации ис- источников света и свойств материалов — и включим их в созданную программу. В заключение мы кратко остановимся на двух наиболее интересных методах реализации глобальной модели распределения света — методе трассировки лучей (ray tracing) и методе излучательности (radiosity). 6.1. Свет и материя Познакомив вас в главах 1 и 2 с основами физиологии органов зрения человека, мы отло- отложили на будущее обсуждение темы взаимодействия света и материи — поверхностей матери- материальных объектов. Для того чтобы понять, как программно реализовать закрашивание объек- объектов сцены, нужно разобраться в физике реального процесса распространения световой энер- энергии в материальной среде. С точки зрения физики поверхность материального тела может либо излучать световую энергию, например поверхность электрической лампочки, либо отражать свет, падающий на нее от внешнего источника. Некоторые тела одновременно и отражают свет, и излучают его вследствие внутренних физических процессов, происходящих в материале. Когда мы смот- смотрим на некоторую точку материального объекта, то ее цвет определяется множеством эле- элементарных актов взаимодействия со светом, падающим на объект как непосредственно от ис- источников света, так и от других отражающих поверхностей. Последовательность этих эле- элементарных актов можно представить в виде рекурсивного процесса. Рассмотрим простую сцену, представленную на рис. 6.1. Часть света от источника освещения, которая попадает на поверхность объекта А, отражается. Часть этого отраженного света попадает на поверхность объекта Б, причем часть этого отраженного света отражается и попадает вновь на объект А. Далее процесс повторяется. Такое рекурсивное отражение света от двух поверхностей приво- приводит к определенным цветовым эффектам, в частности появлению на поверхностях дополни- дополнительных окрашенных бликов. Математически этот рекурсивный процесс описывается инте- интегральными уравнениями, которые называются глобальными уравнениями заполнения {rendering equation). В принципе, их можно было бы использовать для определения распреде- распределения цвета по всем поверхностям объектов сиены, но, к сожалению, такие интегральные уравнения в общем случае не поддаются решению даже с использованием численных мето- методов. Существует множество подходов, основанных на разных вариантах их аппроксимации (например, метод трассировки лучей или метод излучательности), каждый из которых позволяет найти решение уравнений для определенного ти- типа объектов. Но и здесь имеется одно сущест- существенное препятствие — и метод трассировки лу- лучей, и метод излучательности требуют большого объема вычислений, которые на существующей технологической базе не удается выполнить в ре- реальном масштабе времени для сцен даже средней сложности. Поэтому основное внимание мы уде- уделим более простым локальным моделям заполне- заполнения, основанным на модели отражения Фонга (Phong), которая на сегодняшний день представ- представляет собой вполне приемлемый компромисс ме- между физической корректностью и объемом необ- необходимых вычислений. Вместо того чтобы рассматривать уравнения общего баланса световой энергии в сцене, эта модель анализирует световые лучи, испускаемые светоизлучающими поверхностями — Рис. 6.1. Отражающие поверхности 248 Глава 6. Закрашивание
источниками света (light sources), — и их взаимодействие с отражающими поверхностями объектов сцены. В чем-то такой подход сходен с трассировкой лучей, но в отличие от по- последнего метода он анализирует только один акт отражения от отдельной поверхности, а не рекурсивный процесс. Предлагается разделить проблему на две независимые части. Во- первых, в модель сцены нужно включить описания источников света. Во-вторых, сформиро- сформировать модель процесса отражения, которая будет адекватно передавать взаимодействие мате- материала поверхностей объектов сцены и света. Для того чтобы представить себе этот процесс, попробуем проследить, как распростра- распространяются лучи от точечного источника света к сцене, представленной на рис. 6.2. Как отме- отмечалось в главе 1, наблюдатель видит только те лучи, которые отразились от поверхностей объектов и достигли его глаз, "совершив", возможно, довольно сложное "путешествие" в пространстве сцены и "зацепив" по пути не один объект. Если луч попал в глаз непосред- непосредственно от источника, то наблюдатель будет воспринимать цвет света, испускаемого этим источником. Если луч по пути отразился от поверхности какого-либо объекта, то наблюда- наблюдатель видит цвет, зависящий от характера взаимодействия света, падающего на поверх- поверхность, и материала поверхности. В компьютерной графической модели в роли глаза наблюдателя выступает картинная плоскость (рис. 6.3) и изображение формируют только те лучи, которые достигли центра про- проецирования, пройдя внутри зоны, ограниченной рамкой отсечения картинной плоскости. Об- Обратите внимание на то, что большинство лучей, испускаемых источником, не попадает на картинную плоскость, а потому не представляет для нас никакого интереса. Мы воспользуем- воспользуемся этим выводом позже, в разделе 6.10. Будем считать, что картинная плоскость разделена на прямоугольники, каждому из которых соответствует отдельный пиксель экрана, причем цвет пикселя зависит от цвета луча, попавшего на этот пиксель. Рис. 6.2. Свет и поверхности Рис. 6.3. Световые лучи, поверхно- поверхности объектов и изображение на экране компьютера На рис. 6.2 показаны варианты и однократного, и многократного взаимодействия луча с объектами. Именно характер этого взаимодействия и определяет, что же увидит наблюда- наблюдатель— красный объект или коричневый, темный или светлый, матовый или блестящий. 6.1. Свет и материя 249
Когда луч попадает на поверхность, часть его энергии поглощается, а часть — отражается. Если поверхность непрозрачна, то вся энергия луча распределяется между процессами от- отражения и поглощения. Если же поверхность прозрачна, то часть энергии луча проходит через эту поверхность и может взаимодействовать с другими объектами. Характер взаимо- взаимодействия зависит от длины световой волны. Объект, облученный белым светом, кажется наблюдателю красным, потому что большая часть энергии падающего света поглощена ма- материалом объекта, а та, что отразилась, имеет максимум интенсивности в красной части спектра. Объект кажется наблюдателю блестящим, если его поверхность гладкая. И наобо- наоборот, объект, поверхность которого шероховата, кажется наблюдателю матовым. Распреде- Распределение теней на поверхности объекта зависит также от ориентации отдельных участков его поверхности, т.е. от направления вектора нормали в каждой точке этой поверхности. Можно выделить три основных типа характера взаимодействия света и материала поверх- поверхности (рис. 6.4). 1. Зеркальное отражение. Поверхности, таким образом взаимодействующие с падаю- падающим на них светом, выглядят блестящими, поскольку большая часть световой энергии отражается или рассеивается в узком диапазоне углов, близких к углу отражения. Зеркало — это идеально отражающая поверхность. Хотя небольшая часть энергии падающего луча и поглощается, остальной свет отражается под одним углом, причем этот угол равен углу падения луча. 2. Диффузное отражение. При диффузном отражении падающий свет рассеивается в разных направлениях. Такой тип взаимодействия характерен для поверхности равно- равномерно окрашенной стены или поверхности Земли, как она представляется пилоту са- самолета или космического летательного аппарата. 3. Преломление. В этом случае луч света, падающий на поверхность, преломляется и проникает в среду объекта под другим углом. Этот процесс рефракции характерен для стекла и воды. Как правило, при этом отражается часть падающего света. а) б) в) Рис. 6.4. Взаимодействие света и материала: а — зеркальное отражение; б — диффузное отра- отражение; в — преломление В разделе 6.3 будет показано, как моделируются все эти виды взаимодействия и соответ- соответствующие поверхности в системах компьютерной графики. Но сначала мы займемся характе- характеристиками источников света. 6.2. Источники света Свет может исходить от поверхности объекта в двух случаях: либо объект является излу- излучающим, либо объект отражает свет, падающий на него извне. Как правило, источником све- света мы считаем излучающие объекты, хотя, строго говоря, это и не так. Например, в ночных сценах источником может быть Луна, которая сама свет не излучает. Кроме того, некоторые объекты, которые излучают свет, одновременно и отражают свет, падающий на них от других 250 Глава 6. Закрашивание
источников, например колба электрической лампочки. Но в нашей упрощенной модели, не претендующей на абсолютную адекватность реальному физическому миру, мы не будем учи- учитывать эти нюансы. Когда мы будем рассматривать средства моделирования света в OpenGL (раздел 6.7), вы увидите, насколько просто в этой графической системе сформировать источ- источник света. Рассматривая источник света, такой, как показан на рис. 6.5, мы не должны забывать о том, что этот объект имеет определенную поверхность. Каждая точка этой поверхности (х, у, z) может испускать световой луч, который характеризуется направлением эмиссии (8, ф) и распределением световой энергии по длинам волн А.. Таким образом, элементарный источ- источник света в общем случае характеризуется функцией излучения {illumination function), 1(дг, у, г, 0, ф, X), зависящей от шести параметров. Обратите внимание на то, что направление излучения описывается двумя углами и что физический источник полихромного излучения представляется как множество независимых элементарных источников монохроматического света. Рассматривая освещаемую таким источником поверхность, можно получить освещен- освещенность каждой ее точки, интегрируя функцию излучения по поверхности источника света (рис. 6.6). При интегрировании нужно учитывать, во-первых, только те углы излучения, кото- которые обеспечивают попадание лучей на анализируемую точку освещаемой поверхности, а во- вторых, расстояние между элементарным источником и этой точкой. Для распределенного источника света, каковым является электрическая лампа, вычислить такой интеграл довольно сложно как аналитически, так и численно. Для упрощения подобный источник часто модели- моделируется многоугольниками, каждый из которых представляет собой элементарный источник, или аппроксимацией реального источника множеством точечных. Мы рассмотрим четыре основных типа источников света: фонового освещения (ambient lighting), точечные источники {point sources), прожекторы (spotlights) и удаленные источни- источники света (distant light). Этих четырех типов вполне достаточно для моделирования освещения в большинстве простых сцен. Рис 6.5. Источник света Рис. 6.6. Суммирование света от элемен- элементарных источников 6.2.1. Цвет излучения Создать адекватную модель источника довольно сложно, поскольку в ней нужно учиты- учитывать, что от длины волны зависит не только интенсивность, но и направление излучения. Од- Однако мы с самого начала остановились на трехцветной модели зрения человека, которая предполагает, что для получения всей цветовой гаммы достаточно учитывать при анализе три основные (первичные) цветовые составляющие — красного, зеленого и синего цветов. 6.2. Источники света 251
Поэтому в дальнейшем мы будем рассматривать любой источник как состоящий из трех независимых источников первичных цветов и соответственно описывать его трехкомпонент- ной функцией излучения: 7, К Л Следовательно, для вычисления распределения красного цвета в изображении будет исполь- использоваться "красный" компонент функции излучения 1Г. Поскольку вычисления для всех трех цветов выполняются по единой схеме, мы в дальнейшем для краткости будем рассматривать только по одному скалярному уравнению каждого типа и будем считать, что его можно при- применять к любому из первичных цветов. 6.2.2. Фоновое освещение В некоторых помещениях, например учебных аудиториях или в кухне, система освещения организуется таким образом, чтобы обеспечить равномерный свет по всему пространству. Чаще всего для этого используются источники большого размера с рассеивателями. Смоде- Смоделировать такую систему освещения можно, по крайней мере, в принципе, сформировав боль- большое множество маленьких источников, направление излучения которых выбирается случай- случайно, а затем проинтегрировав освещение некоторой точки от всех этих источников. Но при реализации такого подхода на практике придется выполнять массу вычислений, что не позво- позволит графической системе формировать изображение в реальном масштабе времени. Можно поступить и по-другому — подобрать такие характеристики источника, которые обеспечили бы равномерное освещение по всему пространству сцены. Такой источник принято называть источником фонового света {ambient light). Следуя второму подходу, мы можем при моде- моделировании просто считать, что каждая точка на поверхности объектов этой сцены освещена одинаково. Таким образом, функция освещенности каждой точки поверхности характеризу- характеризуется только заданной интенсивностью 1а. Как уже отмечалось выше, будем считать, что источник фонового света состоит из трех источников первичных цветов, а его функция интенсивности — трехкомпонентная: I. = Любой из компонентов функции 1а — красный, зеленый или синий — является скаля- скаляром /а, но в дальнейшем нужно учитывать, что хотя каждая точка на поверхности любого из объектов сцены получает от источника свет одинаковой интенсивности, отражают они его по-разному. 6.2.3. Точечный источник света Идеальный точечный источник света {point source) излучает свет одинаково во всех на- направлениях. Такой источник, размещенный в точке р0, характеризуется трехкомпонентным вектором цвета: 1(Ро) = 'г(Ро)" '.(Ро) 1Л(Ро)_ 252 Глава 6. Закрашивание
Освещенность некоторой точки поверхности светом от этого источника обратно пропорцио- пропорциональна квадрату расстояния между этой точкой и источником. Следовательно, освещенность в точке р (рис. 6.7) светом от некоторого точечного источника может быть представлена мат- матрицей-столбцом: 1(Р.Ро) = - ¦1(Ро)- |Р-Ро. Как и при моделировании фонового света, обозначим через /(ро) любой компонент 1(р0). Использование точечных источников в большинстве приложений определяется скорее простотой работы с ними, чем желанием точно передать характеристики реальных физиче- физических осветительных приборов. Изображение сцены, сформированное с учетом только точеч- точечных источников, получается очень контрастным (как говорят фотографы, жестким), все объ- объекты оказываются либо очень яркими, либо слишком темными. Реально каждый физический осветительный прибор имеет конечные размеры, что приводит к более плавному переходу от полностью светлых участков к полностью затененным (рис. 6.8). Эта зона перехода оказыва- оказывается частично затененной — находится в полутени. В графической программе можно сыми- сымитировать снижение контраста от точечного источника, добавив источник фонового света (фотографы называют его источником заполняющего света). Рис. 6.7. Освещение поверхности точечным источником Рис. 6.8. Формирование теневого перехода источником света, имеющим конечные размеры Учет расстояния от конкретной точки до точечного источника света также вносит свою долю в излишнее "ужесточение" контраста. Хотя с чисто физической точки зрения обратно пропорциональная зависимость между освещенностью и расстоянием до источника вполне корректна, в компьютерной графике нередко используется модифицированная зависимость (а 4 bd ¦+¦ ccf)~\ где d— расстояние между р и р0. Константы a. b и с выбираются из условия "смягчения" светотеневого перехода. Учтите, если точечный источник находится достаточно далеко от всех объектов сцены, то можно считать, что отличием в расстоянии до разных то- точек сцены можно смело пренебречь и что этот источник освещает их совершенно одинаково. 6.2.4. Прожекторы Источники света типа прожектор (.spotlights) отличаются тем, что испускают свет на- направленным пучком, т.е. каждая точка излучающей поверхности посылает свет в одном и том же направлении. Проще всего смоделировать прожектор с помощью точечного источника света, ограничив для него направление, в котором распространяются световые лучи. Таким 6.2. Источники света 253
образом, формируется конус с вершиной в точке р„ ось которого направлена вдоль вектора 1„ а угол наклона образующей к оси равен 9 (рис. 6.9). Обратите внимание на то, что при 0 = 180° такой квазипрожектор превращается в точечный источник. Модель прожектора, более близкая к реальному физическому прибору, характеризуется функцией распределения интенсивности в конусе излучения. Естественно, что наибольшей интенсивностью обладают лучи, направленные вдоль оси конуса. Интенсивность излучения прожектора является функцией от угла ф между осью конуса излучения прожектора и векто- вектором s, направленным на определенную точку освещаемой поверхности, если этот угол мень- меньше 0 (рис. 6.10). Хотя функцию распределения интенсивности можно аппроксимировать по- разному, чаще всего ее задают в виде соб'Ф, где показатель е определяет, насколько быстро убывает интенсивность по мере увеличения угла ф (рис. 6.11). Как вы увидите в дальнейшем в этой главе, тригонометрическая функция косинус очень часто используется при математиче- математическом моделировании освещения. Если и s, и 1 являются векторами единичной длины (ортами), то значение косинуса можно вычислить с помощью скалярного произведения этих векторов: I. COS© = S Вычисление косинуса этим методом не требует никаких таблиц или разложения в ряд таточно выполнить три умножения и два сложения. ДОС- с. 6.9. Прожек- Прожектор Рис. 6.10. Распределение ин- интенсивности в конусе излучения прожектора в ф Рис. 6.11. Экспоненци- Экспоненциальное распределе- распределение интенсивности 6.2.5. Удаленный источник света Характерной особенностью удаленного источника света является то, что все испускаемые им лучи можно считать параллельными. Использование такого источника в сцене избавляет от необходимости рассчитывать направления лучей, освещающих разные точки отображае- отображаемой поверхности, а значит, существенно повышает скорость формирования изображения. Прекрасным примером такого источника является солнце. На рис. 6.12 показано, что в этом случае можно заменить точечный источник света параллельным пучком лучей. На практике обработка удаленного источника выполняется почти так же, как параллельное проецирова- проецирование, — вместо положения источника света учитывается направление его лучей. Следователь- Следовательно, если использовать математический аппарат однородных координат, точечный источник света ро можно представить четырехмерной матрицей-столбцом х У 254 Глава 6. Закрашивание
а удаленный источник света — вектором направления в виде х Ро = Рис. 6.12. Параллель- Параллельные лучи от уда- удаленного источни- источника света В графической системе вычисления, связанные с обработкой уда- удаленного источника света, выполняются значительно быстрее, чем обработка близко расположенных точечных источников и прожекто- прожекторов. Естественно, что одна и та же сцена, освещенная точечным ис- источником или удаленным, выглядит совершенно по-разному. В со- составе OpenGL имеются средства представления и манипулирования и близко расположенными точечными источниками, и прожекторами, и удаленными источниками. 6.3. Модель отражения Фонга Итак, перед нами стоит довольно сложная задача: с одной стороны, нам нужна модель взаимодействия света и материала, которая бы соответствовала реальным физическим про- процессам, а с другой стороны, реализация этой модели в графической системе должна хорошо встраиваться в конвейерную архитектуру и не слишком перегружать компьютер. Ниже будет рассмотрена модель процесса отражения света объектами сцены, предложенная Фонгом (Phong). Практика использования этой модели во множестве графических систем доказала ее эффективность и достаточно близкое приближение к реальным физическим процессам, что позволяет формировать полутоновые изображения высокого качества при самых различных условиях освещения и свойствах материалов. Для вычисления цвета произвольной точки р на поверхности объекта в модели использу- используется четыре вектора (рис. 6.13). Если поверхность криволинейная (а это наиболее распро- распространенный случай в реальном мире вещей), все четыре вектора могут изменяться при пере- переходе от одной точки к другой. Вектор п является нормалью к поверхности в точке р; методи- методику вычисления компонентов этого вектора мы обсудим в разделе 6.4. Вектор v направлен от точки р к наблюдателю или центру проецирования, а вектор 1 задает направление от точки р к произвольной точке источника распределенного света или в нашем случае — к точечному источнику света. И наконец, вектор г— это направление идеального отражения луча, па- падающего на поверхность вдоль вектора 1. Учтите, что вектор г однозначно определяется век- векторам п и 1. Методику расчета компонентов этого вектора мы рассмотрим в разделе 6.4. Модель Фонга адекватно передает три из четырех описан- описанных выше типов взаимодействия света и материала — зеркаль- зеркальное и диффузное отражение и фоновое освещение. Предполо- Предположим, что имеется множество точечных источников света. Мож- Можно считать, что для каждого из трех основных цветов свет от этого источника, влияющий на формируемое изображение, имеет три составляющие, которые назовем компонентами фо- фонового (ambient), зеркального (specular) и диффузного (diffuse) света. (Соответственно в математических выражениях компо- компонент фонового цвета будет иметь индекс "а", зеркального — индекс "г", а диффузного — "сГ.) С физической точки зрения такое предположение выглядит несколько странным, но наша цель состоит в том, чтобы, в конце концов, сформировать в графической системе световые эффекты, близкие к реальным, Рис. 6.13. Векторы, исполь- используемые в модели Фонга 6.3. Модель отражения Фонга 255
уложившись при этом в достаточно жесткие временные рамки. Поэтому для имитации эф- эффектов, которые по самой своей природе носят глобальный характер, мы пытаемся использо- использовать локальную модель. Итак, модель источников освещения включает компоненты фоново- фонового, диффузного и зеркального типов, и для каждой точки р отображаемой поверхности мож- можно вычислить матрицу освещенности размером 3x3 для /-го источника света: Элементы первой строки матрицы представляют интенсивности фонового света соответст- соответственно для красного, зеленого и синего цветов, поступающего от /-го источника. Элементы второй строки представляют цветовые компоненты интенсивности для диффузного света, а третьей — для зеркального. Предполагается, что в этих элементах не учитываются эффекты ослабления интенсивности освещения, связанные с расстоянием до источника. Модель формируется в предположении, что существуют методы вычисления интенсив- интенсивности света, отражаемого любой точкой поверхности, на основе значений элементов мат- матрицы L, для этой точки. Например, зная значение диффузной составляющей LITU красного цвета, поступающего от /-го источника, и определив каким-то способом коэффициент отра- отражения Rni для соответствующего компонента, можно вычислить значение составляющей ин- интенсивности света, отраженного точкой р, в виде произведения RndLlui. Значение коэффици- коэффициента отражения /?,rd зависит от свойств материала поверхности, ее ориентации, направления на источник света и расстояния между источником и освещаемой точкой. Для каждой точки можно вычислить свой набор коэффициентов и объединить их в матрицу: R = R. R.. /?, Для каждой цветовой составляющей света, приходящего от определенного источника, можно по этой же схеме вычислить и интенсивность отраженного света для фонового и зеркального компонентов, а затем все их сложить. Например, для /-го источника интенсивность состав- составляющей красного цвета, отраженной от поверхности в точке р, равна *ir *vra^/ra ^Vrd^ird *Vrs^/rs */ra '/rd *;rs- Интегральную интенсивность отраженного света несложно найти, просуммировав состав- составляющие от всех источников, имеющихся в сцене, и, в общем случае, глобального фонового освещения: /,= /*.+/*-+/*)+/«. где /аг обозначает красную составляющую глобального фонового освещения. Мы упростим в дальнейшем вид выражений, опустив индекс цвета, поскольку все сфор- сформулированные соотношения в равной степени справедливы для каждого из трех основных цветов. Но вычисление коэффициентов отражения для составляющих фонового, диффузного и зеркального света будет существенно отличаться. Отбросив индексы цвета, получим приве- приведенное выше выражение в более компактном виде: / = /, + /„ + /, = RaLa + RtU + RJLS. 256 Глава 6. Закрашивание
Вычисления по приведенной формуле нужно выполнить для каждого основного цвета и каждого из имеющихся в сцене источников, затем просуммировать результаты по всем ис- источникам и прибавить к ним член, зависящий от глобального фонового освещения. 6.3.1. Отражение фонового света Интенсивность падающего на поверхность фонового света La одинакова во всех точках этой поверхности. Частично энергия этого света поглощается материалом поверхности, а частично — отражается. Отражение фонового света характеризуется коэффициентом /?а = ка. Во внимание нужно принимать только положительные значения интенсивности отраженного света, т.е. 0<ка< 1. Следовательно, получим /. = *.?.. Здесь Z.a может принимать индивидуальное значение для каждого из источников или быть единым для всех источников в сцене. Опять напомню, что поверхность характеризуется тремя такими коэффициентами для ка- каждого из основных цветов — клг. /:ае, Aah, причем их значения могут отличаться. В результате изображение сферы будет выглядеть желтым, если коэффициент кяЬ для синего цвета мал, а коэффициенты кат и ка$ для красного и зеленого цветов — велики. 6.3.2. Диффузное отражение Поверхность с идеальным диффузным отражением отражает падающий на нее свет одинаково во всех направлениях. Следовательно, такая поверхность выглядит одинаково для всех наблюдателей. Однако количество отраженного света зависит от материала по- поверхности, поскольку часть энергии падающего светового потока поглощается. Способ- Способность поверхности к диффузному отражению характеризуется ее шероховатостью. Если рассмотреть профиль такой поверхности при сильном увеличении, то, скорее всего, вы увидите картинку, подобную приведенной на рис. 6.14. Лучи света, падающие на поверх- поверхность под практически одинаковыми углами, чаще всего отражаются в совершенно раз- различных направлениях. Поверхность с идеальным диффузным отражением настолько шеро- шероховата, что не существует какого-либо предпочтительного направления отраженных лучей и они с равной вероятностью могут отразиться в любом направлении в верхнем по отно- отношению к поверхности полупространстве. Такие поверхности иногда называют Ламберто- выми поверхностями {Lamberiian surfaces) и математически характер отражения описыва- описывается законом Ламберта. Рассмотрим плоскую шероховатую поверхность (рис. 6.15), ко- которая освещается солнечными лучами. Самой яркой поверхность будет в полдень, а затем, ближе к вечеру, будет казаться все более темной, поскольку, в соответствии с законом Ламберта, для на- наблюдателя яркость поверхности определяется только вертикальной составляющей отраженного от нее света. Это явление можно пред- представить себе следующим образом. Рассмотрим, как отражаются от Рцс- б-Н- Шероховатая такой поверхности лучи от небольшого источника параллельного поверхность света (рис. 6.16). По мере того как источник света будет двигаться по воображаемому небосводу, наклоняясь все ниже и ниже, то же количество световой энер- энергии будет распределяться по все большей площади и поверхность будет восприниматься как более темная. Возвращаясь к точечному источнику на рис. 6.15, можно сформулировать ма- математическую модель диффузного отражения. Закон Ламберта утверждает, что Rj - cos9, 6.3. Модель отражения Фонга 257
где знаком ~ обозначается пропорциональность, а Э — это угол между нормалью п к поверх- поверхности в анализируемой точке и направлением на источник света 1. Если I и п являются еди- единичными векторами1, то cos9 = I • п. Введем в рассмотрение коэффициент пропорциональности kd, который определяет, какая часть падающего на поверхность света отражается, и получим соотношение для составляю- составляющей диффузного отражения в общем отраженном световом потоке: /d = *d(l • n) Ld. В это соотношение можно ввести и множитель, зависящий от расстояния, который позво- позволит учесть ослабление светового потока по мере удаления источника от анализируемой точки поверхности. Мы будем использовать множитель, имеющий в знаменателе квадратный трех- трехчлен от расстояния d: a + bd + cd2 # а) б) Рис. 6.15. Освещение диффуз- диффузной поверхности: а — в полдень; б — после полудня 1МЧ' а) 6/соьв б) Рис. 6.16. Графическая интерпрета- интерпретация закона Ламберта: а — свет падает по нормали; б — свет падает под углом 6.3.3. Зеркальное отражение Включив в модель только фоновое освещение и диффузное отражение, мы получим изо- изображение, на котором уже будут видны тени и, таким образом, передана трехмерная форма объектов, но все поверхности будут выглядеть матовыми, как будто на них лежит слой пуд- пудры. Ни на одном объекте вы не увидите яркого пятна, которое в природе бывает на блестя- блестящих поверхностях. Обычно такое яркое пятно на объекте имеет цвет, отличный от того, ко- который порождается фоновым светом или диффузным отражением. Например, на поверхности красного шара, освещенного источником белого света, вы увидите яркое белое пятно, кото- которое появилось в результате зеркального отражения части светового потока источника от по- поверхности шара в сторону наблюдателя (рис. 6.17). 'Векторы направлений, подобные I и п, широко используются в операциях вычисления скалярного про- произведения при тонировании сцен в компьютерной графике. В программах, реализующих соответствую- соответствующие алгоритмы, следует как молено раньше нормализовать эти векторы, что позволит избежать воз- возможных ошибок. 258 Глава 6. Закрашивание
В то время как диффузное отражение свойственно шероховатым поверхностям, зеркальное отражение появляется на гладких по- поверхностях. Чем более гладкой является поверхность, тем больше она напоминает по своим оптическим свойствам зеркало. На рис. 6.18 показано, почему по мере сглаживания поверхности все большая часть отраженного от нее светового потока концентриру- концентрируется в определенном направлении, т.е. разброс углов отражения от различных точек поверхности все более сужается. Точное модели- моделирование явления зеркального отражения — задача не простая, по- поскольку рассеивание света носит несимметричный характер и зави- зависит от длины волны падающего света, а также меняется при изме- изменении луча отражения. Фонг предложил приближенную модель, в которой учет зер- зеркальною отражения очень незначительно увеличивает объем вычислений по сравнению с моделью, учитывающей только диф- диффузное отражение. В этой модели вводится дополнительный ад- аддитивный член, учитывающий зеркальное отражение. Таким об- образом, поверхность представляется шероховатой для составляю- составляющей диффузного отражения и гладкой — для составляющей зеркального отражения. Интенсивность светового потока, кото- который достигает наблюдателя, зависит от угла ф между вектором г, характеризующим направление идеального зеркального отраже- отражения, и вектором v, направленным к наблюдателю. В модели Фон- Рис. 6.17. Пятно, поя- появившееся в резуль- результате зеркального отражения Рис. 6.18. Зеркальная поверхность га используется уравнение /, = *SZ-Scosa<t>. Коэффициент ks @ < ks < 1) определяет, какая часть светового потока отражается. Пока- Показатель степени a — это коэффициент резкости бликов (shininess coefficient). На рис. 6.19 показано, как при увеличении значения ос отраженный свет концентрируется в зоне, близкой к углу идеального зеркального отражения. В пределе, при возрастании a до бесконечности, получим идеальное зеркало; значения в диапазоне от 100 до 500 соот- соответствуют характеру отражения от большинства металлических поверхностей, а малые значения (<100) соответствуют наиболее распространенным материалам с обычными оптическими свойствами. Рис. 6.19. Эффект изменения коэффициента резкости бликов 6.3. Модель отражения Фонга 259
Располагая данными о нормализованных векторах г и п, можно использовать в модели Фонга операцию скалярного произведения и таким образом несколько сократить объем вы- вычислений. В этом случае член, учитывающий зеркальное отражение, принимает вид /s = As/-,s(r-v)a. В это выражение можно добавить множитель, учитывающий расстояние до источника света, который имеет такой же вид, как и в выражении для диффузного отражения. В результате по- получим выражение для модели Фонга (Phong model), учитывающей это расстояние: •A(r-v)" a + bd + cd2 Вычисления по этой формуле выполняются для каждого источника света, причем для всех трех основных цветов раздельно. На первый взгляд кажется нецелесообразным связывать с каждым источнике*' :^ою ин- интенсивность фонового света или раздельно рассматривать составляющие диффузного и зер- зеркального отражения. Но дело в том, что мы не можем позволить себе роскошь использовать в графической системе полную интегральную модель распределения освещения, а потому вы- вынуждены прибегать к различным искусственным приемам, пытаясь получить результат, близ- близкий к реальному. Рассмотрим, например, сцену, в которой имеется множество объектов. Если включить в нее и источники света, то некоторые объекты будут освещены прямым светом от источников. Изображение таких объектов можно сформировать в графической системе, поль- пользуясь составляющими зеркального и диффузного отражения. Но большая часть светового по- потока от источников достигнет объектов в результате многократного отражения. Этот свет можно смоделировать в графической системе о помощью составляющей фонового освеще- освещения, специфической для каждого источника, причем цветовая характеристика этого фонового освещения должна учитывать как цветовую характеристику источника, так и цветовые харак- характеристики множества объектов сцены. В этом и состоит особенность приближенной модели, в которой мы вынуждены усреднять реальные свойства компонентов. В несколько расширен- расширенном виде такой же анализ можно провести и для составляющей диффузного отражения. Эта составляющая должна неявно учитывать тот факт, что в реальной обстановке рассеянный свет многократно отражается от разных объектов и в результате его цвет может существенно измениться. В модели это можно отобразить, варьируя коэффициенты по основным цветам в составляющих диффузного и зеркального отражения, и таким образом свести все к локально- локальному анализу отдельных поверхностей. На ил. 11 цветной вклейки показано изображение чайников разнсми цвета, сформиро- сформированное OpenGL-программой на основе модели Фонга (подробно об этом — в главе 9). Обратите внимание на то, что изображения разных чайников отличаются друг от друга только свойствами материала поверхности. Как видите, модель Фонга достаточно близ- близко передает световые эффекты на поверхностях разной фактуры — от матовых до почти зеркальных. Мы рассматривали модель Фонга в пространстве объектов, но в графической системе то- тонирование изображения выполняется после того, как описания объектов будут преобразова- преобразованы посредством матриц вида и проецирования. Эти преобразования могут изменить члены, включающие функцию косинуса (см. упр. 6.22). Следовательно, чтобы получить правильный результат, придется либо каким-то образом сохранить используемые пространственные соот- соотношения после выполнения геометрических преобразований объектов, либо в процессе тони- тонирования выполнять необходимые обратные преобразования. 260 Глава 6. Закрашивание
6.4. Вычисление векторов Описанная модель отражения света объектами сцены никак не связана с видом проециро- проецирования (параллельным или перспективным) и является достаточно обшей в том смысле, что может применяться как к плоским, так и к криволинейным поверхностям, причем расстояние между поверхностью и наблюдателем не имеет значения. Большая часть вычислений в про- процессе тонирования изображения пространственной сцены приходится на определение компо- компонентов векторов, используемых в модели Фонга, и их скалярных произведений. В процессе выполнения этих вычислений часто используются разнообразные упрощения, учитывающие специфику конкретной ситуации. Например, если анализируемая поверхность— плоский многоугольник, то для всех ее точек вектор нормали будет одним и тем же. Если источник света удален от поверхности достаточно далеко, то для всех точек этой поверхности вектор 1, характеризующий направление падения лучей от этого источника, будет одним и тем же. В этом разделе мы рассмотрим методы вычисления компонентов векторов для общего случая, а в разделе 6.5 проанализируем возможные упрощения в частных случаях, когда по- поверхности отображаемых объектов можно представить совокупностью плоских многоуголь- многоугольников. Этот частный случай представляет для нас особый интерес, поскольку в большинстве графических систем, в том числе и в OpenGL, при выполнении тонирования криволинейные поверхности аппроксимируются множеством малых плоских многоугольников. 6.4.1. Нормаль к поверхности Для гладкой поверхности вектор нормали существует в каждой ее точке и определяет ло- локальную ориентацию участка поверхности в окрестности этой точки. Метод вычисления компонентов вектора нормали зависит от принятого способа математического описания по- поверхности. На двух примерах — плоскости и сферы — мы продемонстрируем, как определять нормаль и в чем состоят основные сложности вычислительного характера. Плоскость описывается уравнением ах + by -I- cz -r d = 0. Как было показано в главе 4, уравнение плоскости можно записать и в терминах нормали п в точке ро, которая принадлежит этой плоскости: п • (р-ро) = 0, где р— любая точка (х,у,:) на рассматриваемой плоскости. Сравнивая эти два уравнения, получим выражение для вектора нормали: га а или, в однородных координатах, п = с 0 Плоскость может быть также однозначно задана совокупностью трех точек р0, Pi, р;, не ле- лежащих на одной прямой (неколлинеарных). Векторы Р2-Р0 и р|-р0 параллельны плоскости, и для определения нормали можно использовать их векторное произведение: п = (Р2-Ро) X (Pi-Po)- При определении нормали таким способом нужно внимательно отнестись к порядку сомно- сомножителей в векторном произведении. Как уже говорилось в главе 4, для любой поверхности в системе компьютерной графики различают внешнюю и внутреннюю стороны. Изменение по- порядка сомножителей в векторном произведении приводит к инвертированию направления 6.4. Вычисление векторов 261
вектора нормали, а следовательно, внешняя сторона поверхности становится внутренней, а внутренняя — внешней. Это может существенно сказаться на дальнейших операциях, связан- связанных с моделированием световых эффектов. В некоторых графических системах первые три вершины в определении многоугольника автоматически используются для формирования нормали к поверхности этого многоугольника, полагая его плоским. В OpenGL этого не про- происходит, но, как будет показано в разделе 6.5, возможность самостоятельно организовать в программе определение нормали позволяет прикладному программисту гибко управлять свойствами модели освещения. При работе с криволинейными поверхностями способ вычисления компонентов вектора нормали существенно зависит от принятого способа описания такой поверхности. В главе 10 будут проанализированы три разных метода представления кривых и криволинейных поверх- поверхностей. Здесь же мы покажем несколько способов определения нормали к сферической по- поверхности единичного радиуса, центр которой совпадает с началом координат. Обычно такая сфера описывается уравнением в неявной форме (implicit equation): f(x,y,z) = x2+y2 + =2-\ =0, или в векторной форме: Нормаль определяется вектором градиента (gradient vector), который обычно представляет- представляется в виде матрицы-столбца: „= ^ = 2у =2р. av ду Ж. — '2x' 2v 2z Сфера может быть описана и уравнением в параметрической форме (parametric form). При этом координаты x,ywz любой точки на сферической поверхности представляют собой неза- независимые уравнения от двух параметров, и и v: х = х(и, v), у = у(и, v), г = z(u, v). Как будет показано в главе 10, в системах компьютерной графики предпочтение следует отдать параметрической форме, особенно при описании кривых и поверхностей, хотя некоторые виды поверхностей имеют несколько вариантов параметрического представления. К ним принадле- принадлежит и сфера. Один из вариантов параметрической системы уравнений сферы имеет вид х(и, v) = cosm sinv, у(и, v) = cosm cosv, z(u, v) - s'mu. Изменяя и и v в диапазоне -тс/2 < и < я/2, -я/2 < v < я/2, получим все точки на сфере. Исполь- Используя параметрическое представление поверхности, нормаль можно вычислить как характери- характеристику касательной плоскости (tangent plane) в точке р(м, v) = [х(и, v) y(u, v) z(u, v)] (рис. 6.20). Получить уравнение касательной плоскости можно на базе линейных членов разложения параметрических функций х(и, v), y(u, v), z(u, v) в ряд Тейлора в окрестности п ^Р <^Р точки р. Прямые, параллельные векторам — и — и проходящие через точку р, лежат в ка- ои ov сательной плоскости. Векторы — и — определяются следующим образом: он ov 262 Глава 6. Закрашивание
Эр_ du dx du dy du dz du Эр_ dv dx dv dy dv dz dv Рис. 6.20. Касательная Вектор нормали формируется как векторное произведение: плоскость к сфери- сферической поверхности dp dp И — * ЧУ Л ди dv Подставляя в это уравнение параметрические функции сферической поверхности, получим: = COSM cosh sinv cosm cosv sinw = (cost/)p . Поскольку нас интересует только направление вектора нормали, то для сферы единичного радиуса получим п = р. В разделе 6.9 этот результат будет использован при закрашивании сферы, аппроксимирован- аппроксимированной многоугольниками. В графической системе основным типом примитива, с которым выполняются все вычис- вычисления, являются вершины. Таким образом, и вектор нормали следует формировать на основа- основании набора вершин, достаточно близких к той точке, нормаль в которой нас интересует. В рамках конвейерной архитектуры графических систем организовать такое вычисление до- довольно сложно, поскольку по "конвейеру" точки следуют последовательно одна за другой. Поэтому в большинстве графических систем программисту приходится самостоятельно орга- организовывать вычисление векторов нормалей в прикладной программе. Работая с системой OpenGL, можно связать нормаль с некоторой вершиной с помощью пары функций: glNormal3f(nx, ny, nz); glNormal3fv(pointer_to_normal); Аргументы функции glNormal3f () — компоненты вычисленного раньше вектора норма- нормали, а функция glNormal3f v() использует в качестве аргумента указатель на массив компонен- компонентов уже сформированного вектора. Нормали в OpenGL являются переменными режима. Если определить нормаль перед тем, как задавать новые вершины с помощью glVertex(), то эта нормаль будет связана со всеми вершинами, сформированными далее по ходу выполнения программы. Но напомню еще раз, проблему вычисления нормали должен решать прикладной программист. 6.4.2. Угол отражения Определив нормаль к поверхности в анализируемой точке и зная положение источника света, можно вычислить и направление идеально отраженного луча. В идеальном зеркале вы- выполняется закон: угол падения равен углу отражения. Угол падения (angle of incidence) изме- измеряется между нормалью и направлением на источник света (предполагается точечный источ- источник, хотя для отдельного луча это и не принципиально), а угол отражения (angle of reflection) измеряется между нормалью и отраженным лучом (рис. 6.21). 6.4. Вычисление векторов 263
В двухмерном пространстве направление отраженного луча за- задается этим законом однозначно, а в трехмерном пространстве для вычисления направления соответствующего вектора нужно ввести дополнительное условие — в точке р поверхности падающий и от- отраженный лучи, а также нормаль к поверхности должны лежать в одной плоскости, т.е. быть компланарными векторами. В сово- совокупности эти два условия позволяют однозначно определить на- направление отраженного луча г по заданным вектору нормали п и ор- орту падающего луча 1. Поскольку нас интересует только направление отраженного луча г, то в дальнейшем будем предполагать, что все интересующие нас векторы являются ортами — векторами единич- единичной длины, т.е. |1| = |п| = 1. На искомый вектор также накладывается условие |г| = 1 .Если 0, = 0„ то cos0, = cos0r. Используя скалярное произведение, получим соотношение, связывающее углы падения и отражения: COS0, = I • П = COS0r = П • Г. Условие компланарности трех векторов с точки зрения математики означает, что г можно выразить линейной комбинацией 1 и п: Рис. 6.21. Отражение от идеального зер- зеркала Для скалярных произведений на п правой и левой частей этого уравнения получим п • r=al • п + р = I • п. Второе соотношение, связывающее углы а и Р, можно найти из условия, что вектор г должен быть ортом: 1 = г • г = а2 + 2ар1 • п + р2. Решив это уравнение, получим, что г - 2A п)п -1. 6.4.3. Вектор половинного направления При воспроизведении эффекта зеркального отражения в графической системе на основе модели Фонга скалярные произведения г • v нужно вычислять для каждой точки поверхно- поверхностей объектов сцены. Можно несколько упростить этот процесс, включив в рассмотрение промежуточный вектор единичной n h длины, направленный между векторами положения наблюдате- наблюдателя v относительно точки и положения источника света 1: 1 + v h = На рис. 6.22 представлены все пять векторов, которые понадо- понадобятся нам для выполнения тонирования изображения поверхно- поверхности. На этом чертеже угол \|/ между векторами п и h называется рис. 6.22. Определение век- углом половинного направления (half-angle). Если вектор v ле- тора половинного на- нажит в той же плоскости, что и 1, п и г, то выполняется соотно- правления шение (см. упр. 6.7) 2\|/ = ф. Если удастся заменить вычисление скалярного произведения г • v произведением п • h, можно избавиться от вычисления вектора г. Однако угол \|/ меньше угла ф, а следовательно, 264 Глава б. Закрашивание
если использовать тот же показатель степени е в выражении (п • пL, который использовался в члене (г • v)c, то интенсивность зеркальной составляющей уменьшится. Справиться с этой проблемой можно, используя вместо исходного значения е скорректированное значение е', такое, что (п • пL' будет достаточно близким к (г • \)е. Думаю, не стоит доказывать вам, на- насколько желательно уменьшить объем вычислений, но при этом нужно принимать во внима- внимание все последствия такого упрощения для отображения при разном относительном положе- положении источников света как плоских, так и криволинейных поверхностей (см. упр. 6.8). 6.4.4. Преломление света Аналогичная методика используется и при тонировании сцен, в которых нужно учитывать не только отражение света от границы двух сред, но и явление пропускания света из одной среды в другую (такая задача решается в простой программе трассировки лучей, которую мы рассмотрим в разделе 6.10). Рассмотрим поверхность, через которую пропускается весь па- падающий на нее световой поток (рис. 6.23). Если скорость распространения света в двух сре- средах отличается, то на границе этих сред происходит преломление падающего светового луча. Пусть г]/ и Г),— показатели преломления {indices of refraction)— параметры, характеризую- характеризующие относительную скорость распространения света в средах по обе стороны от поверхности их раздела. Закон Снелля {Snell'z law) (см. упр. 6.13) утверждает, что sin О, _ г|, sinG, г\, Направление преломленного луча t находится следующим обра- образом. Значение cos6/ определяется векторами п и 1; если они нормали- нормализованы, то cosG/ равно скалярному произведению этих векторов. По заданному значению cosG, можно найти sinG,, а далее, зная характери- характеристики обеих сред, — и sinG/. Обозначив Г) = г|/т|/, получим cosB. = /1—^(l-cos:0,). -n Рис. 6.23. Идеальное преломление светового луча Как и при вычислении компонентов отраженного луча, в случае пре- преломления векторы t, n и 1 должны быть компланарны, следовательно, t = an + pi. Учитывая, что вектор t должен иметь единичную длину, получим t = - —1 + cosG, cosG, n [r\ [ П J Знак "минус" в этом уравнении появился вследствие того, что вектор t направлен в сторону внутреннего полупространства поверхности раздела сред. Угол, при котором подкоренное выражение для cosQ, становится равным нулю, т.е. выполняется условие sinQ/ = г\, называется критическим углом (critical angle). Если на поверхность раздела сред свет падает под этим углом, возникает явление полного внутреннего отражения, т.е. преломленный луч направля- направляется вдоль поверхности раздела. При дальнейшем увеличении 6/ весь падающий световой по- поток будет отражаться от поверхности раздела сред. Более общий случай того, что происходит на границе раздела двух сред, представлен на рис. 6.24: часть падающего светового потока преломляется, часть отражается как при зер- зеркальном отражении, а остальная часть поглощается. Распределение по направлениям той час- части светового потока, которая прошла во вторую среду, имеет такой же характер, как и в слу- 6.4. Вычисление векторов 265
чае зеркального отражения, но лучи концентрируются в окрестности вектора t. Следовательно, модель преломления для наблюдателя, рас- расположенного со стороны внутреннего полупространства поверхности раздела, может включать член, пропорциональный скалярному произ- произведению t • v. При моделировании преломления можно воспользо- воспользоваться описанной выше методикой использования вектора половин- половинного направления, что позволит несколько упростить вычисления (см. упр. 6.16). Рис. 6.24. Реальная картина взаимодействия света с границей раздела двух сред 6.5. Закрашивание многоугольников Располагая средствами вычисления векторов нормали, можно при заданном расположе- расположении источников света и наблюдателя применить рассмотренные модели ко всем точкам по- поверхностей объектов сцены. Но, к сожалению, использование уравнений вычисления норма- нормали, которые мы рассмотрели в разделе 6.4 применительно к сферической поверхности, при- приводит к неприемлемо большим вычислительным затратам. Раньше мы неоднократно подчеркивали достоинства полигональной модели для представления объектов отображаемой сцены. Использование этой модели для выполнения то- тонирования изображения сцены, состоящей из множест- множества криволинейных объектов, существенно уменьшает объем вычислений. Именно такая модель, предпола- предполагающая аппроксимацию криволинейных поверхностей множеством маленьких плоских многоугольников, и используется в большинстве графических систем, в том числе и в OpenGL. Рассмотрим полигональную сеть, подобную пред- Рис. 6.25. Полигональная сеть ставленной на рис. 6.25. Каждый многоугольник в та- такой сети — плоский, и вычислить компоненты вектора нормали к нему не представляет осо- особого труда. Ниже мы рассмотрим три метода закрашивания многоугольников: плоское, ин- интерполяционное, или закрашивание по методу Гуро (Gouraud), и закрашивание по методу Фонга. 6.5.1. Плоское закрашивание При перемещении от одной точки на поверхности к другой в общем случае могут изме- изменяться три вектора — 1, п и v. Однако, если поверхность плоская, вектор п остается постоян- постоянным для всех точек этой поверхности. Если наблюдатель расположен достаточно далеко от этой поверхности, то изменением вектора v при переходе от точки к точке на поверхности плоского многоугольника небольшого размера также можно пренебречь и считать его посто- постоянным2. И, наконец, если в сцене используется удаленный источник света, то вектор I также считается постоянным для всех точек поверхности, ограниченной закрашиваемым много- многоугольником. В данном случае термин "удаленный" имеет прямой смысл, т.е. считается, что источник бесконечно далеко удален от освещаемой поверхности. Для реализации алгоритма закрашивания нужно в этом случае вместо расположения источника задавать направление на 2В OpenGL существуют режимы "ближнего " и "дальнего " наблюдателя, которые переключаются с по- помощью соответствующего программного флага. В режиме "ближнего" наблюдателя этот флаг должен быть сброшен, а в режиме "дальнего " наблюдателя —установлен. 266 Глава 6. Закрашивание
источник. Но термин "удаленный" можно рассматривать и в относительном смысле, сравни- сравнивая размеры закрашиваемого многоугольника с расстоянием до наблюдателя или до источни- источника (рис. 6.26). Именно такая интерпретация этого термина используется в большинстве гра- графических систем. Рис. 6.26. Удаленные источник света и наблюдатель Если три указанных вектора постоянны для всех точек многоугольника, то все необходи- необходимые вычисления для его закрашивания можно выполнить только один раз и применить ре- результаты ко всем точкам этого многоугольника. Этот метод получил название пчоского {flat), или равномерного закрашивания {constant shading). В OpenGL режим плоского закрашивания задается аргументом GL_FLAT при вызове функции glShadeModel(): glShadeModel(GL_FLAT); В режиме плоского закрашивания OpenGL использует вектор нормали, ассоциированный с первой вершиной каждого очередного закрашиваемого многоугольника. Для примитивов типа полосы из треугольников {triangle strip) OpenGL для первого треугольника полосы ис- использует нормаль к третьей вершине, для второго треугольника — нормаль к четвертой вер- вершине и т.д. Аналогичная последовательность используется и при закрашивании полос из че- четырехугольников {quadrilateral strips). На изображении, сформированном алгоритмом плоского закрашивания, четко видна раз- разница в оттенках цвета отдельных многоугольников сети, причем если источник света или на- наблюдатель расположены достаточно близко к объектам сцены, то это отличие порождается не только разной ориентацией многоугольников (значениями векторов нормалей п), но и раз- разным направлением векторов 1 и v. Но при аппроксимации таким способом гладкой поверхно- поверхности качество изображения может удовлетворить только самого непритязательного пользова- пользователя, поскольку переход между отдельными многоугольниками сети на изображении кажется очень резким (рис. 6.27). Одна из особенностей зрительной системы человека заключается в том, что она очень чувствительна к малым изменениям оттенка соседних участков изображе- изображения вследствие так называемой вторичной задержки {lateral inhibition). Если рассматривать шкалу с дискретно изменяющейся интенсивностью участков (рис. 6.28), то каждый переход будет восприниматься таким образом, что с одной стороны перехода кажется, будто интен- интенсивность участка понизилась по сравнению с номинальной, а с другой стороны перехода уча- участок вначале кажется более темным, чем номинальный (рис. 6.29). Таким образом, в области светотеневого перехода глаз воспринимает полосы Маха {Mach bands). Это явление объясня- объясняется характером связи между колбочками и оптическим нервом, и единственное, что может сделать разработчик компьютерной графической системы для устранения такого неприятного эффекта — постараться по возможности сгладить контраст между оттенками соседних участ- участков на границе многоугольников. 6.5. Закрашивание многоугольников 267
Рис. 6.27. Плоская закраска полигональной сети Рис. 6.28. Шкала интенсивно- стей Воспринимаемая интенсивность- Действительная интенсивность Рис. 6.29. Воспринимаемая и действительная интенсивно- интенсивности в области перепада 6.5.2. Интерполяционное закрашивание и закрашивание по методу Гуро В программе отображения вращающегося куба (см. главу 4) мы уже использовали средст- средства интерполяции цвета, ассоциированного с отдельными вершинами многоугольника, кото- которыми располагает система OpenGL. Если установить режим сглаживания при закрашивании, передав в качестве аргумента функции glShadeModel() значение GL_SMOOTH, то OpenGL будет интерполировать цвет вдоль отображаемого примитива, например прямой. Предположим, что в программе установлены режимы сглаживания закрашивания и учета освещения и что с каждой вершиной ассоциирован вектор нормали соответствующего многоугольника. При вы- вычислении освещения каждой вершины определяется ее цвет, который зависит от свойств ма- материала и векторов v и 1, вычисленных раньше для этой вершины. Обратите внимание на то, что при использовании удаленных источников света и отсутствии зеркальной составляющей алгоритм интерполяционного закрашивания сформирует одинаковый цвет для всей внутрен- внутренней области многоугольника. Возвращаясь к полигональной сети, отметим, что идея использования в вычислениях нормалей, ассоциированных с вершинами такой сети, должна вызвать у любого математика возражения, поскольку с математической точки зрения она абсолютно некорректна. Посколь- Поскольку вершина является точкой пересечения, как минимум, двух по-разному ориентированных многоугольников, то в ней происходит разрыв непрерывности функции вектора нормали. Хотя такая ситуация и может серьезно осложнить математику алгоритма, Гуро (Gouraud) пришел к выводу, что нормаль в точке вершины может быть определена таким способом, ко- который позволит сгладить закрашивание. Рассмотрим одну из вершин в середине сети, в кото- которой пересекаются четыре многоугольника (рис. 6.30). Каждый многоугольник имеет свой вектор нормали. Метод закрашивания Гуро состоит в том, что с вершинами связываются нормали, которые получаются в результате усреднения нормалей многоугольников, пересе- 268 Глава 6. Закрашивание
кающихся в этой вершине. Для примера, представленного на рис. 6.30, с выделенной верши- вершиной ассоциируется нормаль, вычисленная в соответствии с соотношением п= Многоугольники |П, + П, +П3 +П4| Метод Гуро довольно просто реализуется в программе, использующей OpenGL. От приклад- прикладного программиста требуется только правильно вычислить нормали, ассоциируемые с вер- вершинами полигональной сети. В литературе часто смешиваются метод закрашивания Гуро и интерполяционный метод закрашивания. Ино- Иногда это вызывает определенные проблемы. Как определить, какие нормали следует усреднять для вычисления нормали, ассоциируемой с определенной вершиной? Если структура данных в программе ли- линейна, т.е. все вершины перечислены в обычном линейном списке, мы не располагаем информацией о том, какие именно многоугольни- многоугольники пересекаются в определенной вершине. Сам собой напрашивается вывод, что требуется такая структура данных, которая отражала бы связи между многоугольниками в сети. Просматривая такую струк- структуру данных, можно определить те вершины, в которых следует ус- усреднять векторы нормалей. Такого рода структура данных должна связывать, как минимум, многоугольники, вершины и свойства материалов. Один из возможных вариантов ее организации представлен схематически на рис. 6.31. Нас в ней интересует информация о том, какие именно много- многоугольники пересекаются в каждой вершине. Пример изображения со сглаженным закрашиванием представлен на ил. 6 цветной вклейки. Этот цветовой куб уже использовался в примерах, описанных в главах 2 и 3. Текст программы формирования этого куба читатель най- найдет в приложении А. Восьми вершинам куба присвоены разные цвета спектра — черный, белый, красный, зеленый, синий, бирюзовый, фиолетовый и желтый. В программе установлен режим гладкого закрашивания, и OpenGL автоматически выполняет интерполя- интерполяцию цвета по мере перехода от одной вершины к другой. Рис. 6.30. Определение нормали, ассо- ассоциируемой с вер- вершиной полиго- полигональной сети Рис. 6.31. Структура данных по- полигональной сети 6.5.3. Закрашивание по методу Фонга Но даже при использовании метода Гуро в ряде случаев не удается избежать появления на изображении полос Маха. Фонг предложил интерполировать не цвет точек от вершины к вершине, а направление нормалей последовательных точек на ребрах каждого многогранни- многогранника. Рассмотрим отдельный многоугольник полигональной сети, представленный на рис. 6.33. С каждой вершиной можно связать нормаль, которая формируется усреднением нормалей к граням, пересекающимся в этой вершине. Далее с помощью билинейной интерполяции, кото- которую мы уже рассматривали в главе 4, можно интерполировать нормали по всей внутренней области многоугольника. Рассмотрим рис. 6.33. Векторы нормалей в вершинах А и В исполь- используются для интерполяции вдоль ребра, связывающего эти две вершины: 6.5. Закрашивание многоугольников 269
Рис. 6.32. Интерполяция нормалей вдоль ребра полигональной сети Рис. 6.33. Интрополирова- ние нормалей при за- закрашивании по мето- методу Фонга Аналогичную процедуру можно выполнить и для других ребер многоугольника. Затем можно вычислить нормаль в любой точке внутренней области этого многоугольника, зная распределение нормалей на его ребрах: Получив вектор нормали в определенной точке, можно выполнить все необходимые вычис- вычисления, определяемые используемой моделью отражения. Обычно этот процесс реализуется вместе с растровым преобразованием многоугольника, в результате чего отрезок между точ- точками С и D проецируется на участок строки развертки в буфере кадра. Мы рассмотрим эту тему подробнее в главе 7. Метод Фонга позволяет получить более гладкое тонирование изображения, но за это при- приходится платить весьма изрядную цену — объем вычислений резко возрастает (см. упр. 6.23). Существует множество вариантов аппаратной реализации метода Гуро, которые позволяют получать изображение приемлемого качества, практически не увеличивая время закрашива- закрашивания, чего не скажешь о методе Фонга. В результате в настоящее время метод Фонга исполь- используется только в тех системах, где не требуется формировать изображение в реальном масшта- масштабе времени. 6.6. Аппроксимация сферической поверхности рекурсивным разбиением Для демонстрации того, как на практике реализуются различные методы закрашивания криволинейных поверхностей в графической системе, следовало бы использовать сферу. Од- Однако объект этого типа в OpenGL непосредственно не поддерживается. В библиотеках утилит GL (GLU) и инструментальных средств GL (GLUT) имеются различные варианты аппрокси- аппроксимации сферы. В первой используется квадратичная аппроксимация (эту тему мы обсудим в главе 10), а во второй — полигональная. В этом разделе будет рассмотрен упрощенный алгоритм полигональной аппроксимации, на основе которого мы разработаем программу и продемонстрируем, как параметры закра- закрашивания связываются в программе с полигональной аппроксимацией криволинейных по- поверхностей. Вы познакомитесь с методикой рекурсивного разбиения, которая позволяет ап- аппроксимировать кривые и криволинейные поверхности с любой заданной точностью. 270 Глава 6. Закрашивание
Начнем мы с тетраэдра, хотя можно было бы начать с любого правильного многогран- многогранника, гранями которого являются треугольники3. Правильный тетраэдр имеет четыре гра- грани, каждая из которых — равносторонний треугольник, и определяется четырьмя верши- вершинами. Начнем со следующего массива вершин: @, 0, 1), (О, 2V2/3, -1/3), (-л/б/3, -V2/3, -1/3), (V6/3, -V2/3, -1/3). Все четыре вершины лежат на сферической поверхности единичного ра- радиуса, центр которой находится в начале координат. В упр. 6.6 вам предлагается самостоя- самостоятельно разработать метод определения координат этих вершин. Начальное приближение вычертим в режиме построения проволочного изображения. Оп- Определим в программе глобально четыре вершины, используя тип переменных point3, кото- который мы рассматривали в главе 4: point3 v[4]={{0.0; 0.0, 1.0}, {0.0, 0.942809, -0.333333}, {-0.816497, -0.471405, -0.333333}, {0.816497, -0.471405, -0.333333}}; Для вычерчивания треугольника воспользуемся функцией triangle(), текст которой представлен ниже: void triangle( point3 a, point3 b, point3 с) { glBegin(GL_LINE_LOOP); glVertex3fv(a); glVertex3fv(b); glVertex3fv(c); glEnd(); } Отдельную функцию разработаем для вычерчивания тетраэдра: void tetrahedron() triangle(v[0], v[l], v[2] triangle(v[3], v[2], v[l] triangle(v[0], v[3], v[l] triangle(v[0], v[2], v[3] } При перечислении вершин в треугольниках мы придерживались правила правой руки, так что несложно будет преобразовать эту программу для формирования не проволочного, а за- закрашенного изображения. После того как в программу будут добавлены вызовы функций инициализации, она сможет вывести на экран изображение, подобное представленному на рис. 6.34. Это простой правильный многогранник, в котором только при наличии большой фантазии можно угадать будущую сферу. Можно приблизить его к сфере, разделив каждую грань на правильные треугольники меньшего размера. Разбиение на треугольники гарантирует, что все грани нового объекта фи- фигуры будут плоскими. Существуют, по меньшей мере, три способа такого разбиения, которые представлены на рис. 6.35. Можно выполнить разбиение биссектрисами каждого внутреннего угла, которые, поскольку треугольник равносторонний, пересекутся в одной точке. Можно вычислить центр масс треугольника и соединить его с каждой вершиной. Но оба эти метода нам не подходят, поскольку в результате получаются треугольники с разными длинами сто- сторон, что не позволит сформировать на их основе правильный многогранник. Поэтому мы ^Правильный икосаэдр имеет 12 граней, каждая из которых является равносторонним треугольником. Такой многогранник прекрасно подходит для аппроксимации сферы; см [Оре97,а]. 6.6. Аппроксимация сферической поверхности рекурсивным разбиением 271
воспользуемся методом, который применялся при формировании узора Серпинского в гла- главе 2, — разделим каждую сторону треугольника пополам и соединим средние точки, как по- показано на рис. 6.35, в. а) 6) в) Рис. 6.34. Тетраэдр Рис. 6.35. Разбиение треугольника: а — биссектрисами углов; б — линиями, сходящимися в центре тяже- тяжести; в — делением сторон пополам После того как каждая грань будет разделена выбранным способом, четыре новых тре- треугольника будут лежать в плоскости прежней грани. Поэтому нам придется сдвинуть новые вершины на поверхность аппроксимируемой сферы, выполнив нормализацию их координат. Эта операция реализуется приведенной ниже программой: void normal(point3 p) double d=0.0; int i; for(i=0; i<3; d=sqrt(d); if(d > 0.0) for(i=0; i<2; d+=p[i]*p[i p[i)/=d; Таким образом, разбиение треугольника, заданного вершинами с индексами a, b и с, мо- может быть выполнено с помощью следующей программы: point3 vl, v2, v3; int j; for(j=0; j<3; j++) vl[j]=v[a]jj normal(vl); for(j=0; j<3; j++) v2[j]=v[a][j normal(v2); for(j=0; j<3; j++) v3[j]= normal(v3); triangle(v[a], v2, vl); triangle(v[c], v3, v2); triangle(v[b], vl, v3); triangle(vl, v2, v3); Этот код можно использовать в подпрограмме формирования тетраэдра и с его помощью сформировать 16 треугольных граней вместо прежних четырех. Но желательно повторить подобную процедуру п раз, получая после каждого цикла все более близкое приближение многогранника к сфере. Рекурсивно вызывая подпрограмму разбиения, мы можем контроли- контролировать количество циклов, а через него — точность аппроксимации. Первое, что нам придется для этого сделать, — модифицировать подпрограмму формиро- формирования тетраэдра и ввести в нее аргумент номера цикла п, который задает глубину рекурсии: void tetrahedron(int n) 272 Глава 6. Закрашивание
divide_triangle(v[O], v[l], v[2], n); divide_triangle(v[3], v[2], v[l], n ) divide_triangle(v[O], v[3], v[l], n ) divide_triangle(v[O], v[2], v[3], n ) Функция divide_triangle() вызывает себя в процессе рекурсивного формирования мно- многогранника до тех пор, пока переменная п остается больше нуля, а в противном случае фор- формирует треугольник. Ниже приведен программный код этой функции: divide_triangle(point3 a, point3 b, point3 с, int n) { point3 vl, v2, v3; int j; if (n>0) for(j=0; j< normal(vl); for(j=0; j< normal(v2); for(j=0; j< normal(v3); divide_triangle(a divide_triangle(c divide_triangle(b v3[j]= ,v2, vl, ,v3, v2, v3, n- сферы методом по- последовательного разбиения divide_triangle(vl ,v2, v3, n-] else triangle(a, b, c); На рис. 6.36 показан многоугольник, аппроксимирующий сфе- , „ « /^i Рис. 6.36. Аппроксимация ру, который сформирован описанной программой. Сформировав , . i * ' i ill 11 ill r'ftianut шв ли* пАп i# и/1_ приближенное представление криволинейной поверхности объек- объекта, можно приступать к включению в программу источников света и тонированию изображения этого объекта. Но сначала нужно вы- выяснить, какими средствами поддержки модели освещения и тонирования располагает графи- графическая система OpenGL. 6.7. Описание источников света в OpenGL В системе OpenGL поддерживаются источники света четырех типов, описанных выше, причем в одной программе может использоваться до восьми источников света. Каждый ис- источник света имеет свой набор параметров, в том числе и программный флаг включе- включения/выключения. Параметры, описывающие источник света, соответствуют параметрам мо- модели Фонга. Для установки векторных параметров используется функция glLightfv(), кото- которая имеет следующий формат обращения: glLightfv(source, parameter, pointer_to_array); Существует четыре векторных параметра, которые определяют положение или направление лучей источника и цветовой состав его составляющих, — фоновой, диффузной и зеркальной. Для установки скалярных параметров в OpenGL служит функция glLightf (): glLightf(source, parameter, value); 6.7. Описание источников света в OpenGL 273
Пусть, например, требуется включить в сцену источник GL_LIGHT0, который должен нахо- находиться в точке A.0, 2.0, 3.0). Положение источника сохраняется в программе в виде точки в однородных координатах: GLfloat lightO_pos[]={1.0, 2.0, 3.0, 1.0}; Если четвертый компонент этой точки равен нулю, то точечный источник превращается в удаленный, для которого существенно только направление лучей: GLfloat light0_dir[]={1.0, 2.0, 3.0, 0.0}; Далее определяется цветовой состав фоновой, диффузной и зеркальной составляющих ис- источника. Если в рассматриваемом примере источник имеет белую зеркальную составляю- составляющую, а фоновая и диффузная должны быть красными, то фрагмент программы, формирую- формирующий источник, выглядит следующим образом: GLfloat diffuse0[]={1.0, 0.0, 0.0, 1.0}; GLfloat ambient0[]={1.0, 0.0, 0.0, 1.0}; GLfloat specular0[]={1.0, 1.0, 1.0, 1.0}; glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_POSITION, Iight0_pos); glLightfv(GL_LIGHT0, GL_AMBIENT, ambientO); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseO); glLightfv(GL_LIGHT0, GL_SPECULAR, specularO); Обратите внимание на то, что в программе функция glEnable() вызывается дважды: сна- сначала для включения режима анализа освещения, а затем для включения в сцену конкретного источника света. В сцену можно включить и глобальное фоновое освещение, которое не связано ни с каким отдельным источником. Если, например требуется слабо подсветить все объекты сцены бе- белым цветом, в программу следует включить такой фрагмент кода: GLfloat global_ambient[]={0.1, 0.1, 0.1, 1.0}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, global_ambient); В модели освещения член, учитывающий расстояние до источника, имеет вид f(d) = г a + bd +cd' и постоянную, линейную и квадратичную составляющие. Соответствующие коэффициенты для каждого источника задаются индивидуально с помощью функции установки скалярных параметров glLightf (), например: glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, a); Для преобразования точечного источника в прожектор нужно задать направление луча прожектора (GL_SPOT_DIRECTION), показатель функции распределения интенсивности (GL_SPOT_EXPONENT) и угол рассеяния луча (GL_SPOT CUTOFF). Эти параметры устанавлива- устанавливаются с помощью функций glLightf () и glLightfv(). В OpenGL существуют еще два параметра, имеющих отношение к моделированию осве- освещения, — GL_LIGHT_MODEL_LOCAL_VIEWER и GL_LIGHT_MODELJTWOJIDE. С помощью парамет- параметра GL_LIGHT_MODEL_LOCAL_VIEWER в графической системе устанавливается режим "близкого" 274 Глава 6. Закрашивание
наблюдателя. Дело тут вот в чем. Если наблюдатель расположен достаточно далеко от рас- рассматриваемой сцены, то можно считать, что вектор, задающий направление на наблюдателя, для всех объектов сцены один и тот же. Это довольно существенно сокращает объем вычис- вычислений. Если же желательно воспроизвести в изображении все эффекты, связанные с положе- положением наблюдателя относительно отдельных объектов сцены, то следует установить режим "близкого" наблюдателя, воспользовавшись функцией glLightModeli(GL_LIGHT_MODEL_LOCALJ/IEWER, GLJTRUE); Второй из упомянутых параметров— GL_LIGHT_MODEL_TWO_SIDE— имеет отношение к отображению внутренней и внешней сторон поверхностей. В главе 4 отмечалось, что мы раз- различаем у каждого элементарного многоугольника две стороны — внутреннюю и внешнюю. Какая из сторон будет внутренней, а какая — внешней, определяется порядком задания вер- вершин при формировании многоугольника— правилом правой руки. Для большинства случаев в изображение включается только внешняя сторона поверхности, а потому нас не интересует, как OpenGL выполняет закрашивание внутренней стороны. Например, при отображении та- таких выпуклых тел, как сфера или параллелепипед, наблюдатель никогда не видит внутренние грани поверхностей, независимо от того, с какой стороны он рассматривает объект (рис. 6.37). Но если удалить одну из граней куба (например, на сцене появилась открытая ко- коробка) или срезать часть сферы, как показано на рис. 6.38, наблюдатель с определенной по- позиции может "заглянуть" внутрь объекта и увидеть как внешние, так и внутренние его грани. В таком случае от графической системы требуется корректно закрашивать внутренние грани всех поверхностей. Именно такой режим и задается передачей в качестве аргумента функции glLightModel() параметра GL_LIGHT_MODEL_TWO_SIDED: glLightModel(GL_LIGHT_MODEL_TWO_SIDED, GL_TRUE); В OpenGL источники света являются объектами, во многом такими же, как многоуголь- многоугольники или точки. В частности, геометрические параметры источников света преобразуются матрицей вида, поэтому можно задавать их положение, используя привычные средства пре- преобразования. На них распространяется основное правило обработки координат в OpenGL — параметры, характеризующие положение, преобразуются текущей матрицей вида в момент формирования объекта. Таким образом, формируя источник света одновременно с остальны- остальными объектами сцены, его можно "привязать" к этим объектам и затем совместно перемещать в пространстве сцены. Можно поступить и по-другому— сформировать стационарный ис- источник света, который будет оставаться на месте, пока другие объекты перемещаются. Рис. 6.37. Закрашивание вы- Рис. 6.38. Объекты с видимы- пуклых объектов ми внутренними гранями 6.8. Спецификация материалов в OpenGL В OpenGL свойства материалов соответствуют поддерживаемым параметрам источников света и модели отражения Фонга. Программист имеет возможность связывать разные мате- материалы с внутренней и внешней сторонами одной и той же поверхности. Все параметры, обра- обрабатываемые в модели отражения, задаются вызовом двух функций: glMaterialfv(face, type, pointer_to_array); glMaterialf(face, type, value); 6.8. Спецификация материалов в OpenGI 275
Для определения коэффициентов отражения для фоновой, диффузной и зеркальной со- составляющих (ка, kj, ks) по каждому из первичных цветов в программу нужно включить опре- определение трех массивов: GLfloat ambient[] = {0.2, 0.2, 0.2, 1.0}; GLfloat diffuse[] = {1.0, 0.8, 0.0, 1.0}; GLfloat specular[)= {1.0, 1.0, 1.0, 1.0}; Первый массив задает небольшое значение коэффициента отражения фоновой состав- составляющей, причем коэффициент одинаков для всех первичных цветов, что эквивалентно отра- отражению белого света. Для диффузной составляющей набор коэффициентов по отдельным цве- цветам задает в результате отражение желтого цвета, а для зеркальной составляющей коэффици- коэффициенты по всем первичным цветам опять одинаковы. Если внутренние и внешние стороны поверхностей имеют одинаковые параметры материала, то при вызове функции glMaterialfv() ей в качестве аргумента face передается константа GL_FRONT_AND_BACK: glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, diffuse); glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, specular); Учтите, что если параметры для зеркальной и диффузной составляющих одинаковы, то можно задавать их одним вызовом функции glMaterialfv(), передав ей в качестве параметра type константу GL_DIFFUSE_AND_SPECULAR. При индивидуальном определении параметров материала для внешней и внутренней сторон в качестве аргумента face используются со- соответственно константы GL_FRONT и GL_BACK. Коэффициент резкости бликов — показатель степени в члене зеркального отражения модели Фонга — задается вызовом функции glMaterialfv(), которой в качестве параметра type передается константа GL_SHININESS: glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 100.0); Свойства материала являются параметрами режима— их текущие значения ассоциируют- ассоциируются со всеми объектами, формируемыми в программе, до тех пор, пока не будут изменены с помощью glMaterialfv() или glMaterialf (L. В системе OpenGL можно включить в сцену излучающую поверхность, которая сама по себе является источником света. Этот метод используется в тех случаях, когда необходимо, чтобы на изображении был виден источник света. OpenGL не анализирует освещение светя- светящейся поверхности другими источниками света, а также влияние испускаемого ею света на другие объекты сцены. Со всей такой поверхностью ассоциируется постоянный цвет, кото- который задается так же, как и другие свойства материалов. Например, для придания такой по- поверхности сине-зеленого (бирюзового) цвета нужно включить в программу представленный ниже фрагмент: GLfloat emission[]={0.0, 0.3, 0.3, 1.0}; glMaterialfv(GL FRONT AND BACK, GL EMISSION, emission); 6.9. Закрашивание модели сферы Теперь можно вернуться к уже разработанной модели сферы и организовать в программе ее тонирование. Мы не будем останавливаться на стандартных процедурах инициализации OpenGL— полный текст программы читатель найдет в приложении А. Использование моде- модели Фонга требует прежде всего позаботиться о связывании вектора нормали с каждым пло- 4В OpenGL имеется и другое средство изменения свойств материалов — с помощью функции glColorMaterialf). 276 Глава 6. Закрашивание
ским многогранником аппроксимированной поверхности. В первом варианте будем исполь- использовать самый простой алгоритм, обеспечивающий плоское закрашивание, — определим нор- нормаль по трем вершинам многоугольника и свяжем этот вектор с первой вершиной. Следуя подходу, описанному в разделе 6.6, воспользуемся для этого операцией векторного произве- произведения, а затем нормализуем результат. Программный код функции формирования векторного произведения представлен ниже: cross(point3 a, point3 b, point3 с, point3 d); d[l]=(b[2]-a[2])*(c[0]-a[0])-(b[0]-a[0])*(c[2]-a[2]); d[2]=(b[0]-a[0])*(c[l]-a[l])-(b[l]-a[l])*(c[0]-a[0]); normal(d); } Полагая, что источники света уже сформированы в программе и что работа с ними разре- разрешена, внесем изменения в функцию triangle(), которые позволят сформировать изображе- изображение закрашенного треугольника: void triangle( point3 a, point3 b, point3 с) { point3 n; cross(a, b, c, n); glBegin(GL_POLYGON); glNormal3fv(n); glVertex3fv(a); glVertex3fv(b); glVertex3fv(c); glEnd(); } В результате программа сформирует изображение аппроксимированной сферы, представ- представленное на рис. 6.39. Обратите внимание на то, что на контуре изображения сферы отчетливо виден излом между соседними многоугольниками аппроксимирующего многогранника. Перейти от этой программы к варианту, использующему интерполяционное закрашива- закрашивание, можно довольно просто, поскольку для сферы известно направление нормали в каж- каждой точке р — нормаль должна быть направлена вдоль радиуса, а в нашем частном слу- случае— от начала координат к точке р. После этого с каждой вершиной аппроксимирующе- аппроксимирующего многогранника можно связать нормаль именно к поверхности сферы, a OpenGL будет в процессе закрашивания выполнять интерполяцию между вершинами каждого треугольни- треугольника. Таким образом, нам понадобится скорректировать текст функции triangle () следую- следующим образом: void triangle( point3 a, point3 b, point3 с) { point3 n; int i; glBegin(GL_POLYGON); for(i=0;i<3;i++) normal(n); glNormal3fv(n); glVertex3fv(a); for(i=0;i<3;i++) 6.9. Закрашивание модели сферы 277
normal(n); glNormal3fv(n); glVertex3fv(b); for(i=0;i<3;i++) normal(n); glNormal3fv(n); glVertex3fv(c); glEnd(); Результат закрашивания, полученный при выполнении этого варианта программы, пока- показан на рис. 6.40. Рис. 6.39. Изображение сферы, сформирован- сформированное методом плоско- плоского закрашивания Рис. 6.40. Изображение сферы, сформирован- сформированное методом интер- интерполяционного закра- закрашивания Но, к сожалению, такой метод определения нормали можно применять только при работе с поверхностями, которые описываются довольно простыми аналитическими зависимостями. При формировании изображения в этом варианте программы не использовался метод Гуро, иначе пришлось бы при формировании каждой вершины вычислять нормали всех пересе- пересекающихся в ней граней. В программе не создается структура данных, отражающая топологию аппроксимирующего многогранника, а потому в ней отсутствует необходимая для метода Гу- Гуро информация. Попробуйте выполнить упр. 6.9 и 6.10, в которых предлагается сформиро- сформировать необходимую структуру данных. Обратите внимание на то, что в аппроксимирующем многограннике, полученном по методу половинного деления, в каждой вершине пересекают- пересекаются шесть граней, в то время как в исходном тетраэдре — три. 6.10. Глобальное тонирование Естественно, что локальная модель освещенности имеет определенные ограничения. Рас- Рассмотрим, например, группу шаров (сфер), освещенных солнечным светом (удаленным источ- источником), как показано на рис. 6.41, а. Шар, расположенный ближе всех к солнцу, будет закры- закрывать другие шары, т.е. на них появится тень от первого шара. При использовании локальной модели поверхность каждого объекта анализируется отдельно, а значит, сформировать тень от одного объекта на другом не представляется возможным, что и показано на рис. 6.41, б. Если к тому же шары отполированы, то в реальной сцене вы увидите на соседних шарах бли- блики, вызванные зеркальным отражением света от одного шара в направлении другого. И этот эффект не может быть учтен при использовании локальной модели. 278 Глава 6. Закрашивание
а) б) Рис. 6.41. Изображение группы шаров: а— изображение создано методом гло- глобального тонирования; б — изображение создано на основе локальной модели освещенности Если требуется создать изображение, в котором присутствовали бы все описанные оптические эффекты, нужно использовать более сложную, а значит, и более медленную модель глобального распределения света, из которых на сегодняшний день наиболее по- популярными являются модели трассировки лучей и анализа излучательности. В данном разделе мы кратко опишем эти две дополняющие друг друга модели. Модель трассиров- трассировки лучей адекватно передает оптические эффекты в сценах, насыщенных зеркальными поверхностями, если, например, значительная часть объектов сделана из стекла, полиро- полированного металла и других аналогичных материалов. Модель анализа излучательности лучше передает оптические эффекты в сценах с рассеивающими поверхностями, напри- например в интерьере зданий. 6.10.1. Трассировка лучей Модель трассировка лучей является в значительной степени расширением уже рассмот- рассмотренной выше локальной модели распространения света. Она основана на том факте, что из всех лучей, испускаемых источником, на формируемое изображение влияют только те, кото- которые, в конце концов, попадают в глаз наблюдателя или в объектив камеры и проходят через центр проецирования. На рис. 6.42 показаны варианты распространения лучей, испускаемых точечным источником, в сцене, включающей несколько зеркально отражающих поверхно- поверхностей. Луч может попасть в объектив камеры прямо от источника, после однократного отра- отражения от поверхности, видимой объективом камеры, после многократного отражения от раз- разных объектов или после преломления. Большинство лучей, испускаемых источником, не попадают в камеру, а значит, и не влияют на формируемое ею изображение. Следовательно, отслеживать все лучи не имеет ни- никакого смысла — это значит впустую тратить время. Однако если изменить направление рас- распространения лучей на противоположное, то совершенно очевидно, что в создании изобра- изображения "участвуют" только те из них, которые "испускает" центр проецирования с учетом рамки отсечения в картинной плоскости (рис. 6.43). Эти-то лучи и имеет смысл анализиро- анализировать— их принято называть приведенными лучами (cast rays). При реализации метода трас- трассировки рассматриваются лучи, проходящие через отдельные элементарные участки картин- картинной плоскости, — чаще всего, отдельные пиксели. Каждый приведенный луч либо попадает на поверхность какого-нибудь объекта сцены, в том числе и источника света, либо уходит в бесконечность и теряется там. Пикселям, через которые проходят такие "потерянные" лучи, нужно присвоить цвет заднего плана. Для тех приведенных лучей, которые попали на какую- либо поверхность (будем пока что считать, все поверхности непрозрачны), нужно вычислить цвет точки пересечения с объектом. Если применить для этого модель Фонга, то сформирует- сформируется точно такое же изображение, как и при использовании локальной модели. Но можно по- поступить и по-другому. 6.10. Глобальное тонирование
Рис. 6.42. Лучи, испускаемые источ- источником Рис. 6.43. Модель приведения лучей Обратите внимание на то, что процессы, которые требуются для трассировки (моде- (моделирование объектов, проецирование и определение видимых поверхностей),— это отдель- отдельные стадии уже не раз упоминавшегося конвейера обработки информации в графической сис- системе. Но порядок выполнения вычислений совершенно другой. При выполнении тонирования в конвейере последовательность обработки задается последовательностью вершин, а при трассировке лучей — последовательностью пикселей. В главе 7 мы рассмотрим, как эти от- отличия сказываются на реализации графической системы. При трассировке лучей первым делом нужно проанализировать, освещена ли точка пересече- пересечения луча с поверхностью светом от какого-либо источника. Для этого вычисляются закрашиваю- закрашивающие {shadow) или зондирующие лучи {feeler rays) (рис. 6.44), "исходящие" из этой точки поверхно- поверхности в сторону каждого источника света. Если зондирующий луч по дороге "упирается" в ка- какой-либо объект, свет от соответствующего ис- источника не достигает этой точки поверхности и она оказывается в тени по отношению к этому источнику. Поэтому можно считать, что никаких вычислений, связанных с этим источником, для определения освещенности анализируемой точ- точки выполнять не нужно. Если все объекты непро- непрозрачны и в процессе анализа не рассматривается влияние отражения от поверхностей других объ- объектов, то получится изображение, в котором в до- дополнение к тому, что сформировано по модели Фонга, появились еще и тени. Цена, которую придется заплатить за такое дополнение, — вы- выполнение некой модификации алгоритма анализа невидимых поверхностей для каждой точки пересечения приведенных лучей с какими-либо поверхностями. Теперь предположим, что некоторые поверхности обладают свойствами зеркального отраже- отражения (рис. 6.45). Тогда можно проследить дальнейший путь зондирующего луча после отражения от промежуточной поверхности и продолжать эту процедуру до тех пор, пока не выяснится, что луч Рис. 6.44. Лучи от затененных поверхно- поверхностей 280 Глава 6. Закрашивание
ушел в бесконечность или попал в источник. Такую процедуру, очевидно, можно реализовать в ви- виде рекурсивной и принимать во внимание поглощение света при каждом очередном отражении. Рис. 6.45. Трассировка лучей с учетом от- отражения Рис. 6.46. Трассировка лучей при наличии отражения и преломления Особенно хорошие результаты дает метод трассировки лучей при формировании изображения сцен, насыщенных отражающими и преломляющими поверхностями. Используя базовую пара- парадигму этого метода, будем отслеживать дальнейшую траекторию луча, который после "столкновения" с поверхностью частично поглощается, частично диффузно рассеивается, а остав- оставшаяся часть энергии луча делится на две составляющие, из которых одна зеркально отражается, а вторая — преломляется. Если источник света видим из точки пересечения, то сначала нужно вы- вычислить "вклад" от этого источника в освещенность анализируемой точки, используя для этого стандартную модель отражения. Далее нужно разделить трассируемый луч на два: один приводит- приводится к направлению идеального отражения, а второй — к направлению идеального преломления. Эти два луча в дальнейшем будут анализироваться как и начальные приведенные лучи, т.е. они могут пересекаться с другими поверхностями, заканчиваться в каком-либо из источников или навсегда покидать сцену. При столкновении такого вторичного приведенного луча с поверхностью какого- либо объекта могут возникать новые вторичные лучи. На рис. 6.47 показана траектория прохожде- прохождения единственного луча от источника к наблюдателю в довольно простой сцене, а на рис. 6.48 — дерево лучей, сформированное для этого случая. Из этого дерева, которое динамически формиру- формируется в процессе трассировки, видно, какие лучи следует прослеживать. Рис. 6.47. Схема среды, анализи- анализируемой методом трасси- трассировки лучей Рис. 6.48. Дерево лу- лучей, соответ- соответствующее схе- схеме на рис. 6.47 6.10. Глобальное тонирование 281
Сама собой напрашивается идея организовать трассировку в виде рекурсивной процеду- процедуры, которая будет сама вызывать себя, как только выяснит, что анализируемый луч отражает- отражается или преломляется (см. упр. 6.16 и 6.17). Значительная часть вычислений при реализации трассировки лучей приходится на определение пересечения лучей с объектами сцены. По этой причине такой метод очень сложно реализовать при отображении сцен, насыщенных множеством криволинейных объектов. Поэтому большинство систем, в которых использует- используется эта технология, работает исключительно с плоскими или квадратичными поверхностями. В главе 10 будет рассмотрена только часть сложностей, возникающих при решении задачи пересечения лучей с криволинейными поверхностями. Хотя рассмотренный алгоритм трассировки лучей и учитывает диффузное рассеяние света в точке пересечения луча с поверхностью, засветка других поверхностей рассеянным в ре- результате такого отражения светом игнорируется. Если попытаться проследить и траекторию этих лучей, задача неимоверно усложнится и вряд ли какой-нибудь компьютер справится с ней за приемлемое время. Поэтому этот алгоритм имеет смысл применять только при ото- отображении сцен, в которых большинство объектов обладает зеркальными свойствами. На ил. 10 цветной вклейки показано изображение сцены, сформированное с использованием ме- метода трассировки лучей. Хотя сцена включает всего несколько предметов, но никакой другой метод не смог бы так передать в изображении их оптические свойства— прозрачность и зер- зеркальность. Обратите внимание также и на нюансировку теней на рифленых поверхностях ру- ручек — это еще один из эффектов, которые доступны методу трассировки лучей. 6.10.2. Метод анализа излучательности Метод анализа излучательности очень хорошо подходит для тонирования сцен, в кото- которых подавляющее большинство объектов имеет поверхности с диффузным рассеиванием. В этом случае можно сформировать глобальное уравнение энергетического баланса, решение которого позволит получить цвет каждой из полигональных поверхностей. Этот метод до- довольно сложен, и даже его чрезвычайно упрошенное описание не укладывается в рамки этой книги, но основные идеи метода мы все же попробуем объяснить на простом примере. Рассмотрим сцену, которая состоит только из поверхностей, характеризуемых идеальным диффузным рассеиванием (рис. 6.49). Если тонировать такую сцену в графической системе, используя удаленные источники света, то каждый из многоугольников поверхностей будет равномерно окрашен определенным цветом. Но в реальной обстановке часть света, отраженного красной стеной, попа- попадет на белую стену и придаст ей слабый красный оттенок, причем этот оттенок будет сильнее на той части белой стены, которая граничит с красной. Аналогичный эффект на красной стене создаст и диффузно отраженный свет от белой стены. Та простая модель, которую мы рас- рассматривали раньше, эти эффекты не учитывает. Решение уравнения тонирования для этой сцены, если найти при- приемлемые методы его решения, позволит корректно определить цвет в каждой точке отображаемых поверхностей. Если предположить, что все поверхности характеризуются идеальным диффузным отражением, то уравнения тонирования можно упростить до такой степени, что для его решения годятся численные методы. Базовый метод анализа излучательности предполагает, что вся сцена разбивается на большое число многоугольников малого размера— фрагментов {patches), причем каждый из фрагментов можно считать идеальным диффузным рассеивате- лем, который можно закрашивать равномерно (рис. 6.50). Остается только найти, в какой именно цвет нужно окрасить фрагмент. Это делается в два этапа. Сначала необходимо рас- Рис. 6.49. Сцена с диффузно рас- рассеивающими по- поверхностями 282 Глава 6. Закрашивание
Рис. 6.50. Разделение поверхностей объектов сцены на фрагменты смотреть пары фрагментов и определить коэффициенты формы (form /actors), которые описывают взаимное расположение фрагментов каж- каждой пары и их влияние друг на друга. После вычисления всех коэффи- коэффициентов формы общее уравнение тонирования, которое первоначально имеет форму интегрального, упрощается и принимает форму системы линейных уравнений для излучательности граней, в частности вслед- вследствие отражения. Хотя для определения коэффициентов формы требу- требуется довольно много вычислений — порядка О(п2) для п фрагмен- фрагментов, — полученные значения излучательности не зависят от положе- положения наблюдателя и для статических элементов сцены их можно не пе- пересчитывать при изменении положения наблюдателя, если, конечно, все фрагменты позволительно с приемлемой точностью считать иде- идеально рассеивающими. В результате оказывается, что при перемеще- перемещении камеры по сцене тонирование изображения выполняется практи- практически с той же скоростью, что и при использовании локальной модели. При тонировании сцен методом анализа излучательности распределенные источники све- света моделируются активными фрагментами, не только отражающими и рассевающими па- падающий на них световой поток, но и излучающими собственный. 6.11. Резюме В этой главе была рассмотрена модель освещения и распределения света, которая хорошо сочетается с конвейерной архитектурой графической системы. Эта модель достаточно точно передает множество различных световых эффектов и допускает включение в сцену источни- источников света разных видов. Хотя модель и не учитывает глобальных эффектов, связанных с мно- многократным отражением световых лучей от одного объекта к другому, она позволяет на боль- большинстве рабочих графических станций тонировать сцены, включающие полигональные объ- объекты, используя при этом модель отражения Фонга и интерполяцию распределения цвета по поверхности. Время формирования тонированной сцены с использованием такой модели сравнимо с временем формирования сцены без закрашивания. Включение процедуры тониро- тонирования в пользовательскую программу не требует больших усилий — нужно задать параметры источников света и свойства материалов поверхностей. Несмотря на ограничения, присущие локальной модели, результаты, полученные на ее ос- основе в большинстве прикладных графических систем, в том числе и в OpenGL, впечатляют. В этой главе вы могли поближе познакомиться и с методом рекурсивного разбиения мно- многогранников, который был положен в основу методики аппроксимации сферы. В последую- последующих главах, в частности 10 и 11, вы встретитесь с различными приложениями этого метода к аппроксимации кривых и криволинейных поверхностей. С этим же методом вы встретитесь и при анализе технологии моделирования геометрических объектов, когда речь пойдет о внут- внутреннем подобии широкого класса природных объектов. Эта глава завершает изложение основ компьютерной графики полигональных моделей. Материал первых шести глав позволяет вам приступить к самостоятельной разработке про- программ формирования изображений сцен, в которых присутствуют источники света и выпол- выполнено тонирование. Технология формирования более сложных изображений, предполагающих наложение текстуры материалов, требует работы на уровне отдельных пикселей, методика которой описана в главе 9. После тщательной проработки материала первых шести глав самое время приступить к программированию несложных графических приложений. Поэкспериментируйте с парамет- параметрами материалов и источников света. Попытайтесь смоделировать движение источника света в пространстве сцены в разных вариантах — синхронно с другими объектами сцены и неза- 6.11. Резюме 283
висимо от них. Конечно, на первом этапе вы встретитесь со множеством сложностей, форми- формируемое изображение будет иметь изъяны, не будет передавать всех световых эффектов, ха- характерных для реальной сцены. Например, очень сложно передать проблески света через щель между поверхностями. Часто подобные изъяны связаны с накоплением малых числовых погрешностей в вычислениях. Существует множество искусственных приемов, которые по- помогают справиться с последствиями таких ошибок. До некоторых из них вы додумаетесь са- сами в процессе работы, с другими познакомитесь в литературе, которая будет рекомендована в следующем разделе. В главе 7 мы перейдем к практическим вопросам реализации описанного метода тониро- тонирования. Хотя и в этой главе мы затрагивали работу отдельных модулей конвейера в процессе тонирования изображения, в следующей главе мы остановимся на них детально. 6.12. Рекомендуемая литература Тема использования освещения и отражения света в компьютерной графике имеет два ос- основных аспекта: физический и алгоритмический. С точки зрения физики процесса большой интерес представляет работа [Kaj86], в которой выведены уравнения энергетического балан- баланса в среде, основанные на отражательной способности отдельных объектов сцены. Модели отражения, в частности модель Торренса-Спарроу (Torrance-Sparrow) [Tor67] и Кука-Тор- Кука-Торренса (Cook-Torrance) [Coo82], основаны на моделировании криволинейной поверхности множеством малых плоских граней. Анализ этих моделей читатель найдет в работах Холла (Hall) [Ha!89] и Фоли (Foley) [Fol90]. Фонгу (Phong) принадлежит заслуга создания вычислительной модели, в которой учтены все три главные составляющие распределения световой энергии в среде, — фонового света и света, отраженного в результате диффузного и зеркального отражения от объектов сцены [Pho75]. Использовать вектор половинного направления первым предложил Блинн (Blinn) в работе [ВИ77]. Базовая модель пропускания и преломления света впервые была описана Уит- тедом (Whitted) в работе [Whi80]. Позднее эта модель была усовершенствована Хекбертом (Heckbert) и Хенрехеном (Hanrahan) [Hec84]. Гуро (Gouraud) в работе [Gon71] предложил использовать интерполяционное закрашивание. Метод трассировки лучей был изобретен Аппелем (Appel) и впервые описан в работе [Лрр68]. Множество ранних работ по трассировке лучей включено в сборник [Joy>88]. Книга Гласснера (Glassner) [Gla89] будет особенно полезна тем читателям, которые захотят разра- разработать собственный вариант программы трассировки лучей. Метод анализа излучательности впервые был использован в работе [Sie81] для анализа распространения теплового излучения. Заслуга внедрения этого метода в компьютерную графику принадлежит Горэлу (Goral) и его коллегам [Gor84]. С тех пор этот метод был значительно усовершенствован многими иссле- исследователями. В работах [СоИ85, СоИ88] описаны пути повышения его эффективности, а в [SH89J предложено ввести в рассмотрение дополнительные составляющие. Множество полезных советов, помогающих эффективно использовать средства тонирова- тонирования, которыми располагает пакет OpenGL, читатель найдет в руководстве OpenGL Program- Programmer's Guide [Ope97,a]. Упражнения 6.1. В большинстве графических систем используются простые модели освещения и от- отражения, которые были описаны применительно к тонированию поверхностей, со- состоящих из плоских многоугольников. Какие допущения сделаны в каждой из этих моделей? Для каждого из допущений дайте примеры сцен, в которых подобное уп- упрощение может привести к некорректному тонированию. 284 Глава 6. Закрашивание
6.2. При закрашивании больших многоугольников в программе, обращающейся к OpenGL, их цвет на экране оказывается неодинаковым — более ярким в одной об- области и более темным — в другой. Объясните, в чем причина такой неравномерно- неравномерности закрашивания. Как, по-вашему, можно избежать этого эффекта. 6.3. При анализе модели отражения Фонга мы не рассматривали источники света, лучи от которых по пути к анализируемой поверхности "наталкиваются " на другую по- поверхность. Объясните, почему такие источники игнорируются в этой модели. 6.4. Как повлияет на вычисления, связанные с тонированием изображения сцены, учет расстояния между отображаемой поверхностью и наблюдателем? 6.5. При обсуждении алгоритмов тонирования мы рассматривали только RGB-модель представления цвета. Приведите аргументы в пользу использования при тонирова- тонировании системы дополняющих цветов CMY. 6.6. Найдите на поверхности сферы единичного радиуса четыре точки, находящиеся друг от друга на одинаковом расстоянии. Эти точки— вершины вписанного тетраэдра. Указание. Одну из точек можно выбрать произвольно; пусть это будет точка @,1,0). Тогда другие три лежат на плоскости у = -d , где d— некоторое положительное число. 6.7. Покажите, что если v лежит в той же плоскости, что и 1, п и г, то угол половинного направления удовлетворяет условию 2\|/ = ф. Какое соотношение существует между этими углами в случае, если v не является компланарным остальным векторам? 6.8. Рассмотрите все варианты, включающие ближнего и удаленного наблюдателя, ближний и удаленный источники света, плоские и криволинейные поверхности, диффузное и зеркальное отражение. В каких вариантах можно упростить алгоритм закрашивания? В каких вариантах использование вектора половинного направления помогает упростить вычисления? Объясните свои ответы. 6.9. Разработайте структуру данных для представления рекурсивно разделяемого тетра- тетраэдра. Проследите путь по этой структуре, который нужно совершить при использо- использовании метода Гуро для закрашивания изображения сферы, аппроксимированной в результате рекурсивного разбиения тетраэдра. 6.10. Повторите упр. 6.9, но в качестве исходной фигуры используйте не тетраэдр, а икосаэдр. 6.11. Разработайте структуру данных для представления сети, состоящей из четырех- четырехугольников. Запрограммируйте раскрашивание поверхности, представленной таким способом. 6.12. Разработайте программу, которая будет выполнять рекурсивное разбиение четы- четырехугольников в сети, состоящей из многоугольников этого типа. 6.13. Пусть граница между двумя средами с разными оптическими свойствами является плоскостью. Предположим, что скорость света в одной среде v,, а в другой-— v:. Покажите, что при соблюдении закона Снелля свет дойдет от точки в одной среде до точки в другой среде за минимальное время. 6.14. Покажите, что вектор половинного направления h задает такую ориентацию по- поверхности, которая обеспечит максимальное отражение светового потока к на- наблюдателю. 6.15. Найдите такой вектор h' для преломленного луча, который определяет ориентацию поверхности, обеспечивающую максимальное отражение падающего луча в сторону наблюдателя. Упражнения 285
6.16. Хотя в этой главе мы и не рассматривали операции на уровне буфера кадра, вы мо- можете приступить к разработке собственного варианта программы трассировки лу- лучей, предполагая существование в библиотеке функции write_pixel(x, у, color), которая будет закрашивать цветом color (это может быть либо RGB-предствление, либо яркость) пиксель, имеющий в буфере адрес (х, у). Разработайте псевдокод программы ray, которая будет рекурсивно отслеживать приведенный луч. Считайте, что в вашем распоряжении имеется функция, анализирующая пересечение луча с объектом. Подумайте над тем, до каких пор нужно трассировать путь луча. 6.17. Разработайте программу трассировки лучей в сцене, которая включает только сфе- сферы, полагая, что в вашем распоряжении имеется функция окрашивания отдельного пикселя в буфере кадра. В программе используйте точное математическое описание сферы, а не ее аппроксимацию многогранником. 6.18. В программу формирования и отображения лабиринта из упр. 5.13 добавьте источ- источники света и процедуру закрашивания. 6.19. Используя в качестве прототипа программу формирования сферы, приведенную в приложении А, разработайте интерактивную программу, которая позволит размес- разместить в пространстве сцены один или два источника света и изменять свойства мате- материала поверхности. Попробуйте с помощью этой программы подобрать свойства материалов, позволяющих смоделировать на экране поверхности объектов из ме- металла, пластика или натурального камня. 6.20. По мере того как геометрическая информация проходит по "конвейеру", она под- подвергается преобразованиям поворота, сдвига, масштабирования и проецирования. Такое же преобразование выполняется и с векторами, которые определяют коси- косинусные составляющие в модели отражения Фонга. Сохраняет ли какое-либо из этих преобразований углы между векторами? Как повлияет сформулированный ответ на реализацию процедуры закрашивания? 6.21. Сравните объем вычислительных операций, требуемых для реализации методов за- закрашивания Фонга и Гуро. Примите во внимание результаты, полученные при вы- выполнении упр. 6.20. 6.22. Обобщите алгоритм формирования теней, описанный в главе 5, на обработку пло- плоских поверхностей, произвольно ориентированных в пространстве. 6.23. Модифицируйте алгоритм формирования теней таким образом, чтобы он мог рабо- работать с удаленными источниками света. Указание. Вместо перспективного проеци- проецирования используйте параллельное. 6.24. Сравните алгоритм формирования теней, описанный в главе 5, с тем, который будет использовать метод глобального тонирования сцены. Какие тени можно сформиро- сформировать одним из этих методов, но нельзя сформировать другим? 286 Глава б. Закрашивание
ГЛАВА 7 Алгоритмы формирования изображения В этой главе мы рассмотрим, как в графической системе реализуется процесс форми- формирования изображения, т.е. те методы обработки информации, о которых шла речь в предыдущих главах. Анализ алгоритмов реализации этих методов позволит вам, во- первых, получить четкое представление о том, что происходит с разработанной вами про- программой при ее выполнении графическим пакетом, — как вычерчиваются на экране линии и заливаются многоугольники, что происходит с графическими примитивами, которые не умещаются в заданный объем видимости. Во-вторых, как мы уже отмечали выше, не имея четкого представления о том, как реализуется программа, нельзя эффективно использовать графическую систему. Программист должен знать, по крайней мере, какие этапы обработ- обработки выполняются системой без привлечения значительных ресурсов, а какие требуют боль- больших вычислительных затрат и использования специализированных, а потому дорогих ап- аппаратных или программных средств. А в-третьих, знакомство с алгоритмами и средствами реализации методов отображения позволит вам открыть для себя новые возможности ис- использования средств нижнего уровня (в частности, буфера кадра), такие как наложение текстур и организация альфа-канала. Изучение средств реализации графической системы предполагает, прежде всего, подроб- подробное знакомство с соответствующими алгоритмами. Как и при анализе алгоритмов другого на- назначения, мы уделим много внимания сравнению теоретической и реальной производитель- производительности и вариантам аппаратной и программной реализации с учетом специфических характе- характеристик приложений. Хотя корректность той или иной программы на OpenGL всегда можно оценить визуально по тому, что формируется, в конце концов, на экране, существует множе- множество вариантов алгоритмов реализации одной и той же процедуры, которые нужно тщательно проанализировать, поскольку от правильного выбора во многом зависит производительность приложения. Я специально буду обращать ваше внимание на то, как базовые операции реали- реализованы в стандартных API, таких как OpenGL, PHIGS и Renderman. Эти операции по-разному организуются в системах с конвейерной архитектурой и в системах, использующих другие технологии обработки, например трассировку лучей.
7.1. Четыре основные задачи В любой системе отображения геометрических объектов, таких как многоугольники в трехмерном пространстве, по мере обработки информации решаются четыре основные задачи: ¦ моделирование; ¦ геометрическая обработка; ¦ преобразование в растр; ¦ отображение. На рис. 7.1 показано, как организовано решение этих задач в частном случае графической системы с конвейерной архитектурой. Но какой бы ни была архитектура системы, в ней все равно должны быть предусмотрены средства для решения всех четырех задач. ., % ^^ Геометрическая f ^_ Преобразование I ^_ _ „ I Моделирование г > обработка I > 8раСТр > Отображение I Рис. 7.1. Комплекс задач обработки информации в процессе формирования изображения 7.1.1. Моделирование Результатом решения задачи моделирования является множество вершин, однозначно оп- определяющих набор геометрических объектов тех типов, которые поддерживаются программ- программными и аппаратными средствами конкретной графической системы. В нескольких примерах, рассмотренных в предыдущих главах, вы уже имели возможность познакомиться с отдель- отдельными алгоритмами моделирования. В частности, в главе 6 был описан алгоритм аппроксима- аппроксимации сферической поверхности. Подробному анализу алгоритмов моделирования будет по- посвящена глава 8. Сейчас мы рассмотрим средства решения задачи моделирования как "черный ящик", на выходе которого формируются описания геометрических объектов, подлежащих отображе- отображению. Хотя моделирование— это прерогатива прикладной программы, существует еще ряд побочных задач, решение которых также возлагается на средства моделирования. Примером такой вспомогательной задачи является отсечение — процесс разделения объектов на две группы: те, которые попадают в заданную зону видимости и будут выведены на экран, и те, которые в нее не попадают. Пользователь формирует в своей программе необходимые ему геометрические объекты и полагает, что графическая система будет обрабатывать их с той же скоростью, с которой они формируются. Программа моделирования может облегчить "участь" графической системы, взяв на себя заботы по минимизации количества объектов, передаваемых для дальнейшей обработки и отображения. Такой подход означает, что средст- средства моделирования в определенной степени дублируют некоторые функции графической сис- системы, но используют при этом иные алгоритмы. В частном случае задачи отсечения приклад- прикладная программа моделирования, которая располагает информацией о специфике приложения, может использовать эвристические методы для выделения если не всех, то большинства при- примитивов, не попадающих в зону видимости еще до того, как они попадут на стандартный "конвейер" обработки. 288 Глава 7. Алгоритмы формирования изображения
7.1.2. Геометрическая обработка Цель геометрической обработки — определить, какие геометрические объекты подлежат отображению, и сформировать степень почернения или оттенок цвета этих объектов. В ходе геометрической обработки решается четыре связанные друг с другом подзадачи: нормализа- нормализация (normalization), отсечение (clipping), удаление невидимых поверхностей (hidden-surface removal) и закрашивание (shading). После выполнения этих этапов обработки наступает оче- очередь последнего — проективного преобразования трехмерной сцены в двухмерное изображе- изображение на картинной плоскости. Первый этап геометрической обработки — преобразование описания объектов из системы координат пользователя в систему координат камеры или экрана. Учитывая, что геометриче- геометрический процессор имеет дело с описаниями вершин, характеризующих отображаемые объекты, которые, в свою очередь, заданы в однородных координатах, на этом этапе выполняется по- последовательность аффинных преобразований. В большинстве графических систем при этом формируется и нормализованная зона видимости. Перспективная нормализация позволяет не только свести все виды проецирования к ортогональному, но и преобразует все виды сцены к ортогональным видам, что существенно упрощает задачу отсечения, как будет показано в разделе 7.6. Преобразование геометрических объектов выполняется как в процессе моделирования (изменяется форма объекта и его положение в пространстве сцены), так и в процессе его ви- визуализации. В конечном счете после растрового преобразования на экране в любом случае появляются только те объекты, которые находятся в зоне видимости, но было бы крайне не- неразумно подвергать все объекты растровому преобразованию и только после этого выяснить, что большая их часть не умещается на экране и все усилия на их обработку были потрачены впустую. Эту задачу нужно решить гораздо раньше. Но даже если объект и находится в зоне видимости, совсем не обязательно, что его изображение появится на экране, — этот объект может быть закрыт от наблюдателя дру- другими объектами. Алгоритмы удаления невидимых поверхностей или определения видимых поверхностей (visible-surface determination) основаны на анализе отношений в пространст- пространстве между объектами и реализуются как часть процесса геометрической обработки. Модель распределения освещенности, рассмотренная в главе 6, также оперирует с геометрической информацией для вычисления степени почернения или оттенка цвета, в частности необхо- необходимо знать ориентацию закрашиваемых поверхностей, т.е. направление вектора нормали. Следовательно, часть вычислений, связанных с закрашиванием, также реализуется в про- процессе геометрической обработки. При решении всех описанных задач используется единый математический аппарат преоб- преобразований в трехмерном пространстве, причем все операции выполняются над действитель- действительными числами, представленными в формате с плавающей точкой, т.е. реализация всех этих задач требует весьма схожих программных и аппаратных средств. 7.1.3. Растровое преобразование После выполнения проективного преобразования графическая система имеет дело только с двухмерными объектами. Хотя эти объекты и описаны в системе координат экрана, они представлены только характерными точками — вершинами. Например, после проецирования трехмерный прямолинейный отрезок становится двухмерным, представленным парой двух- двухмерных вершин. Для вывода изображения этого отрезка на экран необходимо сформировать на основе координат этих вершин последовательность точек растра— пикселей. Процесс формирования промежуточных точек графического образа объекта и составляет суть растро- растрового преобразования (rasterization, или scan conversion). В последующих разделах будут рас- 7.1. Четыре основные задачи 289
смотрены алгоритмы растрового преобразования отрезков и многоугольников, полагая, что все объекты аппроксимируются этими базовыми примитивами. С точки зрения прикладного программиста растровое преобразование — заполнение бу- буфера кадра— выполняется графической системой автоматически и прикладной программе нет нужды обращаться к отдельным пикселям экрана. Но, тем не менее, в большинстве гра- графических систем, в том числе и в OpenGL, программисту предоставляются средства для дос- доступа к отдельным пикселям в буфере кадра, что открывает новые возможности формирования изображения. Обсуждение этих возможностей мы отложим до главы 9. 7.1.4. Отображение В большинстве графических систем извлечение изображения из буфера кадра и передача его на экран выполняются автоматически, и прикладная программа никак не влияет на этот процесс. Однако существует множество проблем, связанных с качеством изображения, в ча- частности проблема появления "зазубрин" (jaggedness) на отображаемых линиях. В последую- последующих разделах мы рассмотрим алгоритмы сглаживания "зазубрин" и воспроизведения цвета при отображении. 7.1.5. Базовые стратегии реализации Существуют два основных подхода к организации выполнения перечисленных задач в графической системе, которые отличаются порядком выполнения отдельных операций. Что- Чтобы понять суть отличия, будем считать, что вся система реализована в виде единой програм- программы. В качестве исходной информации программа принимает множество вершин, специфици- специфицирующих геометрические объекты, а выходом программы является массив кодов засветки пикселей в буфере кадра. В процессе формирования изображения программа должна опреде- определить коды засветки всех пикселей в буфере, обработав описания всех геометрических объек- объектов сцены и всех источников света. Следовательно, совершенно логично предположить, что в программе должно существовать множество вложенных циклов обработки базовых перемен- переменных, представляющих пиксели, геометрические объекты и источники света. Приступая к разработке такой программы, нужно первым делом решить, что будет пред- представлять собой внешний цикл. Последовательность отдельных этапов обработки в программе зависит от того, как будет сформулирован ответ на этот вопрос. Существуют два основных варианта ответа, т.е. варианта стратегии обработки: стратегия, ориентированная на форми- формируемое изображение (image-oriented), и стратегия, ориентированная на обрабатываемые объекты сцены (object-oriented). Иногда их именуют подходом с сортировкой в начале (sort- first) и подходом с сортировкой в конце (sort-last), принимая во внимание, какое место в по- последовательности обработки отводится задаче удаления невидимых поверхностей. При реализации стратегии, ориентированной на обрабатываемые объекты сцены, внешним в программе формирования изображения является цикл по всем геометриче- геометрическим объектам: for(each_object) render(object); Такая стратегия идеально согласуется с конвейерной архитектурой графической систе- системы. Описания вершин каждого объекта последовательно обрабатываются модулями систе- системы, которые выполняют разного рода преобразования, определяют цвет и видимость. Опи- Описание многоугольника проходит через стадии обработки, схематически представленные на рис. 7.2. Обращаю ваше внимание на то, что после геометрических трансформаций при выполнении растрового преобразования очередного многоугольника его изображение мо- может попасть в любую точку экрана, т.е. до выполнения преобразований невозможно ска- сказать, где именно на экране появится изображение этого многоугольника (и появится ли во- 290 Глава 7. 4лгоритмы формирования изображения
обще). В большинстве систем, в которых реализуется стратегия, ориентированная на объ- объекты, используется конвейерная архитектура, причем часть задач решается программными средствами, а часть — аппаратными. Данные (описания вершин) последовательно прохо- проходят через все эти модули. Проецирование Преобразование в растр Буфер кадра Рис. 7.2. Обработка описания многоугольника в системе, в которой реализуется стратегия, ориентированная на объекты Поскольку такая стратегия предполагает независимую обработку каждого объекта, изо- изображение которого может находиться в любом месте экрана, для ее реализации требуется до- довольно много памяти. При обработке любого очередного объекта должен обеспечиваться доступ ко всем пикселям в буфере кадра, а также к другим вспомогательным буферам, на- например буферу глубины, который используется при удалении невидимых поверхностей. В на- настоящее время эта особенность объектно-ориентированной стратегии уже не вызывает таких затруднений с реализацией, как прежде, поскольку цены на микросхемы памяти значительно снизились, а объем хранимой информации увеличился. Сейчас никого не удивляет аппаратно реализованный геометрический процессор, способный обрабатывать свыше 1 миллиона мно- многоугольников в секунду. Поскольку каждый примитив обрабатывается по одному и тому же алгоритму, значительная часть графической системы может быть реализована аппаратно, причем специализированные модули обеспечивают вполне приемлемую скорость обработки и сравнительно дешевы. При реализации стратегии, ориентированной на формируемое изображение, внешним в программе является цикл по всем пикселям в буфере кадра или по строкам пикселей — стро- строкам развертки (scan lines): for(each_pixel) assign_a_color(pixel); При обработке каждого пикселя (или строки развертки) нужно определить, изображения каких именно объектов "накрывают" этот пиксель (или пересекаются этой строкой разверт- развертки). Каждый из таких "объектов-претендентов" потенциально может внести свой вклад в за- засветку пикселя. Преимущество такого подхода заключается в том, что в каждый момент вре- времени требуется весьма небольшой объем памяти и потенциально можно добиться того, что пиксели будут обрабатываться со скоростью, достаточной для регенерации изображения на экране. Поскольку результаты обработки при переходе от одного пикселя к соседнему не слишком отличаются, можно использовать очень эффективные алгоритмы, работающие с приращениями и учитывающие предысторию. Основной недостаток подхода, ориентирован- ориентированного на изображение, — необходимость предварительного упорядочения описания объектов сцены, которое позволяло бы довольно быстро отыскивать среди них те, которые могут вне- внести свой вклад в засветку определенного пикселя. В дальнейшем мы сосредоточимся на подходе, ориентированном на объекты, хотя для некоторых процедур разработки будут рассматриваться алгоритмы, приемлемые в обоих случаях. 7.1. Четыре основные задачи 291
7.2. Реализация геометрических преобразований Практически во всех существующих на сегодняшний день графических системах реали- реализация геометрических преобразований базируется на математическом аппарате однород- однородных координат. Использование в качестве операторов матриц размером 4x4 позволяет вы- выполнять суперпозицию преобразований перемножением соответствующих матриц. До сих пор при обсуждении преобразований мы ориентировались на используемые в OpenGL мат- матричные параметры состояния — матрицу вида и матрицу проективного преобразования. В этой главе будет использован несколько отличный подход, который, тем не менее, доволь- довольно просто привести к указанным матрицам состояния. Мы будем рассматривать преобра- преобразования как последовательные изменения системы координат, каждое из которых можно представить в виде матрицы преобразования однородных координат. Нас интересуют пять систем координат: ¦ система координат объектов (мировая); ¦ система координат наблюдателя (камеры); ¦ система координат отсечения; ¦ нормализованная система координат устройства отображения; ¦ система координат окна приложения. На рис. 7.3 показана последовательность преобразования отдельного геометрического объекта в процессе перехода от одной системы координат к другой. Сначала объект (куб) и наблюдатель специфицируются в мировой системе координат. Матрица вида {model-view matrix) задает переход от мировой системы координат к системе координат наблюдателя. Следующий переход на рис. 7.3 — проективное преобразование. Если в системе используется математический аппарат, описанный в главе 5, то после этого преобразования сохраняется трехмерность описания, но вследствие нормализации проецирования это описание будет представлять предискаженный объект, готовый к выполнению ортогонального проецирова- проецирования. В ходе нормализации зона видимости, имеющая форму пирамиды для перспективного проецирования или параллелепипеда — для параллельного, преобразуется в куб, центр кото- которого находится в начале координат, а ребра имеют фиксированную длину, составляющую 2 единицы1. После выполнения этого преобразования вершины объекта будут представлены в системе координат отсечения (clip coordinates). Объект Наблюдатель Отсечение Рис. 7.3. Последовательность преобразований Нормализация Экран И преобразование вида, и проективное преобразование представлены 4х4-матрицами од- однородных координат. Если преобразования реализованы в графической системе аппаратно, то для умножения матрицы размером 4x4 на четырехмерный вектор координат вершины ис- используется специализированная микросхема, выполняющая эту операцию за то же время, за которое выполняется перемножение двух действительных чисел. 1В OpenGL, если компонент w не равен I, считается, что грани куба суть плоскости х = ±w, у = ±w и = ±w. 292 Глава 7. Алгоритмы формирования изображения
Следующее преобразование— отсечение (clippingJ. При выполнении этой процедуры определяется, попадает ли объект или его часть в зону видимости, заданную в прикладной программе. Если компонент w вектора координат вершины не равен 1, нужно выполнить пер- перспективное деление и перейти от однородных координат к обычным трехмерным. После за- завершения процедуры остаются только примитивы, которые потенциально могут быть вклю- включены в изображение (мы еще не выполняли удаления невидимых поверхностей), т.е. те, кото- которые расположены внутри куба с гранями: ДС=±1, у=±\, 2=±\. Сформированная система координат получила название нормализованной системы коорди- координат устройства отображения (normalized device coordinates), поскольку она не зависит ни от исходных единиц описания геометрических объектов, ни от единиц растра конкретного устройства отображения, хотя вся необходимая для корректного отображения информация и сохраняется в созданном описании. Обращаю ваше внимание на то, что на этом этапе собст- собственно проецирование еще не завершено — мы по-прежнему имеем дело с трехмерным, а не двухмерным описанием. Остается выполнить заключительную процедуру ортогонального проецирования: хР=х, ур^у, zp=Q. Мы отложили выполнение ортогонального проецирования, поскольку для удаления невиди- невидимых поверхностей нам еще понадобится информация о глубине, которая содержится в треть- третьем компоненте представления точки. Последняя процедура в описываемой после- последовательности преобразует описания вершин в систему координат экрана (screen coordinates). Сформированная проекция объекта должна поя- появиться на экране в заданном видовом окне (viewport). В OpenGL это преобразование вы- выполняется уже после завершения процедуры проецирования и является, таким образом, двухмерным. В результате предыдущих преоб- 'Х '*min' Уmin' разований зона видимости была нормализована Риа 7, Преобразование в систему координат таким образом, чтобы ее стороны соответство- экрана вали сторонам видового окна (рис. 7.4), поэтому преобразование выполняется довольно просто: у, =у™«+{у-у,«п) Утх Ут В некоторых API поддерживаются трехмерные видовые окна, причем завершающий этап проективного преобразования выполняется уже после всех прочих. В этом случае к двум приведенным выше уравнениям необходимо добавить третье: Zv _ 2B литературе, особенно переводной, иногда для обозначения этой процедуры используется термин "кадрирование". —Прим. перев. 7.2. Реализация геометрических преобразований 293
7.3. Отсечение отрезков Модуль отсечения анализирует, какие примитивы или части примитивов будут включены в изображение. Примитивы, которые попадают внутрь зоны видимости, модуль отсечения пропускает дольше, а остальные отбрасывает. Возможен и третий вариант— примитив час- частично попадает в зону видимости. При обработке таких примитивов модуль отсечения разде- разделяет их на части и пропускает дальше только ту из них, которая находится в зоне видимости. В процессе конвейерной обработки процедура отсечения может выполняться поэтапно на разных стадиях. Например, модуль моделирования может взять на себя часть работы, сразу же отбросив те объекты, которые несомненно не попадут в зону видимости, и тем самым из- избавить остальные модули от лишних операций. Отсечение можно выполнять и после проек- проективного преобразования, когда из трехмерного описания объектов будет сформировано двухмерное. В OpenGL можно выполнять отсечение на уровне трехмерной зоны видимости еше до того, как система приступит к проективному преобразованию. Ниже мы рассмотрим несколько алгоритмов отсечения. Начнем с двухмерных алгоритмов отсечения отрезков не только потому, чтобы отдать дань пионерам компьютерной графики, но и из педагогических соображений— поняв идею двухмерного отсечения, значительно проще разобраться и в трехмерных алгоритмах. 7.3.1. Алгоритм Коэна-Сазерленда Суть задачи отсечения двухмерных отрезков поясняется на рис. 7.5. Предположим, что уже выполнено проецирование и в нашем распоряжении имеется двухмерное описание изо- изображения в картинной плоскости. На этой же плоскости определена и рамка отсечения, ко- которая соответствует видовому окну на экране дисплея. Все параметры заданы вещественными числами. Как видно на рисунке, отрезок АВ полностью попадает на экран, а ни один из уча- участков отрезка CD не попадает. Отрезки EF и GH нужно укоротить перед тем, как выводить на экран. Хотя отрезок прямой однозначно описывается координатами крайних точек, на примере отрезка GH вы можете убедиться, что, хотя крайние точки отрезка и лежат вне зоны видимости, часть отрезка все-таки может попадать в эту зону. Можно вычислить координаты точек пересече- пересечения прямой с рамкой видимости и использовать эту информацию для отсечения, но весь-то фокус в том и состоит, чтобы по возможности минимизи- минимизировать объем вычислений и обойтись без опреде- определения точек пересечения, которое непременно включает операцию деления чисел с плавающей Рис. 7.S. Двумерное отсечение точкой- Исторически первым, отвечающим этим требованиям, был алгоритм Коэна-Сазерленда {Cohen-Sutherland algorithm), в котором большин- большинство операций умножения и деления заменены операциями сложения и вычитания действи- действительных чисел и побитовыми логическими операциями булевой алгебры. Выполнение алгоритма начинается с продления сторон рамки отсечения в обе стороны до бесконечности, в результате чего картинная плоскость делится на девять областей (рис. 7.6). Каждой области присваивается четырехразрядный двоичный номер — характеристический код (outcode)— Ь0Ьф2Ьъ который формируется следующим образом. Пусть (х, у)— коорди- координаты некоторой точки на картинной плоскости. Тогда Ьп = 1, если >> = [О-в противном случае. 294 Глава 7. Алгоритмы формирования изображения
1001 0001 0101 1000 0000 0100 1010 0010 оно у у Рис 7.6. Характеристиче- Характеристические коды областей картинной плоскости Аналогично, bt приравнивается 1, если у < .ymim а значения Ь2 и bj определяются отношением между компонентой х и абсцис- абсциссами левой и правой границ рамки отсечения. В результате де- девяти областям присваиваются коды, представленные на рис. 7.6. При анализе отрезка первым делом определяется, в каких областях находятся его конечные точки, и им присваи- присваиваются соответствующие характеристические коды. Эта про- процедура требует выполнения восьми операций вычитания на каждый отрезок. Рассмотрим отрезок, конечные точки которого имеют ха- характеристические коды O| =outcode(jfb^i) и о2 = outcode(x2, у2). Возможны четыре варианта сочетания характеристических кодов двух конечных точек (рис. 7.7). 1. (О) = о2 = 0.) Обе конечные точки лежат внутри рамки отсечения — этот случай пред- представлен отрезком АВ на рис. 7.7. Весь отрезок при этом также находится внутри рамки отсечения и может быть передан дальше для выполнения растрового преобразования. 2. (о} * 0, о2-0 или наоборот.) Одна точка находится внутри рамки отсечения, а вто- вторая — вне ее (отрезок CD на рис. 7.7). В этом случае отрезок необходимо разделить. Отличный от нуля характеристический код указывает, в какой внешней области нахо- находится одна из конечных точек и какая именно граница (или границы) рамки пересека- пересекается отрезком. В этом случае необходимо вычислить одну или в худшем варианте две точки пересечения отрезка с границами рамки. Обращаю ваше внимание на то, что по- после вычисления первой точки пересечения (например, с горизонтальной границей) можно сформировать ее характеристический код и выяснить, требуется ли определять вторую точку пересечения (на этот раз с вертикальной границей). 3. (О| &о2*0.) По результату побитовой операции AND над характеристическими кодами крайних точек можно выяснить, лежат ли они по одну сторону от границы рамки или по разные. Если результат отличен от нуля, то конечные точки лежат по одну сторону от какой-либо границы, а значит, весь отрезок лежит вне рамки отсечения и его можно спокойно отбросить (отрезок EF на рис. 7.7). 4. (О| & о2 = 0.) Обе конечные точки лежат вне рамки отсечения, но по разные стороны от двух ее границ. Этот вариант представлен отрезками GH и IJ на рис. 7.7, и мы не можем с уверенностью сказать, пересекает отрезок зону видимости или нет. Требу- Требуется более тщательный анализ — нужно вычислить точку пересечения с одной из границ рамки и проанализировать характеристические коды крайних точек двух но- новых отрезков. Для анализа характеристических кодов дос- достаточно только булевых побитовых операций над двоичными числами, которые выполняются очень быстро. Вычисление точек пересечения выполняется чрезвычайно редко и только там, где без этой информации не обойтись,— во втором и четвертом вариантах сочетаний харак- характеристических кодов. Алгоритм Коэна-Сазерленда прекрасно ра- работает при анализе сцен, содержащих множест- множество прямолинейных отрезков, немногие из кото- которых действительно попадают в формируемое изображение. В этом случае большинство отрезков отбрасывается в результате логического Рис. 7.7. Варианты расположения отрезков, анализируемых алгоритмом Коэна- Сазерленда 7.3. Отсечение отрезков 295
анализа характеристических кодов конечных точек, что не занимает много времени. Алго- Алгоритм без труда можно распространить и на трехмерный случай. Но мы не рассмотрели, как вычислять точки пересечения отрезка с границами рамки. Ка- Какой из возможных методов предпочесть, зависит от формы представления отрезка в про- программе, но в любом случае потребуется, как минимум, одна операция деления действитель- действительных чисел. Если используется стандартная явная форма представления прямой у = тх + h, где т — коэффициент наклона прямой, a h — ордината точки пересечения с осью у, то зна- значения т и И можно вычислить по известным координатам крайних точек. Но в такой форме нельзя представить вертикальную линию — известный всем недостаток явной формы пред- представления. Если необходимо вычислять точки пересечения только в процессе реализации ал- алгоритма Коэна-Сазерленда, то соответствующая функция будет довольно простой, поскольку отрезок всегда пересекается линией, параллельной одной из координатных осей. Но, скорее всего, нам потребуется функция, которая бы определяла точку пересечения прямых произ- произвольной ориентации. Поэтому лучше ориентироваться на представление прямой в парамет- параметрической форме. Этот способ представления прямой рассматривался в главе 4, а примени- применительно к кривым и поверхностям мы рассмотрим его в главе 10. 7.3.2. Алгоритм Лианга-Барского В алгоритме Лианга-Барского (Liang-Barsky) используется параметрическая форма пред- представления прямой. Пусть отрезок задан двумя крайними точками pi = [x\,y\Y и р2 = [-^.Уг] Т- Тогда параметрические уравнения прямой примут вид = A-сфг, + ах2, = (\-а)у1 + ау2. Переходя к матричной форме записи, получим: р(ос) = A-а)р, +ар2. Параметрические уравнения такого вида описывают прямую любой ориентации, не делая исключений и для вертикальной или горизонтальной. При изменении значения параметра а от 0 до 1 точка на прямой перемещается от конечной точки отрезка р, до другой конеч- конечной точки р2. Отрицательные значения параметра а задают точки, расположенные на про- продолжении отрезка перед конечной точкой рь а значения а > 1 — точки, расположенные на продолжении отрезка после конечной точки р2. Рассмотрим прямую и отрезки на ней, представленные на рис. 7.8,а. Если прямая не па- параллельна одной из границ рамки (а такая ситуация очень просто выявляется), то она пересе- пересекает продолженные в бесконечность в обе стороны границы рамки в четырех точках, причем точкам пересечения соответствуют четыре значения параметра: at, a2, a3, сц. Значение а, со- соответствует точке пересечения отрезка с продолжением нижней горизонтальной границы, а2 — левой вертикальной границы, а3 — верхней горизонтальной границы, а сц — правой вертикальной границы. Если отрезок пересекает рамку видимости, то одно из этих значений соответствует точке, в которой отрезок "входит" в зону видимости, а другое — точке, в кото- которой отрезок "покидает" зону. Не будем сейчас останавливаться на том, как определяются значения параметра в точках пересечения, а обратим внимание на порядок, в котором распо- располагаются значения параметров для точек пересечения с горизонтальными и вертикальными границами. Для варианта, представленного на рис. 7.8,а, порядок будет следующим: 1 > а, > а3 > сс2 > а, > 0. 296 Глава 7. Алгоритмы формирования изображения
Все четыре точки пересечения лежат на отрезке, причем значения параметров для двух из них — ос2 и ос3 — задают крайние точки того участка отрезка, который попадает в зону види- видимости. Теперь рассмотрим случай, представленный на рис. 7.8,6. При таком расположении анализируемого отрезка имеем следующий порядок значений параметров в точках пересече- пересечения с продолжением границ рамки: 1 > сц > а2 > а3 > (Xj > 0. Отрезок сначала пересекает нижнюю и верхнюю границы, а затем — левую и правую. По- Поэтому параметры ос2 и а3 поменялись местами по сравнению со случаем, представленным на рис. 7.8,а. Аналогично можно проанализировать и другие варианты размещения анализируе- анализируемого отрезка относительно рамки видимости. а) б) Рис. 7.8. Два варианта размещения отрезка относительно рамки видимости Для эффективной реализации такой стратегии нужно разработать алгоритм, который ми- минимизировал бы количество операций с числами в формате с плавающей точкой, необходи- необходимых для определения точек пересечения. Множество отрезков можно отсеять, не вычисляя все четыре точки пересечения. При определении точек пересечения также желательно не ис- использовать, где это возможно, операции деления действительных чисел. Значение параметра для точки пересечения с верхней границей рамки находится из соотношения У 2 ~ У1 Аналогичные соотношения имеют место и для точек пересечения с тремя остальными грани- границами рамки. Приведенное соотношение можно представить в виде сс3 Ау = Д.ут где Практически весь логический анализ можно выполнить в терминах Дутах и Ду и аналогичных им для других границ рамки видимости и таким образом избавиться от большинства опера- операций деления. Они понадобятся только в тех случаях, когда окажется, что отрезок пересекает зону видимости и что нужно определить точные значения параметров в точках пересечения границ. Преимущество описанного алгоритма по сравнению с алгоритмом Коэна-Сазерленда состоит в том, что при пересечении зоны видимости можно сразу определить участок, попа- попадающий внутрь зоны, и избежать повторного анализа. Существует и множество других алгоритмов отсечения отрезков на картинной плоскости, но в отличие от рассмотренных их не удается распространить на трехмерный случай. 7.3. Отсечение отрезков 297
7.4. Отсечение многоугольников Необходимость в отсечении многоугольников возникает в разных ситуациях. Естествен- Естественно, что отсечение необходимо выполнять перед или во время растрового преобразования, учитывая заданные размеры области экрана, отводимой для изображения. При этом иногда желательно, чтобы рамка отсечения принимала форму, отличную от прямоугольной. При формировании теней и удалении невидимых поверхностей возникает необходимость выпол- выполнить отсечение одного многоугольника другим многоугольником произвольной формы. На- Например, на рис. 7.9 показана тень, сформированная отсечением многоугольника, ближайшего к источнику света, другим, более удаленным от источника многоугольником. Методы сгла- сглаживания границ и наложения, которые будут рассмотрены в главе 9, также используют отсе- отсечение одного многоугольника другим. Можно разработать алгоритм отсечения многоугольников, взяв за основу алгоритм отсе- отсечения отрезков и применив его последовательно ко всем ребрам многоугольника. Но при этом нужно не забывать, что многоугольник представляет собой единый объект и что при его отсечении может быть сформировано несколько новых объектов-многоугольников. Рассмотрим представленный на рис. 7.10, а многоугольник общего вида—"вогнутый" (concave) многоугольник. Если применить к нему отсечение прямоугольной рамкой, то полу- получим три новых многоугольника (рис. 7.10,6). Но, к сожалению, реализовать модуль отсече- отсечения, который был бы способен из одного объекта сформировать несколько, довольно сложно. Поэтому чаще всего результат отсечения, подобный показанному на рис. 7.10,6, представля- представляется в программе как единый многоугольник, для чего в модуле отсечения выполняется объе- объединение фрагментов в единый объект (рис. 7.11). В таком многоугольнике некоторые ребра накладываются друг на друга, что иногда служит источником проблем при решении других задач. Рис. 7.9. Формирование тени с помощью отсечения одно- одного многоугольника другим б) Рис. 7.10. Отсечение многоугольника общего вида: а — до отсечения; б — после отсечения Рис. 7.11. Слияние много- многоугольников При отсечении одного выпуклого (convex) многоугольника другим выпуклым многоуголь- многоугольником, в частности прямоугольной рамкой зоны видимости, принципиально не может обра- образоваться больше одного многоугольника, причем тоже выпуклого (см. упр. 7.3). Таким обра- образом, в этом случае проблема "размножения" не возникает, и в большинстве графических сис- систем либо разрешается формировать только выпуклые многоугольники, либо имеются средства рассечения (tessellation) многоугольника общего вида на выпуклые (рис. 7.12K. Для отсечения многоугольников прямоугольной рамкой можно использовать оба описан- описанных выше алгоритма отсечения отрезков, применяя их в цикле по отношению к ребрам ана- анализируемого многоугольника. В системах с конвейерной архитектурой более эффективным является другой подход, предложенный Сазерлендом (Sutherland) и Ходжменом (Hodgeman). 3В графической системе OpenGL функция рассечения многоугольников входит в состав библиотеки GLU. 298 Глава 7. Алгоритмы формирования изображения
Рис. 7.12. Рассечение многоугольника общего вида Модуль отсечения отрезка можно рассматривать как "черный ящик", на вход которого подаются две пары координат конечных точек исходного отрезка, а на выходе формируются две пары координат конечных точек того участка этого отрезка, который лежит внутри зоны отсечения, или не формируется ничего, если отрезок не пересекает зону отсечения. 2, У2) а) Модуль отсечения б) <*з< (*4< Рис. 7.13. Отсечение отрезка: а — точки разделения; б — модуль отсече- отсечения как "черный ящик" Будем рассматривать рамку отсечения как объект, сформированный пересечением че- четырех бесконечных прямых, соответствующих верхней, нижней, правой и левой границам зоны видимости. Тогда и модуль отсечения отрезка можно представить как последователь- последовательность четырех более простых модулей (конвейер), каждый из которых имеет дело только с одной границей. (х2, у2) (х2, у2) 'max а) б) Рис 7.14. Отсечение верхней границей рамки: а — геометрия; б — модуль, реализую- реализующий операцию Рассмотрим отсечение верхней границей рамки. Модуль, выполняющий эту операцию, получает на входе координаты крайних точек отрезка и значение ymtx в качестве параметра, задающего зону отсечения (рис. 7.14). Нетрудно показать, используя подобие треугольников 7.4. Отсечение многоугольников 299
на рис. 7.15, что если отрезок пересекает границу, то коорди- координаты точек пересечения определяются уравнениями * *+( ЛН (х2, у2) Уг ~ У\ В результате модуль отсечения вернет одну из трех пар коор- координат: {(Х\,У]),(Х2,У2)}, {(Х\,У\),(хъУтах)} ИЛИ {(хъУтах)> (Х2>У2)}- По этой же схеме организуются и модули отсечения нижней, правой и левой границами рамки, только в соответствующих Рис. 7.15. Пересечение отрезка уравнениях используются другие параметры рамки, а координаты с веРхней границей рамки хчу могут меняться ролями. Каждый модуль работает независи- независимо, но целесообразнее организовать из них конвейер (рис. 7.16). Такой конвейер, реализованный аппаратно, будет корректно обрабатывать четыре вершины. На рис. 7.17 показано, как с помощью такого аппаратного модуля реализуется отсечение многоугольника. (х,-У,) х3, у3) 2, У2) 1<Х2<У2) , Верх 1 > Низ (х3, у3) Рис. 7.16. Объединение отдельных модулей в конвейер: а— геометрический смысл операции; б — структура конвейера Отсечение сверху | Отсечение ^ ¦^^^^^¦ , ..снизу , Рис. 7.17. Отсечение многоугольника 300 Глава 7. Алгоритмы формирования изображения
7.5. Отсечение примитивов других типов В главах 1-6 были рассмотрены программы, в которых все геометрические объекты фор- формировались из прямолинейных отрезков и плоских многоугольников. Криволинейные объек- объекты, которые мы еше будем подробно рассматривать в главе 10, в графических системах, как правило, аппроксимируются маленькими плоскими многоугольниками. В системах с конвей- конвейерной архитектурой вы практически всегда встретитесь с тем или иным вариантом модулей отсечения, ориентированных на работу с отрезками или плоскими многоугольниками. Но, тем не менее, встречаются ситуации, когда нужно выполнить отсечение геометрических объ- объектов еще до того, как они "попадут" в аппаратный конвейер, или применить алгоритмы, оп- оптимизированные с учетом специфики примитивов других типов. 7.5.1. Прямоугольные оболочки Предположим, что нам нужно обрабатывать многоугольники общего вида, имеющие до- довольно много вершин (рис. 7.18,а). Для этого можно воспользоваться одним из алгоритмов от- отсечения, рассмотренных выше, и применить его для обработки каждого из ребер многоугольни- многоугольника. Однако очень часто оказывается, что весь многоугольник лежит вне зоны видимости и что все усилия, затраченные на анализ его многочисленных ребер, пропали впустую. Значительно сократить затраты поможет предварительный анализ, в котором используется прямоугольная оболочка (bounding box) сложного объекта, в нашем примере— многоугольника (рис. 7.18,а). Такая оболочка— это прямоугольник минимального размера со сторонами, параллельными границам рамки отсечения, в который вписывается анализируемый объект. Определение пара- параметров прямоугольной оболочки требует только последовательного просмотра всех вершин многоугольника и выявления максимального и минимального значений координат хну. Далее можно применить алгоритм отсечения к оболочке, что реализуется значительно быстрее, поскольку, во-первых, оболочка имеет только четыре стороны, а во-вторых, эти стороны парал- параллельны границам рамки. Рассмотрим три варианта, представленные на рис. 7.19. Тот многоуголь- многоугольник, который находится выше рамки, сразу отбрасывается, поскольку нижняя сторона его оболоч- оболочки находится выше верхней границы рамки. Многоугольник, который расположен внутри рамки отсечения, также не имеет смысла "запускать" на конвейер отсечения, поскольку его оболочка полностью умещается в зоне видимости. Процедуру отсечения нужно выполнять только по отно- отношению к третьему многоугольнику, так как его оболочка частично перекрывает зону видимости. Прямоугольная оболочка может использоваться и в трехмерном пространстве, причем она форми- формируется еще на этапе моделирования и сохраняется вместе с описанием объекта. а) б) Рис. 7.18. Использование прямоугольной оболочки: а — многоугольник и рамка отсечения; б — многоугольник, прямоугольная оболочка и рамка отсечения >*^_ 1 J Рис. 7.19. Отсечение с при- применением прямоуголь- прямоугольных оболочек объектов 7.5. Отсечение примитивов других типов 301
7.5.2. Кривые, поверхности и надписи Поскольку существует огромное множество типов кривых и криволинейных поверхно- поверхностей, которые можно задавать аналитически, разработать обобщенный алгоритм их отсечения не представляется возможным. Потенциальные сложности, которые возникают при попытке разработки такого алгоритма, можно продемонстрировать даже на примере двухмерных кри- кривых (рис. 7.20). Вычислить точки пересечения квадратичной кривой с границами прямо- прямоугольной рамки можно сравнительно просто, хотя эта процедура и сложнее, чем при работе с прямолинейными отрезками. Но для более сложных кривых, особенно тех, которые имеют характер спирали, довольно сложно определить не только координаты точек пересечения, но даже их количество. Избежать этих сложностей помогает кусочно-линейная аппроксимация. Поможет и анализ прямоугольных оболочек криволинейных объектов, которые позволят сра- сразу выделить те объекты, которые полностью попадают или не попадают в зону видимости. а) Иис. 7.20. Отсечение кривых б) Технология обработки надписей значительно отличается в разных графических системах. Во многих системах пользователю предлагаются средства, позволяющие регулировать сте- степень детализации при воспроизведении надписи. Здесь есть две крайности. В некоторых сис- системах надпись запоминается в виде двоичного образа на уровне пикселей и накладывается на изображение аппаратно без всякой геометрической обработки, а отсечение выполняется уже на стадии занесения образа в буфер кадра. В других системах надпись имеет такой же статус, как и любой другой геометрический объект, и точно так же "запускается" на конвейер обра- обработки. В системе OpenGL поддерживаются оба этих режима, причем прикладному програм- программисту предоставляется возможность выбрать тот из них, который больше подходит для раз- разрабатываемого приложения. Если выбирается режим растровых символов, то они передаются в буфер кадра, минуя все геометрические преобразования. Если же выбирается режим штри- штриховых символов, то надписи из таких символов обрабатываются наравне с прочими геомет- геометрическими объектами. В других графических системах, в частности PHIGS и GKS, поддер- поддерживаются режимы работы с надписями, занимающие промежуточное положение между дву- двумя описанными крайностями. В них текст является отдельным типом примитива, располагающим множеством атрибутов. Помимо атрибутов размера и цвета символов име- имеются и управляющие атрибуты, задающие режим поведения системы, если надпись частично отсекается границами зоны видимости, — можно частично воспроизводить такую надпись либо не выводить ее на экран вовсе. 7.5.3. Отсечение в буфере кадра Конечно, выполнение отсечения можно и отложить до этапа растрового преобразования и заполнения буфера кадра, но целесообразнее каким-либо способом все-таки отсеять те объек- объекты, которые явно не вписываются в заданную зону видимости, а на последнем этапе выпол- выполнять отсечение только тех из них, которые по самой своей природе являются растровыми, в частности блоков пикселей. Такие объекты мы рассмотрим в главе 9. 302 Глава 7. Алгоритмы формирования изображения
7.6. Отсечение в трехмерном пространстве В трехмерном пространстве при выполнении отсечения анализируется положение объекта по отношению к пространственной зоне видимости, а не по отношению к двухмерной рамке. Наиболее просто рассмотренные двухмерные алгоритмы распространяются на трехмерное пространство в том случае, когда зона видимости имеет форму параллелепипеда, заданного соотношениями (рис. 7.21) •*min — X S Л'тах) Утт — У — .Уmax» -min — - — ^тах* Kin' " IX У Z ) * тох' 'тох' тох' Рис. 7.21. Отсечение в трехмерном пространстве Три алгоритма отсечения (Коэна-Сазерленда, Лианга-Барского и Сазерленда-Ходжмена), рассмотренные в предыдущих разделах, можно модифицировать применительно к трехмер- трехмерному случаю. Несложно сформировать и трехмер- трехмерные прямоугольные оболочки и использовать их на этапе предварительного анализа перед собственно отсечением. Модификация алгоритма Коэна-Сазер- Коэна-Сазерленда заключается в том, что вместо 4-разрядного характеристического кода используется 6-разряд- 6-разрядный. Один из дополнительных разрядов устанавли- устанавливается в 1, если точка находится перед передней от- отсекающей плоскостью, а другой, если она находится за задней отсекающей плоскостью (рис. 7.22). Ме- Методика анализа и принятия решения в трехмерном случае ничем не отличается от двухмерного. В модифицированный алгоритм Лианга-Барско- Лианга-Барского нужно добавить уравнение г(а) = A-а)Г| + аг2 Рис 7.22. Разбиение пространства на и использовать, таким образом, параметрическое области при использовании трех- „ п мерной модификации алгоритма уравнение прямой в трехмерном пространстве. В Коэна-Сазерленда процессе анализа нужно рассматривать шесть точек пересечения прямой с плоскостями, ограничивающими зону видимости, но использовать при этом ту же логику принятия решения. В конвейерную архитектуру модуля отсечения нужно добавить два дополнительных узла, анализирующих отсечение передней и задней плоскостя- плоскостями зоны видимости. 7.6. Отсечение в трехмерном пространстве 303
Но главные изменения по сравнению с двухмерным случаем заключаются в том, что для вычисления точек пересечения сопоставляются прямая и плоскость, а не прямая и прямая. При вычислении точек пересечения используется параметрическое представление прямой и плоскости (рис. 7.23). Обозначив через п вектор нормали к плоскости, а через р0 — точку, лежащую в этой плоскости, запишем систему уравнений, решением которой является значе- значение параметра а для точки пересечения прямой с плоскостью: р(а) = A-а)р, +ар2, п • (р(аЬро) = 0. Решение этой системы имеет вид n-(p2-P.)' Вычисление по этой формуле требует шести операций умножения действительных чисел и одной операции деления. Но следует учитывать, что приведенная формула справедлива для прямой и плоскостей общего вида. Стандартные же зоны видимости, например при ортого- ортогональном проецировании (рис. 7.24), имеют граничные плоскости, параллельные координат- координатным, а вычисление точки пересечения с такой плоскостью упрощается и требует только од- одной операции деления, как и в двухмерном случае. Рис 7.23. Пересечение прямой с плоскостью у = у ' ' mm Рис 7.24. Отсечение при ортогональном проецировании Несколько сложнее обстоит дело при использовании косоугольного проецирования (рис. 7.25). В этом случае зона видимости уже не является правильным параллелепипедом, а ее грани не параллельны координатным плоскостям. Вот тут-то нам и поможет нормализация проецирования, которую мы рассматривали в главе 5. Эта процедура позволяет привести косоугольную проек- проекцию к ортогональной, выполнив предыскажение исход- исходного объекта. Преобразование скоса превращает косо- косоугольный параллелепипед зоны видимости в правиль- правильный. На рис. 7.26,а представлен вид сверху на косоуголь- косоугольный параллелепипед видимости, внутри которого нахо- находится отображаемый объект — куб, до выполнения пре- преобразования. Эти же объекты после выполнения норма- нормализации показаны на рис. 7.26,6. Если с точки зрения проективного преобразования количество вычислений, необходимых для косоугольного проецирования или нормализации и ортогонального проецирования, одно и то же, то отсечение в последнем слу- случае выполняется гораздо проще. Именно поэтому следует отдать предпочтение использова- использованию нормализации проецирования. Приведенный пример показывает, насколько важно ана- Рис 7.25. Отсечение при косоуголь- косоугольном проецировании 304 Глава 7. Алгоритмы формирования изображения
лизировать отдельные этапы обработки геометрических объектов в совокупности, учитывая их взаимное влияние на общую производительность графической системы. Картинная плоскость Картинная плоскость , Объект ^^ ^^^ ¦ Искаженный объект Зона видимости -*\ ^Н \. ^Шг Новая зона видимости а) 6) Рис 7.26. Предыскажение зоны видимости при нормализации проецирования: а — вид сверху до выполнения предыскажения; б — вид сверху после пре- преобразования скоса Еще более вескими становятся аргументы в пользу выполнения нормализации при пер- перспективном проецировании. Выполняя описанную в главе 5 процедуру перспективной нор- нормализации, мы получаем в результате возможность использовать в процессе отсечения пря- прямоугольную зону видимости, что, естественно, значительно упрощает вычисления. В OpenGL существует возможность задавать дополнительные, произвольно ориентиро- ориентированные плоскости отсечения. В графической системе, которая обладает такими возможно- возможностями, приходится использовать обобщенную процедуру отсечения, реализованную про- программно, что, естественно, не лучшим образом сказывается на производительности системы в целом. (Бесплатный сыр бывает только в мышеловке.) 7.7. Удаление невидимых поверхностей После того как вершины прошли через все этапы геометрических преобразований и про- процедуру отсечения, "на конвейере" остались только те геометрические объекты, которые по- потенциально могут попасть в формируемое изображение. Но прежде чем приступать к их рас- растровому преобразованию, нужно решить еще одну задачу — удалить объекты, перекрывае- перекрываемые с точки зрения наблюдателя другими объектами. Ниже будут описаны методы решения этой задачи для сцен, состоящих из полигональных объектов. Такое ограничение вводится по той простой причине, что в большинстве существующих на сегодняшний день графических систем все объекты, в конце концов, аппроксимируются множеством плоских многоугольни- многоугольников. Отрезки прямых также обрабатываются подобными методами после их незначительной модификации (см. упр. 7.7). Анализ существующих на сегодняшний день алгоритмов удаления невидимых поверхно- поверхностей позволяет выявить существенную разницу между подходами, ориентированными на отображаемые объекты и формируемое изображение. Особую роль в реализации этой про- процедуры играет возможность использовать предысторию анализа, когда результаты, получен- полученные на предыдущем шаге, учитываются в последующих. Пусть сцена состоит из к трехмерных непрозрачных многоугольников, каждый из кото- которых можно считать независимым объектом. Обобщенный подход, основанный на анализе пространства объектов, предполагает попарное сравнение положения всех объектов по от- отношению к наблюдателю (центру проецирования). Рассмотрим два таких объекта: много- многоугольники А и В. Существует четыре варианта их взаимного положения (рис. 7.27): ¦ А полностью закрывает В — в изображение нужно включить только А; ¦ В полностью закрывает А — в изображение нужно включить только В; ¦ А и В не перекрываются — в изображение нужно включить оба объекта, А и В; 7.7. Удаление невидимых поверхностей 305
А и В частично перекрываются — нужно вычислить, какая часть перекрываемого мно- многоугольника будет видима наблюдателю. Рис. 7.27. Два многоугольника: а— В частично закрывает А; б— А частично закры- закрывает В; в — Аи В не перекрываются; г — Б полностью закрывает А Для того чтобы оценить сложность процесса, будем считать, что анализ, какой вари- вариант сочетания многоугольников имеет место для текущей пары, и вычисление видимой части многоугольника представляют собой единую операцию. Будем обрабатывать мно- множество имеющихся в сцене объектов итеративно. Берем один из к многоугольников и попарно сравниваем его с каждым из оставшихся к-\ многоугольников. После выполне- выполнения этого цикла будет известно, какая часть выбранного многоугольника будет видима наблюдателю (если таковая вообще существует). Закончив с анализом одного много- многоугольника, перейдем к следующему, который точно так же будем попарно сравнивать с оставшимися к-2 многоугольниками. Такой итерационный цикл продолжается до тех пор, пока не останутся только два многоугольника, которые мы и сравним в последнем итерационном цикле. Несложно показать, что оценка сложности всего процесса имеет вид 0{к2). Следовательно, даже не вдаваясь в детали выполнения операций сравнения, можно прийти к выводу, что объектно-ориентированный подход лучше всего применять при отображении сцен, состоящих из относительно небольшого количества объектов. Подход, основанный на анализе пространства изображения, имеет много общего с моделированием процесса визуализации с помощью приведения лучей (рис. 7.28). Рас- Рассмотрим луч, исходящий из центра проецирования и проходящий через определенный пик- пиксель на картинной плоскости. Этот луч где-то пересекается с плоскостью каждого из к многоугольников. Мы можем вычислить координаты этих точек, выяснить, лежит ли точка пересечения во внутренней области многоугольника, и затем выбрать тот много- многоугольник, который первым пересекается этим лучом. Этот пиксель затем можно окрасить в цвет, который присвоен точке пересечения с выбранным многоугольником после выполне- выполнения тонирования. Если размер видового окна изображения п х т пикселей, то такая опера- операция должна быть повторена птк раз4, т.е. оценка сложности имеет вид О(к). Полученная оценка является оценкой сверху, но в любом случае тот факт, что сложность процедуры при таком подходе пропорциональна количеству объектов, а не квадрату этого количества заставляет нас отдать предпочтение анализу видимости в пространстве изображения5. Но следует учесть, что работа на уровне отдельных пикселей может привести к образованию зубцов на границах, разделяющих изображения отдельных объектов, что не свойственно алгоритмам, работающим в пространстве объектов. 4При необходимости повысить качество изображения можно "пропустить " через один пиксель не один, а несколько лучей и каким-то способом суммировать результаты. 5На практике алгоритмы удаления невидимых поверхностей, работающие в пространстве изображе- изображения, справляются с задачей, не перебирая все объекты для каждого луча (см. упр. 7.9). 306 Глава 7. Алгоритмы формирования изображения
Рис. 7.28. Удаление невидимых поверхностей в простран- пространстве изображения 7.7.1. Удаление нелицевых граней Рис. 7.29. Анализ лицевой грани В главе 6 отмечалось, что в OpenGL можно задавать режим тонирования только внешних граней многоугольников. Если сцена состоит только из выпуклых многогранников, то внутрен- внутренние грани многоугольников никогда не будут "показываться на глаза" наблюдателю. Но у такого выпуклого многогранника часть граней будет повернута от наблюдателя (так называемые нелицевые грани) и, следовательно, тоже не будет видна на- наблюдателю. В конце концов, нелицевые грани будут перекры- перекрыты другими, и это сможет выявить универсальный алгоритм удаления невидимых поверхностей, но можно облегчить ему работу, сразу же отбраковав нелицевые грани. Принцип от- отбраковки {culling) нелицевых граней поясняется на рис. 7.29. Внешняя сторона многоугольника будет видима наблюдателю только в том случае, если нормаль к плоскости многоугольника будет направлена к наблюдателю. Обозначим через 6 угол ме- между внешней нормалью плоскости многоугольника и направ- направлением на наблюдателя, тогда многоугольник будет представлять лицевую грань объекта только в том случае, если -90° < 6 < 90°, или, что то же самое, если cos9 > 0. Второе условие анализируется проще, поскольку знак косинуса легко определяется по скалярному произведению и условие можно сформулировать следующим образом: n-v>0. Можно еще упростить этот анализ, если учесть, что обычно он выполняется после пре- преобразования в нормализованную систему координат устройства отображения. В этой сис- системе координат все виды являются ортогональными, и, следовательно, направление на на- наблюдателя задается координатной осью z. Таким образом, в однородных координатах век- вектор v имеет вид " v = 7.7. Удаление невидимых поверхностей 307
Если уравнение плоскости, в которой лежит многоугольник, имеет вид ах + by + cz + d = О, то для определения, является ли грань лицевой или нелицевой, нам потребуется только проверить знак коэффициента с. Такую проверку можно выполнить как программно, так и аппаратно. При использовании отбраковки нелицевых граней нужно только тщательно проанализировать, допустимо ли это делать с учетом специфики конкретного приложения. В графической системе OpenGL включение режима отбраковки нелицевых граней выпол- выполняется функцией glCullFace(). 7.7.2. Алгоритм z-буфера Одним из самых распространенных алгоритмов удаления невидимых поверхностей явля- является алгоритм z-буфера. Алгоритм довольно просто реализуется как программно, так и аппа- аппаратно, прекрасно сочетается с конвейерной архитектурой графической системы и может вы- выполняться со скоростью, соответствующей скорости обработки вершин остальными модуля- модулями конвейера. Хотя алгоритм отталкивается от пространства изображения, он включает цикл просмотра многоугольников, а не пикселей и может быть реализован в процессе растрового преобразования (этот процесс будет подробно рассмотрен в разделе 7.10). Предположим, что выполняется растровое преобразование одного из двух многоугольни- многоугольников, показанных на рис. 7.30. Используя принятую в графической системе модель закрашива- закрашивания, можно вычислить цвет точек пересечения с каждым из многоугольников луча, исходя- исходящего из центра проецирования и проходящего че- через заданный пиксель картинной плоскости. Кро- Кроме того, одновременно нужно определить, являет- является ли данная точка пересечения видимой наблюда- наблюдателю, — из двух анализируемых видимой будет только точка, ближайшая к наблюдателю. Следо- Следовательно, если преобразуется многоугольник В, его образ должен появиться на экране только в случае, если расстояние z2 меньше расстояния z, до многоугольника А. И наоборот, если преобра- преобразуется многоугольник А, то его цвет не должен никак повлиять на формируемый цвет пикселя и изображение этого многоугольника в этой облас- области экрана не должно появиться. Но есть неболь- небольшая загвоздка в реализации этой простой идеи — обработка многоугольников выполняется последо- последовательно, а потому, преобразуя в растр один многоугольник, мы не располагаем информацией о том, как по отношению к нему расположены другие. Решается эта проблема с помощью промежуточного буфера, в который записывается информация о глубине размещения каждого многоугольника. Предположим, что в нашем распоряжении имеется буфер — назовем его z-буфером, — который имеет такую же организацию, как и буфер кадра, а разрядность каждой ячейки дос- достаточна для хранения информации о глубине с приемлемой для качества изображения точно- точностью. Например, если формат изображения 1024x1280 пикселей и расстояние в графической системе представляется действительным числом с однократной точностью, то в z-буфере должно быть 1024x1280 32-разрядных ячеек. Перед началом растрового преобразования объ- объектов сцены в каждую ячейку заносится код, соответствующий максимальному расстоянию Рис. 7.30. Алгоритм z-буфера 308 Глава 7. Алгоритмы формирования изображения
от центра проецирования6. Буфер кадра в исходном состоянии заполняется кодами цвета фо- фона. В процессе растрового преобразования объектов каждая ячейка z-буфера содержит значе- значение расстояния до ближайшего из обработанных ранее многоугольников вдоль проецирую- проецирующего луча, проходящего через соответствующий пиксель пространства изображения. Процесс заполнения z-буфера выглядит в первом приближении следующим образом. Все многоугольники в описании сцены последовательно, один за другим, подвергаются растровому преобразованию одним из методов, описанных в разделе 7.10. Для каждой точки на много- многоугольнике, которая проецируется на определенный пиксель, вычисляется расстояние до центра проецирования. Это расстояние сравнивается с уже хранящимся в той ячейке z-буфера, которая соответствует формируемому пикселю. Если расстояние до текущего многоугольника больше хранящегося в z-буфере, значит, ранее был обработан многоугольник, расположенный ближе к наблюдателю, который, таким образом, загораживает текущий. Следовательно, данная точка те- текущего многоугольника не будет видима и информацию о ее цвете не нужно заносить в буфер кадра. Если же вычисленное расстояние меньше того, что хранится в z-буфере, значит, текущий многоугольник сам загораживает те, что были обработаны ранее, и его код цвета должен заме- заменить в буфере кадра код цвета, сформированный ранее. Одновременно вычисленное только что расстояние заносится в соответствующую ячейку z-буфера7. При работе с графической системой OpenGL в прикладной программе нужно перед формированием любого нового изображения в явном виде подать команду инициализа- инициализации z-буфера. Объем дополнительных вычислений при использовании алгоритма z-буфера относительно невелик, поскольку в нем можно использовать предысторию обработки. Когда выполняется растровое преобразование многоугольника, то точки многоугольника обрабатываются после- последовательно, строка за строкой (об этом мы подробно поговорим в разделе 7.10). Многоуголь- Многоугольник расположен на плоскости, которая описывается уравнением ах + by + cz + d - 0. ПуСТЬ {Х],У\,2\) И (x2,y2,Z2) Две ТОЧКИ внутренней области многоугольника, а следо- следовательно, они принадлежат этой плоскости. Если обозначить то уравнение плоскости можно записать в приращениях: аАх + ЬАу + cAz = 0. В этом уравнении фигурируют координаты видового окна, и, следовательно, для каждой строки растра значение у постоянно. Усло- Условие Ау = 0 выполняется до тех пор, пока об- обрабатывается текущая строка, а по мере пе- перемещения по строке от одного пикселя к s ах + by d = 0 Рис. 7.31. Алгоритм z-буфера в приращениях 6Если перед растровым преобразованием выполняется перспективная нормализация, то само понятие центра проецирования исчезает и вместо него появляется направление проецирования, так как все проеци- проецирующие лучи после такой нормализации становятся параллельными. Однако это преобразование никак не влияет на алгоритм z-буфера, поскольку в этом алгоритме вместо расстояния от центра проецирования можно использовать расстояние от произвольной плоскости, в частности от плоскости z = 0. 7В OpenGL для настройки режима работы системы в случае, если сравниваемые расстояния равны, ис- используется функция glDepthFunc(). 7.7. Удаление невидимых поверхностей 309
другому наращивается только значение х, причем значение Дх также постоянно. Таким об- образом, при переходе от одного пикселя к другому на той же самой строке растра выполня- выполняется соотношение Дг = --Дх. а Это значение нужно вычислить только один раз при обработке определенного многоугольни- многоугольника и далее использовать, пока обработка этого многоугольника не завершится. 7.7.3. Сортировка по глубине Метод сортировки по глубине {depth sort) реализует подход к решению проблемы удале- удаления невидимых поверхностей, основанный на анализе пространства объектов. Мы рассмот- рассмотрим этот метод применительно к сценам, состоящим из плоских многоугольников, но его можно распространить и на другие классы объектов. Метод сортировки по глубине является вариантом еще более простого алгоритма, получившего название алгоритма маляра {painter's algorithm). Предположим, что имеется набор многоугольников, отсортированных по расстоянию от них до наблюдателя. В примере, показанном на рис. 7.32,а, имеются два многоугольника. С точки зрения наблюдателя они выглядят так, как показано на рис. 7.32,6, — один много- многоугольник частично закрывает от наблюдателя второй. Для корректного отображения такой сцены нужно определить, какая часть дальнего многоугольника перекрыта тем, что располо- расположен ближе, "вырезать" ее, а оставшуюся часть подвергнуть растровому преобразованию и вывести на экран. Но можно поступить и по-другому — некоторые считают, что именно так делает маляр, нанося новый рисунок поверх старого. Образы объектов можно формировать в порядке от наиболее удаленного к менее удаленным. В результате образы объектов, располо- расположенных ближе к наблюдателю, автоматически перекроют образы объектов, расположенных дальше, и необходимость в каком-либо "искусственном" анализе перекрытия полностью от- отпадет. Для такого метода придуман даже специальный термин — отображение многоуголь- многоугольников от дальнего к ближнему {back-to-front rendering) . Но реализация описанного подхода требует решения двух проблем: как рассортировать многоугольники в пространстве сцены и что делать, если многоугольники пересекаются. Обе эти проблемы решаются методом сорти- сортировки по глубине, хотя для некоторых ситуаций можно разработать и более эффективные ме- методы (см. упр. 7.10). а) б) Рис. 7.32. Алгоритм маляра: а — два многоугольника и на- наблюдатель; б — многоугольник А частично закрывает от наблюдателя многоугольник В Предположим, что для каждого многоугольника уже вычислены параметры прямо- прямоугольной оболочки. Следующая операция — отсортировать все многоугольники по степени *Некоторые прикладные проблемы молено решить обратным методом — отображением многоугольни- многоугольников от ближнего к дальнему ffront-to-back rendering^ Эту тему мы затронем в главе 12. 310 Глава 7. Алгоритмы формирования изображения
удаления от наблюдателя, причем в качестве основного параметра сортировки берется максимальное значение координаты г-оболочки. Именно эта операция и дала название всему алгоритму — сортировка по глубине {depth sort). Предположим, что после сорти- сортировки многоугольники "выстроились" в порядке, показанном на рис. 7.33. По оси ординат на этом рисунке отложены значения параметров z-оболочек. Если минимальная глубина — значение rmin — для данного многоугольника больше, чем максимальная глубина следую- следующего за ним многоугольника, то после формирования их образов на экране в обратном по- порядке будет получено вполне корректное изображение сцены. Например, многоугольник А на рис. 7.33 расположен позади всех остальных, и его образ следует формировать первым. Но, к сожалению, в других случаях дело обстоит гораздо сложнее, и одними значениями параметров г-оболочек не обойтись. Если z-оболочки двух многоугольников перекры- перекрываются, то порядок формирования их образов выявля- выявляется индивидуальным сравнением каждой пары таких объектов. В алгоритме сортировки по глубине для это- этого используется множество довольно сложных прове- проверок. Рассмотрим пару многоугольников, z-оболочки которых перекрываются (рис. 7.34). Первой выполня- выполняется самая простая проверка— сравнение х- и у- оболочек (рис. 7.34,а). Если либо х-, либо у-оболочки не перекрываются, многоугольники не закрывают друг друга с точки зрения наблюдателя и порядок формиро- формирования их образов не имеет значения9. Но и в случае, если х- или у-оболочки перекрываются, можно оты- отыскать порядок формирования их образов, который по- позволит корректно воспроизвести сцену на экране. Та- Такой случай показан на рис. 7.35. Все вершины одного многоугольника лежат по одну сторону от плоскости другого, и выявить такую ситуацию позволит индивидуальный анализ всех вершин одного из многоугольников (см. упр. 7.12). к X 1 X О m О а. D О о. с о 3. X 0 0 ч 0) х к э J ? 1 А А т В 1 Е . z i i 1 1 1 1 г z . С min г D Многоугольники Рис. 7.33. z-оболочки сортируемых многоугольников 4 / А / ч ж /X б) Рис. 7.34. Анализ перекрытия х- и у-оболочек многоугольников: а — х-оболочки не перекрываются; б — у-оболочки не пе- перекрываются Но остаются еще две ситуации, с которыми справиться особенно трудно. Во-первых, три многоугольника могут перекрывать друг друга циклически, как показано на рис. 7.36. В таком случае вообще не существует такого порядка формирования образов многоугольников, который позволил бы получить корректное изображение. Лучшее, что Сравнивать х- и у-оболочки можно только при параллельном проецировании. Здесь мы опять встреча- встречаемся с доводом в пользу предварительной перспективной нормализации. 7.7. Удаление невидимых поверхностей 311
можно предложить, — разделить по крайней мере один из них на две части и попытаться по- повторить анализ образовавшегося набора из четырех многоугольников. Во-вторых, возможна ситуация, когда один многоугольник "пронзает" другой (или дру- другие), как показано на рис. 7.37. В такой ситуации придется детально проанализировать пере- пересечение, используя обобщенные методы отсечения одного многоугольника общего вида дру- другим аналогичным многоугольником. Если пересекающиеся многоугольники имеют много вершин, можно попытаться использовать какие-либо искусственные методы, учитывающие специфику ситуации и требующие меньшего объема вычислений. Оценить производительность метода сортировки по глубине очень сложно, поскольку многое зависит от специфики приложения. Например, работая с многоугольниками, пред- представляющими собой грани сплошных объемных объектов, мы заранее знаем, что многоуголь- многоугольники не могут пересекаться. Но как бы там ни было, необходимость в предварительной сор- сортировке явно указывает на то, что оценка производительности, как функции от сложности отображаемой сцены, не может быть линейной. Рис. 7.35. Многоугольники с пе- Рис. 7.36. Циклическое Рис. 7.37. Многоугольник, рекрывающимися оболоч- перекрытие много- "пронзающий" куб коми угольников 7.7.4. Алгоритм построчного сканирования В те не столь уж давние времена, когда память стоила довольно дорого и о специализиро- специализированных БИС для создания графических систем с конвейерной архитектурой только мечтали, метод построШьго сканирования считался наиболее эффективным алгоритмом удаления не- невидимых поверхностей. И по сей день отдельные идеи этого метода применяются в специа- специализированных системах. Алгоритм построчного сканирования сочетает удаление невидимых поверхностей с растровым преобразованием. Хотя детальное обсуждение растрового преоб- преобразования еще ждет нас в последующих разделах, основные идеи этого алгоритма мы можем рассмотреть, воспользовавшись рис. 7.38. На этом чертеже вы видите два многоугольника, пересекающихся в пространстве. При растровом преобразовании многоугольников, которое выполняется последовательно, строка за строкой, можно воспользоваться методом вычисле- вычисления глубины в приращениях, рассмотренным ранее. Но присмотревшись к этому чертежу внимательнее, можно отыскать еще более эффективный способ обработки. Перемещаясь вдоль строки растра i, мы пересекаем ребро а многоугольника А. Поскольку это первый мно- многоугольник, который пересекается строкой, нет смысла выполнять какие-либо вычисления, анализирующие его глубину. Начиная с этого пикселя, последующим присваивается код цве- цвета, соответствующий окраске многоугольника А. Но когда по мере перемещения по строке мы встретимся со следующим ребром этого многоугольника, Ь, то это будет означать, что с обработкой многоугольника А на этой строке покончено и можно присваивать последующим пикселям код цвета фона. Так будет продолжаться до тех пор, пока не встретится ребро с многоугольника В; поскольку перед этим на строке отображался фон, можно опять не при- 312 Глава 7. Алгоритмы формирования изображения
нимать во внимание информацию о глубине и считать дальнейший участок строки принадле- принадлежащим образу многоугольника В. Более сложная ситуация возникает на строке j. Сначала мы опять обнаруживаем ребро а и, не прибегая к анализу глубины, можем, начиная с этого пик- пикселя, присваивать последующим код цвета, соответствующий окраске многоугольника А. Но далее встречается ребро с "конкурирующего" многоугольника, и здесь-то без анализа глуби- глубины не обойтись. Этот анализ придется проводить для всех пикселей строки j, пока не встре- встретится ребро d, причем для анализа можно использовать описанный выше метод вычисления глубины в приращениях. Хотя на первый взгляд кажется, что этот алгоритм во мно- многом напоминает алгоритм z-буфера, между ними есть одно принципиальное отличие — алгоритм построчного сканирова- сканирования при обработке каждого пикселя должен просматривать все многоугольники, претендующие на отображение в данной зоне экрана, а алгоритм z-буфера в каждый момент времени имеет дело только с одним многоугольником. Реализация алгоритма построчного сканирования требует тщательно продуманной организации данных о многоугольниках, которая позволит бы- Рис. 7.38. Метод построчно- стро отбирать среди них те, которые претендуют на отображе- го сканирования ние в текущей строке. Чаще всего структура данных базируется на массиве указателей, по одному на каждую строку, каждый из которых указывает на специ- специальную структуру описания ребер многогранников, пересекаемых этой строкой. 7.8. Растровое преобразование Теперь перейдем к анализу методов выполнения завершающей стадии создания изо- изображения — алгоритмам растрового преобразования примитивов и заполнения буфера кадра. Мы детально рассмотрим только два вида примитивов — отрезки и многоугольни- многоугольники, причем и те и другие заданы своими вершинами. Считаем, что все необходимые гео- геометрические преобразования, в том числе и отсечение, уже выполнены и образы всех ос- оставшихся примитивов должны включаться в конечное изображение сцены. Чтобы не от- отвлекаться, пренебрежем необходимостью удаления невидимых поверхностей и преоб- преобразуем каждый объект индивидуально, не используя информацию о его п|вдтранственных отношениях с другими объектами сцены. Будем также считать, что в результате проектив- проективного преобразования все вершины представлены только двумя координатами, причем в системе координат экрана. Рассмотрим такую организацию буфера кадра: массив из п х т элементов, каждый из ко- которых соответствует одному пикселю, причем элемент с адресом в массиве @, 0) соответст- соответствует пикселю, расположенному в левом нижнем углу экрана. Для установки определенного кода цвета пикселя используется единственная функция графической системы, формат вызова которой имеет вид write_pixel(int ix,int iy, int value); Аргумент value может представлять собой либо индекс цвета в режиме индексного задания цвета, либо 32-разрядное число, представляющее цвет в формате RGBA. Адрес в буфере кадра, естественно, является дискретной величиной, но экранные координаты могут быть представле- представлены и действительными числами. Мы вправе говорить о том, что точка имеет координаты на эк- экране, например F3.4, 157.9), но центр ближайшего к ней пикселя находится в точке F3, 158) или F3.5, 157.5). В разных устройствах отображения пиксели могут иметь разную форму и раз- размеры (об этом речь пойдет в разделе 7.11), но сейчас будем считать, что все они являются квад- квадратиками, причем координатами пикселя является центр этого квадратика, а размер в точности 7.8. Растровое преобразование 313
равен шагу размещения пикселей на экране10. Будем также считать, что извлечение информации из буфера кадра производится другим процессом параллельно с его заполнением модулем рас- растрового преобразования. Такое предположение, которое справедливо для большинства графиче- графических систем, использующих так называемую двухпортовую память, избавляет нас от необходи- необходимости обращать внимание на детали вывода изображения из буфера на экран. Простейший алгоритм растрового преобразования линейного отрезка известен как алго- алгоритм ЦДЛ (DDA algorithm). Этот алгоритм "позаимствован" у специализированных вычисли- вычислителей — цифровых дифференциальных анализаторов, — которые в прежние времена исполь- использовались для численного решения систем дифференциальных уравнений. Поскольку прямая описывается дифференциальным уравнением вида dy/dx = т, где т — коэффициент наклона, формирование прямолинейного отрезка есть не что иное, как решение этого простейшего уравнения численным методом. Пусть отрезок задан крайними точками Cxi,j>i) и {х2,у2) (рис. 7.39). Будем считать, что значения координат округлены до ближайшего целого, т.е. отрезок начинается и заканчивает- заканчивается точно в центре соответствующего пикселя". Коэффициент наклона отрезка определяется соотношением _ у: - у у, _ .v2 - .v, Ax Будем считать, что 0 < т < 1. При других вариантах значения т можно использовать симмет- симметричную модификацию алгоритма. В основе алгоритма лежит запись кода цвета в ячейку бу- буфера, представляющую определенный пиксель, для каждого значения ix с помощью функции write pixel, по мере того как х изменяется от х} до дг2. Между элементарными приращения- приращениями &х и 8у координат отображающей точки, которая "перемещается" от одной конечной точки отрезка к другой, существует связь: by = т Ьх. Если на каждом шаге увеличивать значение х на 1, т.е. принять Ьх = 1, то координату у ото- отображающей точки нужно увеличить на величину Ъу = т. Хотя каждое очередное значение дг представляет целое число, очередное значение у таковым не является, поскольку т в общем случае — дробь. Поэтому значение у нужно округлить и найти таким образом подходящий пиксель, предсц^ляющий текущее положение отображающей точки (рис. 7.40). В результате наш алгоритм будет выглядеть следующим образом: for(ix=xl, ix <= х2, y+=m; write_pixel(x, round(y) , line_color); } Здесь функция round() выполняет округление действительного числа до ближайшего це- целого. Причина, по которой мы ограничили значение коэффициента наклона единицей, стано- становится ясной при взгляде на рис. 7.41. Тот алгоритм, который мы сейчас рассматриваем, оты- отыскивает значение координаты у отображающей точки, задаваясь приращениями ее координа- координаты х. При больших значениях коэффициента наклона координаты х и у должны "поменяться WB OpenGL считается, что координаты центров пикселей представляют собой полусумму соседних це- целых чисел. В таком выборе есть определенный резон — см. упр. 7.19. "Такое предположение не является необходимым условием для вывода алгоритма. Если для представле- представления конечных точек используется формат с фиксированной точкой и если в этом же формате выполняются все арифметические операции, то алгоритм будет иметь точно такой лее вид, но точность преобразова- преобразования повысится. 314 Глава 7. Алгоритмы формирования изображения
ролями" — задаваясь значениями координаты у отображающей точки, будем определять под- подходящее значение координаты х (рис. 7.42). Заодно обращаю ваше внимание на то, что ис- использование такой симметричной модификации алгоритма устраняет потенциальные пробле- проблемы с горизонтальными и вертикальными отрезками. Попытайтесь самостоятельно сформули- сформулировать варианты этого же алгоритма для отрицательных значений коэффициента наклона. {Х2'У2\ / у Axv) у *-\-Ax- / л У щ Щ т -я Рис. 7.39. Прямоли- Прямолинейный отрезок в системе коорди- координат экрана Рис. 7.40. Формирование пикселей при использо- использовании алгоритма ЦДЛ Рис 7.41. Искажение изображе- изображения при больших значениях коэффициента наклона Рис. 7.42. Симметричная модификация алго- алгоритма ЦДА Поскольку прямолинейный отрезок определяется вершинами, то при назначении кода цвета каждому очередному пикселю можно также использовать интерполяцию, но не про- пространственных координат, а "цветовых". Кроме того, в процессе интерполяции отрезка мож- можно, управляя цветом, накладывать на образ отрезка штрихи, точки и т.п. Все эти дополни- дополнительные эффекты не рассматриваются как часть алгоритма растрового преобразования, но часто реализуются синхронно с ним. 7.9. Алгоритм Брезенхэма Описанный алгоритм ЦДА при всей его простоте и наглядности обладает одним недостат- недостатком— при формировании координат каждой отображающей точки необходимо выполнять сложение действительных чисел. Более простой целочисленный алгоритм предложил Брезенхэм (Bresenham) в 1965 году, и с тех пор этот алгоритм используется практически во всех графиче- графических системах, причем существуют как программные, так и аппаратные его реализации. Как и при анализе алгоритма ЦДА, будем считать, что конечные точки отрезка представ- представлены целочисленными координатами {хъу\) и (х2,у2), а коэффициент наклона удовлетворяет условию 0 < т < 1. 7.9. Алгоритм Брезенхэма 315
Это условие достаточно критично для работоспособности алгоритма. Суть алгоритма в том, что при построении растрового образа отрезка выбирается пиксель, ближайший по вер- вертикали к соответствующей прямой. Рассмотрим промежуточный шаг растрового преобразо- преобразования отрезка после того, как будет сформирован пиксель (/ + M2J + 1/2) (рис. 7.43). Извест- Известно, что прямая описывается уравнением у = тх + И. При х = / + 1/2 прямая проходит от центра пикселя — точки с координатами (/+ 1/2,у+ 1/2)— не далее, чем на 1/2 шага между пикселями12. В противном случае образ прямой вследствие округления не прошел бы через этот пиксель. При переходе к следующей точке, для которой х = / + 3/2, учитывая ограничения, наложенные на значение коэффициента наклона, образ отрезка должен пройти либо через пиксель с центром в (/ + 3/2,у' + 1/2), либо через пиксель с центром в (/ + 3/2, j + 3/2). Проблема выбора решается с помощью характеристической переменной d - b-a, где а и Ъ — суть расстояния между прямой в точке х = i + 3/2 и верхним и нижним пикселями- кандидатами (рис. 7.44). Если d отрицательно, прямая проходит ближе к центру нижнего пиксе- пикселя и в качестве следующего пикселя образа отрезка выбирается (/ + 3/2,у + 1/2); в противном случае выбирается пиксель с центром в (/ + 3/2,у + 3/2). Можно было бы определить значение d, вычислив у = тх + Ь, но тогда теряется одно из заявленных достоинств алгоритма — использо- использование только целочисленных операций, поскольку т является действительной дробью. f-- 1.. 1 2 -—;- -f- -¦¦— -¦i—- '3 у = mx + h - l + - у = тх + h Рис. 7.43. Алгоритм Брезенхэма Рис 7.44. Характеристическая пере- переменная алгоритма Брезенхэма Поэтому, во-первых, операции над числами с плавающей точкой заменяются операция- операциями над числами с фиксированной точкой, а во-вторых, все вычисления выполняются в приращениях. Изменим определение характеристической переменной. Будем использовать d = (*2 ~Х\)(Ь-а) = ЩЬ-а). Это изменение не влияет на выбор пикселей-кандидатов, поскольку для принятия реше- решения используется только знак характеристической переменной, а он в новой формулировке будет таким же, как и в прежней. Подставляя в выражение d значения а и Ь, полученные из уравнения прямой, и принимая во внимание, что х-, - л:, Дд: Л = у,- тхг, 12При описании алгоритма предполагается, что центры пикселей имеют координаты, сдвинутые на 1/2 шага относительно целочисленной сетки. 316 Глава 7. Алгоритмы формирования изображения
приходим к выводу, что d принимает только целочисленные значения. Итак, нам удалось из- избавиться от операций над действительными числами, но непосредственное вычисление d все же требует большого количества вычислений, хотя и целочисленных. Поэтому несколько изменим подход. Пусть dk— значение d при х = к + 1/2. Попробуем вычислить dk+x в приращениях относительно dk. Значение приращения будет зависеть от того, выполнялось ли на предыдущем шаге приращение координаты у пикселя. Возможные вари- варианты представлены на рис. 7.45. Для параметров аи b — расстояний между прямой и пиксе- пикселями-кандидатами — справедливы следующие соотношения: если на предыдущем шаге произошло приращение у, то ак+\ =ак-т+ 1, т-\, = (Ьк-ак) + 2т-2\ в противном случае a*+i = ак-т, Ьн\ = Ьк + т, = (bk-ak) + 2т. Умножив полученные выражения на Ах, получим, что при переходе к следующему значе- значению х значение d изменяется либо на 2Ау, либо на 2(Ау-Ах). Этот результат можно записать в таком виде: [2Ду, если dk < 0; Длс) - в противном случае. В результате вычисление каждого очередного пикселя растрового образа отрезка потребует только одной операции сложения целых чисел и анализа знака. Алгоритм настолько эффек- эффективен, что в СБИС он реализован в виде одной инструкции. Метод вычисления исходного значения d0 я предлагаю читателям разработать самостоятельно (см. упр. 7.14). Рис. 7.45. Приращение значений параметров я и Ь 7.10. Растровое преобразование многоугольников Одним из главных преимуществ первых растровых систем отображения перед векторны- векторными была возможность формировать изображения областей со сплошной закраской. В те не столь уж давние времена было невозможно в реальном времени сформировать оттенки цвета отдельных пикселей области, а потому термин растровое преобразование многоугольника означал формирование его образа с заливкой внутренней области одним цветом. В отличие от растрового преобразования отрезков, которое практически во всех графических системах реализуется одним и тем же алгоритмом, для растрового преобразования многоугольников 7.10. Растровое преобразование многоугольников 317
существует множество методов, причем отдать предпочтение какому-либо одному довольно сложно. Выбор во многом зависит от архитектуры графической системы, и мы сосредоточим внимание на тех методах, которые совместимы с конвейерной архитектурой и способны под- поддерживать тонирование внутренней области многоугольника разными оттенками цвета. Об- Обзор прочих методов будет сделан в разделах 7.10.4-7.10.6. 7.10.1. Тест принадлежности внутренней области Внутренняя область простого плоского многоугольника определяется однозначно, если многоугольник выпуклый. Он корректно отображается в любой графической системе, в том числе и в OpenGL. Но иногда приходится включать в изображение и многоугольники общего вида, для которых с первого взгляда трудно сказать, что же именно следует считать его внут- внутренней областью. Если все вершины многоугольника не лежат в одной плоскости, то прихо- приходится работать с его проекциями (см. раздел 7.10.2) или использовать для формирования внутренней области плоскость, определяемую первыми тремя вершинами этого многоуголь- многоугольника. Для многоугольников, имеющих пересекающиеся ребра (самопересекающихся много- многоугольников), нужно сформулировать правило принятия решения, как определять принадлеж- принадлежность точки внутренней области такого многоугольника. Концептуально задача заполнения внутренней области многоугольника определенным цветом или трафаретом сводится к задаче определения принадлежности точки его внутренней области. Для решения этой задачи наиболее широкое распространение получил тест пересечения (crossing) или тест на четность-нечетность (odd-even test). Пусть р — точка внутри много- многоугольника. Любой луч, исходящий из точки р в бесконечность, должен пересечь нечетное количество ребер многоугольника. Любой луч, исходящий из точки, расположенной извне многоугольника, и пересекающий его, должен пересечься с четным количеством его ребер прежде, чем уйдет в бесконечность. Следовательно, можно так определить внутреннюю точ- точку многоугольника: провести через эту точку прямую и, взяв в качестве исходной точку на этой прямой, заведомо расположенную извне многоугольника, двигаться вдоль прямой и подсчитывать количество пересечений с ребрами многоугольника до тех пор, пока не будет достигнута тестируемая точка. Если в этот момент окажется, что зафик- зафиксированное количество пересечений нечетно, тестируемая точка при- принадлежит внутренней области многоугольника, а в противном случае — внешней. На рис. 7.46 заливкой показаны внутренние области, найден- найденные таким способом в многоугольнике с самопересекающимися ребра- ребрами в виде пятиконечной звезды. Тест четности-нечетности легко встраивается в стандартные алго- алгоритмы отображения, причем в качестве "испытательных" прямых ис- используются строки растра. Рис. 7.46. Закраска Но в некоторых случаях желательно закрасить звезду так, как по- многоугольника казано на рис. 7.47. Это можно сделать с помощью теста охватыва- ссамопересе- ния (Minding test). В этом тесте многоугольник рассматривается как кающимися своего рода узел из нескольких петель, завязанный вокруг точки или реорами внут- линии Идея состоит в том, что в тестируемой точке помещается на- ренних оолас- „ . . чало системы полярных координат. Далее рассматривается зонои- ных тестом рующий луч, исходящий из этой точки, второй конец которого дви- чептости- жется вдоль контура многоугольника (направление движения значе- нечетности ния не имеет). В тесте фиксируется количество полных оборотов (Minding number) IV, которое совершит этот луч в процессе обхода контура, причем оборот в направлении хода часовой стрелки учитывается положительным приращением значения W, а против часовой стрелки — отрицательным. На рис. 7.47 пока- 318 Глава 7. Алгоритмы формирования изображения
зано, как будет обходиться контур звезды при тестировании точки р. Если тестируемая точка находится вне многоугольника, зондирующий луч не совершит ни одного полного оборота вокруг нее при обходе контура многоугольника и для этой точки W-0. Для тех точек внутри лучей звезды, которые залиты на рис. 7.46, W- 1, а для точки р на рис. 7.47,6 W =2. Таким образом, если считать внутренними точками многоугольника все точки, для которых W> О, то получим закраску, как на рис. 7.47,а. При определении значения W возникает ряд проблем. Рассмотрим S-образную замкнутую кривую на рис. 7.48, которую можно аппроксимировать многоугольником со множеством вершин. Не совсем ясно, как в этом случае определить "обматывание" для точек, располо- расположенных внутри кривой. Можно модифицировать сформулированное ранее определение чет- четности-нечетности, распространить его на количество оборотов и получить таким образом приемлемый способ измерения значения И7 для произвольной точки. Рассмотрим произволь- произвольную прямую (зондирующую прямую), проходящую через внутреннюю точку р, как показано на рис. 7.47,6, причем эта прямая не должна быть параллельна ни одному из ребер много- многоугольника. Значение параметра Jf для этой точки равно количеству ребер, которые пересека- пересекают зондирующую прямую в направлении сверху вниз, минус количество ребер, которые пере- пересекают ее в направлении снизу вверх. Если W не равно 0, то точка лежит внутри многоуголь- многоугольника. Обращаю ваше внимание на то, что для этого теста не имеет значения, что считается низом, а что верхом. Строка растра Рис. 7.47. Сплошная закраска звезды Рис. 7.48. S-образная замкнутая кривая J 7.10.2. Обработка многоугольников общего вида в OpenGL OpenGL гарантирует корректное отображение только выпуклых многоугольников, а по- потому для отображения многоугольников общего вида приходится изобретать различные искусственные приемы. Один из подходов заключается в том, что прикладная программа должна самостоятельно следить за тем, чтобы все многоугольники в сцене удовлетворяли этому ограничению. Другой подход — разбить (tessellate) многоугольник общего вида на плоские выпуклые многоугольники, как правило, треугольники. Существует множество методов выполнения такого разбиения. Хорошим считается метод, который формирует не слишком длинные и узкие треугольники. Кроме того, желательно, чтобы после разбиения многоугольник можно было представить одним из составных примитивов, имеющихся в графической системе. В OpenGL для этого используются примитивы типа полоса из тре- треугольников (triangle strips) и розетка из треугольников (triangle fans). Функция разбиения включена в состав библиотеки GLU. При обработке многоугольника без отверстий сначала создается новый объект разбиения (tessellator object), и в него передаются вершины ис- исходного многоугольника: 7.10. Растровое преобразование многоугольников 319
mytess = gluNewTess(); gluTessBeginPolygon(mytess, NULL); gluTessBeginContour(mytess); for(i=0;i<nvertices;i++) glTessVertex(mytess, vertex[i], vertex[i]); gluTessEndContour(); gluTessEndPolygon(mytess); Основная идея использования имеющихся в OpenGL средств разбиения состоит в том, что от прикладной программы требуется описать контур исходного многоугольника, a OpenGL самостоятельно сформирует его составные части и выполнит их заполнение в соответствии с заданными параметрами. В алгоритмах разбиения, реализованных в OpenGL, используется описанный выше тест охватывания и формируемый в нем параметр W. В зависимости от того, как точка внутри многоугольника охватывается описанным контуром, OpenGL по-разному заполняет области внутри многоугольника при заданных параметрах разбиения (рис. 7.49). В приве- приведенном в конце главы обзоре литературы читатель встретит указания на источники, в ко- которых анализируются различные варианты алгоритмов разбиения. Что касается пользова- пользователей OpenGL, то для них обращение к имеющимся в этой системе средствам разбиения гарантирует, что все дальнейшие операции будут совершаться только над выпуклыми пло- плоскими многоугольниками. б) в) Рис. 7.49. Разбиение многоугольника: а — направление обхода контуров; б и в — варианты разбиения 7.10.3. Растровое преобразование с использованием z-буфера Теперь сведем воедино все, что было сказано в предыдущих разделах и главах об отобра- отображении многоугольников. Проследим, что происходит с многоугольником по мере его про- продвижения по конвейеру обработки. Сначала вершины и нормаль к плоскости многоугольника подвергаются геометрическим преобразованиям. На этом этапе не принимается во внимание, что вершины описывают именно многоугольник, — они обрабатываются как несвязанные геометрические объекты. При выполнении отсечения вершины уже связываются в много- многоугольник. Если многоугольник не отбрасывается в результате отсечения, он передается в мо- модуль закрашивания и удаления невидимых поверхностей. На этой стадии, несмотря на вы- выполнение нормализации проецирования, информация о глубине сохраняется. Если желатель- желательно использовать метод интерполяционного закрашивания, этой информации достаточно, чтобы вычислить освещенность каждой вершины многоугольника. Остается решить три задачи: выполнить окончательное ортогональное проецирование, удалить невидимые поверхности и тонировать образ многоугольника. Тщательно продуман- продуманный алгоритм обработки z-буфера позволяет решать эти три задачи одновременно. Рассмот- Рассмотрим два варианта представления многоугольника, показанные на рис. 7.50. В левой части ри- 320 Глава 7. Алгоритмы формирования изображения
сунка многоугольник изображен в трехмерной нормализованной системе координат устрой- устройства отображения, а в правой — в двухмерной системе координат экрана. Многоугольник обрабатывается построчно, и каждая строка соответствует отрезку на по- поверхности многоугольника, все точки которого имеют постоянное значение координаты у в нормализованной системе координат устройства отображения (рис. 7.51). Сканируя образ многоугольника в координатах экрана, мы перемещаемся на каждом шаге на один пиксель вправо, а использование нормализованных координат позволяет одновременно интерполиро- интерполировать параметр глубины очередной точки поверхности и анализировать ее видимость, исполь- используя информацию из z-буфера. Располагая данными об освещенности (цвете) каждой вершины обрабатываемого многоугольника, можно одновременно интерполировать и цвет точки по- поверхности. Каждая из описанных операций не требует громоздких вычислений, и все их мож- можно выполнять в едином цикле сканирования. К описанным можно добавить и операции, необ- необходимые для наложения текстур, о которых речь пойдет в главе 9. Л,-/ Луг! б) Рис 7.50. Дуальность представления много- многоугольника: а — нормализованная сис- система координат устройства отобра- отображения; б — система координат экрана б) Рис. 7.51. Строка растра в нормализован- нормализованных координатах устройства ото- отображения и в координатах экрана 7.10.4. Заполнение внутренней области и сортировка Другой подход к растровому преобразованию многоугольников основан на идее отдельного блока обработки многоугольников — процессора многоугольников. Концептуально такой про- процессор представляет собой "черный ящик", на вход которого подаются вершины множества двухмерных многоугольников, а выходом является корректно заполненный буфер кадра. Рассмотрим частный случай — контур каждого многоугольника заполняется одним цве- цветом. Это ограничение введено только для того, чтобы изложение идеи было более ясным. Сначала рассмотрим отдельный многоугольник. Основное правило заполнения его контура формулируется совершенно очевидно: если пиксель соответствует точке, которая нахо- находится внутри контура многоугольника, ей должен быть присвоен цвет, назначенный много- многоугольнику (цвет закраски). Из такой формулировки концепции следует, что закраска много- многоугольника сводится, в конце концов, к задаче сортировки — нужно рассортировать все пик- пиксели в буфере кадра на те, которые принадлежат внутренней области многоугольника, и те, которые ей не принадлежат. С точки зрения этой концепции алгоритмы закраски много- многоугольников отличаются друг от друга способом сортировки точек. Мы рассмотрим три вари- варианта алгоритмов: ¦ затека (floodfill); ¦ закраска при построчном сканировании (scan-line fill); ¦ оценка четности-нечетности перехода (odd-even fill). 7.10. Растровое преобразование многоугольников 321
7.10.5. Заливка Образ контура многоугольника в буфере кадра можно сформировать, обрабатывая от- отдельные прямолинейные участки контура по алгоритму Брезенхэма. Предположим, что изо- изображение состоит только из двух цветов: цвета фона (белый) и цвета переднего плана (черный). Образ контура многоугольника в буфере кадра формируется цветом переднего плана (рис. 7.52). Если из- известна какая-либо точка (х,у), лежащая внутри контура мно- многоугольника — точка-источник (родник), — то окружающие ее точки можно просматривать рекурсивно и окрашивать их в цвет переднего плана, если точка не принадлежит ребру кон- контура. Алгоритм заливки можно представить на языке описа- описания алгоритмов (псевдокоде), подразумевая, что функция readjpixel () возвращает код цвета, ранее назначенный ука- указанному пикселю следующим образом: Рис. 7.52. Образ контура многоугольника flood_fill(int х, int у) { if(read_pixel(х,у)==WHITE) { write_pixel(х,у,BLACK) flood_fill(x-l,y); flood_fill(x+l,y); flood_fill(x,y-l); flood_fill(x,y+l); Существует множество вариантов этого алгоритма, не использующих рекурсию. Один из таких способов предусматривает построчное сканирование. 7.10.6. Алгоритмы заполнения построчным сканированием Потенциальное достоинство алгоритмов построчного сканирования состоит в том, что они позволяют при соответствующей реализации формировать коды засветки пикселей син- синхронно с их отображением на экране. Рассмотрим многоугольник на рис. 7.53, на котором показана одна строка растра. Пользуясь сформулированным ранее правилом четности-нечетности для определения при- принадлежности точки внутренней области многоугольника, среди пикселей анализируемой строки можно выделить три группы или участка (spans) таких пикселей. Обращаю ваше внимание на то, что для определения освещенности и глуби- глубины каждый участок можно обрабатывать независимо. Эта стратегия используется в некоторых вариантах аппаратной реализации алгоритма построчного сканирования, которые предусматривают параллельную работу нескольких процессоров обработки участков. В рас- рассматриваемом простом примере равномерной закраски внутренней области после выявления интересующих нас участков все входящие в него пиксели закрашиваются одним цветом. Закрашиваемые участки строки определяются в результате анализа точек пересечения ли- линии строки и контура многоугольника. Вся информация, необходимая для определения точек Л Л Л ' V V ' Рис. 7.53. Строка растра, пере- пересекающая многоугольник 322 Глава 7. Алгоритмы формирования изображения
пересечения, содержится в списке вершин, и порядок выявления этих точек зависит от по- порядка следования вершин в списке представления многоугольника. Рассмотрим, например, многоугольник, представленный упорядоченным списком вершин (рис. 7.54). Само собой на- напрашивается решение формировать точки пересечения строк растра с ребрами многоуголь- многоугольника в порядке описания ребер в списке вершин. Номера точек пересечения на рис. 7.54 по- показывают порядок их вычисления при последовательном просмотре ребер. Весь процесс вы- вычислений можно сформулировать в приращениях (см. упр. 7.18). Но для выполнения закраски внутренней области многоугольника такой порядок следования точек пересечения не годится (или, по крайней мере, очень неудобен). Если в каждом цикле алгоритма нужно формировать участок закраски на одной строке, то необходимо рассортировать точки пересечения в по- порядке следования соответствующих строк, а затем — по значению координаты х на каждой строке, как показано на рис. 7.55. Очевидное решение — отсортировать список точек пересе- пересечения после того, как он будет сформирован в процессе последовательного анализа ребер, — не приемлем, поскольку при обработке многоугольника сложной формы (с "зубцами") коли- количество точек пересечения п может оказаться очень большим и сортировка займет много вре- времени. Если, например, многоугольник пересекает примерно половину строк растра, то оценка сложности сортировки имеет вид О(п \ogn) и для реализации в реальном масштабе времени такой способ явно не годится. Рис. 7.54. Порядок следования точек пересечения при по- последовательной обработке ребер многоугольника Рис. 7.55. Желательный поря- порядок следования точек пе- пересечения Существует множество способов, позволяющих обойтись без глобальной сортировки спи- списка точек пересечения. Один из них, получивший название у-х алгоритма (у-х algorithm), предусматривает формирование отдельного подсписка для каждой строки. По мере обработки ребер многоугольника информация о каждой очеред- очередной точке пересечения помещается в соответствующий подсписок, причем процедура включения нового эле- элемента в подсписок сразу выполняет его упорядочение по значению координаты х. В результате формируется структура данных, показанная схематически на рис. 7.56. Здесь вы видите еще один пример того, как продуманная организация структуры данных повышает скорость выполнения операций в графической системе. Можно еще усовершенствовать алгоритм по- построчного сканирования, изменив, например, способ представления многоугольника. В результате придем т „, ^ Рис. 7.56. Структура данных, исполь- к методу построчного сканирования, описанному в зуел,ая при реализации разделе 7.7. у.х тгоритма 7.10. Растровое преобразование многоугольников 323
7.10.7. Особые случаи При распространении методов закраски внутренней области многоугольников на другие геометрические фигуры нужно не забывать о существовании особых случаев (см. упр. 7.17). Существенным преимуществом работы с многоугольниками является то, что положение их ребер точно известно. Но даже при работе с этими геометрическими фигурами могут возник- возникнуть проблемы, если строка растра проходит через вершину. Рассмотрим два варианта такой ситуации (рис. 7.57). При использовании правила четности-нечетности эти два случая следует обрабатывать по-разному. В случае (а) точку пересечения строки с вершиной следует считать либо за нуль пересечений, либо за два пересечения. В случае (б) точка пересечения строки с вершиной должна учитываться в анализе четности-нечетности как одно пересечение. Учесть эти особые случаи в алгоритме можно двумя способами. Первый вариант— при выявлении пересечения с вершиной анализиро- анализировать, какая из двух описанных ситуаций имеет место, и соответственно модифицировать счетчик текущего количества пересечений. Другой ва- вариант— обеспечить, чтобы значения координаты у вершин всегда были дробными. Тогда вершина многоугольника никогда не будет лежать на строке растра и особые ситуации просто не будут иметь места. Рис. 7.57. Особые случаи: а — при анализе четности-нечетности пересече- пересечение с вершиной считается за нуль или за два пересечения; б — пересе- пересечение с вершиной считается за одно пересечение 7.11. Сглаживание ступенек на изображении линий После растрового преобразования образы прямолинейных отрезков и ребер многоуголь- многоугольников выглядят как бы состоящими из мелких ступенек. Даже на экране ЭЛТ с разрешением выше 1024x1280 можно невооруженным глазом заметить такие искажения. Природа их кро- кроется в самом принципе преобразования непрерывной функции в дискретную, которая пред- представляется на сетке пикселей с конечным разрешением. Появление ступенек объясняется тремя причинами. Во-первых, объем буфера кадра, в ко- котором формируется дискретизированный образ графического объекта, конечен, и представ- представление в нем прямолинейного отрезка в принципе не является взаимно однозначным, т.е. од- одному и тому же растровому образу может соответствовать множество исходных отрезков. Во-вторых, все пиксели имеют строго фиксированное положение на экране и образуют сетку с постоянным шагом. В-третьих, пиксели имеют фиксированный размер и форму. На первый взгляд кажется, что, поскольку все эти причины органически присущи дискрет- дискретному способу представления непрерывного сигнала, вряд ли что-нибудь удастся сделать и что отмеченные погрешности изображения неустранимы. Алгоритмы, подобные предложенному Брезенхэмом, оптимальны в том смысле, что позволяют определить на дискретной сетке пози- позиции, ближайшие к исходной линии. Но если конструкция устройства отображения позволяет воспроизводить оттенки цветов или даже градации одного цвета, эффект ступенчатости изобра- изображения можно значительно ослабить. Как известно, линия как математический объект является одномерным многообразием— характеризуется только длиной, но не имеет ширины (тол- (толщины). В отличие от исходного математического объекта, его растровый образ имеет конечную ширину, иначе он просто не был бы различим визуально. Каждый пиксель, из которых этот об- образ состоит, имеет вполне определенные размеры — размер пикселя принят в наших рассужде- 324 Глава 7. Алгоритмы формирования изображения
ниях за некую метрическую единицу. Поэтому можно считать, что идеализированный образ математической прямой линии на экране имеет толщину 1 единицу (рис. 7.58). Вычертить на экране растро- растрового устройства отображения такой идеальный образ прямой не- невозможно, поскольку он не состоит из отдельных квадратных пик- пикселей. Алгоритм Брезенхэма можно считать способом приб- приближенного представления этого идеального образа на сетке из ре- реальных пикселей. Если присмотреться к идеальному образу пря- прямой, то нетрудно подметить, что многие пиксели он перекрывает частично. В "чистом" виде алгоритм Брезенхэма предполагает, что при коэффициенте наклона, меньшем 1, за каждый шаг перемеще- перемещения по оси * засвечивается точно один пиксель. Но можно посту- поступить и по-другому — засвечивать и те пиксели, которые частично перекрываются идеальным образом линии, но задавать им не пол- полную интенсивность, а частичную, приблизительно пропорциональную степени перекрытия пик- пикселя. В результате сформируется сглаженное изображение прямой, примерно такое, как на рис. 7.59,6. Этот способ получил название сглаживание усреднением по области {antialiasing by area averaging). Для реализации такого способа необходимо выполнить операции, весьма схо- схожие с теми, которые выполняются при отсечении многоугольника. Существуют и другие алго- алгоритмы сглаживания ступеней на изображении, которые применяются при отображении прими- примитивов других типов, в частности многоугольников. Рис. 7.58. Идеальный образ прямой на поле изо- изображения 6) г) Рис. 7.59. Сглаживание изображения прямолинейного отрезка: а— несглаженное ступенча- ступенчатое изображение; б — изображение, сглаженное усреднением по области; в — увеличен- увеличенное несглаженное изображение; г —увеличенное сглаженное изображение Аналогичные проблемы с дискретизацией непрерывного изображения на сетке растра возникают и при использовании алгоритма z-буфера в простейшем варианте. При описании алгоритма было сказано, что цвет пикселя определяется цветом единственного примитива. Но рассмотрим вариант, когда поле пик- пикселя разделено между несколькими примитивами— тремя много- многоугольниками, как в случае, показанном на рис. 7.60. Если цвет каж- каждого многоугольника отличается от цвета остальных "претендентов", то пикселю присваивается цвет того из них, который ближе к наблю- наблюдателю. Но можно более точно передать сложившуюся ситуацию, если присвоить пикселю смешанный цвет, составляющие которого несут информацию о том, какую часть площади пикселя занимает каждый из треугольников-"претендентов". В главе 9 будет рассмот- рассмотрен алгоритм, который использует в такой ситуации накапливающий буфер и с его помощью реализует взвешенное усреднение. Выше мы рассмотрели только эффект пространственной дискре- дискретизации. Но при формировании динамических изображений появля- появляются и эффекты, связанные с дискретизацией во временной области. 1 ¦ Рис. 7.60. Наложение образов много- многоугольников в об- области одного пикселя 7.11. Сглаживание ступенек на изображении линий 325
Пусть, например, в последовательных кадрах динамического изображения образ объекта дос- достаточно малого размера, сравнимого с размерами пикселя, перемешается перед "объективом" камеры (рис. 7.61). Стандартная процедура тонирования предполагает анализ лучей, про- проходящих через центры пикселей, и определя- определяет, на какой объект сцены этот луч "натолк- "натолкнется" первым. При работе с таким малым объектом может оказаться, что в одном кадре анализируемый луч наталкивается на объект и что этот объект соответственно попадет в изо- изображение, а в следующем кадре объект ока- окажется между двумя соседними лучами, и ни один из них его "не заметит". Тогда объект просто не попадет в следующий кадр. В ре- результате пользователь увидит на экране дви- движущуюся мерцающую точку. С этим явлением можно бороться по-разному. Один из вариан- вариантов — использовать несколько зондирующих лучей на каждый пиксель и коды засветки, сформированные в результате анализа совокупности лучей. Резюмируя наш краткий обзор, можно с уверенностью сказать, что, в конце концов, лю- любой метод преодоления эффектов, порождаемых дискретизацией, сводится к уменьшению тем или иным способом шага квантования и соответственно увеличению объема вычислений. Поэтому создание высококачественных изображений со сглаживанием ступенек выполняется в современных графических системах в пакетном режиме, когда можно позволить себе рос- роскошь выполнять длительные вычисления. Рис. 7.61. Эффект дискретизации во времен- временной области при формировании динами- динамических изображений 7.12. Отображение информации Разрабатывая прикладную программу, ориентированную на применение какой-либо из большинства существующих графических систем, программист, как правило, не думает о таких частностях, как вывод на экран образов, сформированных в буфере кадра. В растро- растровых системах выводом на экран занимаются средства, реализующие алгоритм растрового преобразования, причем в большинстве специализированных графических станций для этого используется память с двойным доступом (так называемые двухпортовые запоми- запоминающие устройства — dual-ported memory). В этих устройствах процессы записи в буфер кадра и считывания из него разделены и выполняются независимо. Вывод изображения на экран выполняется аппаратно со скоростью, обеспечивающей требуемую частоту регене- регенерации (в современных системах от 65 до 85 Гц), а от программиста, разрабатывающего прикладную программу, требуется только обеспечить заполнение буфера со скоростью, определяемой спецификой конкретного приложения. При создании динамических изобра- изображений часто применяется двойная буферизация, о которой уже шла речь в главе 3. На качество формируемого изображения влияет довольно много факторов. Часто порабо- поработав с приложением в системе, которая не обеспечивает хорошего качества "картинки", поль- пользователь переносит свое недовольство на приложение в целом, хотя причина кроется, прежде всего, в подсистеме отображения. Например, две ЭЛТ с одним и тем же номинальным разре- разрешением могут иметь разные размеры пикселей, что в итоге приводит к разному качеству изо- изображения (см. упр. 7.22 и 7.23). 326 Глава 7. Алгоритмы формирования изображения
7.12.1. Цветовые системы До сих пор при обсуждении вопросов, связанных с формированием цвета пикселей, мы все время молчаливо предполагали, что используется система аддитивных цветовых состав- составляющих красного, зеленого и синего цветов (RGB-система). Это предположение, базирую- базирующееся на теории цветовосприятия человека, состоит в том, что три компонента цветового ко- кода, который определяется для каждой точки тем или иным способом, соответствуют трем ос- основным цветам спектра, о которых шла речь в главе 2. Однако между разными вариантами RGB-систем есть существенная разница. Например, в OpenGL желтый цвет представляется RGB-триадой @.8, 0.6, 0.0). Если использовать эту триаду для управления компьютерным монитором на ЭЛТ и устройством вывода на пленку, то результаты будут значительно отли- отличаться, хотя в обоих случаях задается один и тот же процентный состав интенсивности ос- основных цветов. Дело в том, что два этих устройства характеризуются разной цветопередачей красителя, используемого в устройстве вывода на пленку, и люминофоров ЭЛТ. Основная парадигма, одобренная сообществом разработчиков средств компьютерной графики, состоит в том, что прикладные программы должны быть независимы от аппаратных средств ото- отображения. Поэтому в большинстве графических API никак не учитывается специфика тех или иных устройств отображения. Необходимую информацию об этой проблеме можно найти в специ- специальной литературе, относящейся к колориметрии. Существуют стандарты для большинства суще- существующих систем воспроизведения цвета. Например, электронно-лучевые трубки должны удовле- удовлетворять требованиям к RGB-системам отображения, сформулированным Национальным комите- комитетом по телевизионным системам (NTSC — National Television Systems Committee). Отличие одной системы представления цветов от другой можно рассматривать как отличие в системах координат представления первичных цветов. Если считать, что Cl = [R],GbBi]1 и С2 = [R2, G2, B2]'— два представления одного и того же цвета в разных системах координат, то должна существовать мат- матрица преобразования цвета М размера 3x3, такая, что С2 = МС,. Как бы ни были определены значения элементов этой матрицы — экспериментально или с помощью специальной литературы, — она позволит добиться единой цветовой гаммы фор- формируемого изображения при использовании в системе разных устройств отображения. На этом пути нас подстерегает множество "подводных каменей". Во-первых, устройства отображения разного типа обладают отличной друг от друга цветовой гаммой. Поэтому, даже уравняв чувствительность по отдельным компонентам, можно не добиться в одной системе того же оттенка, что и в другой. Во-вторых, в полиграфии используется четырехцветная сис- система дополняющих цветов (CMYK-система), в которую добавлен еще один первичный цвет — черный (К). Преобразование между RGB и CMYK часто требует особого искусства, которым обладают только эксперты в этой области. В-третьих, мы используем в своих рас- рассуждениях допущение о линейности наложения первичных цветовых компонентов, в то вре- время как в действительности эта функция нелинейна. Расстояние между точками разных цветов в цветовом кубе не передает точно воспринимаемое глазом человека отличие между этими же цветами. Например, человек воспринимает сильнее цвета, находящиеся в синей части спек- спектра. Эти особенности восприятия цвета человеком учтены в цветовых системах YUV и Lab. Большинство вариантов RGB-систем базируется на цветовых характеристиках реальных первичных компонентов — люминофоров в ЭЛТ и красителей в полиграфических системах. Ни один из существующих вариантов систем не способен воспроизвести всю гамму цветов, воспринимаемых человеком. Большинство цветовых стандартов основано на теории трех первичных цветов, известной среди специалистов под названием цветовой системы XYZ {XYZ color system). В этой системе компонент Y соответствует интенсивности цвета. В систе- системе XYZ все цвета задаются положительными значениями первичных цветовых составляю- 7.12. Отображение информации 321
щих. Для перехода от представления в системе XYZ к любой стандартной системе представ- представления цвета используются ЗхЗ-матрицы. Специалисты в области колориметрии часто пред- предпочитают работать с хроматическими координатами (chromaticity coordinates), а не с пер- первичными цветовыми составляющими. Хроматические координаты определенного цвета пред- представляются отношениями интенсивностей каждого из трех первичных цветов к их сумме. Если для какого-то цвета интенсивности первичных составляющих в RGB-представлении равны Ть Т2 и 7~з> то хроматические координаты этого цвета будут иметь вид Т, Г, . Г, Л =¦ т1+т2+т3 Л = Tt+T2+T3 г, =¦ Т1 + Т2 Суммируя эти три соотношения, получим: /, + U + /3 = 1. Из него следует, что мы можем работать с хроматическими координатами в двухмерном про- пространстве /,, /2, поскольку третья координата /3 однозначно определяется двумя первыми и при необходимости ее всегда можно вычислить. При переходе от представления цвета в ис- исходной системе к хроматическим координатам теряется информация о сумме первичных со- составляющих Тх + Т2 + Т3, которая, в конце концов, представляет суммарную интенсивность цвета. В колориметрии она не так важна, поскольку главное в этой области — правильная пе- передача оттенков и однозначность представления цветов, заданных в разных системах. Поскольку в системе хроматических координат все дробные компоненты должны быть неотрицательными, т.е. должно соблюдаться ограничение 1 > /, > О, то для любого цвета точка, представляющая его в координатах /,, /2, должна находиться внут- внутри треугольника, изображенного на рис. 7.62. Кривая на рис. 7.63 ограничивает на плоскости /i, /2 область цветов, воспринимаемых зрительной системой человека. На этом же рисунке по- показана область, представляющая цветовую гамму типичных цветных ЭЛТ и цветных принте- принтеров. Из этого рисунка ясно, что существующие на сегодняшний день устройства отображения способны воспроизводить отнюдь не все цвета видимой части спектра. 1.0 1.0 Рис 7.62. Треугольная область цветов, воспро- воспроизводимых в системе хроматических ко- координат 328 Глава 7. Алгоритмы формирования изображения
Представление цвета в аддитивной системе RGB базируется на том, как цвет воспроизво- воспроизводится, а не на том, как он воспринимается. Субъективное восприятие человеком цвета приня- принято характеризовать тремя взаимно связанными атрибутами — цветовым тоном, насыщенно- насыщенностью и светлотой. На этих атрибутах основана система HLS (по первым буквам их названий hue — цветовой тон, saturation — насыщенность, lightness — светлота), которая используется художниками и некоторыми изготовителями систем отображения. Цветовой тон (иногда его называют оттенком цвета) ассоциируется в человеческом сознании с обусловленностью окра- окраски предмета, наличием в нем определенного пигмента краски или красителя — это название чистого цвета, например красный, желтый, золотистый и т.п. Светлота (lightness) характери- характеризует степень, уровень, силу выражения цветового тона. Серые тона называются ахроматиче- ахроматическими (бесцветными), и считается, что они не имеют светлоты, а различаются по насыщенно- насыщенности. Изменение насыщенности (saturation) хроматического цвета связывается в сознании с примешиванием к нему ахроматического цвета— белого или черного красителя. В результа- результате получаются так называемые "грязные" цвета. Некото- Некоторые из них (коричневые, бурые) субъективно представ- представляются самостоятельными цветами, хотя являются на са- самом деле грязными оранжевым и красным. Цветовой тон, насыщенность и светлота называются основными "качествами цвета". 500 nm Цвета видимого спектра Цвета, воспроизводимые ЭЛТ 400 nm 1.0 Рис. 7.63. Видимая часть спектра и цветовая гамма типичных устройств отображения Рис. 7.64. Атрибуты HLS-системы представления цвета: а — связь между атрибутами HLS-системы и цветовым ку- кубом в системе RGB; б — цве- цветовой конус HLS-системы Можно связать эти атрибуты качественно и количест- количественно с координатами цвета в системе координат основ- основных цветов RGB, как показано на рис. 7.64,а. Для неко- некоторого цвета, представленного точкой в пространстве цветового куба, светлота — есть мера удаления этой точки от начала координат (вершины куба, соответствующей черному цвету), т.е. параметр L на рис. 7.64,а. Цвета, точки представ- представления которых находятся на главной диагонали цветового куба, являются ахроматически- ахроматическими — оттенками серого. Они отличаются только по светлоте, а насыщенность этих цветов равна нулю. Значение насыщенности хроматического цвета характеризует, насколько удалена представляющая его точка от главной диагонали цветового куба (параметр S на рис. 7.64,а). 7.12. Отображение информации 329
Цветовой тон характеризует направление вектора, связывающего начало координат с точкой представления цвета, — параметр Н на рис. 7.64,а. Из всего сказанного следует, что HLS-сис- тему представления цвета можно описать в терминах цветового конуса, показанного на рис. 7.64,6. С точки зрения системы компьютерной графики систему HLS можно рассматри- рассматривать как представление RGB-цвета в полярной системе координат. 7.12.2. Гамма-коррекция Яркость характеризует восприятие наблюдателем интенсивности света, причем для зри- зрительной системы человека это восприятие описывается логарифмической кривой (рис. 7.65). Из этого факта следует практический вывод, существенный для сис- систем отображения: если необходимо сформировать шкалу нарастания яркости с постоянным шагом, то интенсивность засветки соответст- соответствующих пикселей экрана должна возрастать экспоненциально. При- Приращения интенсивности засветки участков шкалы можно вычислить, взяв за основу значения максимальной и минимальной интенсивно- стей, характеризующих данную систему отображения (см. упр. 7.25). Кроме того, интенсивность / свечения участка экрана ЭЛТ связана с напряжением V на управляющем электроде соотношением г Интенсивность Рис. 7.65. Логарифм- где константы у и с0 характеризуют конкретную ЭЛТ. В результате ческая кривая яр- образ, сформированный в буфере кадра, при отображении на экранах кости разных мониторов будет иметь разную яркость. Один из способов преодоления этого недостатка состоит в том, чтобы сформировать специальную таблицу, значения в которой настраиваются в соответствии с характеристиками конкретного монито- монитора, и использовать эти значения при отображении содержимого буфера кадра. Этот механизм получил название гамма-коррекции {gamma correction). Структура таблицы гамма-коррекции будет рассмотрена в главе 9. 7.12.3. Формирование полутонов Буфер кадра характеризуется пространственным разрешением — количеством пикселей, а также глубиной — количеством цветов, которые может содержать формируемый в нем образ. Если рассматривать обе эти характеристики как независимые, то можно считать, что черно- белый лазерный принтер с высоким разрешением способен воспроизводить изображение, со- состоящее только из одноразрядных пикселей. Рассуждая аналогичным образом, можно прийти к выводу, что в любой черно-белой среде, например в книге, нельзя воспроизвести изображе- изображения, в которых присутствовали бы градации серого, промежуточные между белым и черным. Но наш собственный жизненный опыт говорит, что это не так. Весь фокус в том, что при ра- работе с подобной средой пространственное разрешение приносится в жертву способности воспроизводить градации серого. Технология воспроизведения полутонов в полиграфии ос- основана на моделировании градаций серого цвета плотностью размещения точек, окрашенных "полновесным" черным цветом. Поскольку зрительная система человека усредняет точки ма- малого размера, покрывающие некоторый участок изображения, то изменение плотности их размещения воспринимается как изменение уровня почернения, т.е. как промежуточная гра- градация серого. Метод цифрового формирования полутонов предполагает, что размещение и размер ото- отображаемых пикселей фиксирован. Рассмотрим группу одноразрядных пикселей с размерами 4x4 (рис. 7.66). Если покрыть такими группами пикселей — трафаретом — участок изобра- изображения и смотреть на него издали, то мы увидим не отдельные пиксели, а участок серого цве- 330 Глава 7. Алгоритмы формирования изображения
та, причем степень почернения участка будет зависеть от соотношения между количеством белых и черных пикселей в трафарете. При размере трафарета 4x4 теоретически существует 216 разных вариантов трафаретов, но все эти варианты способны воспроизвести только 17 градаций серого, что соответствует изменению количества черных пикселей в группе от О (абсолютно белый цвет) до 16 (абсолютно черный цвет). Существует множество вариантов формирования полутонового изображения с помощью подобных трафаретов. Самый простой из них— выбрать 17 из 216 вариантов распределения черных и белых пикселей на матрице с размерами 4x4, причем каждый отобранный вариант должен иметь отличное количество за- зачерненных пикселей и использовать отобранные трафареты для воспроизведения одной из 17 возможных градаций серого, независимо от характера формируемого изображения. За это придется заплатить снижением пространственного разрешения в 4 раза. Но такой простой алгоритм может привести к образованию на изо- изображении муара. Чаще всего это происходит, если в изображении име- имеются фрагменты с регулярной структурой. Более сложные алгоритмы используют случайный выбор среди трафаретов с одинаковой характе- характеристикой почернения, что позволяет избавиться от появления муара на _ _ .. _ , _ . то^\ Рис. 7.66. Трафареты изображении (см. упр. 7.26). Азд предста?ления в Такие технологии используются и при формировании цветных изо- изображении града- бражений в устройствах печати, в частности в цветных струйных прин- циц серого цвета терах. Отдельная точка оттиска может представлять собой только пят- пятнышко какого-либо полного цвета, и промежуточные оттенки воспроизводятся за счет изме- изменения плотности расположения этих цветовых пятнышек. В графической системе OpenGL поддерживается такой способ формирования градаций серого или оттенков цветов, для чегсх следует вызвать функцию glEnable(), передав ей в качестве аргумента константу GL DITHER. 7.13. Резюме В этой главе представлен обзор методов реализации функций графической системы, вклю- включая примеры некоторых алгоритмов. Независимо от характеристик конкретных средств реали- реализации (программных или аппаратных), существует ряд задач, которые необходимо решать в системе с любой организацией. К ним относятся задачи геометрических преобразований, отсе- отсечения и растрового преобразования. Мы рассмотрели, как при реализации этих задач взаимо- взаимодействуют программные, аппаратные средства и функции API графической системы. В графических рабочих станциях фирмы Silicon Graphics для решения задач геометрических преобразований и отсечения используется специализированный геометрический процессор Geometry Engine, реализованный аппаратно в виде СБИС с конвейерной архитектурой. Система GL, предшественница OpenGL, разрабатывалась как набор функций API для пользователей этих графических станций и, естественно, основной упор при ее разработке делался на использование особенностей конвейерной архитектуры. Но нельзя забывать, что, в конце концов, OpenGL представляет собой именно API и что ее функции с таким же успехом могут реализовать и алго- алгоритм трассировки лучей при решении задач тонирования изображения. Акцентирование внима- внимания на конвейерной архитектуре в этой главе объясняется тем, что на сегодняшний день именно эта архитектура обеспечивает наиболее эффективное решение всех необходимых в графической системе задач как программными, так и аппаратными средствами. Но что же ожидает нас в будущем? Совершенно очевидно, что графические системы ста- станут работать быстрее, а стоимость их уменьшится. Залогом тому являются успехи в создании новых аппаратных средств, обеспечивающих распараллеливание трудоемких задач, и именно аппаратные средства определяют дальнейшие пути развития графических систем. С точки зрения разработчиков программного обеспечения графических приложений снижение стои- стоимости и повышение производительности используемых аппаратных средств позволит реали- 7.13. Резюме 331
зовать в прикладных системах алгоритмы, обеспечивающие более высокое качество изобра- изображения, в частности учет большинства объективно существующих световых эффектов при вы- выполнении тонирования, и сохранить при этом приемлемое время формирования изображения. Следовательно, пользователь такой системы получит возможность включать в отображаемую сцену больше объектов с самыми разнообразными оптическими свойствами, которые будут моделироваться графической системой в процессе формирования изображения сцены. Пример использования алгоритма z-буфера демонстрирует, как взаимодействуют при ре- решении задач компьютерной графики программные и аппаратные средства. Лет десять назад существовало множество алгоритмов удаления невидимых поверхностей, из которых до се- сегодняшнего дня фактически "дожил" только алгоритм z-буфера. Это объясняется тем, что ключевым элементом для его реализации являлась память, а именно модули памяти за эти го- годы стали значительно быстрее и дешевле. Аналогичным примером может служить и конвей- конвейерная архитектура графических рабочих станций, для реализации которой в современных системах используется множество стандартных БИС для выполнения арифметических опера- операций с числами в формате с плавающей точкой, сигнальных процессоров и специализирован- специализированных БИС (ASICS) для выполнения немногих специфических операций. В настоящее время внимание исследователей переключилось на использование принципа распараллеливания операций применительно к графическим системам. Предмет большинства дискуссий — для решения каких задач компьютерной графики параллелизм принесет наи- наибольший эффект. Рассмотренные нами два подхода к выполнению тонирования сцены (отталкиваясь от пространства объектов или от пространства изображения) предполагают и два принципиально различных пути внедрения параллелизма. К сожалению, ограниченный объем книги не позволяет привести на ее страницах описа- описания множества существующих на сегодняшний день решений. Читатель может обратиться к рекомендуемой в следующем разделе литературе и получить полное представление о разно- разнообразных алгоритмах решения задач компьютерной графики. В главе 8 мы вновь вернемся к современным технологиям, которые поддерживаются в графических API, таких как OpenGL. 7.14. Рекомендуемая литература Множество алгоритмов, которые мы не смогли представить в этой главе из-за ограниченно- ограниченного объема, содержатся в книгах Роджерса (Rogers) [Rog98] и Фоли (Foley) [Fol90]. Я бы реко- рекомендовал также познакомиться с выпусками серии Graphic Gems [Gra90-Gra92, Gra94, Gra95]. Алгоритм отсечения Коэна-Сазерленда появился еще в первые годы становления компью- компьютерной графики, как и алгоритм Брезенхэма [ВгебЗ, Вге87], первая версия которого была пред- предназначена для использования в перьевых плоттерах. Оригинальные описания алгоритмов отсе- отсечения Лианга-Барского и Сазерленда-Ходжмена представлены в статьях [Lia84] и [Snt74, а]. Алгоритм z-буфера был впервые описан Кэтмулом (Catmull) [Cat75]. Обсуждение раз- различных подходов к решению задачи удаления невидимых поверхностей содержится в работе Сазерленда (Sutherland) [Snt74, b]. В этой главе мы обошли вниманием вопросы аппаратной реализации различных алгорит- алгоритмов отнюдь не потому, что они слишком просты или не представляют особого интереса. Скорость, с которой в современной системе графический процессор формирует новое изо- изображение, предполагает использование достаточно остроумных алгоритмов и продуманной конструкции аппаратуры [С/а82, Аке88, Аке93]. Множество подходов к разработке такого рода средств рассмотрено Мольнаром (Molnar) и Фучем (Fuchs) в сборнике под редакцией Фоли (Foley) [Fol90]. В книге Пратта (Pratt) [Pra78] описаны матричные методы преобразования между раз- различными системами представления цвета. Формирование полутонов рассматривается в стать- статьях Джарвиса (Jarvis) [Jar76] и Кнута (Knuth) [Knu87]. 332 Глава 7. Алгоритмы формирования изображения
Упражнения 7.1. Пусть в параметрическом виде заданы два прямолинейных отрезка: = A-сс)р,+(хр2, Разработайте процедуру, которая будет определять, пересекаются ли эти отрезки, и если пересекаются, вычислять координаты точки пересечения. 7.2. По образцу процедуры, разработанной при выполнении упр. 7.1, разработайте про- процедуру анализа пересечения двух плоских многоугольников. 7.3. Докажите, что при отсечении одного выпуклого объекта другим выпуклым объек- объектом образуется не больше одного выпуклого объекта. 7.4. Как можно использовать распараллеливание операций при анализе сцены со сторо- стороны пространства объектов и со стороны пространства изображения? 7.5. Поскольку и векторы, в частности нормали, и вершины можно представить в одно- однородных координатах, то и к тем, и к другим можно применить преобразование, за- заданное матрицей вида. Покажите, что при таком преобразовании свойства вектора как нормали в общем случае не сохраняются. 7.6. Разработайте метод преобразования видового окна. Сформулируйте его в терминах матриц масштабирования и сдвига, которые представляют аффинные преобразова- преобразования в двухмерном пространстве. 7.7. На ранних этапах развития компьютерной графики доминирующее положение за- занимали векторные системы, которые позволяли формировать только изображения, состоящие из отдельных линий (штриховые изображения). При обработке трехмер- трехмерных сцен программно выполнялась процедура удаления невидимых линий. Многие из современных графических API позволяют создавать так называемые проволоч- проволочные, или каркасные (wireframe), изображения объектов, которые также состоят только из линий (ребер поверхностей объектов сцены), но при этом часть линий, представляющих невидимые ребра поверхностей, удаляется из изображения. Чем отличается формулировка задачи удаления невидимых линий от задачи удаления невидимых поверхностей? Разработайте алгоритм удаления невидимых линий при- применительно к изображению, состоящему из ребер плоских многоугольников. 7.8. Часто в графических приложениях приходится формировать семейства графиков функций вида у =J{x, г). Такой график строится в виде сетки из прямоугольных яче- ячеек, формируемых множеством значений {/[х„ г,-)}, элементы которого рассчитыва- рассчитываются с постоянным шагом приращений по переменным д: и z. В результате образу- образуется изображение, которое должно быть обработано алгоритмом удаления невиди- невидимых поверхностей, поскольку некоторые его участки закрыты от наблюдателя другими участками. Разработайте два алгоритма отображения такой сетки, один из которых будет использовать метод удаления невидимых поверхностей, а другой — метод удаления невидимых линий. 7.9. Хотя теоретически сложность решения задачи удаления невидимых поверхностей на основе анализа пространства изображения пропорциональна количеству много- многоугольников, экспериментальное исследование производительности систем показа- показало, что она (производительность) остается практически постоянной. Как вы объяс- объясните этот феномен? 7.10. Рассмотрите сцену, которая состоит исключительно из сплошных трехмерных мно- многогранников. Как в таком случае можно применить алгоритм удаления невидимых Упражнения 333
поверхностей, анализирующий пространство объектов? Насколько облегчит реше- решение этой задачи информация о том, что все многогранники выпуклые? 7.11. Можно рассматривать методы удаления невидимых поверхностей, анализирующие пространство объектов, как некий аналог методов сортировки. Однако было пока- показано, что оценка сложности решения такой задачи имеет вид О(к2). Но известно, что такая оценка является завышенной и справедлива только для несовершенных мето- методов сортировки. Для большинства же алгоритмов сортировки оценка имеет вид О(к \ogk). Следует ли из этого, что такой же вид имеет и оценка сложности методов ре- решения задачи удаления невидимых поверхностей, предполагающих анализ про- пространства объектов? Объясните свой ответ. 7.12. Разработайте метод проверки, расположен ли один плоский многоугольник полно- полностью по одну строну от другого плоского многоугольника. 7.13. Чем отличается подход к удалению невидимых поверхностей путем анализа про- пространства изображения от методов трассировки лучей? Можно ли использовать ме- метод трассировки лучей в качестве альтернативного для решения задачи удаления невидимых поверхностей? В чем достоинства и недостатки такого подхода? 7.14. Разработайте программу, которая будет использовать алгоритм Брезенхэма для формирования координат пикселей, аппроксимирующих прямолинейный отрезок. Обеспечьте возможность обработки этой программой прямых с любым коэффици- коэффициентом наклона и любыми вариантами взаимного положения конечных точек. Как формируется исходное значение переменной <Л 7.15. Принцип, положенный в основу алгоритма Брезенхэма, можно распространить и на формирование растрового образа окружности. Покажите, как это сделать на приме- примере окружности, центр которой находится в начале координат. Какая часть окружно- окружности будет формироваться основным алгоритмом, а какие части можно сформиро- сформировать, используя принцип симметрии? Можно ли выделить такую часть окружности, для которой информация о точке, сформированной на предыдущем шаге, может существенно сократить количество вычислений на следующем шаге? 7.16. Подумайте над тем, как использовать заливку областей при формировании изобра- изображения лабиринта, подобного тому, который был построен при выполнении упр. 2.8. 7.17. Предположим, что предпринята попытка распространить алгоритм заливки, сфор- сформулированный в этой главе для многоугольников, на формирование изображений произвольных замкнутых кривых. Какие проблемы поджидают вас на этом пути? 7.18. Ребро многоугольника задано вершинами (хьу\) и (х2,У2)- Разработайте эффектив- эффективный алгоритм вычисления точек пересечения всех строк растра с этим ребром. Считайте, что операции выполняются в системе координат видового окна. 7.19. При реализации алгоритмов заполнения внутренних областей многоугольников часто приходится сталкиваться с определенными проблемами в ходе обработки вертикальных и горизонтальных ребер. Как следует обрабатывать эти ребра в тех алгоритмах, которые описаны в тексте этой главы? 7.20. В системе двухмерной графики управление порядком отображения накладываю- накладывающихся многоугольников можно реализовать с помощью специального атрибута приоритета, который ассоциируется с каждым примитивом такого типа. При реали- реализации этой идеи объект с наивысшим значением этого атрибута будет отображаться последним и закроет все находящиеся "под ним". Как следует модифицировать рас- рассмотренные в тексте главы алгоритмы растрового преобразования многоугольни- многоугольников, чтобы можно было воспользоваться таким атрибутом? 334 Глава 7. Алгоритмы формирования изображения
7.21. При трассировке лучей для выравнивания ступенек растрового образа часто ис- используется приведение не только луча, проходящего через центр идеального пиксе- пикселя, но и еще четырех лучей, проходящих через углы квадратного пикселя. Насколь- Насколько, по-вашему, при этом возрастает объем вычислений по сравнению с приведением единственного луча? 7.22. Хотя идеальный пиксель должен иметь форму квадрата со стороной единичной длины, в реальных ЭЛТ формируется круглое пятно, которое можно аппроксими- аппроксимировать кругом с постоянной интенсивностью свечения. Если полностью засвечен- засвеченный единичный квадрат имеет интенсивность 1.0, а незасвеченный — интенсив- интенсивность 0.0, как должна изменяться интенсивность свечения реального пикселя в за- зависимости от радиуса круга? 7.23. Рассмотрим двухуровневое устройство отображения с пикселями в виде круга. Как, по-вашему, следует отображать пиксели, окрашенные в цвет переднего плана, — кругами большего или меньшего радиуса? Поясните свой ответ. 7.24. Почему слабую расфокусировку луча ЭЛТ иногда называют "технологией сглажи- сглаживания для бедных"? 7.25. Предположим, что минимальная интенсивность засветки пикселей монохромного устройства отображения равна /min (экран ЭЛТ никогда не бывает абсолютно чер- черным), а максимальная — /тах. Как разделить диапазон интенсивности на к уровней, учитывая, что воспринимаемая яркость зависит от интенсивности засветки по лога- логарифмическому закону, чтобы перепады яркости между соседними уровнями каза- казались наблюдателю постоянными? 7.26. Разработайте алгоритм формирования полутонов на основе следующей идеи. Пред- Предположим, что уровни серого должны изменяться от 0.0 до 1.0 и что в вашем распо- распоряжении имеется генератор случайных чисел с равномерным законом распределе- распределения в диапазоне от 0.0 до 1.0. Если задан уровень интенсивности серого g, то 100g процентов сформированных этим генератором чисел окажется меньше g. 7.27. Устройства отображения с малой глубиной управления по интенсивности форми- формируют изображения, в которых явно заметна граница между участками с разным по- почернением. Один из методов устранения этого недостатка состоит в том, что на сигнал интенсивности засветки пикселей накладывается слабый шум. Каким обра- образом, по-вашему, это помогает сгладить наблюдаемые перепады яркости между со- соседними участками? Шум какой амплитуды следует накладывать? Можно ли отсю- отсюда заключить, что искаженное таким способом изображение будет иметь более вы- высокое качество, чем неискаженное? Упражнен ия 335
ГЛАВА 8 Иерархические графические модели Модели — это абстрактные представления сущностей реального мира, в котором мы живем, и сущностей виртуального мира, сотворенных программами. Думаю, боль- большинство читателей неоднократно имели дело с математическими моделями, кото- которые применяются во всех областях науки и техники. В таких моделях для представления ре- реальных физических явлений используются математические уравнения. В информатике с по- помощью абстрактных типов данных моделируется организационная структура объектов, а в компьютерной графике нам потребуются графические модели геометрических объектов. При создании математических моделей тщательно выбирается подходящий математический аппа- аппарат, который наилучшим образом позволяет отразить в модели сущность моделируемого ре- реального феномена. Например, для моделирования динамического взаимодействия компонен- компонентов системы твердых тел используется аппарат обыкновенных дифференциальных уравнений, а для явлений турбулентности в потоке жидкости больше подходит аппарат дифференциаль- дифференциальных уравнений в частных производных. Как правило, одно и то же явление можно моделиро- моделировать по-разному, в зависимости от того, какие характеристики этого явления подлежат иссле- исследованию и какими средствами работы с моделью располагает исследователь. В этом свете наша задача состоит в том, чтобы найти вид модели, позволяющий наилучшим образом ис- использовать возможности графической системы. В этой главе будут рассмотрены разные подходы к созданию моделей графических объек- объектов и работе с ними в среде, которую поддерживает система компьютерной графики. Будут проанализированы модели, которые используют набор простых графических объектов, — как примитивов, встроенных в графическую систему, так и объектов, созданных пользователем из этих примитивов. Будет также показано, как использовать геометрические преобразова- преобразования, которые мы рассматривали в главе 4, для отображения иерархических отношений между графическими объектами. Рассмотренные в этой главе методики находят широкое примене- применение в таких областях, как робототехника и компьютерная анимация, где динамической харак- характер поведения объектов во многом зависит от пространственных отношений между отдель- отдельными компонентами модели.
Понятие иерархии играет особую роль в объектно-ориентированных методологиях. Мы распространим иерархический подход к организации моделей отдельных объектов на все пространство сцены и включим в иерархическую модель не только отображаемые геометри- геометрические объекты, но и камеры, источники света и свойства материалов. Такие модели позволят расширить возможности средств графического API и превратить их в объектно-ориен- объектно-ориентированную систему, что открывает путь к использованию графических приложений в рас- распределенной сетевой среде, такой как World Wide Web. 8.1. Символы и экземпляры Сначала рассмотрим, каким образом можно хранить модель, которая включает множество достаточно сложных объектов. Тут сразу возникают два вопроса: как описывать объекты, бо- более сложные, чем те, с которыми мы работали до сих пор, и как представлять множество та- таких объектов. В большинстве графических API пользователя не особенно балуют разнообра- разнообразием типов встроенных примитивов. Считается, что можно обойтись минимумом основных примитивов, предоставив пользователю возможность самостоятельно конструировать из них более сложные объекты. Иногда в состав графической системы включаются дополнительные библиотеки, такие как GLU и GLUT, которые мы уже не раз использовали при работе с OpenGL, и в этих библиотеках имеются средства поддержки расширенного набора объектов, созданных на основе базового комплекта примитивов. Будем считать, что нам доступны не- некоторые трехмерные объекты, поддерживаемые такой библиотекой. В последующих главах будут рассмотрены и другие типы геометрических объектов, в частности кривые и криволи- криволинейные поверхности (глава 10) и рекурсивно определяемые объекты (глава 11). Можно воспользоваться неиерархическим подходом к моделированию и рассматривать объекты из базового набора как символы, из которых строится модель. В состав набора сим- символов могут входить не только графические объекты, но и шрифты, и условные графические обозначения, специфичные для определенной области, например обозначения электрических элементов и узлов в системах проектирования электрических схем. Обычно символы в библиотеке представлены как объекты стандартного размера и ори- ориентации, удобные для данного символа. Например, ось цилиндра обычно параллельна одной из координатных осей, сам цилиндр име- имеет единичную высоту и единичный радиус, а центр основания распо- располагается в начале системы координат (рис. 8.1). В некоторых графических API, например в PHIGS и GKS, фрейм, в котором определен символ {фрейм модели — model frame или сис- Рис. 8.1. Символ ци- тема координат модели — model coordinates), не совпадает с миро- лин ра вым фреймом. Такое разделение весьма существенно, если символ представляет собой некую чисто геометрическую фигуру, например условное обозначение электрического элемента. В прикладной программе нужно определить преобразования, свя- связывающие фрейм символа и мировой фрейм, и применить их к матрице вида еще до того, как вызывать функции формирования символа. Преобразования экземпляра, о которых шла речь в главе 4, позволяют разместить в моде- модели каждый экземпляр символа на отведенном ему месте, причем размер символа и его ориен- ориентация также могут изменяться индивидуально для каждого экземпляра. Матрица преобразо- преобразования экземпляра имеет вид M = TRS и представляет собой суперпозицию преобразований сдвига, поворота и масштабирования (рис. 8.2). При необходимости к ним можно еще добавить и преобразование скоса. В резуль- результате в OpenGL-программе часто можно встретить фрагменты такого вида: 338 Глава 8. Иерархические графические модели
glMatrixMode(GL_MODELVIEW); glLoadldentityO; glTranslatef(...); glRotatef(...)» glScalef(...); glutSolidCylinder(...); /* или какой-либо другой символ */ Эту модель можно представить в виде таблицы, в частности такой, как изображена на рис. 8.3. При заполнении таблицы предполагается, что каждый символ имеет уникальный идентификатор. Такая технология моделирования не несет информацию о взаимных связях между объектами, но той информации, которая включена в таблицу, вполне достаточно для того, чтобы сформировать изображение всей совокупности объектов. Модель позволяет оты- отыскать любой объект и изменить параметры преобразований, заданных для этого объекта, уда- удалить из модели объект или добавить новый. Но отсутствие в модели связей между объектами ограничивает возможности работы с ней. Рис 8.2. Преобразования экземпляра Символ 1 2 3 1 1 Масштаб (S) Поворот (R) ****** Сдвиг (Т) d*'dy>dz Рис. 8.3. Таблица параметров преобразо- преобразовании символов 8.2. Иерархические модели Предположим, что перед нами стоит задача сформировать модель автомобиля, которую затем можно было бы использовать в программе анимации. Такую модель можно составить из пяти компонентов — кузова и четырех колес (рис. 8.4), — каждый из которых, в свою оче- очередь, состоит из примитивов графической системы. Два кадра анимации этой модели показа- показаны на рис. 8.5. В программе, которая будет формировать такие кадры мультфильма, нужно учитывать, что при повороте колеса на 360° автомобиль смещается на расстояние 2яг, где г — радиус колеса. В состав программы должна входить функция, которая будет формиро- 8.2. Иерархические модели 339
вать колесо, и функция, которая будет формировать кузов. Обеим функциям передается ин- информация о скорости и направлении движения модели. На языке псевдокода программа должна выглядеть примерно так: main() float s= ...; /* скорость */ float d[3]={...}; /* направление */ draw right front wheel(s,d); draw_left_front_wheel(s,d); draw_right_rear_wheel(s, d); draw_left_rear_wheel(s,d); draw_chassis(s,d); Рис. 8.4. Модель автомобиля Рис. 8.5. Два кадра мультфильма Мы привели эту программу только для того, чтобы показать, как не надо писать такого рода программы. В ней абсолютно игнорируется топологическая связь между отдельными компонентами модели автомобиля. Существуют два вида связей, которые желательно отобра- отобразить в модели. Одна связь определяет, что невозможно отделить движение автомобиля от движения отдельного колеса. Если автомобиль движется вперед, колесо должно вращаться1. Но, кроме того, следует учесть и тот факт, что все колеса автомобиля одинаковы, просто они по-разному расположены по отношению к кузову и имеют разную ориентацию. Отношения между компонентами в модели можно представить и абстрактно, и нагляд- наглядно — в виде графа. С точки зрения математики граф состоит из множества узлов и ребер. Ребра связывают пары узлов, причем не исключен вариант, когда некоторый узел связывается сам с собой. С ребрами можно ассоциировать направление связи, и в этом случае граф назы- называется ориентированным. Ребро ориентированного графа исходит из одного узла и заходит в другой. Мы будем работать с графами одного из самых распространенных видов — древовидным графом (или деревом). Деревом называется конечный связный ориентированный граф, не имеющий циклов (замкнутых путей). Каждый узел дерева, кроме одного — корня, — имеет точ- точно одно заходящее в него ребро. Следовательно, каждый узел имеет точно один родительский узел, с которым его связывает заходящее ребро. Но любой узел может иметь одно, ни одного или несколько исходящих ребер, а соответственно один, ни одного или несколько дочерних уз- узлов. Узел, не имеющий дочерних, называется терминальным узлом, или листай дерева. На рис. 8.6 показано дерево, представляющее отношения между компонентами нашей модели ав- автомобиля. Корнем дерева является объект кузова, а все четыре объекта, представляющие коле- колеса, — его дочерними узлами. Хотя математический граф представляет собой набор узлов и ре- ребер, при программной реализации с узлами и ребрами связывается дополнительная информация. В рассматриваемом примере с каждым узлом может быть связано описание соответствующего 'Неясно, колесо ли заставляет двигаться автомобиль, или движение кузова заставляет вращаться коле- колесо. С точки зрения графической программы скорее всего последний вариант более приемлем. 340 Глава 8. Иерархические графические модели
геометрического объекта. Информация о положении и ориентации каждого колеса может быть связана либо с соответствующим узлом графа, либо с заходящим в него ребром. В некоторых моделях используется по несколько экземпляров идентичных объектов. Именно такой вариант мы наблюдаем и в нашей модели автомобиля — все четыре колеса в ней идентичны. Совершенно очевидно, что нет необходимости дублировать в каждом узле колеса описание этого геометрического объекта, а следует воспользоваться той же идеей, ко- которую мы уже рассматривали при описании приемов работы с экземплярами, — использовать единственное представление объекта-прототипа. Если выполнить такую трансформацию, наше дерево превратится в ориентированный ациклический граф (ОАГ), представленный на рис. 8.7. Хотя в графе такого типа и существуют циклы, в каждом из них обязательно встре- встречается ребро, направленное навстречу направлению обхода. Следовательно, путь обхода в соответствии с направлением ребер обязательно приведет к терминальному узлу. Работа с ациклическими ориентированными графами не сложнее работы с обычными деревьями. В нашем графе, представляющем модель автомобиля, информацию о положении и ориентации каждого колеса можно хранить либо в описании узла кузова, либо в описаниях ребер, связы- связывающих узел кузова с узлом колеса. Кузов Кузов Правое переднее колесо Колесо Рис. 8.6. Дерево представления модели автомобиля Рис. 8.7. Ориентиро- Ориентированный ацикли- ациклический граф мо- модели автомобиля Оба варианта графов — и деревья, и ациклические ориентированные графы — представ- представляют собой иерархические методы описания взаимосвязей, которые существуют между ком- компонентами физической модели. Далее мы рассмотрим, как использовать такую иерархиче- иерархическую модель в графических программах. 8.3. Модель руки робота Робототехника представляет собой благодатное поле для использования методов иерархиче- иерархического моделирования. Рассмотрим упрощенную руку робота, показанную на рис. 8.8,а. Ее модель можно построить из трех простейших объектов или символов, воспользовавшись, например, па- параллелепипедами и цилиндром. Каждый из этих символов формируется из базовых примитивов. Три детали (звенья) руки робота показаны на рис. 8.8, б. Механизм имеет три степени свободы, каждая из которых описывается углом поворота в сочленении, связывающем каждое звено с пре- предыдущим2. Этот угол определяет, как будет располагаться следующее звено (и все дальнейшие) от- относительно предыдущего. Первое звено поворачивается относительно всего остального мира. Угол 'В этой модели не показано неподвижное основание, относительно которого вращается первое звено. — Прим. ред. 8.3. Модель руки робота 341
поворота в сочленении измеряется относительно собственного фрейма каждого звена. Первое зве- звено — тело робота — поворачивается на угол 0 относительно вертикальной оси своего фрейма, а угол измеряется от оси д: до некоторой фиксированной точки на нижней плоскости первого звена. Второе звено — плечо робота — связано с телом сочленением, позволяющим ему наклоняться в плоскости z = 0 фрейма плеча на угол ф, который измеряется от оси х этого же фрейма. Третье зве- звено — предплечье — связано аналогичным сочленением с плечом и может наклоняться относитель- относительно плеча на угол \|/, измеряемый также в его собственном фрейме. Изменение углов в сочленениях приводит к перемещению фреймов плеча и предплечья относительно фрейма тела робота. Управ- Управляя этими тремя углами, можно перемещать конечную точку предплечья (на которой в реальном роботе закрепляется кисть со схватом) в трехмерном пространстве. а) 6) Рис. 8.8. Рука робота: а — составная модель; б— компоненты Перед нами стоит задача разработать программу, которая будет формировать динамиче- динамическое изображение движущегося робота. Вместо того чтобы независимо друг от друга описы- описывать каждый элемент робота и его движение, мы воспользуемся подходом, основанным на суперпозиции движения отдельных звеньев. Тело робота может поворачиваться вокруг оси у собст- собственного фрейма на угол 0. Следовательно, можно описать пере- перемещение любой точки р на теле робота, воспользовавшись мат- матрицей преобразования поворота R,@). Плечо робота поворачивается вокруг оси z собственного фрейма, но этот фрейм нужно привязать к центру верхней плос- плоскости тела робота. Это можно сделать с помощью матрицы пре- преобразования сдвига Т@, Л,, 0), где Л, — высота тела относитель- относительно плоскости основания. Именно в этой точке находится сочле- сочленение между первым и вторым звеньями. Но если поворачивается тело робота, то вместе с ним поворачивается и плечо, точнее фрейм плеча. Следовательно, в дополнение к пре- преобразованию сдвига нужно применить и преобразование поворо- Рис 8.9. Перемещениезвень- та' воспользовавшись матрицей R,<9). Таким образом, положе- евробота и связанных ние любой точки на втором звене, в том числе и вершин много- с ними фреймов гранника, представляющего параллелепипед, в процессе изменения углов в первом и втором сочленениях будет зависеть от произведения матриц КД0)Т(О, hx, 0)К.(ф). Матрицу К,.(в)Т@, А,, 0) можно рассматривать как матрицу преобразования фрейма плеча относительно мирового фрейма, а матрицу К.(ф) как матрицу положения плеча относительно тела робота (рис. 8.9). 342 Глава 8. Иерархические графические модели
Рассуждая аналогично в отношении предплечья, придем к выводу, что это звено описы- описывается матрицей сдвига Т@, h2, 0) относительно фрейма предплечья, а затем матрицей по- поворота Rr(\|/). Положение точек предплечья — вершин представляющего это звено парал- параллелепипеда— определяется произведением R,.@)T(O, Ль O)R.@)T(O, h2, O)R_-(\j/). Функция отображения в OpenGL-программе построена таким образом, что матрица вида изменяется "в приращениях" (на сей раз приращения имеют вид матриц преобразования) перед фор- формированием изображения каждого очередного звена: display() { glRotatef(theta, 0.0, 1.0, 0.0); base(); glTranslatef@.0, hi, 0.0); glRotatef(phi, 0.0, 0.0, 1.0); lower_arm(); glTranslatef@.0, h2, 0.0); glRotatef(psi, 0.0, 0.0, 1.0); upper_arm(); } Обращаю ваше внимание на то, что положение звеньев задается независимо от формы каждого из звеньев. При одних и тех же углах в сочленениях можно изменить внешний вид робота, изменив функции формирования объектов, представляющих его зве- звенья. Такое разделение позволяет отдельно разрабатывать программы, описы- описывающие движение робота и форму его звеньев. На рис. 8.10 иерархия отноше- отношений между звеньями робота представлена в виде дерева. На ftp-сервере по ад- адресу ftp.cs.unm.edu в каталоге pub/angel/BOOK читатели найдут текст программы robot.с, в которой реализована эта структура. Программа позволя- позволяет "оживлять" на экране изображение робота по командам, формируемым с помощью меню. В этой программе тело робота имеет форму цилиндра, а плечо и предплечье — параллелепипедов. Тело робота Предплечье Плечо Рис. 8.10. Дерево представления иерархии звеньев робота Отображение Теперь рассмотрим, каким образом можно реализовать в программе структуру данных, соответствующую структуре дерева на рис. 8.10. Если хранить информацию в описаниях уз- узлов, то каждый узел должен содержать по крайней мере три элемента данных (рис. 8.11): ¦ указатель на функцию, которая формирует изображение объекта, представленного этим узлом; ¦ матрицу преобразований в однородных коорди- координатах, задающую отношение в пространстве ме- между объектом данного узла (и всеми объектами его дочерних узлов) и объектом его родительско- родительского узла; ¦ указатель на дочерний узел. В структуру узла можно включать и другую инфор- информацию, например набор атрибутов (цвет, трафарет за- заполнения, свойства материала и т.п.). Формирование изображения модели, описанной такой иерархической структурой данных, требует обхода дерева (tree traversal)— нужно "посетить" каждый узел и при обработке каждого узла вычислить элементы матрицы, которая м Дочерний узел -Дочерний узел I—Ш^- Рис 8.11. Представление узла струк- структурой данных в программе 8.3. Модель руки робота 343
будет определять преобразования примитивов, описывающих соответствующий графический объект, и затем уже сформировать их изображения. В той программе, которую мы рассмот- рассмотрели выше, такой обход был реализован неявно. Рассмотренный пример довольно прост — каждый узел в нем имеет только один дочер- дочерний. В следующем примере мы встретимся с более сложной иерархией объектов. 8.4. Обход деревьев п На рис. 8.12 показана фигура киборга — персонажа множества комиксов. Если выбрать кор- корпус киборга в качестве объекта корневого узла иерархии, то всю фигуру можно представить де- деревом, показанным на рис. 8.13. После того как положение корпуса будет задано, положение и ориентацию его конечностей можно определить углами поворота в соответствующих сочленениях. Можно "оживить" в программе фигуру киборга, изменяя значения уг- углов в сочленениях. В этой простейшей модели в коленях и локтях имеются сочлене- сочленения с одной степенью свободы, но можно создать и более сложную модель, поместив сочленения с двумя степенями свободы. А в сочленении, моделирующем шею, мож- можно и в этом простом варианте определить две степени свободы. Рис. 8.12. Фигурка киборга Рис. 8.13. Представление фигурки киборга в виде дерева Предположим, что разработано несколько функций вроде head() или left_upper_arm(), ко- которые формируют изображения отдельных компонентов фигурки (символов) в собственных системах координат. Далее можно сформировать набор описаний узлов дерева определить в них матрицы преобразования символа, с которым связан узел, относительно символа родительского узла, точно так, как мы это делали при построении иерархии звеньев робота в предыдущем раз- разделе. Будем считать, что каждый символ описан таким образом, что не требуется изменять его масштаб. Тогда матрицы преобразований в узлах будут включать произведение только матриц сдвига и поворота. На схеме дерева соответствующие матрицы можно использовать для марки- маркировки ребер, как это показано на рис. 8.143. Не забывайте, что каждая матрица представляет преобразование относительно предыдущего узла и что для получения преобразования относи- относительно мирового фрейма нужно перемножить матрицы по пути обхода графа. J Индексы матриц на рис. 8.14 — аббревиатуры от английских названий элементов модели киборга: h — head (голова), lua — left-upper ami (левое плечо), rua — right-upper arm (правое плечо), lul — left-upper leg (левое бедро), nil — right-upper leg (правое бедро), Па — left-lower arm (левое предплечье), rla — right-lower arm (правое предппечье), 111 — left-lower leg (левая голень), rll — right-lower leg (правая голень). — Прим. ред. 344 Глава 8. Иерархические графические модели
Корпус М м Рис. 8.14. Маркированное дерево представления модели киборга Самое интересное в этом примере — способ обхода дерева при формировании изображе- изображения фигурки. В принципе можно использовать один из множества существующих на сего- сегодняшний день алгоритмов, например алгоритмы поиска в глубину {depth-first) или поиска в ширину {breadth-first). Хотя в большинстве приложений не имеет существенного значения, какой именно алгоритм обхода используется, но желательно, чтобы это был один и тот же ал- алгоритм для всех графов. Мы будем всегда обходить дерево слева направо, реализуя поиск в глубину. Таким образом, начнем с левого ребра, исходящего из корневого узла, и будем сле- следовать по левым ребрам, исходящим из каждого очередного дочернего узла, пока не придем к терминальному. Затем вернемся на уровень вверх по дереву и исследуем следующее его реб- ребро, придерживаясь того же принципа. Вся процедура рекурсивно повторяется. Такой способ обхода получил название обхода в прямом порядке (pre-order traversal). Функцию обхода дерева можно запрограммировать двумя способами: выполнять обход в программном коде в явном виде, используя стек для хранения матриц и атрибутов по мере перемещения от узла к узлу, или использовать рекурсивную процедуру. Второй вариант при- приводит к более компактной программе, причем сохранение матриц и атрибутов выполняется неявно. Мы рассмотрим ниже оба варианта, поскольку по ходу их анализа вы получите воз- возможность по-новому взглянуть на методику построения графических систем. 8.4.1. Алгоритм обхода с использованием стека Будем считать, что формирование изображения фигурки выполняется функцией figure (), которую можно вызывать как из функции отображения display (), так и из функции обработ- обработки сообщений мыши mouse(). Последний вариант используется, когда с помощью мыши пользователь управляет углами поворота в сочленениях. Матрица вида М в момент обраще- обращения к функции figure () определяет положение фигурки в пространстве сцены по отношению к камере. При обработке первого узла дерева, представляющего корпус фигурки, соответст- соответствующий примитив отображается с использованием преобразования, заданного матрицей М. Затем следует переход по левому ребру, исходящему из первого узла дерева, к узлу, который представляет голову фигурки. Голова отображается функцией head(), причем перед вызовом этой функции матрица вида модифицируется, и при отображении используется произведение ММЛ. Узел Голова является терминальным, а потому алгоритм обхода возвращается к узлу Корпус и анализирует следующее по порядку ребро. Оно "приводит" алгоритм к поддереву Левое плечо, которое повторяет дерево модели робота, рассмотренное нами в предыдущем разделе. Для вычерчивания левого плеча используется матрица вида ММ/„„, а левого пред- предплечья — матрица вида ММ/мс/М//а. Узел Левое предплечье является терминальным, а потому алгоритм обхода возвращается к узлу Левое плечо. Этот узел имеет только один дочерний 8.4. Обход деревьев 345
узел, который алгоритм уже "посетил", а потому вновь возвращаемся к узлу Корпус. Далее исследуется следующее по порядку ребро, исходящее из этого узла, которое ведет к поддере- поддереву Правое плечо. Процедура обхода этого поддерева аналогична уже рассмотренной процеду- процедуре обхода поддерева Левое плечо. Поддеревья Левое бедро и Правое бедро обходятся точно таким же образом. При завершении обхода каждого поддерева алгоритм возвращается к кор- корневому узлу и восстанавливает исходное значение М матрицы вида. Возможно, проще было бы рассматривать весь этот процесс с помощью текущей матрицы преобразования — матрицы вида С, которая применяется по отношению к примитивам, оп- определенным в узле (ее структура и роль в графической системе описаны в главе 4). Исходное значение С равно М и преобразуется в ММЛ при обработке узла Голова, потом в ММ/„О, затем в ММ/„„М//<, и т.д. Обновление значения С должно выполняться прикладной программой пе- перед вызовом каждой функции отображения примитива, заданной в очередном узле дерева в порядке его обхода. Для реализации этого алгоритма обхода в графической системе OpenGL, кроме функций формирования матриц поворота, сдвига и масштабирования, нам потребуют- потребуются еще и функции glPushMatrix() — для сохранения текущей матрицы преобразования в стеке и glPopMatrix() —для извлечения значения матрицы из стека. Рассмотрим программный код начального фрагмента функции figure(), в котором опу- опущены значения аргументов функций преобразования: figure() { glPushMatrix(); torso(); glTranslate glRotate3 head(); glPopMatrix(); glPushMatrix(); glTranslate glRotate3 left_upper_arm(); glTranslate glRotate3 left_lower_arm() glPopMatrix(); glPushMatrix() glTranslate glRotate3 right_upper_arm(); glPopMatrix(); glPushMatrix() При первом вызове glPushMatrix() в стеке сохраняется текущее значение матрицы вида (предполагается, что перед этим уже была вызвана функция glMatrixMode(GL_MODELVIEW)). Сохранив это значение на будущее, можно сразу же приступать к модификации матрицы ви- вида— далее вызываются функции glTranslate() и glRotate(), которые формируют матрицу Mh и одновременно домножают текущую матрицу вида на сформированные матрицы преоб- преобразования. После этого вызывается функция отображения головы фигурки head(). Поскольку узел Голова является терминальным, нам нужно вернуться к его родительскому узлу. В про- программе это реализуется восстановлением предыдущего значения текущей матрицы преобра- 346 Глава 8. Иерархические графические модели
зования из стека — вызовом функции glPopMatrix (). Затем программа переходит к обходу следующего поддерева, и первая операция — вновь сохранить текущее значение матрицы ви- вида в стеке, т.е. вызвать функцию glPushMatrix(). Обход последующих поддеревьев выполня- выполняется в программе аналогично. На ftp-сервере по адресу ftp.cs.unm.edu в каталоге pub/angel /BOOK читатели найдут текст программы figure. с, в которой добавлено обращение к специальному меню, позво- позволяющему пользователю управлять углами поворота в сочленениях. В этой программе прими- примитивы, описывающие отдельные элементы фигурки, сформированы из эллипсов и цилиндров, а вся модель закрашивается с помощью методов, рассмотренных в главе 6. При подготовке текста рассмотренной программы совершенно не учитывалась обработка атрибутов (цвета и свойств материалов), которые можно ассоциировать с каждым узлом ие- иерархической модели. Атрибуты являются в OpenGL параметрами режима, т.е. они устанавли- устанавливаются программой в режимных переменных и воздействуют на отображение всех после- последующих примитивов до тех пор, пока программа не изменит текущее значение атрибута. По- Поэтому при обходе дерева с атрибутами следует поступать так же, как и с текущими значениями матрицы преобразования, — сохранять в стеке и восстанавливать из стека. Пред- Предположим, например, что при выполнении функции вычерчивания корпуса torso () был уста- установлен красный цвет, а при вычерчивании следующего компонента фигурки — головы — со- соответствующая функция устанавливает синий цвет. Если в программах отображения осталь- остальных компонентов фигурки нет операторов установки текущего цвета, то все они будут нарисованы синим цветом. Этот же цвет останется текущим и после завершения выполнения функции figure(). При обходе дерева в другом порядке может случиться так, что большин- большинство элементов будет вычерчено красным цветом. Такая ситуация может привести вас в замешательство, но существуют методы, которые позволяют успешно справиться с ней. С атрибутами следует поступать так же, как и с теку- текущей матрицей преобразования, — помещать текущее значение в стек при переходе к очеред- очередному узлу (поддереву) и восстанавливать из стека после возврата из этого узла (поддерева). В OpenGL это выполняется с помощью функций glPushAttrib() и glPopAttrib(). Если при входе в функцию figure() поместить атрибуты в стек, а перед выходом извлечь их из стека, текущие значения атрибутов восстановятся. Более того, внутри функции figure () можно до- дополнительно сохранять и восстанавливать значения атрибутов. В OpenGL переменные со- состояния можно разделить на группы и записывать в стек или извлекать из стека всю группу единым обращением к соответствующим функциям. Для этого в прикладной программе нуж- нужно установить определенные разряды в маске, которая является аргументом функции glPushAttrib(). Группы атрибутов включают и источники света, информацию о которых можно таким же способом помещать в стек и извлекать из него. Описанные идеи можно применять в моделях рекурсивно. Если, например, желательно сформировать более сложную форму головы фигурки — выделить в ней нос, глаза, уши и т.п.,— все эти компоненты можно описывать и отображать отдельно. В результате объект Голова будет иметь собственную иерархию, а программный код формирования изображения головы также будет включать обращения к функциям записи в стек и извлечения из стека матриц преобразования и атрибутов. Если дерево включает два или несколько идентичных поддеревьев, то его можно преобра- преобразовать в ориентированный ациклический граф. Запись в стек и восстановление из стека по- позволяют изолировать процесс выполнения отдельных фрагментов программного кода. В гра- графической системе OpenGL один дисплейный список может вызывать другой дисплейный список, и эта возможность позволяет программисту использовать еще один метод обработки деревьев и ориентированных ациклических графов. 8.4. Обход деревьев 347
В системе PHIGS используется аналогичный, но менее мощный метод для работы с де- деревьями и ориентированными ациклическими графами. В этой системе данные помещают- помещаются в группы — структуры, — которые в первом приближении можно считать аналогами дисплейных списков. Одни структуры могут вызываться из других структур функцией execute_structure(). Такой механизм позволяет формировать ориентированные ацикличе- ациклические графы. PHIGS поддерживает как локальные, так и глобальные матрицы преобразования, оперируя с которыми прикладная программа может эмулировать те же процедуры обращения к стеку матриц и атрибутов, что и в системе OpenGL. И в OpenGL, и в PHIGS используется фиксированный порядок обхода графов. В других API, включая и JAVA, поддерживаются древовидные структуры, которые можно обходить в разном порядке, формируя при этом од- одно и то же изображение, которое не зависит от порядка обхода узлов графа. Тот подход, который был рассмотрен выше, позволяет создавать работоспособные про- программы, но имеет известные ограничения. Структура модели представлена неявно в про- программном коде в виде последовательности операторов записи в стек и извлечения из стека, а это не позволяет гибко модифицировать модель. Чтобы избежать этого, следует отделить описание структуры модели от программного кода ее обработки. Мы рассматривали метод "жесткого" программирования модели с единственной целью— дать читателям наглядное представление о процессе обхода дерева. В следующем разделе мы рассмотрим более общий метод описания иерархических моделей и обхода их графов. 8.5. Обход древовидных структур Описанный в этом разделе подход основан на стандартных средствах представления в программе древовидных структур иерархических моделей, обработка которых произ- производится средствами, не зависящими от вида конкретной модели. Мы будем рассматри- рассматривать структуру, которая получила название слева — ребенок, справа — братья и сестры {left-child right-sibling) Рассмотрим альтернативное представление иерархии компо- компонентов фигурки (рис. 8.15). Это дерево построено таким образом, что все элементы од- одного уровня иерархии связаны слева направо. Дочерние узлы данного узла представлены вторым списком, элементы в кото- котором перечислены в порядке от самого левого до самого правого. Этот второй список представлен на рис. 8.15, снизу. Такое пред- представление описывает структуру фигурки, но в нем отсутствует графическая информация. Каждый узел должен хранить информацию, необходимую для отображения соответствующего компонента фигурки: функ- функцию формирования изображения компонента и матрицу преоб- преобразования в однородных координатах, которая определяет по- положение компонента относительно его родителя. Рассмотрим программную структуру, представляющую узел такого дерева: typedef struct treenode Корень Братья и сестры GLfloat m[16]; void (*f)(); struct treenode *sibling; struct treenode *child; }treenode; Рис. 8.15. Представление фигурки с помощью структур слева — ре- ребенок, справа — бра- братья и сестры 348 Глава 8. Иерархические графические модели
Массив m хранит по столбцам 4х4-матрицу преобразования в однородных координатах, как это принято в графической системе OpenGL. При обработке такой структуры в программе сначала выполняется умножение текущей матрицы вида на матрицу, хранящуюся в массиве ш, а затем вызывается функция формирования изображения компонента f (), в которой генери- генерируются нужные графические примитивы. В структуре также хранятся указатель child на ана- аналогичную структуру, представляющую самого левого ребенка, и указатель sibling на струк- структуру, представляющую цепочку расположенных правее его братьев и сестер. Если узлы того или иного вида в иерархии модели отсутствуют, соответствующий указатель имеет значение NOLL. Для представления рассмотренной в предыдущем разделе фигурки киборга нам пона- понадобится восемь узлов, соответствующих восьми компонентам фигурки: treenode torsojiode, head_node, luajiode, ruajiode, lll_node, rll_node, lla_node, rlajiode, ruljiode, luljiode; В программе эти узлы можно сформировать либо в функции main(), либо в функции myinit(). Рассмотрим, например, корневой узел дерева, представляющий компонент Корпус. Его ориентацию можно изменять, поворачивая вокруг оси у. В OpenGL формирование соот- соответствующей матрицы поворота выполняется функцией glRotatef(), а затем элементы сформированной матрицы копируются в массив m структуры treenode. Реализующий эти операции фрагмент программного кода приведен ниже: glLoadIdentity(); glRotatef(theta[0], 0.0, 1.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX,torsojiode.m); Формирование изображения компонента выполняется функцией torso(), которая вычер- вычерчивает цилиндр. Следовательно, остальная часть программного кода заполнения структуры узла выглядит следующим образом: torso_node.f = torso; torsojiode.sibling = NULL; torso_node.child = &head_node; Фрагмент программного кода, заполняющий структуру описания узла Левое плечо, выгля- выглядит так: glLoadIdentity(); glTranslatef(-(TORSO_RADIUS+UPPER_ARM_RADIUS), 0.9*TORSO_HEIGHT, 0.0); glRotatef(theta[3], 1.0, 0.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX,luajiode.m); luajiode.f = left_upper_arm; luajiode.sibling = fcruajiode; luajiode.child = sllajiode; Для этого узла матрица преобразования есть произведение матрицы сдвига левого плеча относительно фрейма корпуса и матрицы поворота на заданный угол в сочленении. Параметр сдвига фактически определяет положение сочленения с правым плечом на корпусе фигурки. Дочерним для этого узла является узел Левое предплечье, а очередным братом/сестрой — узел Правое плечо. Прочие узлы формируются аналогичными процедурами. Обход дерева выполняется тем же способом прямого обхода {preorder traversal), что и в ранее рассмотренном примере, но на сей раз реализуем рекурсивную процедуру об- обработки узла: 5.5. Обход древовидных структур 349
void traverse(treenode* root) { if(root==NULL) return; glPushMatrix(); glMultMatrixf(root->m); root->f(); if(root->child!=NULL) traverse(root->child); glPopMatrix(); if(root->sibling!=NULL) traverse(root->sibling); } При обработке узла, указатель на который не равен NULL, сначала в стеке сохраняется те- текущая матрица преобразования, для чего вызывается функция glPushMatrix(). Затем матри- матрица, хранящаяся в структуре узла, модифицирует текущую матрицу вида (предполагается, что соответствующий режим работы с матрицами установлен раньше). Далее формируется изо- изображение компонента, для чего вызывается функция, указатель на которую хранится в члене f структуры узла. Затем следует рекурсивное обращение к этой же функции, но для дочернего узла. Обращаю ваше внимание на то, что перед обращением к обработке дочернего узла в матрице вида уже установлена матрица преобразования текущего узла и именно она будет использована при формировании матрицы вида для отображения дочернего узла. А вот перед переходом к обработке "братского" узла нужно восстановить прежнюю матрицу вида — из- извлечь ее из стека с помощью функции glPopMatrix(). Если при обработке узла нужно уста- устанавливать новые значения атрибутов, то прежние следует сначала запомнить в стеке, а затем восстановить. Лучше всего это делать в функции отображения компонента либо выполнять эти операции синхронно с записью/извлечением матрицы вида. Одна из наиболее привлекательных особенностей рассмотренного метода программной реализации обхода такого иерархического дерева состоит в том, что он никак не связан со структурой конкретной модели. В результате в программе можно использовать родовую функцию отображения с обратным вызовом, которая имеет следующий вид: void display(void) { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glLoadIdentity(); traverse(&torso_node); glutSwapBuffers(); } Файл figuretree.c текста программы отображения подвижной фигурки киборга, в кото- которой используется рекурсивная функция обработки узлов дерева, читатель найдет на ftp- сервере по адресу ftp.cs.unm.edu в каталоге pub/angel /BOOK. Работая с этой программой, пользователь может изменять углы в сочленениях фигурки, вызывая те или иные команды меню, и наблюдать на экране, как движется фигурка. Поэтому все управление динамикой изображения сосредоточено в функции обработки сообщений мыши. Здесь изменяются углы в сочленениях, заново формируются матрицы преобразования для соответствующих узлов, а затем вызывается функция отображения: void mouse(int btn, int state, int x, int y) { if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) 350 Глава 8. Иерархические графические модели
theta[angle] += 5.0; if( theta[angle] > 360.0 ) theta[angle] -= 360.0; } if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) { theta[angle] -= 5.0; if( theta[angle] < 360.0 ) theta[angle] += 360.0; } glPushMatrix(); switch(angle) { case 0 : glLoadIdentity(); glRotatef(theta[0], 0.0, 1.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX,torso_node.m); break; /* блоки case для других значений индекса angle */ glPopMatrix(); glutPostRedisplay(); } Для того чтобы продемонстрировать гибкость рассмотренного метода, добавим в про- программу возможность динамически вставлять и удалять узлы в дерево. Такой динамический узел формируется приведенным ниже фрагментом программы: typedef treenode* tree_ptr; tree_ptr torso_ptr; torso_ptr = malloc(sizeof(treenode)); Заполняется узел, как и в предыдущем варианте, следующим программным кодом: glLoadIdentity(); glTranslatef(-(TORSO_RADIUS+UPPER_ARM_RADIUS), 0.9*TORSO_HEIGHT, 0.0); glRotatef(theta[3], 1.0, 0.0, 0.0); glGetFloatv(GL_MODELVIEW_MATRIX,lua_ptr->m); lua_ptr->f = left_upper_arm; lua_ptr->sibling = rua_ptr; lua_ptr->child = lla_ptr; Для обхода дерева, состоящего из динамических узлов, рекурсивной функции передается в качестве аргумента указатель на структуру корневого узла дерева: traverse(torso_ptr); Конечно, для отображения фигурки киборга использование динамических узлов вряд ли может дать какие-либо преимущества, но в более сложных программах этот подход позволя- позволяет формировать модели в интерактивном режиме, по командам пользователя. В частности, на его основе можно разработать приложение, при работе с которым пользователь сможет ре- редактировать структуру сложного объекта, добавляя или удаляя компоненты по своему выбо- выбору. Именно на таком принципе создаются программы редактирования сцены, которые мы рассмотрим в разделе 8.8. 8.5. Обход древовидных структур 351
8.6. Анимация В обоих рассмотренных примерах используются подвижные, шарнирные (articulated) объекты, которые состоят из компонентов неизменной формы, соединенных подвижными сочленениями — шарнирами. Такие модели несложно заставить двигаться — для этого нуж- нужно динамически изменять состояние сочленений, количество которых, как правило, невелико. Иерархическая программная модель позволяет сформировать сложное движение за счет од- одновременного изменения состояния нескольких сочленений. Но в предыдущих разделах мы не рассматривали вопрос, как организовать в программе управление изменением состояния сочленений во времени, которое воспроизводило бы задуманное пользователем перемещение объекта и позволяло создавать "оживляемые" изображения (выполнять анимацию). Особое место среди множества существующих на сегодняшний день методов анимации занимают два метода, один из которых является дальнейшим развитием традиционных тех- технологий создания мультипликационных фильмов, а другой "заимствован" у робототехники. Рассмотрим проблему перемещения из одной позиции в другую конечной точки руки ро- робота, со структурой которого мы познакомились в разделе 8.3. Как вы, надеюсь, помните, ро- робот состоит из трех звеньев, связанных тремя сочленениями (считая и сочленение с непод- неподвижным основанием), и, следовательно, имеет три степени свободы. Для каждого сочетания углов поворота в сочленениях (в робототехнике они называются обобщенными координата- координатами робота) несложно определить положение конечной точки руки в декартовом пространст- пространстве, но выполнить обратный расчет значительно сложнее. Возможны случаи, когда рука не может прийти в заданную точку ни при каких значениях обобщенных координат, заданное положение достигается при единственном значении набора обобщенных координат или при двух, или нескольких значениях набора. Задача отыскания набора обобщенных координат, позволяющего привести характеристи- характеристическую точку робота (как правило, конечную точку руки) в желаемое положение, в робото- робототехнике получила название обратной задачи кинематики (inverse kinematics problem). Рань- Раньше мы имели дело с прямой задачей кинематики (direct kinematics problem), суть которой — определить положение характеристической точки в декартовом пространстве по заданным обобщенным координатам. В робототехнике прямая задача решается либо на основе той же самой иерархической модели объединения звеньев робота, либо с помощью записанных в яв- явном виде тригонометрических соотношений между углами поворота в сочленениях и декар- декартовыми координатами характеристической точки. Если вектор обобщенных координат выра- выразить через в, а через р — положение характеристической точки в декартовом пространстве, то прямое уравнение кинематики можно записать в виде Р -/в). Аналогично, задавшись скоростями изменения обобщенных координат (углов в сочлене- сочленениях), можно вычислить вектор скорости характеристической точки. В кинематической модели робота не учитываются факторы, которые проявляются в ди- динамике, — инерция и трение. Можно вывести уравнение динамики робота, в котором учиты- учитываются обобщенные силы, — эта тема активно исследуется в робототехнике. Но нас больше интересуют обратные задачи кинематики и динамики. Для определенных структур объединения звеньев в единый механизм функция может быть неоднозначной или иметь точки разрыва. Задача осложняется еще и тем, что нам не только нужно найти сочетание углов поворота в сочленениях, которое обеспечит требуе- требуемое положение характеристической точки, но и такую траекторию движения робота из теку- текущего состояния в целевое, которая обеспечит обход препятствий — других объектов, распо- 352 Глава 8. Иерархические графические модели
ложенных в рабочей зоне робота. Существует и множество других ограничений, которым должна удовлетворять сформированная траектория. В явном виде обратные задачи удается решить только для таких простых кинематических структур, как у нашего трехстепенного робота. Более сложные кинематические структуры требуют иных подходов, значительно более сложных, в чем нетрудно убедиться, рассматри- рассматривая кинематическую структуру фигурки киборга. В системах компьютерной графики, где не требуются учет всех физических факторов и высокая точность, для решения обратной задачи кинематики может быть использован под- подход, заимствованный у создателей рисованных мультипликационных фильмов. Этот подход предусматривает формирование так называемых ключевых кадров (key-frame), которыми ху- художник-мультипликатор "размечает" траекторию сложного движения персонажа. Затем соз- создаются кадры, отображающие промежуточные фазы движения. В компьютерной графике этот процесс несложно реализовать, используя линейную интерполяцию в пространстве обобщенных координат шарнирного механизма. Можно применять и более сложные виды интерполяции, в частности непрерывными сплайнами, о которых речь пойдет в главе 10. Сплайн-интерполяция обеспечивает гладкий переход между ключевыми кадрами. Эту идею можно реализовать разными методами, с которыми вы сможете ознакомиться в специальной литературе. Частично перечень таких источников приведен в последнем разделе данной главы. Например, можно изменять не только положение, но и форму отдельных объ- объектов в последовательных ключевых кадрах и выполнять интерполяцию параметров прими- примитивов, определяющих форму трансформируемого объекта. Эта технология получила название морфинга (morphing). Основная сложность при реализации морфинга — правильно сопоста- сопоставить вершины модифицируемого объекта в последовательных ключевых кадрах и подобрать метод удаления "лишних" вершин или формирования новых (при необходимости). 8.7. Графические объекты Мы познакомили вас со множеством парадигм организации графических систем, но ос- основное внимание в этой книге все же уделяется конвейерной реализации модели синтези- синтезированной камеры. Это сделано по той простой причине, что именно эта архитектура в на- настоящее время поддерживается наиболее распространенными и доступными программны- программными и аппаратными средствами. Более того, в подавляющем большинстве примеров программ мы используем режим немедленного воспроизведения {immediate-mode), который предполагает, что объект отображается на экране сразу же после выполнения фрагмента программы, формирующего этот объект. После того как объект появится на экране, ника- никаких его следов в памяти программы не остается. Любые изменения атрибутов объекта или его положения требуют повторного выполнения фрагмента прикладной программы, фор- формирующего этот объект. Кроме того, желание познакомить читателей с основными методиками реализации функ- функций графической системы заставляет нас представлять программы, в которых детали реали- реализации методов видны довольно отчетливо. Этот подход, несмотря на довольно очевидные методические достоинства, не позволяет нам продемонстрировать альтернативные методы разработки графических приложений, основанные на более высоком уровне абстракции. В этом разделе мы постараемся восполнить этот пробел и познакомим вас с двумя кон- концепциями создания именно таких высокоуровневых графических приложений. Во-первых, мы расширим понятие объекта и будем рассматривать не только геометрические объекты, такие как многоугольники и векторы, но и другие элементы графических программ — ка- камеры (наблюдателей), источники света и свойства материалов. Во-вторых, мы остановимся на таких объектах, которые продолжают существовать в программе и после того, как их образ появится на экране. Более того, такие объекты могут существовать в программе, ни- 8.7. Графические объекты 353
когда не появляясь на экране. Выше уже говорилось о том, что такие объекты можно фор- формировать, используя дисплейные списки, а в этой главе мы рассмотрим альтернативные подходы, основанные на использовании классов языка C++ или структурных типов языка С. Хотя существующие на сегодняшний день версии API OpenGL не поддерживают объ- объектно-ориентированную технологию в явном виде, мы отнюдь не собираемся порывать с этой графической системой. Она будет использоваться по своему прямому назначению — формировать изображения объектов на экране. Программы, о которых пойдет речь в этом разделе, образуют своеобразную надстройку над OpenGL. 8.7.1. Методы, атрибуты и сообщения Создаваемые нами программы манипулируют данными. Данные могут представлять раз- различную информацию — от чисел и строк символов до информации о составных геометриче- геометрических объектах, сформированных в приложении. Традиционная процедурная технология про- программирования предполагает, что программист разрабатывает программный код, в котором манипулирование данными выполняется набором функций или процедур. Данные передаются в функцию через список аргументов и возвращаются аналогичным образом. Для того чтобы функция могла корректно оперировать с данными, она должна знать, как эти данные органи- организованы. Рассмотрим, например, куб, который мы не раз использовали в предыдущих приме- примерах. Уже не раз отмечалось, что его можно моделировать разными способами — с помощью указателей на вершины, списка ребер или списка вершин многоугольников граней. Програм- Программист, разрабатывающий прикладную программу, не очень-то хочет входить во все подробно- подробности конкретной реализации модели куба— он предпочитает рассматривать его как некую атомарную сущность или объект. Более того, прикладному программисту даже не желатель- желательно уделять внимание тому, каким образом формируется образ куба на экране, какая модель закрашивания и алгоритм заливки многоугольников при этом используются. Программист скорее предпочел бы, чтобы куб "сам знал", как отобразить себя на экране, т.е. чтобы алго- алгоритм отображения уже был "встроен" в этот куб, а ему (программисту) оставалось бы только вызвать этот алгоритм в том месте прикладной программы, где этот куб следует включить в изображение. В определенной мере система OpenGL поддерживает такую точку зрения, ис- используя для управления процессом формирования изображения параметры состояния графи- графической системы. Например, цвет куба, его ориентация и освещение граней могут рассматри- рассматриваться как компоненты текущего состояния и, следовательно, не зависеть от способа модели- моделирования конкретного куба. Однако такой взгляд на вещи несколько расходится с привычной методикой работы с фи- физическим кубом. Положение куба, его цвет, размер и ориентация кажутся нам тесно связан- связанными с самим физическим объектом. Хотя в OpenGL и можно связать с виртуальным кубом некоторые свойства — для этого можно воспользоваться технологией записи в стек и извле- извлечения из стека матриц и атрибутов, — сама по себе программная модель объекта не поддер- поддерживает этой идеи, а описанная технология выглядит довольно искусственной. Например, процедуры преобразования куба, отличные от функций работы с однородными координата- координатами, должны точно "знать", как организована модель куба в программе, и будут работать по схеме, показанной на рис. 8.16. Приложение Данные Результат Функция Рис. 8.16. Парадигма процедурного программирования 354 Глава 8. Иерархические графические модели
Программист разрабатывает программу, которая принимает в качестве аргументов указа- указатель на данные о кубе и список параметров преобразования. Получив эту информацию, про- программа должна что-то сделать с описанием куба и вернуть затем управление прикладной про- программе, ее вызвавшей. При этом, возможно, в вызывающую программу будут переданы ка- какие-то дополнительные данные. Совершенно другую технологию манипулирования объектами предлагает объектно-ориен- объектно-ориентированный подход к программированию. Еще на ранней стадии становления этого подхода ав- авторы Smalltalk, одного из первых объектно-ориентированных языков программирования, обра- обратили внимание на то, что компьютерная графика является одной из наиболее многообещающих областей приложения этой новой идеи. Сейчас одной из доминирующих тенденций в развитии компьютерной графики является симбиоз конвейерной архитектуры и объектно-ориентиро- объектно-ориентированного подхода, который позволит создавать еще более мощные графические системы. В объектно-ориентированных языках программирования объекты играют роль модулей, строительных блоков, из которых строится программа. Такой модуль включает данные, ха- характеризующие объект, например вершины нашего куба, его свойства (в нашей терминоло- терминологии — атрибуты) и функции {методы) манипулирования данными объекта и его атрибута- атрибутами. Для вызова того или иного метода объекта ему нужно послать сообщение. Такая модель представлена на рис. 8.17. Приложение Сообщение Методы объекта Рис. 8.17. Объектно-ориентированная парадигма про- программирования Специалиста, занимающегося разработкой прикладной программы, такой подход избавля- избавляет от необходимости вдаваться в какие-либо подробности внутреннего представления объек- объекта, в нашем случае — куба. Единственное, что его теперь интересует, — спецификация мето- методов, которые этот объект поддерживает (или которыми располагает). Именно эти специфика- спецификации и определяют формат сообщений, передаваемых объекту прикладной программой. Структурные типы в языке С обладают определенными чертами объекта в том понима- понимании, которое вкладывает в него парадигма объектно-ориентированного программирования, но далеко не всеми. В языке C++ на смену структурному типу struct пришел тип class. Классы C++ позволяют в полной мере воспользоваться всеми преимуществами объектно- ориентированного подхода, причем для тех, кто имеет достаточный опыт работы с языком С, переход на новый язык не представляет особого труда. 8.7.2. Объект cube Пусть перед нами стоит задача определить на языке С объект, представляющий куб, кото- который обладает атрибутом цвета и характеризуется определенным преобразованием в однород- однородных координатах. Воспользуемся для этого структурным типом языка С: struct cube { float color[3]; float matrix!4][4]; /* здесь будут размещены данные, описывающие собственно куб */ } 8.7. Графические объекты 355
Та часть определения, в которой представлены данные о структуре куба, как правило, ма- мало интересует программиста прикладных программ. Глядя с его "колокольни", интерес пред- представляют только атрибуты цвета и матрица преобразования, определяющая положение и ори- ориентацию куба (и, возможно, его размеры). Если в программе определен структурный тип cube, то экземпляры (instances) объекта ку- куба создаются в программе точно так же, как и переменные базовых типов: cube a, b; Атрибуты, которые являются частью определения класса, можно изменять индивидуально для каждого экземпляра куба. Если, например, нужно установить для куба а красный цвет, то это выполняется следующими операторами: a.color[0]=1.0; a.color[l]=a.color[2]=0.0; В OpenGL-программе атрибуты созданного объекта используются так же, как обычные переменные. Например, в программе, формирующей изображение куба, вы наверняка встре- встретите оператор установки текущего цвета glColor3fv(a.color); Но хотя таким способом мы и создали в программе объект, сохраняющийся и после его вывода на экран, возможности манипулирования им довольно ограничены. Как правило, это отдельные функции. Например, обращение к функции формирования изображения куба будет выглядеть примерно так: render_cube(а); Для поворота куба в пространстве сцены можно использовать функцию rotate_cube(a, theta, d); где d — массив, представляющий вектор оси поворота. Такой способ реализации объекта создает определенные неудобства. Одно из них состоит в том, что нужно разработать отдельные функции отображения и поворота для каждого типа объектов, существующих в приложении. Язык C++ предлагает другую методику организации объектов. Классы в C++ могут со- содержать общедоступные и закрытые члены. Общедоступные члены {public members) анало- аналогичны членам структурного типа struct в языке С, и к ним возможен доступ извне объекта, т.е. со стороны прикладной программы. Закрытые члены {private members) невидимы извне и могут быть изменены только функциями, которые сами являются член^.н этого класса. Та- Таким образом, определение объекта cube на языке C++ будет иметь следующий вид: class cube { public: float color[3]; float matrix[4][4]; private: /* здесь будут размещены члены данных, описывающие собственно куб */ } В объекте такого типа детали реализации внутренней структуры куба скрыты от приклад- прикладной программы. Класс C++ может располагать встроенными функциями — членами класса или методами класса. Собственно в этом и есть одно из главных отличий класса C++ от структурного типа С. Добавим в определение класса cube общедоступные методы: 356 Глава 8. Иерархические графические модели
public: void render(); void translate(float x, float y, float z); void rotate(float theta, float axis_x, float axis_y, float axis_z); Обращаясь к этим методам, прикладная программа сможет создавать, сдвигать, поворачи- поворачивать и отображать объект с помощью следующих операторов: cube a; a.rotateD5.0, 1.0, 0.0, 0.0); a.translateA.0, 2.0, 3.0); a.render(); Концептуально этот код предполагает, что, когда вызывается метод а. rotate (..), экзем- экземпляр объекта а сам "знает", как "поворачиваться". Можно считать, что, вызывая этот метод, прикладная программа посылает экземпляру а класса cube сообщение. Зная особенности гра- графической системы OpenGL, несложно разработать программный код реализации методов rotate (), translate() и пр. Тут следует отметить одну интересную особенность. Разработ- Разработкой системы классов и прикладной программы, работающей с этими классами, могут зани- заниматься разные специалисты. Более того, библиотека классов может быть создана в третьей организации и распространяться как коммерческий продукт, доступный любому желающему. Программист, разрабатывающий прикладную программу, нуждается только в описаниях классов и их методов, т.е. он должен располагать информацией о том, к какому результату приведет вызов того или иного метода класса, и его может совершенно не интересовать, как именно будет это выполнено. Итак, задумаемся над вопросом, к какому результату должен привести вызов метода render () или метода rotate () класса cube в программе, работающей в среде системы OpenGL? Прежде всего, нужно четко представлять, что переход на объектно-ориенти- объектно-ориентированное программирование в графической системе OpenGL означает отказ от режима не- немедленного вывода изображения. Каждый экземпляр какого-либо графического объекта су- существует в программе сколь угодно долго и в любой момент может быть изменен соответст- соответствующими методами. Одним из способов реализации таких "долгоживущих" экземпляров в OpenGL является использование дисплейного списка, но возможны и другие. Что для нас важнее всего, так это то, что после создания экземпляра объекта он где-то продолжает суще- существовать в программе, причем его внутренняя структура может быть совершенно неизвестна прикладной программе. Атрибуты объекта можно изменять специальными методами, подобными rotate(). Ме- Метод render () формирует изображение объекта, используя при этом не столько текущее со- состояние графической системы, сколько значения внутренних атрибутов экземпляра, включая и матрицу преобразования. В OpenGL в реализации метода render () вы, скорее всего, встре- встретите такую последовательность операторов: glBegin(GL_POLYGON) glVertex3f( ) Координаты вершин, необходимые для формирования образа многоугольника, хранятся в тех членах класса, которые находятся в секции private. Возможен и другой вариант — метод будет запускать процедуру повторного вывода изображения фрагмента дисплейного списка, включающего экземпляр объекта. 8.7. Графические объекты 357
8.7.3. Объекты и иерархия Одним из основных достоинств объектно-ориентированной технологии программирова- программирования является возможность повторного использования программного кода и построения более сложных объектов из небольшого набора простых. В разделе 8.4 была рассмотрена иерархи- иерархическая структура фигурки киборга. Пользуясь объектно-ориентированной технологией, мож- можно построить объект такой фигурки, состоящей из цилиндров, причем каждый экземпляр фи- фигурки может иметь собственный цвет, размеры, положение и ориентацию в пространстве сцены. Класс фигурки будет ссылаться на классы рук и ног. В приложениии, формирующем изображение автомобиля, класс автомобиля будет ссылаться на классы колес и кузова. Сле- Следовательно, в системе классов мы будем иметь древовидное представление структуры объек- объекта, в чем-то сходное с рассмотренным в разделе 8.5. При работе с классами часто возникает необходимость представить более сложные отно- отношения между объектами, чем отношение "родитель-потомок". Поскольку мы используем древовидные структуры, т.е. такие, в которых самый верхний уровень иерархии принадлежит корневому объекту, то отношения "родитель-потомок" в такой структуре имеют скорее смысл "обладает...". Так, наша фигурка киборга "обладает" двумя руками и двумя ногами, а автомобиль "обладает" кузовом и четырьмя колесами. Можно и по-другому рассматривать иерархию объектов, расположив простейшие из них на самом верхнем уровне. В таком случае между объектами более верхнего уровня и связан- связанными с ними объектами нижних уровней существуют отношения "принадлежит к...". Такой тип иерархии присущ таксономии (системы классификации). Примат принадлежит к живот- животным, а человек— к приматам. Такая иерархия уже использовалась раньше при рассмотрении методов проецирования. Параллельная проекция принадлежит к проекциям на плоскость, а косоугольная проекция принадлежит к параллельным. Отношение "принадлежит к..." позво- позволяет описать, как сложный объект состоит из более простых, а также организовать наследо- наследование сложным объектом свойств более простых. Таким образом, если разработать програм- программу обобщенного параллельного проецирования, то программа косоугольного проецирования может использовать фрагменты ранее разработанной программы и изменить только те ее час- части, которые касаются специфики косоугольного варианта параллельного проецирования. При работе с геометрическими объектами можно определить базовый класс, в котором имеется набор свойств, присущих всем геометрическим объектам данного вида, например цвет и свойства материала. В классах объектов частного вида можно использовать этот же набор свойств или добавить к нему новые, специфичные для данного вида объекта. Эта концепция поддерживается практически во всех объектно-ориентированных языках, таких как C++, в виде механизма наследования классов. 8.7.4. Геометрические объекты А теперь рассмотрим, как можно реализовать идеи объектно-ориентированного програм- программирования в графической системе. Прежде всего, следует задуматься над вопросом, а какие объекты (классы) имеет смысл включить в такую систему? Совершенно очевидно, что в ней должны присутствовать классы точек, векторов, многоугольников, прямоугольников, тре- треугольников (два последних, скорее всего, должны быть оформлены как подклассы класса "многоугольник"). Менее очевидно, как следует поступить с атрибутами, источниками света и камерами (наблюдателями). Например, неясно, стоит ли ассоциировать свойства материа- материалов с объектом, таким как куб, или они должны представлять собой отдельный класс? Имеет право на существование и первый, и второй варианты. Можно так определить класс куба, что в нем будут отдельные члены, представляющие оптические свойства для фоновой, диффуз- диффузной и зеркальной составляющих отраженного света, которые используются в модели Фонга, 358 Глава 8. Иерархические графические модели
описанной в главе 6. Альтернативный вариант— объявить отдельный класс оптических свойств материала, например, в таком виде: class material { public: float specular[3]; float shininess; float diffuse[3]; float ambient[3]; } Можно связать свойства материала с геометрическим объектом с помощью соответст- соответствующего метода класса геометрического объекта: cube a; material b; a.setMaterial(b); Источники света относятся к геометрическим объектам, поскольку обладают такими ха- характеристиками, как положение и ориентация. Объявление класса источника света может вы- выглядеть так: class light { public: boolean type; boolean near; float position[3]; float orientation[3]; float specular[3]; float diffuse[3]; float ambient[3]; } Определив набор классов геометрических объектов, мы сможем описать сцену, вклю- включающую экземпляры таких объектов. Описание сцены также удобно оформить в виде древо- древовидной структуры. Эту новую структуру будем называть графом сцены (scene graph). 8.8. Граф сцены Мы считаем сцену совокупностью геометрических объектов, образы которых должны появиться на экране, и объектов других типов (камеры и источники освещения), влияющих на то, как будут выглядеть геометрические объекты. Оба типа объектов характеризуются опре- определенными геометрическими параметрами и негеометрическими атрибутами, в частности цветом. Между всеми объектами сцены существуют определенные иерархические отноше- отношения. Например, в тот момент, когда примитив объявляется в программе, текущие параметры камеры используются для формирования образа этого примитива. Если в дальнейшем изме- изменить параметры камеры, например фокусное расстояние объектива, и создать новый геомет- геометрический объект (пусть даже такой же формы), его образ будет отличаться от образа первого объекта. Хотя с помощью реальной фото- или кинокамеры такое изображение получить не- невозможно, компьютер позволяет нам проделывать еще и не такие фокусы. Для описания от- отношений между объектами сцены — геометрическими, камерой и источниками света — мы можем использовать все ту же древовидную структуру графа. 8.8. Граф сцены 359
Поскольку методы программирования обхода дерева в графической программе уже были описаны выше, нам остается только задуматься над изменением структуры описания узла. Помимо матрицы преобразования экземпляра и указателя на функцию отображения объекта в нее необходимо включить еще и разнообразные атрибуты объекта. Но есть и другой вари- вариант— включить в дерево узлы новых типов, один из которых будет определять атрибуты, а другой — матрицы преобразования. Рассмотрим дерево (рис. 8.18), в котором присутствуют отдельные узлы цвета, матриц вида и специальные узлы разветвления. Узлы разветвления служат для программы обхода своего рода "дорожными знаками", указывающими, что в этом месте нужно сохранить текущее состояние и таким образом изолировать состояние поддере- поддерева, которое начинается после этого узла, от остального дерева. OpenGL-программа обхода дерева, в которой используется метод прямого обхода, будет выглядеть примерно так: glPushAttrib glPushMatrix glColor glTranslate glRotate objectl() glTranslate object2() glPopMatrix glPushMatrix glTranslate glRotate object3() glPopMatrix glPopAttrib Узлам-разделителям в этом фрагменте соответствуют вызовы функций glPushMatrix() и glPopMatrix (). При входе в этот фрагмент текущие атрибуты и матрица вида сохраняются, а пе- перед завершением программы они восстанавливаются. Программа устанавливает цвет, который ис- используется при формировании образов всех прочих объектов сцены, и обходит дерево примерно так же, как это делалось в рассмотренном выше примере отображения фигурки киборга. Цвет I Сдвиг I В: Объект;Т.щ Сдвиг J к Объект 3 I:jijy-V>-: :Ф:"<": ¦"; У',¦¦¦¦;.¦ S^ffi^isi ¦ *'*?*¦> ' ' • И% СД^ЙГД:Щ| :=,:¦: Поворот 1 mm^mmi m.. . I Рис. 8.18. Дерево сцены Поскольку стеки атрибутов и матриц можно использовать для сохранения параметров ви- визуализации, то в дерево можно включить и узлы, представляющие камеру. Хотя вряд ли в ка- каком-то приложении понадобится формировать изображение, в котором каждый объект будет использовать свою камеру, но вполне возможен вариант, когда на одном экране (но в разных видовых окнах) сцена отображается разными камерами, т.е. с разных точек зрения. Вот такое изображение и можно сформировать, используя в дереве сцены несколько камер. 360 Глава 8. Иерархические графические модели
API высокого уровня Граф сцены, который мы только что рассмотрели, эквивалентен тексту OpenGL-npor- раммы в том смысле, что можно чисто механически написать текст программы, имея перед глазами этот граф. Такой подход реализован в надстройке над OpenGL — объектно-ориен- объектно-ориентированном API Open Inventor. Программой для Open Inventor является описание дерева сцены. Выполнение программы организовано как обход графа сцены и вызов необходимых функций OpenGL. Граф сцены очень хорошо сочетается с идеями объектно-ориентированного программи- программирования. Все объекты, представленные узлами графа (примитивы, атрибуты, преобразова- преобразования), можно реализовать в виде отдельных классов и манипулиро- манипулировать экземплярами этих классов в программе обхода графа. Более того, такой подход позволяет легко организовать анимацию. Изме- Изменяя определенные параметры отдельных объектов, можно получать последовательные фазы движения персонажей, повторно запускать после этого программу обхода графа и выводить на экран следую- следующий кадр мультфильма. В Open Inventor программные объекты реа- реализованы с помощью функций OpenGL, а граф сцены представляет собой базу данных, которая включает все элементы сцены. OpenGL выступает в роли средства создания изображения, но не принимает никакого участия в спецификации сцены. В этом смысле Open In- Inventor очень напоминает систему PHIGS: задача прикладной про- программы в этой графической системе состоит в том, чтобы поместить описания объектов в базу данных. Часть базы данных может быть отослана на рабочую графическую станцию, которая и берет на себя все заботы, связанные с формированием изображения сцены. Программные графические системы занимают один из уровней в иерархии, представленной на рис. 8.19. OpenGL/X Виртуальная машина Аппаратура Рис. 8.19. Современная архитектура гра- графических систем Такие системы, в частности OpenGL, берут на себя функции формирования изображения и могут управлять либо непосредственно аппаратными средствами отображения, либо про- программными средствами более нижнего уровня иерархии. В качестве последних могут исполь- использоваться программные продукты, функциональные возможности которых аналогичны про- программе DirectX фирмы Microsoft. Прикладная программа занимает в этой иерархии самый верхний уровень. 8*9* Другие типы древовидных структур Деревья и ориентированные ациклические графы являются не только мощным средством описания сцен, но и находят широкое применение при решении других проблем компьютер- компьютерной графики. В этом разделе будут рассмотрены три из них: использование деревьев для опи- описания структуры сложных сплошных (solid) объектов в рамках конструктивной геометрии тел и два подхода к описанию пространственной иерархии, которые позволяют значительно по- повысить эффективность алгоритмов отображения. 8.9.1. Деревья в конструктивной геометрии тел Полигональное представление геометрических объектов, которое мы использовали до сих пор, имеет не только множество достоинств, но и ряд недостатков. Наиболее серьезным из них является то, что многоугольники — базовый элемент полигонального представления — позволяют описывать только "оболочку" вокруг какого-либо объема, а не собственно объем. Этот недостаток особенно отчетливо проявляется в системах автоматизации проектирования, где проектировщика интересует не только внешняя форма объекта, но и его объемные свой- 8.9. Другие типы древовидных структур 361
ства — масса, моменты инерции и т.п. Кроме того, представление пространственного объекта при отображении ребрами или гранями может стать причиной неоднозначной интерпретации пользователем предъявляемого образа. Например, "проволочное" изображение объекта, представленное на рис. 8.20, можно интерпретировать и как куб, в котором высверлено от- отверстие, и как узел, составленный из двух деталей. Конструктивная геометрия тел (CSG — constructive solid ge- geometry) — направление в компьютерной графике, призванное ре- решать подобные проблемы. В рамках этого направления в качестве примитивов используются сплошные геометрические тела кано- канонических типов — параллелепипед, цилиндр, сфера и т.п. Атрибу- Атрибуты объектов включают как оптические свойства их поверхностей (фактуру, отражательные свойства, цвет и т.п.), так и объемные свойства— размеры и плотность. При описании сцены, состоя- состоящей из таких объектов, принимаются во внимание точки внутрен- внутреннего подпространства, принадлежащего объектам, и, наоборот, объекты рассматриваются как совокупности (множества) опреде- определенных точек пространства, из которых (множеств) с помощью операций алгебры множеств можно формировать другие множе- множества — новые объекты. Рис. 8.20. Проволочное изо- изображение объекта, которое может быть неоднозначно интер- интерпретировано наблю- наблюдателем Методика моделирования в конструктивной объемной геометрии основана на использо- использовании трех основных операций алгебры множеств: ¦ объединение (union) двух множеств А и В, которое записывается в форме Alj В, — есть новое множество, включающее все элементы (точки) обоих исходных множеств А и В; ¦ пересечение (intersection) множеств А и В (А п В) представляет собой множество всех точек, которые одновременно входят и в множество А, и в множество В; ¦ операция вычитания множеств (set difference), A-B, формирует новое множество, со- состоящее только из тех точек множества/!, которые не принадлежат множеству В. На рис. 8.21 показаны два исходных объекта и три объекта, сформированных из них раз- разными операциями теории множеств. AUB АПВ А-В Рис. 8.21. Операции теории множеств 362 Глава 8. Иерархические графические модели
Эти операции описывают объекты, созданные из примитивов (или других объектов, соз- созданных ранее из примитивов). На рис. 8.22 показаны четыре примитива и объект, описывае- описываемый выражением (А-В) n(Cu D). А В С D (А - В) П (С U D) Рис. 8.22. Формирование объекта в конструктивной геометрии тел Как правило, для обработки алгебраических выражений используется дерево операций, внутренние узлы которого представляют операции, а терминальные — операнды (примитивы и объекты, созданные другими выражениями). Выражение (А-В) п(С u D) будет представ- представлено в графической системе деревом, показанным на рис. 8.23. Для обхода такого дерева и вычисления выражения используется метод обратного (post-order) обхода. Суть его состоит в том, что дерево обрабатывается рекурсивно — сначала левое поддерево, исходящее из узла, а потом правое поддерево, исходящее из узла. Результаты обработки каждого поддерева ис- используются в качестве операндов при выполнении операции (как правило, двухместной), спе- специфицированной в самом узле. Изображение моделей, созданных с по- помощью конструктивной геометрии тел, фор- формируется, как правило, с помощью метода трассировки лучей (см. упр. 8.10). Читатель вправе задать вопрос, а чем же кардинально отличается полигональное пред- представление объекта от представления такого же по форме объекта в рамках конструктивной геометрии тел? Отличие— в представлении объема объекта. При полигональном предс- представлении объем, как таковой, не существует. Существуют только участки поверхностей, которые могут ограничивать какой-то объем, но, в принципе, это совсем не обязательно. В конструктивной геометрии тел существуют только объемы, а поверхности — это только границы объемов. Проще всего уяснить себе суть этого отличия на простом примере. Пусть необходимо смоделировать некоторый реаль- реальный объект, который имеет форму параллелепипеда. С точки зрения полигонального пред- представления параллелепипед— это полый ящик, стенки которого сделаны из какого-либо лис- листового материала, причем достаточно тонкого, в идеале — нулевой толщины (картон, фанера, жесть и т.п.). Этот ящик может быть закрытым (состоять из шести граней) или открытым (состоять из пяти или даже четырех граней). На экране образ открытого и закрытого ящиков будет выглядеть по-разному, если по отношению к этому образу будет применен алгоритм удаления невидимых поверхностей, но проволочное изображение закрытого ящика нельзя будет отличить от изображения открытого. В конструктивной геометрии тел ящик, как тако- таковой, смоделировать очень трудно. В этом варианте модели примитивом является именно па- параллелепипед— брусок из какого-либо материала (дерева, металла, пластмассы и т.п.). Объ- Рис. 8.23. Дерево выражения, формирующего объект методами конструктивной гео- геометрии тел 8.9. Другие типы древовидных структур 363
ем бруска — основная характеристика этого объекта. Если же нам нужен именно ящик, то в конструктивной геометрии тел он создается из параллелепипедов конечной толщины, пред- представляющих отдельные стенки (например, тарные дощечки). Нужно будет "сбить" из таких дощечек ящик точно так же, как это делает столяр. Объем ящика — сумма объемов отдель- отдельных дощечек, естественно, с учетом их толщины. При этом никто не заставляет конструктора делать ящик из дощечек одинаковой толщины — на дно можно пустить доски потолще, а бо- боковые стенки сделать, например, из фанеры. Чтобы определить объем внутреннего простран- пространства такого ящика, его придется чем-то "заполнить". Эта процедура выполняется с помощью все тех же базовых операций теории множеств. С подобной проблемой часто встречаются конструкторы суден. Корпус судна— это своеобразный ящик (хотя и криволинейный), а во- водоизмещение— это масса воды, заполняющей часть этого ящика (по ватерлинию). Конст- Конструктора интересует как объем (масса) самого корпуса, так и его водоизмещение. Решить эту задачу можно только в рамках конструктивной геометрии тел, хотя отобразить на экране форму корпуса вполне возможно и с помощью полигонального моделирования. 8.9.2. Бинарные деревья разделения пространства Деревья, представляющие графы сцен и объекты в конструктивной геометрии тел, опи- описывают иерархические отношения между компонентами объектов. Эти деревья можно ис- использовать для описания мирового пространства объектов и инкапсулировать в них про- пространственные отношения между группами объектов. Один из подходов к представлению пространственной иерархии объектов основан на том очевидном факте, что плоскость раз- разделяет пространство на две части — два подпространства. Полученные подпространства можно затем делить плоскостями на подпространства все меньших размеров. Наглядно это можно представить в двухмерном случае, когда пространство — двухмерная плоскость — делится прямыми. Рассмотрим многоугольники и наблюдателя, показанных на рис. 8.24. Рис. 8.24. Наблюдатель и множество многоугольников Как мы показали в главе 7, существует такой порядок включения образов многоугольни- многоугольников в суммарное изображение сцены, который обеспечивает последовательное перекрытие образов одних многоугольников другими, и в результате создается корректное изображение всей совокупности. Эту идею можно реализовать не только с помощью метода сортировки, но и с помощью дерева, представляющего пространственные отношения между многоуголь- многоугольниками в совокупности4. Процесс формирования дерева начинается с разделения пространст- пространства сцены плоскостью одного из многоугольников, причем, какого именно, принципиального значения не имеет. В результате такого разбиения все многоугольники оказываются разделе- разделены на две группы — одни принадлежат тому полупространству, которое находится перед разделяющей плоскостью, а другие принадлежат полупространству, которое находится за 4По сути, это тоже один из видов сортировки. — Прим. ред 364 Глава 8. Иерархические графические модели
разделяющей плоскостью. Например, рассмотрим простую сцену, в которой все многоуголь- многоугольники параллельны и ориентированы таким образом, что их нормали направлены вдоль оси г. Это предположение не влияет на общность метода, а только упрощает его понимание и гра- графическое представление. Если многоугольники не параллельны и разделяющая плоскость пе- пересекает какие-либо из них, то последние разбиваются на два многоугольника, один из кото- которых включается в первую группу, а второй — во вторую. Вид на такое пространство со сто- стороны координатной оси у показан на рис. 8.25. Рис. 8.25. Вид сверху на разделяемые многоугольники Плоскость многоугольника А разделяет все многоугольники на две группы, одна из кото- которых содержит В и С (они находятся перед плоскостью А), а вторая — многоугольники D, Е и F. которые находятся за плоскостью А. С этой плоскости мы начнем формирование бинарно- бинарного дерева разделения пространства (BSP — binary spatial-partition tree). Узлы этого дерева представляют плоскости, разделяющие пространство (плоскости многоугольников, входящих в состав сцены), а ребра задают последовательность разделения. В корне бинарного дерева на рис. 8.26 находится многоугольник А, многоугольники В и С принадлежат левому поддереву, a D, Е и F — правому. Просматривая дерево рекурсивно, приходим к выводу, что С распо- расположен за плоскостью многоугольника В, а в правом поддереве плоскость D разделяет мно- многоугольники Е и F. Обращаю ваше внимание на то, что одна и та же конфигурация много- многоугольников может быть по-разному представ- представлена с помощью бинарного дерева простран- пространственного разбиения. Форма дерева сущест- существенно зависит от порядка разбиения, т.е. порядка, в котором обрабатываются много- многоугольники в ходе формирования дерева. В общем случае, если обнаруживается, что Рис. 8.26. Бинарное дерево разделения про- пространства плоскость некоторого многоугольника разре- разрезает другие многоугольники, пересекаемый многоугольник разделяется на два, первый из ко- которых помещается в левое поддерево, а второй — в правое, подобно тому, как мы поступали в главе 7 при реализации алгоритма сортировки. 8.9. Другие типы древовидных структур 365
Это дерево может использоваться при выводе на экран образов многоугольников, причем для обхода дерева используется алгоритм обратного обхода в центрированном порядке {backward in-order traversal). В соответствии с этим алгоритмом дерево обходится рекурсив- рекурсивно. В каждом цикле сначала "посещается" правое поддерево, потом корень, а затем левое поддерево. Одно из главных достоинств бинарных деревьев разделения пространства состоит в том, что одно и то же дерево можно использовать при формировании изображения, видимого раз- разными наблюдателями (с разных точек зрения). Наблюдатель может переместиться, например, в противоположный угол сцены, как показано на рис. 8.27, а мы сможем сформировать поря- порядок вывода образов многоугольников, обходя дерево с помощью стандартного центрирован- центрированного порядка обхода, — левое поддерево, корень, правое поддерево (когда наблюдатель рас- располагался так, как на рис. 8.24, мы использовали обратный центрированный порядок). Обра- Обращаю ваше внимание также на то, что алгоритм выполняется рекурсивно во всех тех случаях, когда можно разделить множество многоугольников или любых объектов на группы, назы- называемые кластерами (clusters). Следовательно, сгруппировав многоугольники в многогранник, можно затем сгруппировать многогранники в кластеры и рекурсивно применять алгоритм к кластерам. В приложениях типа симуляторов полета, для которых модель остается практиче- практически неизменной в процессе работы программы, а меняется в основном только положение на- наблюдателя, использование бинарных деревьев рассмотренного типа позволяет с высокой ско- скоростью формировать изображение, которое "видит" пилот из кабины летящего самолета. Де- Дерево содержит всю информацию об объектах сцены и их расположении относительно друг друга, а положение наблюдателя определяет выбор алгоритма обхода дерева. Рис. 8.27. Перемещение наблюдателя относительно объектов сцены 8.9.3. 4-арное и 8-арное деревья Поскольку в качестве плоскостей разбиения в ходе формирования бинарных деревьев раз- разделения пространства используются плоскости многоугольников, эти плоскости могут иметь произвольную ориентацию. Но расчет положения точки относительно произвольной плоско- плоскости занимает довольно много времени, учитывая то огромное количество точек, которые нужно обработать при построении дерева. Справиться с этой проблемой позволяет использо- использование 8-арных (octree) и 4-арных (quadtree) деревьев, поскольку при их построении исполь- используются разделяющие плоскости, параллельные координатным. Рассмотрим эти типы деревьев применительно к двухмерному пространству (рис. 8.28). Предположим, что изображение состоит из пикселей только двух цветов — черных и бе- белых, — которые сформированы каким-то алгоритмом отображения трехмерной сцены. Чтобы сохранить сцену, это изображение нужно поместить в двоичный массив. Но можно поступить и по-другому, учитывая согласованность в расположении групп пикселей. Можно провести две прямые, как на рис. 8.29, и разделить всю область на четыре квадранта. Обратите внима- 366 Глава 8. Иерархические графические модели
ние — один квадрант заполнен только белыми пикселями, а в остальных есть пиксели и того и другого цвета. Поскольку один квадрант полностью состоит из пикселей белого цвета, то этот цвет можно назначить всему квадранту. Три остальных квадранта придется опять разде- разделить и продолжать операцию рекурсивно с каждым из них таким же образом, пока в резуль- результате последовательных разбиений не окажется, что какая-либо из областей полностью запол- заполнена пикселями одного цвета. Вся информация сохраняется в дереве, которое получило на- название 4-арного {quadtree). Уровни такого дерева соответствуют последовательным разбие- разбиениям, а каждый узел имеет четырех потомков (рис. 8.30). ¦Г St., **& X Г ЛЯ •¦"¦ \:- 'й- • 'Г Рис. 8.28. Двухмерное про- пространство пикселей Рис. 8.29. Первое разбиение про- пространства Рис. 8.30. 4-арное дерево Поскольку при формировании 4-арного дерева мы разбиваем пространство линиями, па- параллельными осям координат, то эта процедура выполняется быстрее по сравнению с анало- аналогичным разбиением в процессе формирования бинарного дерева. Не менее существенно и то, что 4-арное дерево требует для хранения описания изображения меньшего объема памяти. 4-арные деревья представляют разбиение области двухмерного пространства. Их можно использовать и для разбиения объектного пространства примерно по такому же принципу, что и бинарные деревья, а затем обходить в порядке, зависящем от положе- положения наблюдателя. Это позволит сформировать корректное изображение каждой подоб- подобласти. Аналогом 4-арного дерева для трехмерного пространства служит 8-арное дерево. Разбиение выполняется не прямыми, а плоскостями, параллельными координатным, и каждый очередной цикл разбиения приводит к созданию восьми октантов трехмерного пространства (рис. 8.31). 8.9. Другие типы древовидных структур 367
Рис. 8.31. 8-арное дерево 8-арные деревья используются для представления множества объек- объектов, которые состоят из объемных элементов, называемых векселями (voxels) (рис. 8.32). Приложения, в которых используется такое разбие- разбиение пространства, будут рассмотрены в главе 12. Рис. 8.32. Множество объемных элементов 8.10. Графика и Web World Wide Web оказывает огромное влияние на пути развития компьютерных приложе- приложений практически любого типа, и графические приложения не является исключением. Гло- Глобальные информационные сети, подобные Web, позволяют выполнять обмен информацией в любой форме, предлагая новые технологии взаимодействия пользователя и программы. Для графических приложений подключение к Web открывает возможность более эффективного распространения информации, предъявления ее многим пользователям, расположенным где угодно, и доступ к распределенным ресурсам. Система OpenGL и ее расширения оказали большое влияние на развитие трехмерных сетевых графических приложений и соответст- соответствующих стандартов. Ниже мы рассмотрим, какие возможности сулит графическим прило- приложениям использование сетевой среды и что необходимо сделать для реализации этих воз- возможностей. Думаю, что некоторые концепции, в частности модель "клиент/сервер", читате- читателям уже знакомы. Мы затронем и вопрос о том, как следует организовать графическое приложение, чтобы его реализация не зависела от API. 8.10.1. Сети и протоколы При обсуждении в главе 3 характеристик графических приложений, работающих в сете- сетевой среде, в контексте систем "клиент/сервер" мы не останавливались на вопросе, как именно циркулирует информация между отдельными компонентами такой среды. Большинство мо- моделей сети базируется на представлении ее в виде многоуровневой структуры, подобной той, что показана на рис. 8.33. Здесь представлена крайне упрощенная версия модели, разрабо- разработанной в Международной организации по стандартизации (ISO — International Standards Or- Organization). На самом нижнем уровне располагаются физические средства, работающие на уровне отдельных битов. Следующий уровень занимает аппаратура передачи данных. Эле- Элементы этого уровня — кабельные каналы связи, телефонные линии, оптоэлектронные каналы и множество других видов аппаратных средств, физически реализующих передачу информа- информации на большие расстояния. Далее в дело вступают программные средства. Самый низкий уровень программных средств принадлежит протоколам передачи битовой информации меж- 368 Глава 8. Иерархические графические модели
ду абонентами сети, гарантирующим сохранность сообщения и устранение возможных иска- искажений. На этом уровне мы чаще всего встречаемся со схемами адресации, обеспечивающими отсылку сообщений и корректную их доставку адресатам, обнаружение искажений и восста- восстановление исходной информации на основе избыточности. На следующем уровне располага- располагаются протоколы, определяющие обмен информацией между процессами, реализуемыми на разных компьютерах сети. В то время как протоколы более низкого уровня определяют поря- порядок передачи информации от одного узла сети к другому, в компетенцию протоколов этого уровня входит обмен содержательной информацией между процессами. Для множества ком- компьютеров, подключенных к сети Internet, главным из протоколов этого уровня является TCP/IP (Transmission Control Protocol/Internet Protocol). Одно из определений сети Internet гласит, что она является "сетью тесно связанных компьютеров, использующих Internet Proto- Protocol". Самый верхний уровень иерархии занимают пользовательские приложения, обмени- обменивающиеся информацией по сети. При использовании таких графических API, как OpenGL, мы работаем именно на этом уровне. Рассмотрим приложение, в котором используется дисплейный список (архитектуру таких приложений мы обсуждали в главе 3). На одном компьютере в этом приложении выполняется пользовательская программа (в терминологии систем "клиент/сервер"— программа клиен- клиента), которая использует удаленный графический сервер для создания графических образов объектов, причем описания объектов создаются этой же клиентской программой и помеща- помещаются в дисплейный список. Самый нижний уровень в таком приложении занимают аппарату- аппаратура и стандарты подключения к сети Ethernet (рис. 8.34). Средний уровень отводится протоко- протоколу TCP/IP, согласно которому выполняется обмен содержательной информацией между ком- компьютерами клиента и сервера. Следующий уровень занимают, во-первых, Х-протоколы обмена графической информацией, которые являются расширением стандарта OpenGL, а во- вторых, сама система OpenGL. С точки зрения приложения эта графическая система занима- занимается единственной работой — воспроизведением на экране содержимого дисплейного списка. Самый верхний уровень принадлежит, естественно, прикладной программе. Приложение Процесс Сеть Каналы связи Физический уровень Рис. 8.33. Иерархическая мо- модель сетевой среды, раз- разработанная в ISO Рис. 8.34. Иерархия прото- протоколов в приложении, использующем OpenGL Существуют специальные протоколы стыковки, которые позволяют приложениям высо- высокого уровня взаимодействовать через сети разных стандартов. Если бы можно было так орга- организовать дело, чтобы все пользователи использовали в графических приложениях только OpenGL, в Web циркулировали бы огромные потоки графической информации в форме дис- дисплейных списков, поскольку многие приложения использовали архитектуру "клиент/сервер". Но отнюдь не все приложения используют один и тот же интерфейс API, и не все компьюте- 8.10. Графика и Web 369
ры, поддерживающие один и тот же интерфейс API, имеют одинаковую архитектуру. Вслед- Вследствие этого появляются дополнительные возможности. Оставим на время графику и рассмот-, рим сетевую среду с несколько другой точки зрения — с точки зрения накопления информа- информации, поступающей от множества источников. 8.10.2. Гипермедиа и HTML По мере развития Internet и проникновения ее во все сферы жизни современного общества расширялся и набор протоколов высокого уровня, применяемых для передачи сообщений электронной почты, файлов и информации других видов. Системы, подобные X Window, по- позволяют пользователю открывать окна на удаленном компьютере и передавать в это окно графическую информацию. Расширение объема информации, доступной широким кругам пользователей по сети Internet, потребовало от инженеров разработки все более сложных ме- методов совместного использования информации, хранящейся на разных узлах сети в отличаю- отличающихся форматах. Создание таких методов потребовало решения трех ключевых вопросов. 1. Разработка схемы адресации, которая позволила бы пользователям однозначно иден- идентифицировать ресурсы в сети. 2. Разработка методов кодирования информации разного вида, не только текстовой — изображений, ссылок на другие ресурсы. 3. Разработка методов интерактивного поиска необходимых ресурсов. Первые две проблемы были решены исследователями из Европейского центра ядерной физики CERN5, которые разработали спецификацию сетевой гипертекстовой системы World Wide Web. В этой системе ресурсы — файлы — идентифицируются уникальным универсаль- универсальным локатором ресурса (URL — Uniform Resource Locator), который состоит из трех компо- компонентов: идентификатора протокола передачи документа, идентификатора сервера и иденти- идентификатора раздела (каталога) сервера, в котором хранится документ. Например, URL для тек- текстов программ из этой книги выглядит так: ftp://cs.unm.edu/pub/angel/BOOK. Первая часть URL— ftp— означает, что для передачи этой информации будет использоваться стандартный протокол передачи файлов FTP (File Transfer Protocol). Второй компонент URL — cs.unm.edu — идентифицирует Web-сервер, а последний — pub/angel/BOOK — ка- каталог на этом сервере, в котором хранятся файлы текстов программ. URL сервера Организа- Организации пользователей OpenGL (OpenGL Organization) — http://www.opengl.org. Первый ком- компонент этого URL — http — указывает, что для считывания документов с этого сервера сле- следует использовать протокол пересылки гипертекстов HTTP (HyperText Transport Protocol), a документы находятся на сервере www.opengl.org. Поскольку в URL не указан конкретный каталог на сервере, после обращения по этому адресу пользователь получит доступ к каталогу по умолчанию — начальной странице (home page). Другой важнейший вклад специалистов CERN — разработка языка разметки гипертек- гипертекста (HTML — Hypertext Markup Language). Этот язык позволяет довольно просто, на уровне, доступном и непрофессионалу, описать документ, состоящий из собственно текста, ссылок на другие ресурсы (связей — links) и изображений. HTML-документ представляет собой текст, в котором используется ASCII-кодировка латинских букв и один из стандартных наборов сим- символов расширения для языков, отличных от английского, в том числе для славянских, кавказ- кавказских и пр. В совокупности схема адресации, принятая для формирования URL, и стандартизация формата HTML-документов составляют тот базис, на который могут опираться разработчики 5Аббревиатура CERN происходит от французского названия центра, размещенного в Женеве (Conseil European pour la Recherche Nucleaire). — Прим. перев. 370 Глава 8. Иерархические графические модели
средств доступа к ресурсам. Первым из таких средств, ориентированных именно на широкие круги пользователей-непрофессионалов, был броузер Mosaic, разработанный в Националь- Национальном центре суперкомпьютерных приложений (NCSA — National Center for Supercomputer Applications). Появление Mosaic дало толчок развитию отдельного класса интерактивных программ просмотра и перегрузки документов, доступных по сети Web, — броузеров. Mosaic и пришедший вскоре на смену ему броузер Netscape Navigator "прорубили" для огромной массы пользователей окно в мир Web. 8.10.3. Базы данных и VRML При всех его достоинствах, главнейшие из которых — простота и доступность в освое- освоении, язык HTML не рассчитан на поддержку работы в интерактивном режиме с трехмер- трехмерной графикой в сети Web. Это связано с тем, что изображение в HTML-документе — это только набор пикселей, представленных в одном из двух возможных форматов — TIFF и JPEG. В HTML отсутствуют какие-либо средства представления геометрических объектов, даже двухмерных. После того как спала первая волна энтузиазма, пользователи довольно быстро осознали этот недостаток, и в ответ на потребность компьютерного сообщества появилось множество предложений с проектами других языков. В результате поисков, сравнения и анализа из множества предложений "выжил" только проект языка моделиро- моделирования виртуальной реальности VRML (Virtual Reality Modeling Language). Существует множество способов передавать графическую информацию по сети. Можно, как это сделано в HTML, передавать статические изображения, но, совершенно очевидно, этот подход не сулит особенно широких возможностей взаимодействия пользователя с гра- графической информацией. Второй метод — пересылать исходный код программы формирова- формирования изображения на каком-либо общедоступном и распространенном языке, например OpenGL. Такой подход имеет очевидные достоинства и является еще одним серьезным дово- доводом в пользу стандартизации языковых средств компьютерной графики. Но использование в качестве носителя графической информации кода программы потребует установки на клиент- клиентском компьютере сети компилятора графической программы, например включения его в со- состав броузера. Третий подход — использование модели "клиент/сервер", подобной той, что применяется в OpenGL. Но при этом нужно согласиться на использование единого API и при- приемлемого для всех формата передачи дисплейного списка. Именно такая философия положе- положена в основу Java. Существует еще и четвертый подход, который сулит существенное повышение гибкости методов создания, хранения и передачи графической информации. Этот подход основан на идее применения баз данных для описания сцены, включая такие ее компоненты, как геомет- геометрические объекты, источники света, материалы и пр. База данных сохраняется в стандартном текстовом формате и легко передается по сети. Получатель описания сцены в виде базы дан- данных может сформировать соответствующее изображение, установив нужные программы на своем компьютере. Этот подход используется в системах, основанных на языке VRML. Разработка баз данных такого типа основана на тех же концепциях, которые мы уже об- обсуждали в главе 2, когда речь шла о функциональных возможностях графических API. По- Поскольку многие вопросы, в частности выбор набора примитивов, остаются прежними, имеет смысл выбрать такой состав элементов базы данных, который соответствует возможностям наиболее распространенных API, поддерживаемых на большинстве аппаратных и программ- программных платформ. Одним из фаворитов в конкурсе API является OpenGL, но у этой системы, по крайней мере в том виде, в котором она существует на сегодняшний день, есть существенный недостаток— отсутствие встроенных объектно-ориентированных средств для описания сложных сцен. Разработчики VRML отдали предпочтение базе данных Open Inventor. Как уже отмечалось в разделе 8.7, Open Inventor представляет собой надстройку над OpenGL, ко- 8.10. Графика и Web 371
торая расширила функции этой графической системы и, в частности, добавила возможность работы с графами сцен. Open Inventor позволяет пользователю сохранять описание сцены в виде текстового файла. Добавив связи в базу данных, мы получаем в свое распоряжение практически все возможности, необходимые для описания трехмерных сцен, компоненты ко- которых распределены по разным узлам сети. Язык VRML очень тесно связан с OpenGL. VRML предоставляет пользователю методы описания распределенного мира трехмерных объектов, а броузер VRML выполняет "сборку" изображения, объекты которого разбросаны по узлам сети Web. Хотя в принципе броузер может использовать любую технологию формирования изображения — конвейерную, осно- основанную на трассировке лучей или на методе излучательности, — тот факт, что язык базирует- базируется на Open Inventor, склоняет к выбору технологии, реализованной в OpenGL. 8.10.4. Java и аплеты Использовать те средства, которые были рассмотрены в предыдущих главах, для органи- организации взаимодействия пользователя с программой при работе в сети Web довольно пробле- проблематично. Базы данных VRML существуют в текстовом формате, а это предполагает, что сер- сервер формирует образы всех объектов, "прописанных" в базе данных. Следовательно, пользо- пользователь не имеет возможности разработать программный код на одном узле сети и быть уверенным, что этот код будет выполняться на другом, поскольку компьютеры на этих узлах могут быть совершенно различными. Язык Java позволяет частично решить эту проблему, поскольку он предполагает работу в программно созданной "виртуальной" машине, которая может, по крайней мере теоретиче- теоретически, быть реализована на любой аппаратной и программной платформе. Программа на языке Java компилируется в байтовый код {byte code), который затем выполняется на любой Java- машине, независимо от того, на какой платформе она реализована. Программы небольшого объема в байтовом коде получили название аплеты (applets). Такие программы "понимаются" любыми моделями Web-броузеров и в настоящее время широко используются для расширения возможностей динамической обработки информации в сети Web. В исходной версии язык Java поддерживал только двухмерную графику. В последнее вре- время появилось много предложений о создании версии Java-3D, способной работать с трехмер- трехмерными графическими объектами, но на момент подготовки этой книги к печати автору не была известна ни одна практическая реализация этой идеи. Тем не менее надежда на то, что в бли- ближайшее время появятся универсальные технологии обмена трехмерной графической инфор- информацией по сети Web, имеет достаточно твердую почву. 8.11. Резюме Разительное повышение быстродействия современных аппаратных средств формирования графических изображений открывает широкие возможности для реализации самых разных технологий моделирования в разных областях приложения компьютерной графики. В этой главе мы познакомили читателей с основами иерархического моделирования геометрических объектов. В главах 9, 11 мы продолжим эту тему, и читатель получит возможность комбини- комбинировать разные технологии моделирования, выбирая наиболее подходящую к специфике при- приложения. Освоить соответствующие методики читателям поможет литература, предложенная в следующем разделе для самостоятельного изучения. В этой главе мы осветили основные темы, которые имеют отношение к большинству су- существующих методик моделирования. Одна из главных— использование иерархического подхода, который позволяет адекватно описать отношения между объектами сцены. Было показано, как в рамках этого подхода использовать фундаментальные структуры данных — 372 Глава 8. Иерархические графические модели
деревья и ориентированные ациклические графы — для представления таких отношений, как реализовать обход графа в процессе формирования изображения. Использование древовид- древовидных структур для описания сцен с помощью Open Inventor, языков VRML и Java-3D позволит пользователям создавать сложные динамически обновляемые сцены, комбинируя заранее подготовленные и самостоятельно созданные программные модули. Модели, базирующиеся на древовидных структурах, используются и в языке Renderman Shading Language. С помо- помощью этого языка можно описать сложные варианты закрашивания сцены, в которых изобра- изображение создается с учетом взаимодействия источников света, свойств материалов, эффектов распространения света в воздушной или иной среде и используются различные локальные модели отражения. Существуют технологии тонирования, развивающие эту идею и позво- позволяющие использовать разные методы тонирования по отношению к отдельным объектам сцены в сложных графических приложениях, в том числе и распределенных по узлам сети. Но еще раз оговорюсь, что потенциальные возможности Renderman Shading Language ис- используются в настоящее время не полностью. Это объясняется тем, что большинство оконеч- оконечных систем основано на OpenGL, поскольку эта система наиболее эффективно реализуется имеющимися аппаратными средствами. 8.12. Рекомендуемая литература Методика использования иерархических преобразований посредством стека матриц опи- описана еще более 20 лет назад [New73]. Впервые эта методика была реализована в виде стан- стандартного компонента системы PHIGS [ANS188]. Описание технологии использования шар- шарнирных фигурок в программах анимации рассмотрено в книге Уатта (Watt) [lVat93J. В статье Лесситера (Lassiter) [Las87] анализируется взаимосвязь традиционной технологии создания движущихся изображений при производстве мультфильмов и технологии, используемой в компьютерной графике. Графы сцен — это базовый компонент системы Open Inventor [Wer94]. Формат базы данных Open Inventor послужил основой для языка VRML [Har96]. Большинство совре- современных API, таких как Java-3D [Rus97] и Direct 3D [Kov97], являются объектно-ориен- объектно-ориентированными. С анализом графических возможностей языка Java и аплетов читатель может познакомиться в книгах [Cha98] и [Агп9б]. Древовидные структуры являются базовым элементом языка Renderman Shading Language f(Jps89J, где они используются для фор- формирования модулей тонирования. Упражнения 8.1. Определите множество точек, которые входят в зону "досягаемости" робота, пред- представленного в разделе 8.3. 8.2. Выведите уравнения, описывающие прямоугольные координаты положения любой точки, принадлежащей последнему звену робота, в зависимости от его обобщенных координат (углов поворота в сочленениях). Можно ли определить эти углы по за- заданному положению конечной точки последнего звена? Поясните свой ответ. 8.3. Пусть заданы две точки в рабочем пространстве робота. Опишите траекторию пути между этими точками в терминах углов в сочленениях. 8.4. Разработайте простую циклическую программу, использующую таблицу "символ — преобразование экземпляра". Символами являются условные обозначения эле- элементов принципиальных электрических схем (резисторов, конденсаторов, дио- диодов и т.п.) или условные обозначения логических элементов (И, ИЛИ, НЕТ). Упражнения 373
8.5. Бинарное дерево, которое нужно обходить в процессе формирования изображения, можно представить в виде списка узлов, составными элементами которых являются указатели на узлы-потомки (правый и левый). Разработайте OpenGL-программу, ко- которая будет формировать изображение дерева, принимая в качестве исходной ин- информации такой список. 8.6. Роботы — далеко не единственный вид составных объектов, для которых харак- характерна зависимость движения одних компонентов от других. К таким объектам можно отнести большинство машин — велосипед, у которого вращаются колеса и педали, аэроплан, у которого вращаются пропеллеры, и даже детскую карусель с лошадками. Выберите какой-либо объект, реализующий подобные сложные движения, и разработайте графическую программу, которая воспроизводила бы это движение на экране. 8.7. Пусть заданы два многоугольника с равным количеством вершин. Разработайте программу, которая будет формировать динамическое изображение, демонстри- демонстрирующее процесс трансформации одного многоугольника в другой. 8.8. В структуру узла, описанную в разделе 8.5, добавьте новый член, представляющий атрибут, и внесите соответствующие изменения в алгоритм обхода. 8.9. Разработайте простую систему построения графа сцены, который включал бы мно- многоугольники, источники света, наблюдателя и материалы. 8.10. Почему методы трассировки лучей и приведения лучей наилучшим образом соче- сочетаются с использованием описания сцены в виде дерева в рамках конструктивной геометрии тел. 8.11. Покажите, каким образом 4-арные деревья можно использовать для вычерчивания изображения с разным разрешением. 8.12. Разработайте программу, которая позволит пользователю конструировать шарнир- шарнирные фигурки из небольшого набора базовых компонентов. С помощью этой про- программы пользователь должен иметь возможность указать места сочленений и "оживлять" сконструированную фигурку. 374 Глава 8. Иерархические графические модели
ГЛАВА 9 Операции с изображением на уровне растрового представления В течение многих лет графические программы имели дело исключительно с геометриче- геометрическими объектами, такими как прямые, многоугольники и многогранники. Хотя первые растровые системы появились еще лет 20 назад, но до последнего времени прикладные программы не имели прямого доступа к буферу кадра, поскольку в составе подавляющего большинства графических API отсутствовали функции чтения или записи отдельных пиксе- пикселей. В последнее десятилетие появились методы, которые, оперируя напрямую с содержи- содержимым буфера кадра и других внутренних двухмерных буферов графической системы, позво- позволяют добиваться очень интересных изобразительных эффектов. Именно таким способом вы- выполняется наложение текстур, сглаживание дискретизированного изображения, реализуется идея полупрозрачного альфа-канала. В этой главе читатель познакомится с такого рода тех- технологиями, причем, как и раньше, главное внимание будет уделено тем из них, которые под- поддерживаются средствами OpenGL и аналогичных ей API. Начнем мы с анализа методов наложения {mapping). Наложение применяется в процессе тонирования изображения и позволяет создавать иллюзию поверхности со сложной тексту- текстурой, хотя в геометрической модели эта поверхность может быть представлена самым зауряд- заурядным многоугольником. Методы наложения основаны на использовании массива пикселей, который определяет, как модифицируются параметры алгоритма тонирования (о таких алго- алгоритмах шла речь в главе 6), чтобы создать иллюзию сложной рельефной поверхности. Далее будут рассмотрены средства, позволяющие прикладной программе модифицировать отдель- отдельные пиксели в буфере, виды дискретных буферов и их назначение, методы доступа к ним, поддерживаемые OpenGL. В частности, будут рассмотрены технологии создания составных изображений, компонентами которых являются пара или более первичные изображения. Для этого нам понадобится четвертый компонент вектора RGBA, и вы узнаете, как с помощью альфа-канала можно накладывать изображения друг на друга, создавая эффект полупрозрач- полупрозрачности. В завершение мы еще раз вернемся к проблеме сглаживания границ областей, необхо- необходимость в котором возникает вследствие дискретной природы растрового преобразования.
9.1. Буферы и наложение В некоторых алгоритмах, рассмотренных в предыдущих главах, уже использовались два внутренних буфера графической системы — буфер кадра и буфер глубины (или z-буфер). Позже будет рассказано и о других. У всех внутренних буферов системы есть одна общая черта — их дискретная структура. Все буферы имеют ограниченное разрешение — ограни- ограниченное количество ячеек (пространственное разрешение) и ограниченную разрядность (разрешение по глубине). Можно дать определение двухмерному1 буферу как блоку памяти с пространственным разрешением (размерностью) их т ячеек и разрядностью каждой ячейки к бит (рис. 9.1). Параметры п и т буфера кадра соответствуют параметрам разрешения экра- экрана, а от параметра к зависит максимальное количество цветов, которые система способна создать в одном изображении. Параметры пит буфера глубины также соответствуют про- пространственному разрешению экрана, а параметр к определяет разрешение графической сис- системы по глубине— расстоянию между объектом и картинной плоскостью. Под термином к-я битовая плоскость (bitplane) будем понимать совокупность k-х разрядов во всех п х т ячейках буфера, а под термином пиксель — все к разрядов одной ячейки, которая соот- соответствует одному элементу изображения. Такое определение означает, что пиксель может иметь формат byte, integer или даже float, в зависимости от того, как используется данный буфер и какой формат выбран для хранения в нем информации. Наиболее интенсивно внутренние буферы используются при то- тонировании изображения поверхностей. Возможности моделирова- моделирования объектов набором геометрических примитивов и воспроизведе- воспроизведения образов этих примитивов на экране отнюдь не беспредельны. Предположим, что нужно изобразить на экране апельсин. Первое, что приходит в голову,— сформировать сферу примерно так, как это было сделано в главе 6. В результате будет сформирована ап- аппроксимация сферической поверхности множеством маленьких тре- Рис. 9.1. Структура угольников, затем эту аппроксимированную поверхность можно внутреннего буфе- "пропустить" через один из уже известных нам алгоритмов тониро- ра графической вания. Но вряд ли вас устроит результат — полученное изображение системы апельсина очень легко будет принять за изображение баскетбольно- баскетбольного мяча, — слишком уж "правильный" получится апельсин. Такого в супермаркете не увидишь. Можно поступить по-другому. В главе 10 рассказывается о том, как создавать поверхности нерегулярной формы, представляющие собой случайные искаже- искажения правильной формы. Описанные в ней методы дают программисту возможность контро- контролировать нерегулярности формы объекта, и полученное изображение уже не спутаешь с мя- мячом, но на настоящий апельсин оно тоже будет походить мало. В главе 11 описаны еще более сложные методы внесения нерегулярностей, но использовать их в графических системах ши- широкого применения (не супербыстродействующих) довольно затруднительно, поскольку, в конце концов, все они сводятся к усложнению трехмерной модели объекта— возрастанию количества треугольных граней, аппроксимирующих поверхность объекта. Но существует и альтернативный подход. Можно не усложнять трехмерную модель, а по- попытаться внести нерегулярность в двухмерный образ модели на стадии тонирования и рас- растрового преобразования. При построении образа поверхности — то ли криволинейной, то ли плоского многоугольника — его (образ) можно разбить на мелкие фрагменты, каждый из ко- которых имеет размер, не превышающий размер пикселя экрана. То, что размер фрагмента меньше одного пикселя, позволяет объединять образы нескольких поверхностей, каждый из ' Можно организовать одно-, двух-, трех- и четырехмерные буферы. Выбор того или иного варианта за- зависит от назначения буфера и методов работы с ним. 376 Глава 9. Операции с изображением на уровне растрового представления
которых вносит свой "вклад" в засветку пикселя (его цвет). При формировании окончатель- окончательного изображения нужно назначить тон или цвет каждому фрагменту. Все начинается с вы- выбора подходящей модели закрашивания из тех, что были рассмотрены в главе 6. Далее в ра- работу вступает алгоритм наложения {mapping algorithm). Его можно рассматривать либо как алгоритм модификации параметров модели закрашивания, который опирается на некоторый двухмерный массив данных — карту (тар), либо как алгоритм модификации параметров по- поверхности, обрабатываемой алгоритмом закрашивания, например свойств материала или на- направления нормали. Таким образом, в рамках этого подхода можно выделить ¦ наложение проективной текстуры; ¦ наложение микрорельефа; ¦ наложение параметров среды (environmental mapping). Алгоритм начожения проективной текстуры (texture mapping) использует некоторый шаб- шаблон (или текстуру) для формирования цвета фрагмента. Текстура может иметь регулярный ха- характер и храниться как фиксированный массив — именно такого типа текстуры часто исполь- используются для заполнения внутренних областей многоугольников. Иногда применяются методы динамического формирования текстур или в качестве текстуры используется внешнее изобра- изображение. В любом случае можно считать, что наложение проективной текстуры на поверхность, как это показано на рис. 9.2, является частью процесса тонирования этой поверхности. Наложение проективных текстур позволяет "проработать" детали образа гладкой поверхно- поверхности. Другой метод — наложение микрорельефа (bump mapping)— позволяет сделать поверх- поверхность менее гладкой, наложив на нее "пупы- "пупырышки" — карту микрорельефа (bump map), и превратить баскетбольный мяч в некое подо- подобие апельсина. Наложение карт отражения (reflection maps) или карт среды (environmental maps) позволяет сформировать изображение, Рис. 9.2. Наложение проективной текстуры напоминающее то, которое создается при трас- на поверхность сировке лучей, хотя сама процедура трассиров- трассировки и не выполняется. В этом случае изображение окружающих предметов (среды) накладывает- накладывается на поверхность и создается иллюзия зеркального отражения. Три перечисленные группы методов имеют много общего. Все они модифицируют ре- результат тонирования, никак не затрагивая форму объекта (точнее, его геометрическую мо- модель). Все методы реализуются синхронно с тонированием как часть заключительного этапа этого процесса. Все методы базируются на некоторых образцах — картах, которые хранятся в виде одно-, двух- или трехмерного дискретизированного изображения. Все методы требуют принятия специальных мер для сглаживания ступенек или зубцов на границах областей. 9.2. Наложение проективных текстур Образцы текстур могут иметь самый разный вид — от полос и клеток до сложных изо- изображений, воспроизводящих срез натуральных материалов, таких как мрамор, гранит, дерево. Очень часто мы распознаем материал реального объекта именно по его текстуре. Поэтому, если перед разработчиком графического приложения стоит задача создать иллюзию нату- натурального объекта, ему никак не обойтись без наложения на объект соответствующей тексту- текстуры. В этом разделе мы рассмотрим только работу с двухмерными текстурами, хотя исполь- используемые при этом методы вполне приложимы и к одно-, трех- и четырехмерным текстурам. 9.2. Наложение проективных текстур 377
На цветной вклейке вы найдете множество примеров применения наложения проектив- проективных текстур. На рис. 6 цветной вклейки показано изображение, созданное в среде графиче- графической системы OpenGL, причем текстуры сформированы также средствами этой системы. На ил. 10 наложение текстуры позволило передать фактуру деревянной столешницы, а на рис. 4 — фактуру кирпичных стен интерьера церкви. Большинство деталей поверхностей, изображенных на ил. 7, сформировано также с применением технологии наложения проек- проективных текстур. Это изображение создано в приложении, к которому предъявляются весьма жесткие требования по скорости обработки графической информации, поскольку они моде- моделируют динамические процессы. Когда такие приложения выполняются на рабочих графиче- графических станциях, укомплектованных аппаратными средствами выполнения наложения, процесс наложения текстуры никак не сказывается на скорости обработки информации. 9.2.1. Наложение двухмерных проективных текстур Процесс наложения проективных текстур выполняется в несколько этапов. Перед началом процедуры мы располагаем двухмерным образцом (шаблоном) текстуры — функцией интен- интенсивности T(s, /). Независимые переменные s и / называются координатами текстуры (texture coordinates). Сейчас можно считать образец текстуры массивом бесконечного (или, по край- крайней мере, очень большого) размера, хотя реально он, естественно, хранится в памяти конеч- конечного объема из п х т ячеек. Элементы массива образца текстуры иногда по аналогии с пиксе- пикселями называют текселями (texelsJ. He теряя общности, можно изменить масштаб представ- представления координат текстуры и выйти за диапазон @, 1). Карта наложения (texture map) ассоциирует с каждой точкой геометрического объекта интенсивность Г соответствующей точки образца, причем точки поверхности, в свою оче- очередь, отображаются на пространство координат экрана. Если объект представлен в про- пространственных (геометрических) координатах ((дг, у, z) или (дг, у, г, n)), можно далее рассу- рассуждать в терминах математической функции отображения пространства координат тексту- текстуры на пространство геометрических координат и функции проективного преобразования, отображающей пространство геометрических координат на пространство координат экра- экрана. Если описать геометрический объект в параметрической форме в виде функции пара- параметров (и, v), как мы это делали при работе со сферической поверхностью в главе 6, то в этой цепочке отражений появится еще одно звено. В этом случае нужно учитывать сущест- существование двух параллельных процессов отражения: первый отражает координаты текстуры на геометрические координаты, а второй — параметрические координаты на геометриче- геометрические, как это схематически показано на рис. 9.3. Третья функция отражения переносит нас в пространство координат экрана. Если не вдаваться в подробности, то процесс наложения текстуры выглядит достаточно простым. Малая область образца отображается на область геометрической поверхности, со- соответствующую одному пикселю окончательного изображения. Предполагается, что значения интенсивности образца Т представляют собой цветовой код RGB и их можно использовать либо для модификации цвета поверхности, определяемого из других соображений (источники света, отражение и т.п.), либо для назначения поверхности цвета только на основании образ- образца текстуры. Этот процесс выполняется синхронно с алгоритмом тонирования или закраши- закрашивания поверхности. Но при более внимательном взгляде обнаруживается множество "подводных камней". Во- первых, нужно определиться с методом сопоставления координат текстуры с геометрически- геометрическими координатами. Двухмерный образец обычно определяется на прямоугольной области в пространстве текстуры. Функция отображения этого прямоугольника на область произволь- 2В четырехмерном случае значение текселя Т есть точка в пространстве /s, t, r, q/. 378 Глава 9. Операции с изображением на уровне растрового представления
ной формы в трехмерном пространстве может быть достаточно сложной или обладать неже- нежелательными свойствами. Например, если желательно отобразить прямоугольную область на сферу, это невозможно сделать, не искажая форму образца или пропорции его компонентов (отношения между расстояниями). Во-вторых, сама организация процесса тонирования (он выполняется последовательным перебором пикселей) заставляет нас интересоваться скорее обратной функцией отображения, т.е. отображением координат экрана на координаты тек- текстуры. Нас интересует при этом, какая точка на образце соответствует определенному пиксе- пикселю образца поверхности, а это требует пересчета координат экрана в координаты текстуры. В-третьих, поскольку мы вычисляем коды засветки пикселей, каждый из которых определяет цвет элементарной прямоугольной области изображения, то нас интересует не функция ото- отображения точки одного пространства на точку другого, а области в одном пространстве на область в другом. Здесь мы вновь сталкиваемся с проблемой сглаживания границ между об- областями, которой следует уделить особое внимание. В противном случае появляются совер- совершенно неожиданные эффекты типа муара. Рис. 9.3. Карты наложения при параметрическом задании поверхности Рис. 9.4. Обратное отображение пикселя 9.2. Наложение проективных текстур 379
На рис. 9.4 схематически показана суть возникающих при наложении сложностей. Пред- Предположим, что вычисляется цвет квадратного пикселя, центр которого находится в точке {xs, ys) в системе координат экрана. Точка (дг„ ys) соответствует точке (х, у, -) в пространстве геометрического объекта, но если объект имеет криволинейную форму, то проекция угловых точек прямоугольной области пикселя обратно на пространство объекта образует криволи- криволинейное отображение пикселя. В терминах изображения (функции интенсивности) образца T(s. () обратное отображение пикселя приводит к выделению некоторой области с криволи- криволинейными границами, которая вносит вклад в закраску пикселя. Теперь на время оставим задачу определения функции отображения и обратим взор на ме- метод определения засветки пикселей. Одно из возможных решений — использовать точку, по- полученную при обратном отображении центра пикселя, и извлечь соответствующее значение функции окраски образца. Этот метод прост, но приводит к появлению на изображении де- дефектов квантования, которые особенно заметны в том случае, когда изображение образца но- носит периодический характер. На рис. 9.5 показано, в чем причина подобных искажений. На этом рисунке слева вы видите регулярную текстуру в виде полос, которую нужно наложить на плоскую поверхность. Обратная проекция центра каждого пикселя попадает как раз в про- промежуток между темными полосами, и в результате вся поверхность будет светлой. В общем случае, если не принимать во внимание конечные размеры пикселя, на изображении появится муар. Лучшие результаты можно получить дру- другим методом (но его сложнее реализовать) — формировать возвращаемое значение интенсив- интенсивности образца, усредняя значения интенсивности Т по области, обратной проекции пикселя. Этот метод дает лучший результат, но и он не идеален. Например, на рис. 9.6 показано, что происходит при его применении. На сей раз мы опять не по- получим на изображении полос, поскольку оно бу- будет иметь усредненный цвет— ни светлый, ни темный. Таким образом, нам и на этот раз не уда- удалось избавиться от дефектов, связанных с огра- ограниченным разрешением буфера кадра и образца. Наиболее заметны эти дефекты при регуляр- регулярной структуре образца текстуры. Вернемся вновь к проблеме отображения одного пространства на другое. В компьютер- компьютерной графике большинство криволинейных поверхностей задается в параметрической форме. Точка р на поверхности является функцией двух параметров и и v. Для каждой пары значений параметров формируется точка: x(u,v) z(n.v) Процесс формирования параметрической поверхности мы более подробно рассмотрим в главе 10, а сейчас нас интересует, как отобразить точку в пространстве текстуры (s, t) на точку р(и, v), принадлежащую такой поверхности. Рассмотрим линейную форму функции отображения: и = as+bt+c, v = ds+el^f. Эта функция отображения является обратимой, если ае Ф bd. Линейная функция отобра- отображения позволяет легко сопоставить текстуру с группой участков параметрически заданной Рис. 9.5. Эффект дискретизации при на- наложении текстуры 380 Глава 9. Операции с изображением на уровне растрового представления
поверхности. Например, как показано на рис. 9.6, участок образца, заданный угловыми точ- точками (smin, /min), (smax, /max), соответствует участку поверхности с параметрами угловых точек (Wmim vmin), (мтах, vmax), и в пределах участков функция отображения имеет вид и = м„:„ + v = v_,_ + • Такую функцию отображения несложно реализовать, но она не учитывает кривизны по- поверхности. В результате участки образца с равными размерами "натягиваются" на участки поверхности. Существует другой подход к проблеме выбора функции отображения, который предполагает разбиение процесса на две ста- стадии. На первой стадии текстура отображает- отображается на промежуточную поверхность стандарт- стандартного вида— сферу, цилиндр или куб. На второй стадии стандартная поверхность отображается на поверхность тонируемого объекта. Такой двухэтапный процесс можно применять в отношении как параметрически Рис. 9.6. Линейное отображение пространств заданных поверхностей, так и поверхностей, описываемых функциями в геометрических координатах. Рассмотренный ниже пример будет, по существу, одинаковым при любом способе задания поверхности. Предположим, что координаты текстуры имеют интервал представления @, 1) и что в ка- качестве промежуточного объекта используется цилиндр высотой А и радиусом г, как показано на рис. 9.7. Точки цилиндрической поверхности заданы в параметрической форме: х = г cosB7tz/), у = г sinBft//), z = v/A, причем параметры и и v также изменяются на интервале @, 1). Следовательно, можно ис- использовать функцию отображения в виде 5 = U, t = v. Значит, образец накладывается на цилиндр без искажения формы, т.е. без изменения соотноше- соотношения между отдельными элементами. Однако при отображении на замкнутую поверхностью, на- например сферическую, нужно будет каким-то образом ввести искажение формы. Эта задача но- носит примерно такой же характер, как отображение глобуса на плоскую поверхность географиче- географической карты, — при этом неизбежно возникают искажения пропорций. В обоих случаях — и при картографировании больших участков земной поверхности, и при наложении текстуры на сфе- сферическую поверхность — нужно выбрать один из подходящих способов представления, которые отличаются характером искажений. Например, знакомая многим проекция Меркатора предпо- предполагает наибольшие искажения в приполярных областях. Если в качестве промежуточного объ- объекта используется сфера радиуса г, то один из вариантов функции отображения имеет вид дг = г cosBtim), у = г sinBrci/) cosBkv), z = r sinB7ii/) sinBnv). 9.2. Наложение проективных текстур 381
На рис. 9.8 показано, как использовать в качестве промежуточной поверхности куб. В этом случае текстура фактически проецируется на развертку куба, причем про- проективное наложение часто применяется в комбинации с наложением среды, которое будет описано в разделе 9.3. Рис. 9.7. Отображение образца текстуры на цилиндриче- цилиндрическую поверхность Рис. 9.8. Наложение образца текстуры на развертку куба Суть второго этапа — отображение промежуточной поверхности с уже наложенной тексту- текстурой на поверхность тонируемого объекта сцены. На рис. 9.9 схематически представлены три возможных варианта стратегии отображения. В первом варианте (рис. 9.9,а) значение интенсив- интенсивности образца в точке промежуточной поверхности передается точке объекта, в которую "упирается" нормаль к промежуточной поверхности. Второй вариант реализует примерно такую же стратегию, но в обратном направлении — от объекта к промежуточной поверхности. Точке объекта передается значение интенсивности образца текстуры в той точке промежуточной по- поверхности, в которую "упирается" нормаль к поверхности объекта (рис. 9.9,6). Третий вариант реализует "центральную" стратегию. Если объект имеет такую форму, что к ней приложимо по- понятие центральной точки (например, уже упоминавшийся выше апельсин), то можно переносить на поверхность объекта значения интенсивности текстуры в точках промежуточной поверхно- поверхности, лежащих на пересечении с исходящим из центра лучом (рис. 9.9,в). Внимательный чита- читатель, несомненно, отметит, что математически первый и третий варианты должны дать один и тот же результат, если промежуточная поверхность имеет форму сферы, центр которой совпада- совпадает с центром тонируемого объекта сцены. Промежуточная поверхность б) в) Рис. 9.9. Второй этап наложения: а— использование нормали к промежуточной поверхности; б — ис- использование нормали к поверхности тонируемого объекта; в — использование центра тонируемого объекта 382 Глава 9. Операции с изображением на уровне растрового представления
9.2.2. Проективное наложение текстуры в системе OpenGL Графическая система поддерживает несколько вариантов реализации проективного нало- наложения текстур. В первой версии OpenGL можно было накладывать одно- и двухмерные тек- текстуры на одно-, двух-, трех- и четырехмерные графические объекты. В последней версии реа- реализовано наложение трехмерных текстур, но эта функция поддерживается только аппарат- аппаратными средствами профессиональных графических станций. Поскольку управление процессом требует настройки множества параметров, мы рассмотрим здесь только методику работы в OpenGL с двухмерными текстурами. Метод наложения, реализованный в OpenGL, учитывает особенности конвейерной архи- архитектуры этой графической системы. До сих пор мы делали основной упор на конвейерной обработке геометрической информации. Но в системе имеется и отдельный конвейер обра- обработки пикселей, причем результаты обработки на обоих конвейерах сливаются на этапе тони- тонирования изображения (рис. 9.10). В последующих разделах будут описаны операции, выпол- выполняемые в этом конвейере, но главное, что любое изображение, сформированное им, может быть наложено на изображение геометрических объектов. Такая архитектура предопределяет тип функций наложения, которые она способна поддерживать. В частности, проективное на- наложение выполняется в процессе растрового преобразования. Этот процесс отображает трех- трехмерные точки пространства сцены на двухмерное пространство экрана (пиксели). Каждый генерируемый фрагмент тестируется на видимость (с помощью z-буфера) и, если он видим, закрашивается. Можно считать, что наложение текстуры входит как отдельный этап в про- процесс закрашивания. Вершины отображаются на координаты текстуры, а нужные значения ин- интенсивности текстуры вычисляются путем интерполяции, как это делалось при закрашивании внутренней области многоугольника, вершинам которого назначены разные цвета. Вершины. Обработка геометрической информации Операции с пикселями / Растровое преобразование / Отображение Пиксели Рис. 9.10. Конвейеры обработки геометрической информации и пикселей Непременным компонентом программы наложения двухмерной текстуры является массив текселей. Предположим, что ранее программой каким-то образом сформировано или считано из файла изображение с размерами 512x512 и сохранено в массиве my_texels. GLubyte my_texels[512][512]; Вызов функции glTexImage2D() специфицирует этот массив как предназначенный для хранения двухмерного образца текстуры: glTexImage2D(GL_TEXTURE_2D, 0, 3, 512, 512, О, GL_RGB, GL_UNSIGNED_BYTE, my_texels); Формат вызова функции glTexImage2D() для спецификации двухмерного образца тексту- текстуры выглядит следующим образом: glTexImage2D(GL_TEXTURE_2D, <уровень>, <компонентЫ>, <ширина>, <высота>, <граница>, <формач!>, <гил>, <массив>); 9.2. Наложение проективных текстур 383
Массив для хранения образца текстуры должен иметь размерность <ширина> х <высота>. Значение параметра <компоненты> — количество (от 1 до 4) цветовых компонентов (RGBA), которые желательно использовать в образце. Задавая значения параметров <уровень> и <граница>, можно выполнять тонкую настройку режима работы с образцом, и мы на методи- методике такой настройки останавливаться не будем. Для того чтобы выполнять наложение в программе, требуется явно разрешить этот режим с помощью функции glEnable(): glEnable(GL_TEXTURE_2D); Следующий этап — указать графической системе, как текстура должна отображаться на геометрический объект. Образец текстуры имеет две координаты, s и t, которые, как правило, изменяются в интервале @.0, 1.0). В нашем случае пара значений @.0, 0.0) соответствует эле- элементу массива my_texels[0][0), а пара A.0, 1.0)— элементу массива my texels[511 ] [511 ] (рис. 9.11). Назначить координаты текстуры вершинам E11,511) можно с помощью функции glTexCoord2 f (): glTexCoord2f(s,t); Как и при работе с цветом и нормалями, при этом устанавливается переменная со- состояния (текущие координаты текстуры), ко- которая является компонентом набора перемен- ' ' ' ных текущего режима работы системы. Сле- Рис. 9.11. Соответствие между координа- довательно, координаты текстуры должны томи текстуры и элементами массива быть установлены перед тем, как будет вы- хранения образца звана функция формирования вершины. Ниже приведен фрагмент программы, в котором текстура накладывается на четырехугольник (четырем его вершинам назначаются соот- соответствующие координаты текстуры). glBegin{GL_QUAD); glTexCoord2f@.0, 0.0); glVertex2f(xl, yl, zl); glTexCoord2fA.0, 0.0); glVertex2f(x2, y2, z2); glTexCoord2fA.0, 1.0); glVertex2f(x3, y3, z3); glTexCoord2f@.0, 1.0); glVertex2f(x4, y4, z4); glEnd(); Перед формированием очередной вершины можно не только назначить новые текущие координаты текстуры, но и новый цвет или направление нормали. В приведенном выше фрагменте для наложения использовался весь массив текселей, что соответствует изменению координат текстуры от 0.0 до 1.0 (рис. 9.12,а). Если желательно использовать только часть полного интервала изменения координат s и /, например @.0, 0.5), нужно соответственно использовать и часть массива my texels. Изменив значения аргументов вызова функций glVertex2f() в приведенном выше фрагменте про- программы, получим изображение, представленное на рис. 9.12,6. Исполнительная система OpenGL будет при выполнении программы интерполировать значения координат s и t при за- закраске внутренней области четырехугольника и отображать интерполированные значения на 384 Глава 9. Операции с изображением на уровне растрового представления
пространство индексов в массиве my_texels. Пример с наложением текстуры на внутреннюю область четырехугольника кажется тривиальным, поскольку способ совмещения координат текстуры с вершинами четырехугольника совершенно очевиден. В случае многоугольников другой формы выбор того или иного варианта ассоциирования вершин с координатами тек- текстуры существенно изменяет вид сформированного изображения (рис. 9.13). Рис. 9.12. Наложение текстуры в виде шахматной клетки на четырехугольник: а — использование всего массива текселей; б — использование части массива текселей Рис. 9.13. Наложение текстуры на многоугольники: а и б — наложение текстуры в виде шах- шахматной клетки на треугольники; в — наложение текстуры в виде шахматной клетки на трапецию В общих чертах методика программирования наложения текстуры выглядит очень просто. Сначала нужно сформировать массив текселей, описывающий образец текстуры. Элементы массива должны иметь формат представления цвета, поддерживаемый графической систе- системой. Затем следует назначить значения нормированных координат текстуры формируемым вершинам графических объектов. Но в этой методике есть некоторые нюансы, без знания ко- которых вы не сможете эффективно пользоваться наложением текстур. Как часто бывает в по- подобных случаях, перед программистом всегда стоит задача выбора между более высоким ка- качеством изображения и высокой производительностью. В частности, существует проблема выбора формата представления образца текстуры (мы рассмотрим ее в разделе 9.5). Вторая проблема связана с тем, что назначенные значения координат s или / могут выйти за пределы интервала @.0, 1.0). В этом случае возможны два варианта: либо повторять тек- текстуру за пределами интервала, либо использовать предельные значения: 1.0 — если значение координаты превышает 1.0, или 0.0 — если значение координаты оказывается меньше 0.0. Настройка режима повторения образца за пределами номинального интервала изменения ко- координаты 5 выполняется вызовом функции glTexParameter(GL_TEXTURE_WRAP_S, GL_REPEAT); 9.2. Наложение проективных текстур 385
Для аналогичной настройки координаты / следует использовать константу GL_TEXTURE_WRAP_T. Для настройки режима офаничения в качестве второго аргумента функции glTexParameter() не- необходимо передать константу GL_CLAMP. Третья проблема — влияние дискретизации на изображение наложенной текстуры. При отображении координат текстуры на пространство индексов массива текселей очень редко получается так, что координаты соответствуют центру текселя. Кроме того, размер закраши- закрашиваемого пикселя на экране может быть больше или меньше размера текселя (рис. 9.14). Пиксели Пиксель а) б) Рис. 9.14. Отображение текселей на пиксели: а —увеличение; б —уменьшение В первом случае размер текселя больше размера пикселя, а во втором — меньше. В обоих случаях быстрее всего изображение будет формироваться, если задать режим выбора бли- ближайшего текселя. Режим увеличения или уменьшения текстуры настраивается с помощью ар- аргументов вызова функции glTexParameterf (): glTexParameterf(GL TEXTURE 2D, GL TEXTURE MAG_FILTER, GL NEAREST); glTexParameterf(GL_TEXTURE_2D, GL TEXTURE MIN FILTER, GL_NEAREST); Но такая стратегия может привести в снижению качества изображения. Лучшие результа- результаты дает усреднение интенсивности текселя. В OpenGL используется область усреднения с размерами 2x2, и такой режим настраивается заменой в списке аргументов функции glTex- glTexParameterf () константы GL_NEAREST на константу GL_LINEAR. В OpenGL можно использовать и другой способ разрешения проблемы неравенства раз- размеров — он получил название мип-наложения (mipmapping). Если проекция объекта на экран мала по сравнению с размерами образца текстуры, нет смысла в полной мере использовать степень детализации текстуры, обеспечиваемую массивом хранения образца. OpenGL позво- позволяет создать несколько массивов образцов с уменьшенным разрешением. Если исходный массив имел размеры 64x64, можно, воспользовавшись функцией gluBuild2DMipmaps () из библиотеки GLU, создать массивы с размерами 32x32, 16x16, 8x8, 4x4, 2x2 и 1x1: gluBuild2DMipmaps(GL TEXTURE_2D, 3, 64, 64, GL RGB, GLJJNSIGNEDJBYTE, my_texels); Ту же самую процедуру можно выполнить и с помощью функций библиотеки GL. На- Настройка режима автоматического использования полученных мип-образцов текстуры выпол- выполняется передачей соответствующих аргументов функции glTexParameterf (): glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); Еще одну проблему порождает механизм взаимодействия процессов тонирования и наложе- наложения. При использовании RGB-цветов профаммисту нужно выбрать один из двух вариантов. 386 Глава 9. Операции с изображением на уровне растрового представления
Текстура может "модулировать" оттенок, назначенный участку изображения в процессе тонирования. Это означает, что значения интенсивности наложенной текстуры умножаются на значения интенсивностей основных цветов, сформированных алгоритмом тонирования. Режим модуляции считается в OpenGL основным и устанавливается по умолчанию. В явном виде этот режим устанавливается вызовом функции glTexEnv(): glTexEnv(GL_TEX_ENV, GL_TEX_ENV_MODE, GL_MODULATE); Если в вызове этой функции в качестве третьего аргумента передать не GL_MODULATE, a GL DECAL, то цвет, назначенный алгоритмом наложения, "убьет" цвет, назначенный алгорит- алгоритмом тонирования. В результате имеющиеся в пространстве сцены источники света никакого влияния на изображение объекта оказывать не будут. Эта технология называется декодирова- декодированием (decaling). Эффект от наложения текстуры зависит и от того, какой тип проецирования используется при формировании изображения. По умолчанию исполнительная система OpenGL использует для формирования интенсивности накладываемой текстуры линейную интерполяцию в про- пространстве координат экрана. При ортогональном проецировании такое линейное отражение пространства текстуры на поверхность объекта (фактически на образ поверхности) является корректным, но при перспективном проецировании следовало бы учитывать нелинейные эф- эффекты по глубине. Можно так настроить режим работы исполнительной системы OpenGL, что она будет использовать более сложный метод интерполяции (если таковой поддержива- поддерживается в данной реализации системы), но за это придется платить снижением производительно- производительности. Настройка выполняется оператором glHint(GL_PERSPECTIVE_CORRECTION, GL_NICEST); Графическая система OpenGL поддерживает и несколько функций тонкой настройки про- процесса наложения. Например, можно выделить на изображении полосой заданной ширины (п пикселей) границы области, на которую накладывается текстура. В этом случае пользователь увидит на экране нечто, напоминающее мозаику из плиток разных материалов. Для автоматиче- автоматической генерации координат текстуры в OpenGL можно использовать функцию glTexGen(), кото- которая имеет несколько режимов работы. Один из них позволяет сформировать линейное отраже- отражение пространства геометрических координат на пространство координат текстуры. Одно из ин- интересных приложений этого режима— воспроизведение на экране рельефа с наложенной текстурой (скалы, трава и т.п.). Текстура непосредственно проецируется на трехмерную сеть. Другой режим позволяет использовать проекцию текстуры на сферическую поверхность. Очень интересные эффекты можно получить, управляя режимом "привязки" текстуры к координатам объекта или наблюм««?ля. Если привязать текстуру к координатам наблюдателя, то при переме- перемещении объекта в поле зрения наблюдателя текстура будет оставаться на месте и у пользователя создастся иллюзия, будто объект "проходит" сквозь текстуру. Технология работы с текстурами требует очень много памяти, особенно если в изображе- изображении используется множество образцов текстур. В подобных приложениях может потребо- потребоваться по ходу дела повторно генерировать образцы или загружать из файлов, что, конечно же, отнимает много времени. В OpenGL 1.1 появились объекты текстур {texture objects), благодаря которым в прикладной программе можно определить несколько образцов и сохра- сохранять их в памяти, выбирая одну в качестве текущей. 9.2.3. Генерация образцов текстур Одна из наиболее привлекательных особенностей технологии наложения текстур — это возможность воспроизводить в изображении достаточно много деталей, не затрудняя себя необходимостью формировать множество геометрических объектов. Высококлассные графи- графические системы, например Infinite Reality Engine фирмы SGI, способны выполнять трехмер- 9.2. Наложение проективных текстур 387
ное наложение текстур в реальном масштабе времени, т.е. обновление вида текстуры от кадра к кадру практически не сказывается на скорости формирования изображения по сравнению с воспроизведением чистой геометрии объектов. Каждая графическая карта в современных ПК оснащается памятью для хранения текстуры, что позволяет разработчикам игровых программ передавать на экране сложную, динамически обновляемую обстановку и создавать полную иллюзию погружения в реальную среду. Если, например, нам нужно имитировать на экране поле, поросшее травой, то можно по- получить текстуру травы, отсканировав фотографию. Это будет значительно быстрее, чем фор- формировать двух- или трехмерные объекты, которые будут выглядеть, как стебельки травы. В картографических приложениях воспроизведение мелких деталей рельефа в виде геометриче- геометрических объектов можно заменить наложением текстуры, отсканированной с карты. В приложениях можно использовать не только "заимствованные" текстуры, но и форми- формировать их непосредственно в программе с помощью соответствующих процедур. Особый ин- интерес представляют текстуры, характерные для натуральных материалов,— песк~, травы и различных минералов. В подобных текстурах можно выделить две составляющие — регуляр- регулярную и случайную. В большинстве методов генерирования таких текстур используется генера- генератор случайных чисел, выходные данные которого "пропускаются" через фильтр (рис. 9.15). Идеальный генератор случайных чисел формирует белый шум — последовательность некор- некоррелированных в статистическом смысле чисел. Управляя передаточной функцией фильтра, можно сформировать из белого шума последовательность чисел с заданными статистически- статистическими характеристиками (дисперсией, функцией корреляции и т.п.) и сформировать с их помо- помощью псевдослучайную текстуру. Методы обработки случайных чисел используются и Белый шум I Текстура д^я фОрМИрОвания трехмерных текстур. Функция интен- I сивности T(s, t, г) образца такой текстуры зависит от трех параметров, и ее можно непосредственно ассоциировать с Рис. 9.15. Процедурная генерация точкой (х, у, г) трехмерного пространства, что позволяет текстур обойти все сложности процесса наложения двухмерной текстуры на трехмерный объект. От пользователя (прог- (программиста, разрабатывающего прикладную программу) требуется только подобрать подходя- подходящую трехмерную текстуру с определенными пространственными свойствами. Концептуально работа с трехмерной текстурой напоминает нанесение рельефа на заготовку объемной скульптуры. Подобная технология используется при создании объектов, которые будут вы- выглядеть подобно высеченным из каменной глыбы — мрамора или гранита. 9.3. Наложение изображения окружающих предметов Если в составе сцены есть предметы с гладкой поверхностью, то в реальной обстановке на поверхности таких предметов мы всегда видим отраженное изображение близлежащих объ- объектов сцены — отражение среды. Примеров тому не счесть. Если в центр комнаты помес- поместить гладкий металлический шар, то на его поверхности вы увидите, как в кривом зеркале, всю обстановку комнаты. Такое изображение в графической системе можно сформировать, воспользовавшись методом трассировки лучей. Но применение этого метода связано с боль- большим объемом вычислений, а потому формирование изображения, если не использовать спе- специализированную аппаратуру сверхвысокого быстродействия, отнимает довольно много вре- времени. Отраженное изображение можно сформировать и другим способом, во многом анало- аналогичном наложению текстуры. Этот метод использует примерно такую же технологию, как и метод наложения текстур, но вместо искусственно созданных образцов текстур используются карты среды (environmental maps) или карты отражения (reflection maps). 388 Глава 9. Операции с изображением на уровне растрового представления
В основе метода лежит чрезвычайно простая идея. Всем известно, что на поверхности зеркала мы видим отражение окружающей обстановки. Если не пытаться воспроизводить программно процесс отражения, а просто принять к сведению, что на поверхности нужно сформировать подобное изображение, то это можно сделать с помощью двухэтапной проце- процедуры. На первом этапе нужно получить изображение среды в том виде, в каком оно проеци- проецируется на промежуточную воображаемую картинную поверхность (не обязательно плос- плоскость). Это можно сделать примерно так, как было описано в разделе 9.2. Центр проецирования размещается при этом в центре объекта с отражающей поверхностью, а при формировании про- проекции сам объект изымается из сцены (рис. 9.16). Сформированные в результате проекции изо- изображения в дальнейшем используются как обычные образцы текстуры, а их наложение выпол- выполняется одним из ранее рассмотренных способов. Но следует учесть, что, поскольку мы стремим- стремимся воспроизвести эффект отражения, нужно в процессе переноса изображения с промежуточной поверхности на поверхность объекта изменить положение наблюдателя и направление нор- нормали к поверхности, как показано на рис. 9.17. Объект с гладкой поверхностью Промежуточная поверхность Рис. 9.17. Наложение изображения, сформированного на промежуточ- промежуточной поверхности Промежуточная поверхность Проекция объекта сцены Рис. 9.16. Наложение изображения окружаю- окружающей среды Технология наложения изображений окружающих предметов поддерживается набором функций OpenGL. После того как сформирована нужная текстура— то ли сканированием изображения, то ли с помощью проецирования сцены, — OpenGL автоматически формирует координаты текстуры для сферического наложения. Алгоритмы, реализованные в виде функ- функций OpenGL, сопоставляют касательную к поверхности объекта с касательной к поверхности сферы, на которую накладывается текстура, как показано на рис. 9.18. У Рис. 9.18. Принцип наложения изображения сре- среды, реализованный в OpenGL Располагая координатами вершин и компонентами векторов нормалей, можно вычислить угол отражения. Затем нужно отыскать на сфере точку, касательная к которой имеет такое же направле- направление. В программе этот алгоритм реализуется следующей последовательностью операторов: glTexGenfv(GL_S, GL_SPHERE_MAP, 0); glTexGenfv(GL_T, GL_SPHERE_MAP, 0); glEnable(GL TEXTURE GEN_S); glEnable(GL~TEXTURE~GEN_T); 9.3. Наложение изображения окружающих предметов 389
На ил. 14 цветной вклейки показано изображение, сформированное в игровой программе Geri's Game, разработанной в Pixar Animation Studio. При создании изображения очков пер- персонажа в этом кадре использован метод наложения среды. Для формирования карты отраже- отражения была использована поверхность куба (ил. 15 вклейки), а затем сформированное на ней изображение было наложено на очки персонажа. 9.4. Наложение микрорельефа Теперь вернемся к нашему примеру с формированием изображения апельсина. Его можно создать, воспользовавшись фотографией настоящего апельсина, — отсканировать участок его поверхности на фотографии, сформировать из отсканированного изображения образец про- проективной текстуры и наложить его на объект апельсина в моделируемой сцене. Но если сце- сценарием предусмотрен поворот апельсина или изменение положения источников света в про- пространстве сцены, то внимательный зритель сразу отметит "искусственность" созданного изо- изображения апельсина. Дело в том, что для поверхности реального апельсина характерна не столько вариация окраски, сколько вариация рельефа — микрорельеф, — а последние не пе- передаются наложением на изображение проективных текстур. Технология наложения микро- микрорельефа (bump mapping) вносит определенные искажения в параметры формы поверхности, в частности в компоненты нормали к разным ее участкам, которые используются алгоритмами тонирования. В результате этот алгоритм вносит нерегулярность в вычисление цвета отдель- отдельных пикселей изображения поверхности на экране. Начнем с констатации того факта, что нормаль к участку поверхности в определенной точке характеризует форму поверхности в этой точке. Внесение небольших искажений в компоненты вектора нормали приведет к появлению локальных искажений формы поверхно- поверхности — созданию эффекта микрорельефа. Учет этих искажений на стадии тонирования по- поверхности объекта сохранит влияние на изображение текущей конфигурации источников све- света и прочих факторов, учитываемых при тонировании. Внести искажения в компоненты вектора нормали можно разными способами— ниже будет продемонстрировано, как это делается, если поверхность задана в параметрической форме. Пусть р(и, v) — точка на поверхности, описываемой параметрическими уравнениями. Единичный вектор нормали к поверхности в этой точке есть векторное произведение векто- векторов касательных, образуемых как частные производные по основным параметрам: Р.хр,- где 'дх ди ду ди dz ди • Pv = 'дх' dv ду dv dz .dv P. = т- Предположим, что в некоторой точке выполнено смещение в направлении нормали малого участка поверхности. Это смещение описывается известной функцией микрорельефа (bump function), d(u, v), причем значения функции достаточно малы (\d(u, v)| « 1): р' = р + d(u, v)n. 390 Глава 9. Операции с изображением на уровне растрового представления
Но нам требуется не изменять геометрию объекта, а только создать у наблюдателя иллюзию такого изменения. Для этого будем изменять не координаты точки р, а компоненты вектора нормали в этой точке п, а затем измененный вектор передавать алгоритму тонирования. Нормаль в точке р' есть векторное произведение п1 = р„ х р\,. Векторы р'„ и р\, можно вычислить следующим образом: dd Р =Р +—n + d(u,v)a. ди 3d Р, =Р,- +—n + d(u,v)nv. dv Если d мало, то последними членами в правой части обоих уравнений можно пренебречь и, учитывая п х п=0, после их подстановки в выражение для векторного произведения получим выражения для вычисления компонентов искаженной нормали: dd dd n =n+—nxp(+——пхри. Эй ov Вектор разности между исходной и искаженной нормалями лежит в плоскости, касатель- касательной к поверхности в точке р. Чтобы выполнить наложение карты микрорельефа, нам потре- потребуются два массива, которые будут содержать значения ddldu и dd/dv. Элементы этих масси- массивов следует вычислить заранее, воспользовавшись методами, аналогичными тем, которые применялись при формировании проективных структур. Искажение компонентов нормалей реализуется в процессе тонирования. 9.5. Запись в буферы Работая с современной графической системой, программист может записывать данные в промежуточные буферы и считывать данные из них. Но на выполнение этих операций оказы- оказывают влияние два фактора, которые заставляют выполнять их не так, как обычные операции чтения/записи в память. Надо сказать, что потребность в таких операциях на уровне отдель- отдельных пикселей возникает довольно редко в прикладной графической программе. Как правило, требуется считывать или записывать целый блок пикселей (или битов). Например, при залив- заливке внутренней области многоугольника единой процедурой формируется целая строка растра. Чтобы вывести на экран изображение точечного символа, его образ переносится в небольшой блок пикселей. При очистке экрана нужно единой процедурой записать нули во все ячейки буфера кадра. Следовательно, необходимо располагать как программными, так и аппаратны- аппаратными средствами выполнения таких групповых операций над блоками пикселей, как правило, — прямоугольными, причем эти средства должны обеспечивать максимальную скорость выпол- выполнения операций. Такие операции даже обозначаются специальным термином "пересылка би- битового блока" (bit-block transfer, или bitblt}). Иногда можно встретить и термин растровые операции (raster operations, или raster-ops). Предположим, что возникла необходимость перенести блок пикселей размером пхт из одного буфера в другой. Первый буфер в такой операции — буфер-источник (source buffer), a второй— буфер-приемник (destination buffer). На рис. 9.19 схематически представлен про- процесс передачи битового блока, причем в роли буфера-приемника в этой операции выступает буфер кадра. В программе операция пересылки битового блока выглядит примерно так: write_block(source, n, m, x, у, destination, u, v); 9.5. Запись в буферы 391
Буфер-приемник- 5 «•?.*..**»¦ С^ Буфер кадра Буфер-источник - Память Рис. 9.19. Пересылка битового блока из од- одного буфера в другой В этом операторе source и destination — адреса буферов, тип — размеры блока. В ре- результате выполнения оператора блок с размерами nxm , начальный элемент (точка) которого имеет параметры (х, у), будет перемещен из буфера-источника source в буфер-приемник destination и размещен в этом буфере, начиная с '< " ''}."'-. -V- ^' »« \ ¦ элемента (u, v). Хотя существует множество ню- нюансов, на которые следует обращать внимание при программировании подобной операции (напри- (например, а что произойдет, если блок выходит за гра- границы буфера-приемника), суть ее состоит в том, что вся процедура выполняется как единая опера- операция, которая изменяет содержимое буфера-прием- буфера-приемника. Обратите внимание на то, что с точки зре- зрения аппаратной реализации выполняемые при этом действия никак не связаны с характеристи- характеристиками, затрагивающими обработку геометрических объектов. Следовательно, аппаратные средства для выполнения операций с битовыми блока- блоками имеют совершенно другую архитектуру, чем аппаратные средства реализации конвейер- конвейерной обработки геометрических объектов. 9.5.1. Режимы записи битовых блоков Второе отличие операций с битовыми блоками от обычных операций с ячейками па- памяти состоит в том, что содержимое соответствующего блока в буфере-приемнике после выполнения операции может быть изменено множеством способов, т.е. имеется множе- множество режимов записи информации в буфер-приемник. При обычных операциях записи в память новое значение заменяет значение, хранившееся в заданной ячейке (или блоке) ранее. При выполнении в программе, написанной на языке С, оператора такого вида (оператора присваивания): у=х; значение из ячейки памяти х дублируется в ячейке памяти у. Но это не единственно возможный вариант передачи информации в буфер-приемник. Предположим, что нам нужно поразрядно обрабатывать информацию в буферах. Рассмотрим модель, представленную на рис. 9.20. Обозначим через s выбранный бит буфера-источника, а соответствующий ему бит в буфере-приемнике — через d. Если возможно перед записью в буфер-приемник прочесть текущее значение бита d, то изменение информации в буфере- приемнике можно описать функцией преобразования: Буфер-источник [ read_pixel 6' write_pixel -Буфер-приемник! Рис. 9.20. Режимы записи 392 Глава 9. Операции с изображением на уровне растрового представления
Существует 16 вариантов совместной обработки двух одноразрядных двоичных чисел — 16 логических функций f. Эти логические функции представлены в таблице на рис. 9.21, при- причем каждая колонка в правой части таблицы соответствует одному из возможных вариантов функции/ Десятичный эквивалент двоичного числа, образованного содержимым ячеек таб- таблицы в каждой из колонок, будем использовать в качестве номера режима побитовой записи (writing mode). Можно обозначать режимы и соответственно наименование логической функ- функции, определяемой каждой колонкой таблицы (дизъюнкция, конъюнкция, импликация и т.д.). Предположим, что значение 1 соответствует цвету фона (background color) (например, чер- черному), а значение 0 — цвету переднего плана (скажем, белому). Можно наглядно представить результат, который получится в каждом из режимов (при каждом варианте определения вида функции У), в виде соответствующего изображения. Режимы 0 и 15 задают операции очист- очистки — независимо от значения s и прежнего значения d новое значение d будет равно либо О (в режиме 0), либо 1 (в режиме 15). Режимы 3 и 12 являются режимами обычной записи (режим 12 — записи с инвертированием). Режим 3 соответствует функции d<— s. В этом режиме значение s заменяет прежнее значение d. Режим 7 соответствует логиче- логической операции OR (ИЛИ): Если выполняется запись цвета переднего плана поверх цвета фона, то в режимах 3 и 7 получится одинаковый результат, но если записывается цвет фона поверх цвета переднего плана, то результат будет разным. Какой режим выбрать, зависит от того, какой эффект жела- желательно получить в данном приложении. S 0 0 1 1 6 0 1 0 1 0 0 0 0 0 0 0 I 0 0 1 0 0 0 1 1 0 1 0 0 0 1 0 1 0 1 1 0 d' 0 1 1 1 1 0 0 0 1 0 в 1 1 0 1 0 1 0 1 1 1 1 0 0 1 1 0 1 1 1 1 0 0 1 1 1 Рис. 9.21. Логические операции над двумя однобитовыми величинами 9.5.2. Режим записи XOR Режим 6 соответствует логической операции X0R (исключающее ИЛИ), которая обознача- обозначается значком ©. Этот режим позволяет получать очень интересный эффект при использова- использовании его в графических системах для операций с содержимым буфера кадра. В отличие от ре- режимов 3 и 12, режим 6 нельзя реализовать, если невозможно прочесть прежнее состояние би- бита d. Интересные особенности режима 6 связаны с тем, что для функции XOR переменных х и у справедливо соотношение (х@у)@у = х. Таким образом, если по отношению к некоторому биту дважды применить функцию X0R, то получим исходное значение бита, каким оно было до выполнения операций. На рис. 9.22 режимы 6 и 7 представлены в виде изображений, которые образуются в ре- результате вывода содержимого буфера кадра, образованного после выполнения операций за- записи в этих режимах. 9.5. Запись в буферы 393
i 1 t 1 Ш Очень часто этот режим используется в приложе- приложениях, в которых пользователь интенсивно взаимодей- взаимодействует с программой с помощью разных меню. (Такую программу мы рассматривали в главе 3.) В ответ на щелчок кнопкой мыши на экране должно появиться меню, которое частично перекроет ранее сформиро- сформированное изображение. После того как пользователь за- завершит работу с меню, его нужно убрать с экрана, а в области, перекрытой этим меню, восстановить исход- исходное изображение. Иногда такая операция реализуется с помощью дополнительной памяти, в которой хра- хранится растровый образ меню в виде битового блока М. Обозначим через S содержимое той части буфера кадра, которая соответствует участку основного изображения, перекрываемому образом меню при выводе последнего на экран. Рассмотрим последовательность операций: Режим З Режим 7 Рис. 9.22. Операции записи в режи- режимах б и 7 М, M<-S@M, S<r-S@M. Предполагается, что операция XOR применяется к соответствующим битам в блоках 5" и Л/. Если подставить первое соотношение во второе, а второе — в третье, то окажется, что после выполнения третьей операции на экране появится изображение меню. Исходное изображение при этом окажется во вспомогательной памяти, на том месте, где раньше было изображение меню. Таким образом, с помощью операций пересылки битового блока будет выполнен об- обмен изображениями между буфером кадра и вспомогательной памятью. Этот метод обмена содержимым двух буферов существенно отличается от привычного, который предполагает использование еще одного, промежуточного буфера. Существует множество вариантов применения такой технологии. Например, она исполь- используется при перемещении по экрану изображения курсора, вычерчивании резиновых линий или фигур. Последнюю функцию очень трудно реализовать в API, не рассчитанных на спе- специфику растровых дисплеев (см. упр. 9.1 и 9.2). 9.6. Операции с пикселями в OpenGL Оцифрованное изображение состоит из Л-разрядных пикселей, где к может быть любым числом от 1 (черно-белое изображение без градаций серого) до 32 и даже больше (изображение в формате RGBA с высоким разрешением по цвету). Хотя в принципе черно-белое (одноразрядное — bitmap) изображение отличается от многоградационного (скажем, одно- или двухбайтового) только количественно, оно обрабатывается и программными, и аппаратными средствами совершенно по-другому. Для аппаратной обработки черно-белого изображения тре- требуются только логические операции с одной битовой плоскостью буфера. Как правило, двугра- дационное представление используется для шрифтов, масок и образцов. OpenGL располагает средствами работы как с черно-белым, так и с многоградационным изображением. Операции с блоками битов имеют смысл при работе с черно-белым изображением, напри- например изображением символов шрифта или курсоров. Значительно менее понятно, как использо- использовать такие операции при работе с многоразрядными пикселями или многоградационным пред- представлением цвета, поскольку далеко не все логические операции из имеющихся \в типов дают визуально ощутимый эффект. OpenGL поддерживает операции с блоками битов только в режи- режиме индексирования цвета, для чего в составе API имеется функция glLogicOp(). OpenGL поддерживает работу не только отдельного конвейера обработки пикселей, но и множество отдельных буферов, предоставляя в распоряжение программиста операции пере- 394 Глава 9. Операции с изображением на уровне растрового представления
сылки данных между этими буферами и между буферами и памятью процессора. Вид инфор- информации, которую можно хранить в том или ином буфере — от индексов основных цветовых компонентов до данных о глубине, — зависит от типа буфера. Кроме того, программист мо- может обращаться и к основной памяти процессора, используя стандартные типы перемен- переменных — байты, целые числа или вещественные числа в формате с плавающей точкой. Такое разнообразие форматов имеет и обратную сторону — оно подчас затрудняет выбор самого эффективного способа организации данных при создании сложных приложений. В этой книге мы не будем обсуждать все нюансы, а ограничимся кратким обзором тех функциональных возможностей, которые предоставляются программисту при работе с OpenGL. 9.6.1. Буферы OpenGL Графическая система OpenGL поддерживает работу со следующими буферами: ¦ буферы цвета; ¦ буфер глубины; ¦ буфер-накопитель (аккумулятор); ¦ буфер маски. Группа буферов цвета включает буфер кадра, но, как мы уже говорили при обсуждении методики двойной буферизации в главе 3, таких буферов может быть несколько. При исполь- использовании двойной буферизации обычно говорят о рабочем {front) и фоновом (back) буферах. В приложении можно сформировать и стереопару, для чего используется буфер правого и ле- левого изображений стереопары, т.е. изображения, которое видимо правым или левым глазом. Как правило, в одном из буферов цвета программа создает образы геометрических объектов, но программист может и напрямую обращаться к некоторому пикселю в буфере, считывать его текущее значение или записывать в него определенный код цвета. Только информация из буферов цвета может появиться на экране. Буфер глубины обычно используется при реализации алгоритмов удаления невидимых по- поверхностей, но к нему также можно обращаться непосредственно и считывать/записывать данные. В разделе 9.8 будет показано, как при выполнении различных операций можно ис- использовать буфер-накопитель (аккумулятор). Буфер маски используется для формирования масок, "вырезающих" из общего массива те пиксели, которые следует вывести на экран. В дальнейшем, если не оговаривается иное, рассматривая операции с буферами, будем считать, что речь идет о буферах цвета, чаще всего — буфере кадра, хотя аналогичные опе- операции можно применять и в отношении других буферов. Обращаю ваше внимание на то, что конкретная версия системы, установленная на вашем компьютере, может содержать только часть из перечисленных выше буферов, причем разрядность (глубина) буферов также меняет- меняется от версии к версии. OpenGL работает с позицией растра (raster position), не принимая во внимание информа- информацию о размещении геометрических объектов на экране. Параметр позиция растра является одним из компонентов текущего состояния графической системы. Можно считать, что для графической системы позиция растра определяется положением курсора в координатах видо- видового окна, начиная с которой размещается специфицированный в очередных операциях рас- растровый примитив (массив пикселей). Позиция растра устанавливается в OpenGL с помощью одного из вариантов функции glRasterPos*(). Например, в качестве аргументов вызова этой функции можно использовать набор координат (х, у, z) в формате с плавающей точкой, при- причем в отношении точки, заданной этим набором, все текущие преобразования вида и проеци- проецирования выполняются прежде, чем она будет подвергнута растровому преобразованию: glRasterPos3f(х, у, 2); 9.6. Операции с пикселями в OpenGL 395
Эта позиция, как и все вершины, сохраняется в формате четырехмерного вектора од- однородных координат. Но после определения положения точки в координатах видового окна запись кодов засветки пикселей или иной двоичной информации, начиная с этой позиции, требует задания аргументов в единицах растра, т.е. в формате целых чисел. Считается, что центры пикселей размещаются на середине интервала между соседними целыми числами. 9.6.2. Использование растровых образов символов шрифта В этом разделе будет продемонстрирована технология работы с позицией растра на при- примере операций с текстовыми шрифтами. В главе 3 уже шла речь о том, что вывод на экран текста, представленного растровым шрифтом, осуществляется значительно быстрее, чем текста, представленного штриховым шрифтом. Определенный шрифт специфицирует набор символов единого начертания, или стиля, например Times, Courier, Computer Modern. Как правило, каждый шрифт включает несколько наборов символов разных размеров, например размером 10 пунктов или 24 пункта3, и разных начертаний — курсив, полужирный, с подчер- подчеркиванием. Единица измерения высоты шрифта— пункт— соответствует 1/72 дюйма. Но, когда мы будем говорить о растровом шрифте, речь будет идти только об одном наборе символов единого размера и начертания. Так, шрифт размером 8x13, ко- который поддерживается библиотекой GLUT, обозначается системной кон- константой GLUT BITMAP_8_BY_13, а пропорциональный шрифт4 высотой 10 пунктов— константой GLUT_BITMAP TIMES_ROMAN_10. Как правило, шрифт включает 128 или 256 символов. Каждый символ растрового шрифта задается определенным растровым трафаретом (рис. 9.23), пред- представленным в виде двоичного кода. Учтите, что при таком представлении растрового шрифта его размер на экране зависит от разрешения конкрет- конкретной системы отображения, что принципиально отличает растровые шрифты от штриховых. При разработке формы символов растрового шрифта приходится учи- учитывать множество факторов, определяющих "читабельность" символов, и в то же время "уложиться" в заданное поле пикселей. Как видно на при- примере символа, изображенного на рис. 9.23, при выборе формы символа даже самого малого размера на поле пикселей следует оставлять интервал между буквами как по горизонтали, так и по вертикали, предусмотреть место для выступаю- выступающих частей таких букв, как g и у (а в наборе символов кириллицы еще и буквы Й), обеспечить различную высоту строчных и прописных букв. Разработанный шрифт сохраняется в виде двоичного кода. Например, для шрифта из 128 символов размером 8x13 потребуется выде- выделить массив GLubyte my_font[128][13]j Перенос двоичного массива bitmap некоторого символа на экран в текущую позицию рас- растра выполняется обращением к функции glBitmap(): glBitmap(width, height, xo, yo, xi, yi, bitmap); Аргументы width и height задают соответственно ширину и высоту символа в пикселях. Аргументы хо и уо имеют формат числа с плавающей точкой и задают смещение положения Рис. 9.23. Символ растрового шрифта 3В русскоязычной терминологии эта характеристика шрифта называется кеглем — название достаюсь компьютерной технологии по наследству от ручного типографского набора. — Прим. перев. ''Символы пропорционального шрифта, в отличие от моноширинного, имеют разную ширину букв, на- например "I" и "W". 396 Глава 9. Операции с изображением на уровне растрового представления
выводимого образа (его левого нижнего угла) относительно текущей позиции растра, а аргу- аргументы xi и yi, которые также имеют формат чисел с плавающей точкой, — приращение те- текущей позиции растра после завершения операции (рис. 9.24). Рис. 9.24. Вывод на экран образов символов растрово- растрового шрифта В главе 3 уже рассматривалась методика использования дисплейных списков для вывода текста. Если массив образов символов my_f ont уже определен, то новый дисплейный список будет сформирован следующим фрагментом программы5: base=glGenListsA28) for(i=0, glNewList(base+i, GL_COMPILE); glBitMap(8, 13, 0.0, 2.0, 10.0, 0.0, my_font[i]); glEndList(); Обратите внимание на то, что каждый символ размещается на два пикселя вверх относи- относительно текущей позиции растра, а после завершения вывода символа позиция растра "сдвигается" вправо не на 8, а на 10 пикселей, что обеспечивает интервал между соседними символами и соседними строками в тексте. 9.6.3. Пиксели и изображения В OpenGL пиксели обрабатываются в виде групп 8-разрядных байтов, которые могут представлять разные форматы описания изображения. В RGB-формате интенсивность ка- каждого первичного цвета кодируется одним байтом, а в формате представления монохром- монохромного (одноцветного) изображения каждому пикселю соответствует число в формате с пла- плавающей точкой. В составе API OpenGL есть три функции перемещения участков описания изображения: 'Обращаю ваше внимание на то, что выделять память под первые 32 символа набора в кодировке ASCII не имеет смысла, поскольку эти символы играют роль управляющих и на экран не выводятся. 9.6. Операции с пикселями в OpenGL 397
glReadPixels(x, y, width, height, format, type, image); glDrawPixels(width, height, format, type, image); glCopyPixels(x, y, width, height,type); Функции glReadPixels() и glDrawPixels () позволяют считывать и записывать прямо- прямоугольные участки пикселей из массива или в массив, заданный аргументом image. В первом случае буфер OpenGL является буфером-приемником, а во втором — буфером-источником. Функция glCopyPixels() копирует участок пикселей из одного места определенного буфера цвета в другое. Аргументы х и у функции glReadPixels() задают смещение начала считы- считываемого участка пикселей относительно начала буфера кадра, а аргументы width и height — соответственно ширину и высоту участка. Хотя в большинстве приложений операции на уровне пикселей с помощью функций glReadPixels() и glDrawPixels () выполняются над содержимым буфера кадра (этот режим устанавливается по умолчанию), их можно выполнять с любым буфером. 9.6.4. Таблицы соответствия цветов Если в приложении используется режим индексированных цветов, то OpenGL позволяет изменить палитру цветов сформированного изображения с помощью внесения изменений в таблицу соответствия цветов. Предположим, что во всех буферах содержатся 8-разрядные коды цветов. Следовательно, каждому коду может быть сопоставлена строка в таблице соответствия, имеющей 256 строк (рис. 9.25). Таким образом, в буферах фактически содержатся не абсолютные ко- коды цветов, а индексы, отсылающие исполнитель- исполнительную систему OpenGL к таблице соответствия. За- Загрузка сформированной пользователем таблицы вы- выполняется функцией glPixelMap(). Использование таблицы соответствия позволяет очень легко преобразовать монохромное изображе- изображение в цветное. Единственное, что требуется сделать для такого преобразования, — настроить исполни- исполнительную систему OpenGL на работу с другой табли- таблицей соответствия. Создаваемое таким способом изображение иногда называют псевдоцвет- псевдоцветным (pseudocolor). В главе 11 будет показано, как на практике используется такая технология на примере вывода изображения множества Мандельброта (Mandelbrot). Если в приложении решено по тем или иным причинам использовать технологию ин- индексирования цвета, то перед разработчиком возникает проблема выбора палитры, которая включала бы 256 цветов из 224 доступных на современном типовом устройстве отображе- отображения. Одна из методик рекомендует использовать так называемую термальную шкалу, в ко- которой более "теплые" цвета (красный, белый и желтый) передают большие значения, а "холодные" цвета (синий и зеленый) — меньшие. Предположим, что прикладная програм- программа сформировала монохромное изображение, в котором между важностью участка и зна- значением его интенсивности установлена монотонная связь (не обязательно даже пропор- пропорциональная). На рис. 9.26 показан возможный вид функций отображения интенсивности на значения основных цветовых составляющих (красной, зеленой и синей), который пример- примерно соответствует термальной шкале. На ил. 9 цветной вклейки показано, как используется термальная шкала при визуализации результатов научных расчетов. Цвет на этой картинке позволяет зрителю оценить распределение температуры в мантии Земли, смоделированное компьютерной программой. Индекс 0 1 255 Красный Зеленый Синий 8 бит 8 бит 8 бит Рис. 9.25. Таблица соответствия цве- цветов 398 Глава 9. Операции с изображением на уровне растрового представления
Интенсивность серого Интенсивность серого Интенсивность серого Рис 9.26. Функции отображения интенсивности на значения основных цветовых составляющих, воспроизводящие термальную шкалу Другая область применения технологии индексирования цветов — преобразование цве- цветов и настройка нелинейной шкалы яркости. Например, при использовании большинства упрощенных алгоритмов закрашивания (а такие алгоритмы получили наибольшее распро- распространение в графических системах на базе ПК, поскольку применить более сложные не по- позволяют аппаратные средства) изображение получается темным и неконтрастным. График распределения яркости (гистограмма яркости) в типовом изображении, полученном та- таким способом, выглядит примерно так, как на рис. 9.27. Эта кривая свидетельствует о том, что большая часть изображения имеет слабую яркость. Можно частично исправить качест- качество изображения, применив таблицу соответствия, воспроизводящую нелинейную функцию передачи, приведенную на рис. 9.28. Приращение интенсивности между элементами таб- таблицы, соответствующими малым значениям индексов (входного параметра), будет тогда больше, чем между элементами, которые соответствуют большим значениям. В результате удается выровнять гистограмму яркости изображения на экране и несколько увеличить его контрастность. 255 Интенсивность 255 Рис 9.27. Гистограммы распределения ярко- яркости типового изобра- изображения до коррекции Si 4> О U 0 255 Прежняя интенсивность Рис 9.28. Нелинейная характери- характеристика, воспроизводимая кор- корректирующей таблицей со- соответствия Таблицу соответствия можно использовать и для преобразования цветов. В главе 7 уже отмечалось, что используемые в большинстве API, в том числе и в OpenGL, значения компо- компонентов RGB-формата не "привязаны" к какой-либо конкретной системе воспроизведения изображения. Если сфотографировать изображение с экрана ЭЛТ, то даже невооруженным глазом можно обнаружить несовпадение цветовой палитры фотографии и исходного изобра- изображения. Это объясняется различием спектральных кривых чувствительности фотографических эмульсий фотопленки, фотобумаги, люминофоров ЭЛТ и глаза наблюдателя. С помощью таблицы соответствия можно добиться коррекции цветовой гаммы в определенном диапазоне значений, подстраивая значения по каждому из основных цветовых компонентов (R, G и В). Этот процесс получил название регулировки цветового баланса (color balancing) и выполня- выполняется перед фотографированием изображения с экрана монитора. Вообще говоря, повозив- повозившись с содержимым таблицы соответствия, можно добиться практически одинаковой цвето- цветопередачи изображения, сформированного разными системами отображения. 9.6. Операции с пикселями в OpenGL 399
9.6.5. Использование буферов в процедуре указания объекта Поработав некоторое время с буферами и набравшись опыта, вы убедитесь, что с их по- помощью можно выполнять практически любые процедуры формирования и модификации изо- изображения. Но операции с буферами могут использоваться и при выполнении интерактивных процедур. Рассмотрим самую распространенную из них — указание графического объекта на экране. При выполнении этой процедуры система должна вернуть прикладной программе идентификатор того графического объекта, на котором находится курсор (маркер положения мыши). В составе API OpenGL имеется механизм поддержки обратной связи между растро- растровым изображением и описанием геометрических объектов сцены с помощью специальных списков (hit lists). Выбранный способ поддержания обратной связи объясняется особенностя- особенностями работы геометрического конвейера при формировании изображения. При появлении за- запроса на выполнение процедуры указания организуется повторный цикл формирования изо- изображения, но если в изображении не слишком много геометрических объектов, то эту задачу можно решить и иным способом, воспользовавшись буферами. Предположим, что в изображение входит не более 256 объектов (примитивов), т.е. за ка- каждым объектом можно "закрепить" уникальный 8-разрядный идентификатор. В программе организуется дополнительный 8-разрядный двоичный pick-буфер, размер которого соответ- соответствует разрешающей способности изображения, т.е. за каждым пикселем изображения будет "закреплена" 8-разрядная ячейка. При формировании экранного образа очередного примити- примитива синхронно с заполнением буфера кадра в одноименные ячейки pick-буфера записывается код идентификатора примитива (объекта). Располагая таким буфером, не представляет труда, пользуясь описанными выше функциями OpenGL, считать по заданным координатам курсора мыши идентификатор объекта, образ которого включает эту точку экрана. Если объединить этот процесс с алгоритмом z-буфера, то из всех объектов, проецирующихся в указанную точ- точку, можно выбрать тот, который ближе всех расположен к камере. 9.7. Технология комбинирования изображений В главе 6 были рассмотрены алгоритмы формирования изображений прозрачных объек- объектов. Описанные в той главе методы могут применяться совместно с технологией трассировки лучей, которая сопряжена с достаточно большим объемом вычислений. OpenGL предоставля- предоставляет в распоряжение прикладного программиста иной механизм формирования прозрачных объектов, позволяющий обойтись без трассировки лучей в явном виде. Этот механизм полу- получил название альфа-смешивание {alpha-blending, или OL-blending). Интенсивность альфа- канала входит в качестве четвертой компоненты в вектор цвета в формате RGBA (или RGBa). Прикладная программа может управлять интенсивностью альфа-канала точно так же, как и интенсивностью каждого из основных цветов, т.е. задавать значение интенсивности А для каждого пикселя. Но исполняющая система OpenGL трактует это значение по-иному. Для нее заданное значение интенсивности компоненты А определяет коэффициент пересчета зна- значений основных цветов при записи их в буфер кадра. В результате оказывается, что в код за- засветки пикселя вносит свой вклад несколько геометрических объектов и формируется сме- смешанное, или комбинированное, изображение. 9.7.1. Поглощение света и смешивание изображений Коэффициент поглощения а определенной среды есть количественная мера, позволяю- позволяющая оценить, какая доля светового потока, падающего на поверхность раздела сред, проходит дальше, а какая поглощается. Если коэффициент поглощения равен 1 (а=1), то свет полно- полностью поглощается на поверхности раздела. Поверхность, для которой коэффициент а равен О, 400 Глава 9. Операции с изображением на уровне растрового представления
является идеально прозрачной — весь падающий на нее световой поток проходит дальше. Коэффициент прозрачности т {transparency) поверхности (фактически среды) связан с ко- коэффициентом поглощения простой зависимостью т=1-сс. Рассмотрим три равномерно окра- окрашенных многоугольника, показанных на рис. 9.29. Предположим, что средний многоугольник непрозрачен, а ближайший к наблюдателю — прозрачен. Если он будет сделан из идеально прозрачного материала, то наблюдатель вообще его не увидит — он увидит только располо- расположенные за ним непрозрачные предметы. Однако если ближайший многоугольник только час- частично прозрачен (часто говорят— полупрозрачен), как солнцезащитные очки, то наблюда- наблюдатель увидит в этом месте некоторый совмещенный цвет, в котором присутствует и цвет полу- полупрозрачного объекта, и цвет расположенного за ним непрозрачного. Поскольку средний многоугольник непрозрачен, то третьего многоугольника наблюдатель за ним не увидит. Если ближайший многоугольник красный, а расположенный за ним — синий, то комбинированный цвет будет иметь фиолетовый оттенок вследствие наложения {смешивания) цветов. Если же и средний многоугольник полупрозрачен, то наблюдатель увидит еще более сложное сочетание цветов всех трех объектов. В системах компьютерной графики образы многоугольников формируются последова- последовательно, причем порядок их размещения по от- отношению к наблюдателю в типовой системе не влияет на порядок формирования экранных образов (об этом шла речь в главе 7). Следо- Следовательно, если желательно получить такое смешанное изображение, нужно изобрести способ, позволяющий учитывать коэффици- коэффициент прозрачности объектов на стадии растро- растрового преобразования. Мы будем в дальней- дальнейшем использовать понятия пикселя-источника {source pixel) и пикселя-приемника {destination pixel) в том же смысле, что и понятия буфера- источника и буфера-приемника, которые были введены в разделе 9.1. При обработке очеред- очередного многоугольника вычисляется фрагмент изображения размером с пиксель, и если этот фрагмент видим, то пикселю присваивается цвет, сформированный в соответствии с приня- принятой моделью закрашивания. До сих пор мы использовали вычисленный алгоритмом тониро- тонирования цвет фрагмента, на который при необходимости еще и была наложена определенная текстура, для замены кода засветки соответствующего пикселя в буфере кадра. Если рассмат- рассматривать вычисленный код засветки в качестве интенсивности пикселя-источника, а текущий код засветки в этой ячейке буфера кадра как интенсивность пикселя-приемника, то эти две интенсивности можно скомбинировать самыми разными способами. Использование а-канала является одним из способов выполнить наложение (смешивание) цветов на уровне отдельных пикселей. Такое наложение моделирует замену двух полупрозрачных цветных стекол одним, которое будет иметь цвет и коэффициент поглощения, отличные от соответствующих харак- характеристик каждого из исходных компонентов. Если представить пиксель-источник и пиксель-приемник в виде массивов из четырех эле- элементов (RGBa): s = [sr sg sh sa], d = [drdgdhda], то результатом смешивания цветов будет новый код засветки: d1 = [b Рис. 9.29. Наложение полупрозрачных и не- непрозрачных объектов 9.7. Технология комбинирования изображений 401
Массивы констант b и с называются соответственно коэффициентами смешивания {blending factors) источника и приемника. Как и базовые цветовые составляющие RGB, зна- значение параметра а не может превышать 1.0 и быть меньше 0.0. Подбирая значения компо- компонента а и коэффициентов смешивания, можно получить массу интересных эффектов при смешивании изображений. 9.7.2. Смешивание изображений Самый очевидный способ использовать имеющийся в графической системе механизм смешивания цветов с помощью альфа-канала— совмещать на уровне отдельных пикселей изображения объектов, образы которых формируются независимо друг от друга. В таком случае можно рассматривать каждый из них как компонент сложного изображения, который вносит посильный вклад в результирующее изображение. Поскольку обычно желательно удерживать в окончательном изображении значения основных цветовых составляющих в ин- интервале 0.0-1.0 (т.е. не заходить в зону насыщения), то необходимо соответствующим обра- образом подобрать или значения а, или коэффициенты смешивания источника и приемника. Предположим, что нужно сформировать смешанное изображение п объектов, причем ка- каждый должен внести равный вклад. Для данного пикселя взнос /-го объекта будет равен С,(Х/. Здесь через С, обозначена тройка основных цветовых составляющих (Rlt Gh В,). Подставим вместо С/ значение С,/л, а вместо а,— значение 1/я. Теперь можно будет просто добавлять растровый образ каждого очередного объекта в буфер кадра (перед началом операции во всех ячейках буфера кадра необходимо установить код черного цвета и значение а=0). Другой ва- вариант— настроить значение коэффициента смешивания источника равным Мп, а коэффици- коэффициента смешивания приемника — равным 1. Хотя математически оба варианта должны привес- привести к одному результату, второй вариант аппаратно реализуется более эффективно. 9.7.3. Смешивание изображений в OpenGL Настройка режима смешивания изображений в OpenGL выполняется с помощью функции glEnable(), которой в качестве аргумента передается константа GL_BLEND: glEnable(GL_BLEND)j Затем нужно выполнить настройку значений коэффициентов смешивания источника и приемника: glBlendFunc(source_factor, destination factor); В OpenGL существует множество предопределенных значений коэффициентов смешивания, представленных символическими константами, включая значения 1 (константа GLJ3NE) и 0 (константа GL_ZERO). Можно задать использование в качестве коэффициентов смешивания ис- источника значения а или 1-а (константы GL SRC ALPHA и GL_ONE MINUS_SRC ALPHA). Аналогич- Аналогичную настройку приемника выполняют константы GL DST_ALPHA и GL ONE MINUS_DST ALPHA. В прикладной программе сначала задаются необходимые настройки, после чего можно исполь- использовать формат представления цвета RGB А. Основная сложность в управлении процессом формирования смешанного изображения состоит в том, что на результат влияет порядок формирования образов многоугольников, ко- который очень сложно контролировать из прикладной программы. Например, в некоторых приложениях используется режим, когда коэффициент смешивания источника приравнивает- приравнивается к значению а, а коэффициент смешивания приемника— к значению A—ос). В результате после выполнения смешивания очередного изображения получим: (Rd., Gj; В,, ov) = (аЛ+О-а,)/?* aJJ+(\-a,)GA 402 Глава 9. Операции с изображением на уровне растрового представления
Эта формула показывает, что результат будет корректен как для прозрачных, так и для непрозрачных объектов, но он все-таки существенно зависит от того, в каком порядке будут обрабатываться объекты при растровом преобразовании. Следовательно, в отличие от других случаев, когда прикладной программе не было никакого дела до порядка обработки объектов при растровом преобразовании, в данном случае желаемый эффект может быть получен только при условии соблюдения определенного порядка. При использовании смешивания изображений возникает проблема и с механизмом удале- удаления невидимых поверхностей. Обычно в такой ситуации этот механизм нужно отключать, по- поскольку он не делает различия между непрозрачными и прозрачными объектами, а следова- следовательно, любой объект, который расположен за другим объектом, пусть он даже имеет коэф- коэффициент поглощения 0.01, просто не будет подвергаться растровому преобразованию и никак не "проявится" в результирующем изображении. Нам же нужно, чтобы те объекты, которые расположены за полупрозрачными объектами, включались в изображение. Этого можно дос- достичь достаточно простым приемом, управляя маской обновления z-буфера. Дело в том, что для определенных объектов, в данном случае — прозрачных, можно при работе с z-буфером выборочно настраивать режим "только для чтения". Для этого следует вызывать функцию glDepthMask(GL_FALSE); Когда буфер глубины работает в режиме "только для чтения", прозрачный объект, распо- расположенный за непрозрачным, образ которого уже сформирован, растровому преобразованию подвергаться не будет. Если же прозрачный объект расположен перед непрозрачным, то он будет подвергаться растровому преобразованию, но в результате сформируется смешанный цвет. При этом данные о глубине размещения прозрачного объекта в буфер глубины занесены не будут. При обработке непрозрачных объектов восстанавливается нормальный режим ра- работы z-буфера, и процесс идет как обычно. Программа, демонстрирующая методику смеши- смешивания цветов, находится в файле blend.с. 9.7.4. Сглаживание погрешностей дискретизации Очень часто альфа-канал применяется для устранения погрешностей изображения, свя- связанных с дискретностью растра {antialiasing). В главе 7 было отмечено, что в идеале образ прямой на растре должен везде иметь одну и ту же толщину, причем она должна быть равна шагу между точками растра. Такой идеальный образ частично перекрывает некоторое коли- количество пикселей, как видно на рис. 9.30. Предположим, что при обработке фрагмента прямой (напомню, фрагментом в данном случае считается участок образа прямой, не превышающий размеров пикселя), которая выполняется на этапе обработки геометрических данных в ходе растрового преобразования, мы установим значение а для соответствующих пикселей про- пропорционально (или почти пропорционально) той части площади пикселя, которая перекрыва- перекрывается образом прямой. В самом простом случае значение (X может быть приравнено к относи- относительной площади пикселя, перекрытой образом. Затем это значение можно использовать в качестве коэффициента полноты значений цветовых составляющих, вычисленных алгорит- алгоритмом тонирования. При этом следует настроить такой режим смешивания, который преду- предусматривает применение в качестве коэффициента смешивания приемника значения 1-чх, а в качестве коэффициента смешивания источника — значение а. Однако если пиксель частично перекрывается не одним, а двумя или несколькими фрагментами, то появляется несколько ва- вариантов их наложения (рис. 9.31). В правой части рисунка показан случай, когда фрагменты не накладываются друг на друга, а в левой части — когда накладываются. Рассмотрим эту проблему с точки зрения программирования преобразования, которое по очереди независимо обрабатывает все объекты (многоугольники). Предположим, что перед началом процесса в пикселе-приемнике установлен непрозрачный фон цвета Со. Можно установить значение 9.7. Технология комбинирования изображений 403
ао=0, поскольку пиксель еще не перекрыт фрагментом какого-либо многоугольника. Затем обрабатывается первый многоугольник. В результате в пикселе-приемнике будет установлен код цвета Cj и значение ad: Q = (I-a1)C0+a,C1, со= a,. Рис. 9.30. Растровый образ прямой а) б) Рис. 9.31. Перекрытие одного пикселя двумя фрагментами Из приведенных соотношений следует, что если фрагмент первого многоугольника пол- полностью перекрывает пиксель (в этом случае (Xi=l), то пикселю-приемнику присваивается цвет этого многоугольника, причем пиксель-приемник после завершения операции становится аб- абсолютно непрозрачным. Если же до выполнения операции пиксель-приемник имел черный цвет (С0=0), то после завершения он будет окрашен в цвет (X|Ci. Теперь рассмотрим, что произойдет после обработки фрагмента второго многоугольника, "претендующего" на тот же пиксель. Как будут учтены в окончательной засветке пикселя- приемника цвет второго многоугольника и значение его a-компоненты, зависит от того, какой способ интерпретации перекрытия мы выберем. Если перекрытие отсутствует (как на рис. 9.31, слева), можно использовать обычную процедуру смешивания полупрозрачных пикселей: Q = (l-a2)((l-a,)C0+a1Cl)+a2C2, olj = a,+a2. Значения компонентов смешанного цвета Cj не выходят за пределы заданного диапазона, следовательно, к ним не нужно применять отсечение. Значение CLj после выполнения опера- операции будет соответствовать размеру части пикселя, закрытой фрагментами обоих многоуголь- многоугольников. Из приведенного выше соотношения для Cj следует, что результирующий цвет будет зависеть от порядка обработки многоугольников. Значительно сложнее решается вопрос определения смешанного цвета, если фрагменты перекрываются, как в правой части рис. 9.31. Не так просто определить и сам факт перекры- перекрытия. Это можно сделать, например, исходя из вероятности наступления такого события, рас- рассматривая ее как функцию значений at и а2. Эти значения пропорциональны доле площади пикселя, закрытой соответствующим фрагментом, а другой информацией о взаимном поло- положении фрагментов мы, как правило, не располагаем (если не усложнять алгоритмы растрово- растрового преобразования и не разбивать пиксель еще на несколько более мелких элементов). В среднем область перекрытия двух фрагментов можно считать равной произведению ot|(X2. На рис. 9.32 представлена графическая интерпретация такого усредненного случая. Новое значе- значение а</ пикселя-приемника при такой интерпретации можно получить следующим образом: 404 Глава 9. Операции с изображением на уровне растрового представления
Сложнее решить, какой цвет должен иметь пиксель-прием- пиксель-приемник, поскольку для этого нужно знать, какой из фрагментов на- находится ближе к наблюдателю. Такая информация может стать доступной, если объединить процесс смешивания с процессом удаления невидимых поверхностей и таким способом получить доступ к буферу глубины. Настройка графической системы OpenGL на режим сглажи- сглаживания выполняется таким образом, что прикладной программе не приходится в явном виде обрабатывать значения компонен- компонентов а. В программе требуется только заказать режимы смешива- смешивания и сглаживания прямых и точек. Это можно сделать, напри- например, в такой последовательности операторов: glEnable(GL_POINT_SMOOTH); glEnable(GL_LINE SMOOTH); glEnable(GL_SMOOTHING); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); Но учтите, что выполнение сглаживания с использованием большими затратами времени. Рис. 9.32. Усредненный вари- вариант перекрытия двух фрагментов в пределах одного пикселя альфа-канала сопряжено с 9.7.5. Тонирование сцен со множеством полупрозрачных объектов Хотя использование альфа-канала и позволяет создать в изображении иллюзию прозрач- прозрачности объектов, с помощью этой методики нельзя корректно передать характер реальных оп- оптических эффектов, которые возникают при прохождении света через полупрозрачные объ- объекты. На рис. 9.33 показаны некоторые из таких эффектов в реальной сцене. В частности, ме- методика работы с альфа-каналом совершенно игнорирует эффект преломления световых лучей при прохождении через границу раздела полупрозрачных сред. Предположим, что объект, расположенный в глубине сцены, абсолютно непрозрачен, но обладает свойствами частично- частичного зеркального отражения, а два объекта, расположенных ближе к наблюдателю, полупро- полупрозрачны (рис. 9.33). Рассмотрим, какие пертурбации претерпевают в этой сцене лучи света, испускаемые источником. Часть из них освещает объект, расположенный в глубине, и соот- соответствующие пиксели изображения должны быть окрашены в цвет, который образуется в ре- результате взаимодействия цвета источника и материала поверхности этого объекта. Причем есть еще один нюанс — часть лучей попадает на поверхность этого объекта прямо от источника света, а часть — отразившись от поверхности по- полупрозрачных объектов, расположенных ближе к наблюдателю. Эти последние лучи будут окраше- окрашены соответственно цветом тех объектов, от кото- которых они отразились по пути. Еще более сложный вариант представляют лучи, которые по очереди проходят через оба полупрозрачных объекта или, отразившись от первого, попадают на объект в глубине сцены, отражаются от него, проходят че- через второй полупрозрачный объект и только потом попадают к наблюдателю. Графической системе, имеющей конвейерную архитектуру, не под силу решить задачу корректного моделирования оптических эффектов в такой сцене, а потому для создания фо- фотореалистических изображений сцен со сложными зеркальными и прозрачными объектами используются значительно более сложные алгоритмы трассировки лучей. Такие алгоритмы не удается реализовать на существующей аппаратуре в реальном масштабе времени. Рис. 9.33. Сцена с несколькими полупро- полупрозрачными объектами 9.7. Технология комбинирования изображений 405
9.7.6. Эффект тумана и создание иллюзии глубины пространства Иллюзию глубины пространства можно передать в изображении не только чисто геомет- геометрическим преобразованием (перспективным искажением размеров), но и "квазиоптическими" средствами. Живописцы давно подметили, что контуры удаленных предметов кажутся нечет- нечеткими, размытыми, а цвета не таким яркими, как предметов, расположенных близко к наблю- наблюдателю. В компьютерной графике эту иллюзию можно создать, сформировав полупрозрач- полупрозрачную среду между наблюдателем и объектами сцены, и получить таким образом эффект тума- тумана. Технически это выполняется смешива- смешиванием при формировании растрового пред- представления объекта его цвета с цветом, плот- плотность которого зависит от расстояния меж- между наблюдателем и объектом. Введем в рассмотрение коэффициент туманности (fogfactor) j"и обозначим через z расстояние между объектом и наблюдате- наблюдателем. Если фрагмент объекта имеет цвет С„ а полупрозрачная среда (туман) — цвет С/, то для окраски пикселя можно использо- : вать цвет Расстояние Рис. 9.34. Моделирование оптической прозрачно- Если коэффициент/изменяется линей- сти среды но на некотором интервале, то с помощью такого нехитрого механизма можно создать оптический эффект глубины пространства сцены. Используя нелинейную функцию_Дг) (рис. 9.34), можно с помощью того же самого механизма создать эффект тумана. В OpenGL поддерживается линейная, экспоненциальная и гауссова формы кривой оптической прозрачности среды. Например, приведенный ниже фрагмент программы настраивает в графической системе функцию оптической прозрачно- прозрачности среды в виде / = е~°'5г . GLfloat fcolor[4]={...}; glEnable(GL_FOG); glFogf(GL_FOG_MODE, GL_EXP); glFogf(GL_FOG_DENSITY, 0.5); glFogfv(GL_FOG_COLOR, fcolor); Обращаю ваше внимание на то, что при формировании значения J(z) используется коор- координата z в системе координат наблюдателя (камеры). 9.8. Использование буфера-накопителя В некоторых графических системах, в том числе и в OpenGL, есть буфер-накопитель {accumulation buffer), с помощью которого можно на двоичном уровне реализовать дополни- дополнительную пространственную фильтрацию изображения. Этот буфер имеет такой же размер, как и буфер кадра, но большую разрядность, что позволяет накапливать в нем последователь- последовательные кадры изображения. В OpenGL для работы с буфером-накопителем требуется предварительно очистить его (эта процедура выполняется функцией glClear(), как и очистка других буферов), а затем вы- вызвать функцию glAccum(), которая позволяет либо добавить к содержимому буфера- накопителя данные из буфера кадра, либо поэлементно их перемножить, либо переписать со- 406 Глава 9. Операции с изображением на уровне растрового представления
держимое накопителя в буфер кадра. Ниже приведен фрагмент программы, в котором соз- созданная пользователем функция display image () используется для формирования последова- последовательности изображений в буфере кадра. glClear(GL_ACCUM_BUFFER_BIT) for(i=0; i< num_images; i { glClear(GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT); display_image(i); glAccum(GL_ACCUM, 1.0/(float)num_images); } glAccum(GL_RETURN, 1.0); Каждое очередное изображение накапливается в буфере-накопителе, причем при накоп- накоплении используется масштабный множитель, обратно пропорциональный количеству изо- изображений в последовательности. После завершения цикла формирования всей серии усред- усредненное в результате накопления изображение возвращается в буфер кадра. В принципе, ту же задачу можно было решить и с помощью смешивания цветов, но при смешивании пришлось бы снизить точность представления цвета во избежание выхода за пределы интервала пред- представления. Использование буфера-накопителя, имеющего большую разрядность, позволяет выполнить эту процедуру, не снижая точности. Одно из наиболее интересных применений буфера-накопителя связано со сглаживанием погрешностей дискретизации изображения. Вместо того чтобы сглаживать растровые образы отдельных примитивов, можно за счет накопления реализовать сглаживание изображения сцены целиком. Идея состоит в следующем. Если сформировать два изображения одной и той же сцены, сдвинув в одном из них все объекты (или наблюдателя) на расстояние, меньшее, чем размер пикселя, то погрешности дискретизации в этих изображениях проявятся по- разному. Усреднив их с помощью накопления, получим сглаженное изображение. Естествен- Естественно, что программно проще всего изменить положение наблюдателя, для чего требуется толь- только изменить один аргумент вызова функций glPersective() или glOrtho(). Но перед этим нужно вычислить, на сколько нужно сдвинуть камеру, чтобы приведенное к координатам эк- экрана значение сдвига не превышало, скажем, половины размера пикселя. В терминах теории квантования (о ней подробно рассказывается в разделе 9.9) нам потребуется уменьшить шаг квантования по независимым переменным (в данном случае— пространству), а затем ис- использовать усреднение для восстановления непрерывного сигнала на основе квантованного. Можно объединить в программе накопление в буфере-накопителе с наложением пикселей и таким способом реализовать различные виды цифровой фильтрации изображения. Предпо- Предположим, что мы имеем дискретизированное изображение. Оно может быть как синтезирован- синтезированным программно с помощью одного из алгоритмов тонирования, так и полученным в резуль- результате сканирования фотографии (непрерывного изображения). Дискретизированное изображе- изображение можно представить в виде матрицы отсчетов размера NxM, элементами которой являются скалярные величины: Если каждую из основных цветовых составляющих обрабатывать независимо от других, то можно интерпретировать элементы матрицы А либо как интенсивности отдельных состав- составляющих, либо как интенсивность серого при воспроизведении монохромного изображения. В результате линейной фильтрации формируется новая матрица В, элементы которой вычисля- вычисляются по формуле bu= Z EA«Wr 9.8. Использование буфера-накопителя 407
Мы говорим, что В есть результат свертки (convolving) изображения А матрицей свертки (фильтром) Н. Как правило, на практике используются малые значения т и п, и матрицу Н можно без особого труда представить поэлементно. Схематически процедура фильтрации по- показана на рис. 9.35. На каждый пиксель а„ в изо- изображении А "накладывается" матрица свертки, и формируется взвешенная сума пикселей, окру- окружающих а,. Значения элементов матрицы сверт- свертки — суть веса, с которыми в этой сумме учиты- учитываются соответствующие соседние пиксели. При п=т=\ усредняется обрабатываемый пик- пиксель и восемь его ближайших "соседей", а матри- матрица фильтра имеет размеры 3x3. Варьируя значе- значения элементов матрицы фильтра, можно из этих восьми элементов отбирать те, которые считаете нужными. Например, если желательно учитывать в сумме только четыре из восьми соседних элементов, используйте матрицу фильтра вида Рис. 9.35. Фильтрация и свертка н 1 ~5  1 0 1 1 1 0 1 0 Такой фильтр помогает сгладить контуры изображения. Можно использовать и более слож- сложный фильтр, в котором центральные элементы включаются в сумму с более высокими веса- весами, чем угловые: Н= — 16 Обратите внимание на то, что вокруг исходного изображения А нужно сформировать допол- дополнительную "каемку" шириной т пикселей по бокам и п пикселей сверху и снизу. Только в этом случае результирующее изображение В будет иметь такой же размер, как и А. Пользуясь матрицами фильтра небольшой размерности, можно получать разные интересные эффекты. Например, приведенная ниже матрица фильтра позволяет подчеркнуть границы областей, за- залитых разным цветом: 1 2 1 2 4 2 1 2 1 н = 0-10 -1 4 -1 0-10 Можно использовать и матрицы фильтров большой размерности, kxk, и накапливать к' изо- изображений в буфере-накопителе, причем каждый раз добавлять слегка измененную версию А, варьируя коэффициент в функции glAccum(). Буфер-накопитель позволяет организовать фильтрацию не только в плоскости изображе- изображения, но и во временной области или по глубине. Если, например, в накапливаемых в буфере изображениях один из объектов слегка сдвигать (на величину, меньшую размера пикселя), а остальные оставить нетронутыми, то в суммарном изображении этот объект будет выглядеть слегка размытым. Если объект сдвигать не случайным образом, а перемещать по определен- определенной траектории, то в результирующем изображении увидим "тянущийся" образ объекта, хо- хорошо передающий динамику движения. Этот эффект очень напоминает фотографии быстро движущихся объектов, сделанные при большом времени экспозиции. Варьируя значение ве- 408 Глава 9. Операции с изображением на уровне растрового представления
сового коэффициента при вызове функции glAccum() для добавления в буфер, можно доби- добиваться самых неожиданных эффектов, выделяя изображения одних объектов и "забивая" изо- изображения других. Фильтрация по глубине позволяет сформировать эффект ограниченной глубины резкости. При работе с фотокамерой далеко не все объекты оказываются в фокусе. В зависимости от величины относительного отверстия объектива изменяется и глубина резко воспроизводимо- воспроизводимого пространства — глубина резкости. В сис- системе компьютерной графики этот эффект можно сымитировать с помощью фильтра- фильтрации по глубине. Для этого следует несколь- несколько сдвинуть камеру в поперечном направ- направлении и одновременно изменить ее ориен- Xmin' Ут|П' тацию таким образом, чтобы определенная плоскость в пространстве сцены (та, кото- которую желательно изобразить "в фокусе") ос- осталась на своем месте, а остальные после повторного проецирования оказались сдви- Рис. 9.36. Имитация ограниченной глубины резкости нутыми (рис. 9.36). Предположим, что же- желательно сфокусировать камеру на плоскость г=гу, причем ближняя (z=zmin) и дальняя (г=гтах) плоскости отсечения должны остаться неизменными. В вызове функции glFrustum() можно задать положение отсекающих прямоугольников (дгт|п, д:тах, >>min, ymax) в пирамиде видимости. Если после этого сдвинуть положение камеры на Ас, то значение jcmin нужно будет изменить: Аналогичным образом перед формированием второго изображения нужно скорректировать и значения дгтах, ;min и _>>тах. Оба изображения накладываются друг на друга в буфере- накопителе, и в результате получим изображение, в котором одни объекты будут выгля- выглядеть резкими, а другие — размытыми. Чем большее значение Ах и Ду будет использовано для сдвига камеры перед формированием второго изображения, тем меньшую глубину рез- резкости получим в результате. 9.9. Дискретизация изображения Мы уже неоднократно встречались на страницах этой книги с приложениями, в которых преобразование непрерывного представления изображения в дискретное приводило к появ- появлению определенных искажений. При работе с буферами нам всегда приходится иметь дело с дикретизированным изображением, и если не принять определенных мер, то эти искажения могут значительно ухудшить качество картинки. В этом разделе мы проанализируем характе- характеристики дискретного изображения и выясним, какие факторы оказывают наиболее сущест- существенное влияние на его качество и как можно улучшить качество. Начнем с анализа непрерывной двухмерной функции Ддг,^), описывающей некоторое изо- изображение. Значение/этой функции — суть интенсивность почернения (плотность) точки мо- монохромного изображения с координатами (х,у) или значение интенсивности любого из базо- базовых цветов в цветном изображении. В компьютере мы можем обрабатывать только дискрети- зированное изображение, которое представляется в форме массива пикселей с размерами пхт (п строк по т пикселей в каждой). Каждый пиксель может иметь интенсивность, представ- представленную в виде ^-разрядного двоичного числа. При переходе от непрерывного представления к дискретному сначала нужно выполнить квантование по пространству— сформировать 9.9. Дискретизация изображения 409
выборки (sample) непрерывной функции в пт точках на некоторой регулярной сетке с шагом (hx, hy) и получить множество значений/,. Каждая выборка представляет собой значение ин- интенсивности/, измеренной в бесконечно малой окрестности точки (xo+ihx,yo-rjhy) в области представления функции J{x,y). Затем значение каждой выборки/, нужно представить в виде ^-разрядного двоичного числа — квантовать по уровню. 9.9.1. Теория квантования по независимым переменным Рассмотрим прямоугольную сетку на плоскости {х,у}, которая служит для выполнения квантования по пространству функции J[x, у) (рис. 9.37). Предполагается, что шаг сетки по- постоянный во всей области существования функции, а значения выборок формируются иде- идеальным импульсным элементом: Здесь hx и hY— шаг сетки квантования в на- направлении осей х и у. Оставим в стороне вопрос о технической реализации идеального импульсного элемента применительно к изображению (реаль- (реальный элемент имеет апертуру конечного размера, т.е. обязательно усредняет по какой-то, пусть и ма- малой, области), а сосредоточим внимание на двух вопросах. Во-первых, нас интересует, какие ошиб- ошибки могут возникнуть в процессе считывания выбо- выборок, т.е. какая информация, имеющаяся в исходном изображении, может быть утеряна в процессе квантования по пространству. Во-вторых, попро- попробуем проанализировать, насколько точно по кван- квантованному изображению можно восстановить ис- исходное непрерывное изображение. м Рис. 9.37. Квантование изображения по пространству 6) Рис. 9.38. Разложение Фурье функции одной перемен- переменной: а — исход- исходная функция; б — ее состав- составляющие Восстановление изображения осуществляется на экране устройства отображения (чаще всего — ЭЛТ) в процессе переноса на него содержимого буфера кадра. Для математического анализа процесса квантования используется аппарат преобразования Фурье. Суть его состоит в том, что любая функция (не имеет значения, задана она в пространственной или во временной области) может быть представлена в виде суммы ряда, воз- возможно, бесконечного, элементами которого являются синусоиды с раз- разной частотой. Эта концепция легко интерпретируется в отношении зву- звуковых сигналов в терминах спектра такого сигнала. Когда речь идет о двухмерной функции изображения, составляющие Фурье имеют вид пространственных синусоид с кратными частотами. На рис. 9.38,а по- показан одномерный периодический сигнал сложной формы, а на рис. 9.38,6 — две его спектральные составляющие. На рис. 9.39 показа- показаны две пространственные синусоиды — периодические функции двух переменных. Как следует из теории Фурье, любая двухмерная про- пространственная функция имеет две эквивалентные формы представле- представления — в пространственной области fix, у) и в частотной области g(?, r\). Значения функции g — это "взнос" периодической функции с частота- частотами D, Л) в пространственный спектр изображения. Использование представления изображения в частотной области позволяет проанали- проанализировать многие его свойства, в том числе и те, которые сказываются на результатах квантования по пространству. 410 Глава 9. Операции с изображением на уровне растрового представления
а) Рис 9.39. Периодические функции двух переменных Постараемся пояснить суть процесса квантования, не прибегая к сложным математиче- математическим выкладкам. Теория дискретизации изображения базируется на теореме Найквиста о выборках {Nyquist sampling theorem). Эта теорема состоит из двух частей, первую из которых мы будем использовать при анализе погрешностей квантования, а вторую — при анализе вос- восстановления непрерывного изображения по дискретным выборкам. Теорема Найквиста о выборках (часть 1). Ансамбль идеальных выборок непрерывной функции содержит всю информацию об исходной функции тогда и только тогда, ко- когда частота квантования как минимум вдвое превышает максимальную частоту в спектре исходной функции6. Из этой теоремы следует, что если перед нами стоит задача не потерять информацию об изо- изображении в процессе его дискретизации, то это можно осуществить только в том случае, если в частотной области все компоненты исходного изображения, выходящие за пределы интер- интервала шириной в частоту квантования по обе стороны от начала координат, имеют нулевую интенсивность. Наивысшая частота в спектре исходной функции, не превышающая половину частоты квантования, называется частотой Найквиста (Nyquist frequency). Функции, спектр которых не выходит за определенный интервал, называются функциями с ограничен- ограниченным спектром. При работе с функциями двух переменных, подобных функциям интенсивно- интенсивности изображения, пространственные частоты квантования по каждой переменной на двух- двухмерной сетке с осями хну равны соответственно \lhx и \lhy. В формулировке теоремы пред- предполагается ансамбль идеальных выборок бесконечной длины, причем каждая выборка соответствует значению исходной функции в бесконечно малой окрестности узла сетки. На практике этот идеал недостижим, поскольку количество выборок ограничено размером буфе- буфера, имеющегося в графической системе. Следовательно, мы не сможем точно воспроизвести идеальную функцию с ограниченным спектром. Результатом нарушения сформулированного Найквистом критерия является лестничный эффект (aliasing) — проявление погрешности квантования в сформированном изображении. И исходная непрерывная функция, и последовательность выборок — решетчатая функция, полученная в результате квантования по независимой переменной, — могут быть представле- представлены в частотной области. Спектр решетчатой функции — ее фурье-образ — имеет периодиче- периодический характер и представляет собой сумму фурье-образов исходной непрерывной функции, смещенных по оси частот на величину, кратную частоте квантования. На рис. 9.40,а показаны 6Не вступая в спор о научном приоритете, отметим, что в отечественной литературе эта теорема связывается с именем В. А. Котельникова, который опубликовал свое исследование в 1933 году (см. Котель- Котельников В. А. О пропускной способности "эфира " и проволоки в электросвязи// Материалы к 1-му Всесоюзно- Всесоюзному съезду реконструкции дела связи и развития слаботочной промышленности. — Изд-во ред. упр. связи РККА, 1933.). — Прим. ред. 9.9. Дискретизация изображения 411
функция одной переменной и выборки на ней, а на рис. 9.40,6 — ограниченный спектр этой непрерывной функции. На рис. 9.40,в вы видите спектр решетчатой функции, который повто- повторяет в каждом периоде спектр исходной непрерывной функции. Поскольку частота квантова- квантования при построении решетчатой функции была выбрана выше частоты Найквиста, между пе- периодами в спектре решетчатой функции отчетливо видны участки с нулевой интенсивностью. А теперь рассмотрим случай, представленный на рис. 9.41. В нем критерий Найквиста на- нарушен, а потому копии фурье-образа исходной функции в спектре решетчатой функции пере- перекрываются. Центральная часть этого графика в увеличенном масштабе показана на рис. 9.42. На нем вы видите копию фурье-образа исходной непрерывной функции, построенную в нача- начале координат частотной области, и первую правую от нее копию, сдвинутую на период часто- частоты квантования ?,. Частота ^о выше частоты Найквиста bJ2. В точке ^v-^o, которая ниже час- частоты Найквиста, на составляющую центральной копии накладывается составляющая Z& пра- правой копии. Если исходная функция представляет звуковые колебания, то в квантованном сигнале мы услышим усиление мощности звука на этой частоте. Точно так же, если сигнал с подобными характеристиками (но уже двухмерный) представляет изображение, то на экране будут заметны искажения формы картинки. Мы говорим, что в квантованном сигнале частота ?о имеет побочную реплику (alias) на частоте ?,--4о- Обращаю ваше внимание на то, что при появлении побочной реплики ее нельзя отличить от составляющей основной копии фурье- образа исходной функции, т.е. информация, которую несет эта частота в исходном сигнале, будет искажена побочной репликой. \зШ\ 19,AI /v/v/\ a) 6) в) Рис. 9.40. Функция с ограниченным спектром: а — функция и выборки на ней в пространственной области; б — спектр непрерывной функции; в — спектр решетчатой функции \№\ /YYYYX Рис. 9.41. Перекрытие фурье-образов ис- исходной функции в спектре решет- решетчатой функции Рис. 9.42. Появление побочной низко- низкочастотной составляющей в спектре решетчатой функции Можно продемонстрировать эффект искажения сигнала и без анализа Фурье. Рассмотрим гармоническое колебание (рис. 9.43). Если выбрать частоту квантования вдвое выше частоты синусоиды, то исходный сигнал можно будет восстановить по двум выборкам решетчатой функции. Но точно такой же вид будет иметь и решетчатая функция, полученная при кванто- квантовании гармонического колебания с вдвое большей частотой, а в общем случае — колебаний с частотами, кратными этой частоте. Все эти частоты являются побочными репликами основ- основной частоты. Но если заранее известно, что исходная функция имеет ограниченный спектр, 412 Глава 9. Операции с изображением на уровне растрового представления
не выходящий на предел частоты Найквиста, то решетчатая функция однозначно описывает исходную непрерывную. Если провести спектральный анализ реальных изо- изображений, то окажется, что в большинстве случаев в спектре преобладают низкие частоты, а составляющие выше частоты Найквиста весьма незначительны. Следо- Следовательно, хотя теоретически изображение ограниченного размера и не может быть описано функцией с ограничен- ограниченным спектром, погрешности пространственной дискрети- дискретизации, вызванные наличием побочных частот, оказыва- оказываются очень малыми. Исключения составляют изображе- изображения, которые имеют явно выраженный регулярный (периодический) характер. Если соответствующие часто- частоты оказываются выше частоты Найквиста, то на восста- восстановленном после дискретизации изображении может поя- появиться муар. Такой эффект заметен, например, на видео- видеоизображениях, где персонажи носят рубашки в полоску или галстуки в клетку. Минимизировать такой эффект можно с помощью предварительной фильтрации на стадии сканирования изображения или настройки размеров области усреднения. На рис. 9.44 показаны два способа сканирования одного и того же изображения. Слева вы видите "идеальное" сканирование, когда апертура приемника минимальна. В таком случае можно считать, что формируется идеальная решетча- решетчатая функция: Рис. 9.43. Побочные составляющей гармонического колебания Справа сканер настроен таким образом, что вы- выполняет усреднение (часто взвешенное) по некоторой области. В результате выборки имеют значения Рис. 9.44. Сканирование изображения: а — точечная выборка; б —усреднение по области Л = J,..,/2 Jv _s/1 f(x,y)Mx,y)dydx . Подбирая s, размер области усреднения, и весо- весовую функцию и1, можно ослабить высокочастотные составляющие в изображении и таким образом час- частично избавиться от нежелательных эффектов. К сча- счастью, реальные сканеры всегда имеют конечную апертуру, так что усреднение происходит в любом случае, даже если пользователь об этом и не подозревает. 9.9.2. Восстановление непрерывной функции по дискретным выборкам Предположим, что в нашем распоряжении имеется решетчатая функция (множество вы- выборок), полученная при дискретизации функции одной переменной, причем частота дискре- дискретизации выбрана таким образом, что критерий Найквиста удовлетворяется. Восстановление исходной непрерывной функции по имеющейся решетчатой основано на второй части тео- теоремы Найквиста о выборках. Теорема Найквиста о выборках (часть 2). Непрерывную функцию J(x) можно восста- восстановить по соответствующей решетчатой {/,}, воспользовавшись формулой 9.9. Дискретизация изображения 413
/(*) = _Х/¦ sine (дг-*,.), где функция sinc(;c) определена следующим образом (рис. 9.45): sin7U: sinc(.v) = • тгх А/с. 9.45. Функция sinc(x) Для функции двух переменных /х, .у) формула восстановления по идеальной решетчатой функции {//у} принимает вид /(*.>')=? 2 Лsinc(Л ~ •*".- )sinc(^ - У>) • Из этих формул следует, что при отсутствии искажающих побочных частот в спектре ре- решетчатой функции можно восстановить по ней исходную непрерывную функцию с помощью фильтра с полосой частот, ограниченной интервалом (-^J2, ?/2), — фильтра низких частот. Восстановление функции одной переменной показано на рис. 9.46. При восстановлении функции двух переменных необходимо использовать фильтр, воспроизводящий двухмерную функцию sinc(jr, у), представленную на рис. 9.47. К сожалению, не представляется возмож- возможным воспроизвести эту функцию в реально существующих системах отображения, поскольку в некоторой области значения функции отрицательны. Рассмотрим с этой точки зрения дис- дисплей на ЭЛТ. В нашем распоряжении имеется дискретизированное изображение— массив выборок. Подавая определенные сигналы в отклоняющую систему, можно вывести электрон- электронный луч ЭЛТ в точку экрана, соответствующую узлу сетки дискретизации. Значение выборки задает интенсивность свечения этой точки, которым можно управлять, изменяя ток электрон- электронного луча. Форма светового пятна на экране определяется системой фокусировки электрон- электронного луча. Если пытаться точно воспроизвести формулу восстановления, то распределение интенсивности в пятне должно воспроизводить вид функции s\nc(x,y), но это физически не- невозможно, поскольку электронный луч не может иметь отрицательную интенсивность. Сле- Следовательно, использование для восстановления изображения реальных физических приборов принципиально сопряжено с внесением искажений в исходное изображение. Можно проана- проанализировать, насколько отличается реальная функция распределения интенсивности пятна от идеальной функции sine. На рис. 9.49 показаны возможные варианты приближенного воспро- воспроизведения одномерной функции sine. Гауссова кривая распределения характерна для ЭЛТ, а прямоугольная форма — для жидкокристаллических экранов с квадратной формой пикселя. Учтите, что в определенных пределах размер пятна можно регулировать. Анализ функции распределения интенсивности пятна в частотной области показывает, что при увеличении 414 Глава 9. Операции с изображением на уровне растрового представления
размеров пятна лучше воспроизводятся низкие частоты в спектре исходной функции, а при уменьшении— высокие частоты. На практике размер пятна выбирается компромиссным, причем даже невооруженным глазом видны отличия при воспроизведении одного и того же изображения на мониторах с разной настройкой. Рис. 9.46. Восстановление функции одной пе- переменной Рис. 9.47. Двухмерная функция sinc(x, у) Рис. 9.48. Электронно-лучевая трубка а) б) в) г) Рис. 9.49. Распределение интенсивности в пятне: а — идеальное; б — прямоугольное; в — кусочно- линейное; г — гауссово 9.9.3. Квантование по уровню Проведенный математический анализ процесса квантования функции по независимым пе- переменным показал, что в этом процессе есть довольно много интересных моментов, сказы- сказывающихся на качестве конечного результата. Однако мы на время отложили рассмотрение 9.9. Дискретизация изображения 415
квантования выборок по уровню. Будем считать, что любая выборка может быть представле- представлена числом, соответствующим одному из к дискретных уровней. Пусть задана скалярная вели- величина g, значение которой лежит в некотором интервале: 6min — о — оmax* Определим следующим образом функцию квантования по уровню q: если g, < g < g,+ 1, то q(g) = qt. Следовательно, для каждого значения g назначается один из к уровней квантования, как показано на рис. 9.50. В общем случае разработка преобразователя, реализующего квантова- квантование по уровню, включает выбор ансамбля уровней квантования {q,} и пороговых значений {gi}. Если известно распределение вероятностей/?^) значений преобразуемой величиныg, то при выборе {q,} и {g,} можно руководствоваться принципом минимизации среднеквадратич- среднеквадратичной ошибки: e = l{g-q(g)J p{g)dg . Однако чаще всего при разработке преобразователя важнейшую роль играют соображе- соображения, которые мы обсуждали в главе 1. Простое правило, которым часто руководствуются на практике, гласит, что мы не должны в конечном результате ч(э) различать изменения на уровне соседних уровней квантования, 1 но перепад значения в два уровня квантования должен быть отчетливо виден. Выбор на основе этих соображений порого- 4 вых значений для квантования интенсивности изображения q3 - . I приводит к тому, что нам потребуется 7 или 8 двоичных разря- разрядов для хранения результата квантования. В конечном счете, q2" j как правило, используется именно такая точность представле- представления преобразованных в двоичный код значений интенсивности, что соответствует 128 или 256 уровням квантования. Следует qo'r—J ? i :j > д также принимать во внимание и логарифмический характер и9о 9\ 9г 9з 94 г „ восприятия интенсивности светового сигнала зрительной сис- Рис. 9.50. Квантование по темой человека. Это приводит к тому, что уровни квантования уровню распределяются не равномерно в заданном диапазоне значе- значений, а по экспоненциальному закону. В результате оказывает- оказывается, что погрешность восприятия при переходе от одного уровня к следующему остается прак- практически единой во всем диапазоне значений. 9.10. Резюме В прежние времена существовало представление, что те, кто работает с компьютерной графикой, имеют дело исключительно с трехмерными геометрическими объектами и не "опускаются" до уровня отдельных пикселей, а работа с двухмерными картинками — исклю- исключительная прерогатива специалистов по обработке изображений (распознаванию образов, те- телевизионным измерениям и т.п.). Развитие аппаратных средств за последние два десятилетия привело к тому, что области компьютерной графики и обработки изображений практически стали неразличимы. Для тех, кто занимается синтезом изображений — а это подавляющая часть задач в компьютерной графике,— такое слияние областей исследования послужило мощным толчком к расширению арсенала применяемых технологий. Мысль о том, что нало- наложение двухмерного изображения (текстуры) на трехмерную поверхность может занимать не больше времени, чем тонирование этой поверхности по известным алгоритмам, казалась еще 416 Глава 9. Операции с изображением на уровне растрового представления
совсем недавно чем-то из области околонаучной фантастики. А теперь такая технология при- применяется повсеместно. Технологии, подобные наложению текстур, позволяют передавать совершенно фантасти- фантастические эффекты в приложениях, связанных с анимацией, созданием виртуальной реальности, визуализации результатов научных расчетов. Современная техника позволяет реализовать эти технологии на аппаратном уровне и выполнять операции в реальном масштабе времени. В этой главе мы ограничились описанием только тех технологий, которые поддерживают- поддерживаются существующими в настоящее время аппаратными и программными средствами, а следова- следовательно, доступны рядовому читателю. Но существует еще много других технологий, которые на момент подготовки книги к печати находились в стадии становления, хотя я и не могу га- гарантировать, что в то время, когда вы читаете эти строки, они уже не стали повседневным инструментом специалистов по компьютерной графике. 9.11. Рекомендуемая литература Методика наложения микрорельефа впервые была описана Блинном (Blinn) в работе [ВИ77]. Он же совместно с Ньюэллом (Newell) в работе [ВН76] предложил методику наложе- наложения изображения окружающих предметов. Наложение текстур впервые было использовано Кэтмуллом (Catmull) [Hec86]. Аппаратная реализация наложения текстур впервые появилась в системе SGI Reality Engine — ее описание есть в работе [АкеЯЗ]. Перлин (Perlin) и Хоф- ферт (Hoffert) в работе [Рег89] предложили использовать случайный шум для процедурного формирования двух- и трехмерных текстур. Множество методов формирования текстур рас- рассмотрено в работе Эберта (Ebert) [Ebe94]. Проблема погрешностей дискретизации в компью- компьютерной графике подробно исследована в работе Кроу (Crow) [Сго81]. Разным аспектам этой проблемы посвящены также работы [Mag85] и [Gla89]. Тем, кто интересуется проблемами дискретизации сигналов, я рекомендую познакомиться с книгами [Pra78], [Gon87], [Cas96J. Книга Гласснера (Glassner) [Gla95] представляет взгляд на эту проблему с точки зрения спе- специалиста по компьютерной графике. Множество методов комбинирования изображений, в том числе и использующих идею а- канала, рассмотрено Портером (Porter) и Даффом (Duff) в работе [Рог84]. В "Руководстве программиста OpenGL" (OpenGL Programmer's Guide) [Ope93, а] вы найдете много практи- практических примеров программ, работающих с буферами. Наиболее свежую информацию по это- этому вопросу можно найти на страницах периодических изданий Computer Graphics и IEEE Computer Graphics and Applications. Упражнения 9.1. Покажите, что режим записи XOR можно использовать для реализации алгоритма заливки многоугольников по принципу тестирования на четность-нечетность. 9.2. Какой визуальный эффект даст использование режима XOR записи в буфер при пе- перемещении курсора по экрану? 9.3. Чем изображение, полученное в результате наложения окружающих предметов, от- отличается от изображения той же сцены, полученного методом трассировки лучей? 9.4. При просмотре кинофильма или телевизионной передачи часто создается впечатле- впечатление, что колеса автомобилей вращаются в обратную сторону. Чем это, по-вашему, можно объяснить? Что необходимо сделать для устранения такого эффекта? Пояс- Поясните свой ответ. 9.5. Можно воспроизвести на экране график некоторой функции, выведя точки, соот- соответствующие ее выборкам, и предоставив пользователю самому мысленно объеди- Упражнения 417
нить их в кривую. Какую потенциальную опасность таит в себе такой способ, если при дискретизации не были выполнены условия теоремы Найквиста? 9.6. Почему изображение рубашки или галстука в полоску изменяется на экране при движении актера в пространстве сцены? 9.7. Почему для устранения отрицательных эффектов дискретизации предпочтительнее использовать методику предварительной обработки данных, чем постобработки? 9.8. Предположим, что имеются две полупрозрачные поверхности с коэффициентами поглощения СХ| и а2. Какой коэффициент поглощения будет иметь двухслойный ма- материал, созданный наложением одной поверхности на другую? Выведите формулу для расчета коэффициента поглощения комбинированного материала. 9.9. Предположим, что полупрозрачный материал используется в качестве светофильт- светофильтра. Разработайте модель смешения цветов для такого светофильтра, используя фор- формат представления цвета CMY. 9.10. В разделе 9.8 в качестве значения альфа-составляющей приемника и источника ис- использовались 1чхиа соответственно. Изменим значение альфа-составляющей при- приемника и установим его равным 1. Как это проявится в созданном изображении? 9.11. В программу закрашивания, рассмотренную в главе 3, добавьте кисть, которая сможет изменять цвет по определенной шкале. Используйте в этой программе метод смешивания цветов для частичного затенения (затуманивания) изображе- изображения на экране. 9.12. Разработайте метод использования наложения текстур для отображения объектов, представленных в виде множества элементарных кубиков — вокселей. 9.13. Покажите, как можно использовать гистограмму распределения яркости изображе- изображения (см. рис. 9.27) для формирования такой таблицы соответствия цветов, которая позволила бы выровнять форму гистограммы. 9.14. Почему при применении метода размывания границ для сглаживания изображения нужно использовать генератор случайных чисел? 9.15. Предположим, что при отображении нескольких объектов использовалось наложе- наложение текстуры в виде полосок или шахматной клетки. Как изменится вид картинки при переходе от параллельной проекции к перспективной? 9.16. Пусть имеется сцена, состоящая из объектов стандартной формы (например, па- параллелепипедов), но разного размера, причем в вашем распоряжении есть един- единственная текстура, которую требуется применить ко всем объектам. Как следует настроить процесс наложения, чтобы узор имел одинаковый размер на всех гра- гранях объектов? 418 Глава 9. Операции с изображением на уровне растрового представления
ГЛАВА 10 Кривые и криволинейные поверхности Втом, что наш мир не является плоским, просвещенное человечество перестало сомне- сомневаться в 1492 году. Но, тем не менее, в компьютерной графике виртуальное пространст- пространство продолжает "заселяться" преимущественно плоскими объектами. И на то есть до- довольно веские причины. Современная графическая система способна с высокой скоростью выполнять операции закраски проекций плоских многоугольников, удаления невидимых по- поверхностей, представленных в форме множества плоских многоугольников, и наложения на них разного рода текстур. Поэтому, если возникает необходимость включить в состав сцены криволинейный объект, например сферу, его зачастую стремятся с самого начала приближен- приближенно представить (аппроксимировать) множеством плоских многоугольников. Альтернативный способ, который мы рассмотрим в этой главе, состоит в том, чтобы предоставить пользовате- пользователю средства работы с криволинейными объектами, возложив функцию их тонирования, кото- которое не обходится без аппроксимации, на реализацию графической системы. В этой главе читатели познакомятся с тремя методами моделирования (формами пред- представления) кривых пчний и криволинейных поверхностей, причем основное внимание будет уделено параметрической полиномиальной форме представления. Мы также обсудим, как можно в современной графической системе реализовать тонирование таких объектов. Этот процесс, как правило, включает разбиение криволинейных поверхностей на большое число мелких плоских примитивов. Этот процесс абсолютно прозрачен с точки зрения программи- программиста, разрабатывающего прикладную программу, поскольку его реализация возлагается на системные средства графического пакета. Но о том, как выполняются эти операции, про- программист должен четко себе представлять — характер операций определяет ограничения, ко- которые накладывает процесс реализации на неуемную фантазию пользователя. 10.1. Представление кривых линий и поверхностей В этом разделе мы кратко повторим, что нам известно о трех главных формах математи- математического представления кривых и поверхностей — явной, неявной и параметрической, — и проанализируем достоинства и недостатки каждой из них. Для иллюстрации тех или иных
положений будем использовать простейшие геометрические объекты — прямые линии, ок- окружности, плоскости и сферические поверхности. 10.1.1. Представление в явной форме Явная форма представления кривой в двухмерном пространстве (иногда говорят "представление кривой в виде явной функции") представляет собой уравнение, в левой части которого стоит зависимая переменная, а в правой части — функция, аргументом ко- которой является независимая переменная. В пространстве переменных х,у уравнение линии в явной форме имеет вид у =>W. Некоторые функции/имеют обратную g, которая позволяет изменить назначение зависимой и независимой переменных в уравнении, т.е. выразить х как функцию от у: х = gi}'). Нет никакой гарантии, что для определенной линии существует явное уравнение в том или ином виде (у от х или х от у). Для прямой линии уравнение обычно записывается в терминах углового коэффициента т и ординаты h точки пересечения с осью у, хотя в такой форме невозможно описать вертикальную прямую. Это одно из множества особенностей, которыми обладают формы представления, зависимые от системы координат, и которые следует учитывать при разработке программных средств обработки объектов в графиче- графических системах. Линии (мы будем говорить о прямых или окружностях, но это касается ли- линий любого вида) существуют независимо от формы представления, а потому невозмож- невозможность представить в некоторой форме определенный вариант линии является серьезным недостатком такой формы представления. Еще более интересную пищу для размышления предоставляет окружность. Рассмотрим окружность радиуса г, центр которой находится в начале координат. Окружность имеет по- постоянную кривизну — меру изгиба линии в точке. Более симметричной кривой в двухмерном пространстве, чем окружность, не существует. Но, пользуясь явной формой уравнения ок- окружности, единственное, что можно сделать, — описать половину окружности, находящуюся в верхней полуплоскости, одним уравнением: а вторую половину, находящуюся в нижней полуплоскости, другим: В трехмерном пространстве линия описывается системой из двух уравнений в явной форме. Если, например, переменная х снова выбрана в качестве независимой, то имеем два уравне- уравнения для зависимых переменных: У = - = g(x). Для описания поверхности потребуется использовать две независимые переменные, и урав- уравнение поверхности в явном виде будет выглядеть так: Как и в двухмерном пространстве, в трехмерном пространстве также не все кривые и поверх- поверхности могут быть описаны уравнениями в явной форме. Например, система уравнений у = ах + Ь, 420 Глава 10. Кривые и криволинейные поверхности
описывает прямую в трехмерном пространстве, но таким способом нельзя описать прямую, лежащую в вертикальной плоскости ,x=const. Точно так же сферу нельзя представить уравне- уравнением вида z-Дх, у), поскольку заданным значениям хну соответствуют две точки на сфере. 10.1.2. Неявная форма представления Большинство кривых и поверхностей, с которыми приходится работать на практике, можно описать с помощью уравнений в неявной форме. В двухмерном пространстве неявная форма уравнения линии имеет вид Прямая и окружность с центром в начале координат описываются соответственно урав- уравнениями ах + by + с = О, х2+у2-г2 = 0. Функция /, по сути, выделяет из всех точек пространства те, которые принадлежат описы- описываемой линии. Значение этой функции позволяет проверить для каждой пары значений коор- координат х, у, лежит ли описываемая ими точка на данной линии. В общем случае, однако, неяв- неявная функция не позволяет определить значение координаты у точки на кривой для заданного значения д", или наоборот. Неявная форма представления является менее зависимой от систе- системы координат, поскольку позволяет представлять прямые или окружности во всех вариантах. В трехмерном пространстве уравнение в неявной форме вида /U,>,r)=0. описывает поверхность. Например, плоскость описывается уравнением ах + by ¦+ cz + d = О, где а, Ь, с и d— константы. Сфера радиуса г с центром в начале координат описывается уравнением х2+ у2+ z2- Г= 0. Описать линию в трехмерном пространстве не так просто. Она может быть представлена только системой уравнений, описывающих поверхности, пересечение которых и образует эту линию, если таковые существуют: Следовательно, задавшись точкой с координатами (х, у, г), нужно проверить, лежит ли она на обеих поверхностях, и, если лежит, считать ее точкой, принадлежащей формируе- формируемой линии. Алгебраической считается поверхность, для которой функция j{x,y,z) есть сумма полино- полиномов трех переменных. Частный случай алгебраической поверхности — квадратичные по- поверхности, в функции / представления которых отсутствуют степени переменных1 выше 2. Квадратичные поверхности представляют для нас особый интерес не только потому, что к ним относятся такие распространенные поверхности, как сфера, цилиндр и конус, но и пото- потому, что такая поверхность пересекается прямой не более чем в двух точках. Это свойство квадратичных поверхностей будет использовано при разработке методов их тонирования в разделе 10.9. 'Степень полинома определяется как максимальная сумма степеней переменных в отдельном члене. Так, в полиноме второй степени могут присутствовать члены ху wiu г2, а член ху2 не может. 10.1. Представление кривых линий и поверхностей 421
10.1.3. Параметрическая форма представления В параметрической форме значение каждой координаты точки, принадлежащей кривой, представляется функцией независимой переменной и, которая называется параметром этой кривой. В трехмерном пространстве кривая описывается системой из трех параметри- параметрических уравнений: х = х(и), z = z(u). Одно из главных достоинств параметрической формы представле- представления — ее единообразие в двух- и трехмерном пространствах. В первом случае нужно просто отбросить третье уравнение для координаты z. Параметрическую форму представления можно интерпретировать как способ представления на экране образа кривой р(и)=[дг(м) у(и) z(u)]T, изменяя значения параметра и, как показано на рис. 10.1. Производную от параметрически заданной векторной функции dx(u) du du dy(u) du dziu) du Рис. 10.1. Кривая, за- заданная в пара- параметрической форме можно рассматривать как вектор скорости обхода кривой, который в каждой точке этой кри- кривой направлен по касательной к ней. Для описания поверхности в параметрической форме требуется использовать два пара- параметра. Система уравнений поверхности имеет вид х = х(и, v), y=y(u,v), z = z(u, v). Эти же уравнения можно записать в виде матрицы-столбца p(w, v)=[x(i/, v) y(u, v) z(u, v)]T. Изменяя значения параметров и и v в некотором интервале, можно сформировать значения координат р(и, v) всех точек поверхности. При анализе примера сферической поверхности в главе 6 мы уже отмечали, что матрицы-столбцы dp/дм и dp/dv описывают плоскости, каса- касательные к поверхности в каждой ее точке. Параметрическая форма описания кривых и поверхностей является, во-первых, наи- наиболее гибкой, а во-вторых, устойчивой к любым вариациям формы и ориентации объек- объектов, что делает ее особенно удобной для использования в математическом обеспечении систем компьютерной графики. Возможно, у кого-либо из читателей появится возраже- возражение, что и в этой форме мы привязаны к системе координат х, у, z, но это не так. Можно модифицировать параметрические уравнения и использовать для описания кривой в трехмерном пространстве только касательные и нормали, которые образуют так назы- называемый сопровождающий трехгранник (трехгранник Френе), имеющий отличную ори- ориентацию в каждой точке кривой. Что касается задач компьютерной графики, то исполь- использование параметрической формы представления координат х, у, z вполне удовлетворяет все их потребности. 422 Глава 10. Кривые и криволинейные поверхности
10.1.4. Параметрические полиномиальные кривые Параметрическая форма представления определенной кривой или поверхности не явля- является уникальной. Объект можно представить множеством способов, но мы сосредоточим внимание на полиномиальной форме, т.е. все функции параметра и при описании кривых или параметров и и v при описании поверхностей являются полиномами. Эта форма чаше всего используется в компьютерной графике, а ее достоинства будут проанализированы в разделе 10.2. Рассмотрим уравнения кривой в виде2 р(и) = (n) Полиномиальная параметрическая кривая степени п имеет вид где с* имеет независимые компоненты х, у, z, т.е. с, с. с. = LC:*J Матрица {сА}, состоящая из /7+1 столбцов, объединяет коэффициенты полиномов для компонентов р; это означает, что у нас есть 3(и+1) степеней свободы в выборе коэффициен- коэффициентов для конкретной кривой р. Уравнения для трех компонентов х, у, - независимы и каждое из них имеет вид а следовательно, обладает /7+1 степенью свободы. Кривую можно определить на любом интервале изменения параметра и Mmin < U < Wmax, но, не теряя общности рассуждений, можно положить, что 0 < и < 1 (см. упр. 10.3). По мере того как параметр и будет "пробегать" зна- значения в этом интервале, отображающая точка будет перемещаться по сегменту кривой (рис. 10.2). Рис. 10.2. Сегмент кривой 10.1.5. Параметрические полиномиальные поверхности Параметрическая полиномиальная поверхность описывается уравнением вида .v(m,v) v(m,v) z(u.v) =0 y=O 2При изложении этого материала мы не будем пользоваться аппаратом однородных координат. Об ис- использовании этого аппарата мы поговорим в разделе 10.8, когда будем обсумсдать кривые в форме рацио- рациональных В-сплайнов на неравномерной сетке (NURBS-спчайнов). }В OpenGL часто используется термин "порядок" полинома (oxtex), который имеет значение на 1 боль- большее, чем степень полинома. 10.1. Представление кривых линий и поверхностей 423
Таким образом, для определения конкретной поверхности р(г/, v) необходимо задать 3(п+\)(т+\) коэффициентов. Можно при анализе положить п=т, а параметры и и v изменять на ин- интервале 0 < и, v < 1 и таким образом определить порцию поверх- поверхности {surface patch4), как показано на рис. 10.3. Обращаю ваше внимание на то, что определенный таким способом участок по- поверхности можно рассматривать как предел, к которому стре- стремится множество кривых, которые формируются, когда один из параметров и или v пробегает значения в своем интервале, в то Рис. 10.3. Участок поверх- время как другой сохраняет постоянное значение. В дальнейшем ности при обсуждении поверхностей будет использована следующая стратегия: мы будем определять полиномиальные кривые, а за- затем применять их для формирования поверхности с аналогичными характеристиками. 10.2. Общая характеристика полиномиальной параметрической формы представления В компьютерной графике и прикладных системах автоматизации проектирования, кото- которые строятся на базе систем компьютерной графики, кривые и поверхности используются до- довольно специфическим образом, в корне отличном от других применений. Существует мно- множество доводов в пользу использования в этой области именно параметрической формы представления криволинейных объектов. Среди них упомянем основные: ¦ возможность локального контроля формы объекта; ¦ гладкость и непрерывность в математическом смысле; ¦ возможность аналитического вычисления производных; ¦ устойчивость к малым возмущениям; ¦ возможность использовать относительно простые, а значит, высокоскоростные мето- методы тонирования. Понять смысл сказанного можно на простом примере. Предположим, требуется построить деревянную модель планера летательного аппарата. Каркас модели (авиаконструкторы гово- говорят "набор") можно создать следующим образом: сначала сделать из относительно коротких реек несколько поперечных сечений, а затем связать их стрингерами из длинных гибких и уп- упругих реек (рис. 10.4). Чертежи поперечных сечений строятся либо по фотографиям реально- реального аппарата, либо по эскизным кривым — наброскам, сделанным конструктором. Его типовое поперечное сечение (специалисты называют соответствующий конструктивный элемент на- набора шпангоутом) показано на рис. 10.5. Можно попробовать построить обобщенное уравне- уравнение кривой шпангоута, но, скорее всего, оно окажется слишком сложным и вряд ли нас уст- устроит. При изготовлении шпангоута деревянная рейка может быть изогнута только до опреде- определенных пределов (если гнуть дальше, она просто сломается), причем изогнутая рейка имеет гладкую форму. Таким образом, кривая на рис. 10.5 представляет собой приближенный эскиз формы шпангоута, который реально можно изготовить из имеющихся у нас материалов. Ско- Скорее всего, реальная форма будет такой, как на рис. 10.6. На практике шпангоут придется из- изготовить из нескольких реек, каждая из которых становится, таким образом, отдельным сег- сегментом кривой. Следовательно, гладким должен быть не только каждый сегмент по отдель- отдельности, но и в точках сопряжения (Join points) сегментов должна быть обеспечена определенная степень гладкости кривой. Учтите, что именно гладкости в точках сопряжения 4В дословном переводе surface patch — лоскут поверхности. — Прим. перев. 424 Глава 10. Кривые и криволинейные поверхности
приходится уделять больше всего внимания при выборе способа аппроксимации. На рис. 10.7 показана непрерывная линия, первая производная которой не является гладкой функцией, — претерпевает разрыв в точке сопряжения. Общепринятое определение гладкости кривой связано с характеристиками функций первой производной по параметру и производных более высоких порядков. Для нас представляют интерес гладкие кривые, т.е. такие, функция первой производной которых непрерывна на всем интервале существования параметра. В некоторых случаях гладкой должна быть не только функция первой производной, но и производных бо- более высокого порядка. Эти определения станут более понятны для вас после изучения мате- материала раздела 10.3. Сейчас же отмечу, что для полиномиальной кривой все производные до п-то порядка включительно являются гладкими и могут быть определены ана- аналитически. Следовательно, единственное место, где могут возникать разрывы при аппроксимации произвольной гладкой кривой сегментами полиномиальных кривых — это точки сопряжения. Желаемая кривая p(v Рис. 10.4. Модель планера Рис. 10.5. Кривая попе- поперечного сече- сечения модели Аппроксимирующая Рис. 10.6. При- Приближенное пред- представление кри- кривой поперечного сечения Точка сопряжения яМ Рис. 10.7. Разрыв первой произ- производной в точке сопряжения сег- сегментов Процесс формировании кривой желательно организовать таким образом, чтобы каждый сегмент строился индивидуально, а не строить все сегменты единой глобальной вычисли- вычислительной процедурой. Причина, но которой такая организация предпочтительнее, заключается в том, что в таком режиме пользователю гораздо удобнее подбирать форму кривой на от- отдельных участках, следуя специфическим требованиям приложения. Внесение небольших из- изменений в спецификацию кривой приводит только к локальным изменениям ее формы на том участке, с которым в текущий момент работает пользователь, и мало влияет на остальные участки. Такой способ реализует устойчивую стратегию конструи- конструирования, которая неформально может быть охарактеризована сле- следующим образом: малые вариации исходных параметров на входе должны приводить к малым вариациям результата на выходе. Работая с деревянными рейками, желательно иметь возмож- возможность гнуть их таким образом, чтобы как можно точнее воспроиз- воспроизвести желаемую форму шпангоута. Более того, желательно свести всю процедуру к выбору небольшого ансамбля опорных или кон- контрольных точек {control points), которые будут полностью харак- характеризовать форму сегмента кривой. На рис. 10.8 показан сегмент кривой и ансамбль описывающих его контрольных точек. Обратите внимание на то, что через некоторые контрольные точки сегмент проходит, а некоторые располагаются вблизи дейст- действительной кривой. Как будет показано ниже, во многих задачах компьютерной графики и ав- автоматизации проектирования нас вполне устраивает, если кривая проходит в малой окрестно- окрестности заданных контрольных точек и при этом обеспечивается ее гладкость. Рис. 10.8. Сегмент кривой и контрольные точ- точки 10.2. Общая характеристика полиномиальной параметрической формы... 425
Этот пример наглядно демонстрирует, почему в задачах компьютерной графики предпочте- предпочтение отдается классу полиномиальных кривых. Даже наименование класса составных полиноми- полиномиальных кривых— сплайн-кривые, или сплайны— произошло от английского наименования гибкой деревянной рейки или упругой стальной ленты, с помощью которой в кораблестроении вычерчивались гладкие контуры. Такая лента вставлялась между штифтами, соответствующими положению контрольных точек на кривой, и за счет упругости образовывала гладкую линию контура. Оказалось, что эта линия математически описывается сегментами полиномиальных кривых степени не выше третьей. За такими составными полиномиальными кривыми и закрепи- закрепилось название сплайнов. Сплайны мы подробно рассмотрим в разделах 10.7 и 10.8. Возвращаясь к задачам компьютерной графики, напомню, что помимо собственно мате- математического описания кривых, нам понадобится разработать и методы их отображения. Ма- Математический аппарат, даже очень изящный, останется "вещью в себе", если мы не сможем им воспользоваться для представления на экране кривых и поверхностей. Нам необходимы такие способы отображения криволинейных объектов, которые не превосходили бы по слож- сложности аналогичные методы, которыми мы пользуемся для отображения прямолинейных и плоских объектов, и обладали теми же функциональными возможностями — раскрашивать и тонировать объекты, накладывать на них текстуры разного вида. 10.3. Параметрически заданные кубические кривые Остановившись на классе полиномиальных кривых в параметрической форме, мы должны выбрать степень полинома для описания кривой. Если воспользоваться полиномом очень вы- высокой степени, то при конструировании кривой определенной формы у нас будут в значи- значительной степени "развязаны руки", поскольку в нашем распоряжении будет больше коэффи- коэффициентов, но процесс расчета координат точек на кривой потребует большего количества вы- вычислений. Кроме того, при работе с полиномами высоких степеней возрастает опасность получить кривую волнистой формы. С другой стороны, выбор полинома слишком низкой степени может привести к тому, что в нашем распоряжении окажется слишком мало регули- регулируемых параметров — коэффициентов полинома — и, следовательно, не удастся воспроизве- воспроизвести с требуемой точностью форму кривой. Выход можно найти в том, чтобы не описывать единым полиномом высокой степени всю кривую, а разбить ее на сегменты небольшой дли- длины, которые можно описывать полиномами низкой степени. Хотя такой полином и обладает малым, количеством степеней свободы, их может оказаться вполне достаточно, чтобы вос- воспроизвести форму кривой на отрезке небольшой длины. Основываясь на этих достаточно общих рассуждениях, многие конструкторы, не мудрствуя лукаво, приступая к формирова- формированию кривых, начинают с кубического полинома — начинать с квадратичной формы, если на то заранее нет веских оснований, все-таки несерьезно, а сразу браться за полином четвертой степени боязно, а вдруг третьей степени вполне хватит. Описать кубическую полиномиальную кривую можно следующим образом, воспользо- воспользовавшись матрицами-строками и матрицами-столбцами: л :!+СзМ3 = ^с4и* =и/с, где С — «О с, с2 л. " 1' и иг и' г -1 Lx L *:J 426 Глава 10. Кривые и криволинейные поверхности
В этих выражениях с представляет матрицу коэффициентов полинома. Именно ее нам и потребуется вычислить по заданному ансамблю опорных точек. Ниже мы рассмотрим раз- разные классы кубических кривых, которые отличаются характером сопоставления с опорны- опорными точками. Для каждого типа будет сформирована система из 12 уравнений с 12-ю неиз- неизвестными, но, поскольку параметрические функции для компонентов х, у и z независимы, эти 12 уравнений будут разделены на три группы по 4 уравнения с 4-мя неизвестными. Вычисление значений коэффициентов определенного типа кубической кривой выпол- выполняется по заданному ансамблю опорных точек, соответствующих некоторым значениям независимого параметра и. Эти данные могут иметь форму ограничений, требующих, что- чтобы формируемая полиномиальная кривая проходила через некоторые из заданных точек и в окрестности других точек. Кроме того, эти данные накладывают и определенные условия на гладкость кривой, например, первая производная и производные более высоких поряд- порядков должны быть непрерывными в заданных точках сопряжения отдельных сегментов. Та или иная формулировка этих условий определяет класс формируемой кривой, причем кри- кривые разного класса, сформированные на одном и том же ансамбле опорных точек, могут существенно отличаться. 10.4. Интерполяция Первым мы рассмотрим класс кубических интерполяционных полиномов {interpolating polynomial). Хотя этот класс кривых очень редко используется в задачах компьютерной гра- графики, их анализ поможет вам составить четкое представление о характеристиках полиноми- полиномиальных кривых общего вида и о тех операциях, которые необходимо выполнять при форми- формировании кривых других классов. Пусть имеется четыре опорные точки в трехмерном пространстве: р0, Pi, P2 и р3. Каждая точка представлена тройкой своих координат: хк V* Л- Наша задача— отыскать элементы матрицы коэффициентов с, такие, что полином p(w)=u7c будет проходить через заданные четыре опорные точки. Вывод соответствующих соотношений особого труда не представляет. Мы располагаем четырьмя трехмерными точками, через которые должна проходить формируемая кривая. Следовательно, можно составить 12 уравнений с 12 неизвестными— элементами матрицы с. Но сначала нужно решить, какому значению независимого параметра и соответствует каждая опорная точка. Не располагая никакой другой информацией, кроме координат точек и порядка их разме- размещения вдоль формируемой кривой, будем считать, что значения ик (А=0,..,3) распределены равномерно на интервале [0, 1], т.е. м=0, 1/3, 2/3, 1 (напоминаю, мы договорились, что па- параметр и изменяется только на интервале [0, 1]). Соответствующие четыре уравнения в векторной форме имеют вид - I с^+1 - 3 10.4. Интерполяция 427
Перепишем эти уравнения в матричной форме следующим образом: р = Ас, где Р = А = 1 О 2 3 1 1 1 - - 1 1 Проанализируем матрицу А. Если интерпретировать рис как матрицы-столбцы из 12 элементов, то правило умножения матриц соблюдено не будет. Но мы можем и по-другому интерпретировать матрицы рис — рассматривать их как матрицы-столбцы из четырех элемен- элементов, каждый из которых, в свою очередь, является матрицей-строкой. Тогда умножение элемен- элемента матрицы А, скаляра, на элемент матрицы с, трехмерную матрицу-строку, даст в результате трехмерную матрицу-строку, т.е. элемент того же вида, что и элементы матрицы-столбца р. Можно показать, что матрица А не является вырожденной и, следовательно, ее можно обратить и получить базисную интерполяционную матрицу (interpolating geometry matrix): 1 -5. 9 -4. 5 5 0 9 -22.5 13.5 0 -4. 18 -13 5 .5 0 1 -4.5 4.5 Располагая значениями элементов М/, можно вычислить искомые значения коэффициентов: с = М,р. Теперь предположим, что кривая задана не четырьмя, а т опорными точками р0, pi,.., рш. Такую кривую можно представить интерполяционным полиномом (m-l)-ro порядка и, ис- используя описанную выше методику, вычислить Зхт коэффициентов сч (i=0,..,m-\;j=x, у, г). Но можно поступить и по-другому — считать -"*"' л" эту кривую состоящей из нескольких сегмен- сегментов, каждый из которых является кубической кривой и задается очередной группой из четы- четырех опорных точек. Непрерывность кривой можно обеспечить тем, что считать последнюю опорную точку предшествующей группы (последнюю точку сегмента) первой опорной точкой следующей группы (первой точкой очередного сегмента) (рис. 10.9). Следовательно, для формирования первого сегмента будут использованы опорные точки р0. Рь Рг, Рз, Для формирования второго — точки р3, р4, Р5, Рб и т.д. Обращаю ваше внимание на то, что если Рис. 10.9. Сопряжение интерполяционных сегментов 428 Глава 10. Кривые и криволинейные поверхности
на каждом сегменте полагается, что параметр и изменяется в интервале [0, 1], то матрицы М/ для всех сегментов одинаковы. Хотя таким способом мы и получим непрерывную кривую, функции производных по параметру будут претерпевать разрывы в точках сопряжения. 10.4.1. Функции смешивания Можно иначе подойти к анализу гладкости интерполяционных полиномиальных кривых. Перепишем выведенные в предыдущем разделе соотношения в слегка измененном виде. Под- Подставив коэффициенты интерполяции в полином, получим р(м)=и'с=и7М/р. Это соотношение можно записать в виде р(м) = Ь(и) ;р, где Ь(м) = М/и есть матрица-столбец из четырех полиномиальных функций смешивания {blending polynomials): Ь(м)= ' ЬЛи) В каждой функции смешивания полином является кубическим. Выразив р(г/) как сумму поли- полиномов смешивания, получим = bo(u)p0+bl(u)p]+b2(u)p2+bi(u)pJ = Из этого соотношения следует, что полиномиальные функции смешивания характеризуют вклад, который "вносит" каждая опорная точка, и таким образом позволяют оценить, на- насколько скажется на виде конечной кривой изменение положения той или иной опорной точ- точки5. Графики функций смешивания для кубического интерполяционного полинома показаны на рис. 10.10, а соответствующие аналитические выражения приведены ниже: д( iV -Л bJu)-— и— и-- (и-1), 0 2 3 3 V ' Поскольку все нули функций смешивания лежат в интервале [0, 1], то их значения могут су- существенно изменяться на этом интервале, а сами функции не являются монотонными. Эти ха- характеристики функций смешивания следуют из того факта, что интерполяционная кривая долж- 5 В литературе по отношению к тем функциям, которые мы назвали функциями смешивания, иногда ис- используется и другой термин — "полиномиальные весовые функции при опорных точках". — Прим. ред. 10.4. Интерполяция 429
на точно проходить через опорные точки, а не в ближайшей их окрестности. Еще более харак- характерны такие особенности для интерполяционных полиномиальных кривых порядков выше чет- четвертого. Плохая гладкость кривой, а также отсутствие непрерывности производных в точках со- сопряжения сегментов и объясняют, почему интерполяционные полиномиальные кривые редко используются в компьютерной графике. Но пользуясь той же методикой анализа, можно оты- отыскать и тип представления кубической кривой, более подходящий для наших задач. Рис. 10.10. Полиномиальные функции смешивания для случая кубической интерполяции 10.4.2. Порция кубической интерполяционной поверхности Все, что было сказано о сегментах интерполяционных кривых, можно достаточно просто распространить и на трехмерный случай порции интерполяционной поверхности. Бикубиче- Бикубическое уравнение порции поверхности можно записать в следующем виде: Здесь c,v — трехкомпонентная матрица-столбец, элементами которой являются коэффициен- коэффициенты при одинаковых степенях независимой переменной в уравнениях для х, у н z компонент. Определим 4х4-матрицу таким образом, что ее элементами будут трехкомпонентные матри- матрицы-столбцы: Тогда описать порцию поверхности можно следующим образом: p(w, v) = u7Cv, где  Конкретная порция бикубической поверхности определяется 48 значениями элементов матрицы С — 16-ю трехмерными векторами. Допустим, что имеется 16 трехмерных опорных точек р,,, /=0,..,3,/=0,..,3. Эти точки мож- можно использовать для определения порции интерполяционной поверхности (рис. 10.11). Будем считать, что эти данные используются для интерполирования с равным шагом по обоим 430 Глава 10. Кривые и криволинейные поверхности
независимым параметрам и и v, которые принимают значения 0, 1/3, 2/3 и 1. Отсюда получим три набора из 16 уравнений с 16 неизвестными в каждом. Например, при m=v=0 получим три независимых уравнения: Роо = [1 О О 0]С Рис. 10.11. Порция интерполяционной поверхности Poo Р.о P2O .Рзо. -urC 1 О О О Мы не будем выписывать и решать эти уравнения, а поступим проще. Если зафиксировать v=0, то, изме- изменяя и, получим кривую, проходящую через р^, Рю, Рго» Рзо- Используя результаты, полученные в предыдущем разделе, можем записать для этой кривой следующее соотношение: р(и,0) = При значениях v=l/3, 2/3, 1 можно определить три другие интерполяционные кривые, каж- каждую из которых можно описать тем же способом. Объединив уравнения для всех кривых, по- получим интересующую нас систему из 16 уравнений: u7M/P = urCAr, где А — матрица, обратная М/. Решением этого уравнения будет искомая матрица коэффициентов: С = М/РМ/. Подставляя ее в уравнение поверхности, окончательно получим p(w, v) - u7'M/PM/v. Этот результат можно интерпретировать по-разному. Из него следует, во-первых, что резуль- результаты, полученные при анализе интерполяционных кривых, можно распространить на соот- соответствующие интерполяционные поверхности. Во-вторых, мы можем распространить мето- методику использования полиномиальных функций смешивания на поверхности. Зная, что Ч/и описывает интерполяционную функцию смешивания, можно записать уравнение для порции поверхности в таком виде: /¦о у-о Каждый член b,(u)bj(v) описывает в этом выражении порцию смешивания (blending patch). По- Поверхность формируется смешиванием 16 простых порций, для каждой из которых в качестве ве- весового коэффициента выступают координаты определенной опорной точки6. Основные свойст- свойства порций смешивания определяются теми же полиномами смешивания, которые мы анализи- анализировали в предыдущем разделе, и, следовательно, большинство характеристик поверхности аналогично характеристикам соответствующих кривых. В частности, порции смешивания немо- нотонны и не'отличаются гладкостью, поскольку нули функций b,(u)bj(y) лежат в пределах еди- 6Иногда можно встретить в литературе выражение "функция' смешивания при определенной опорной точке ". — Прим. ред. 10.4. Интерполяция 431
ничного квадрата в пространстве {//, v}. Поверхности, образованные кривыми в соответствии с этим методом, получили название поверхностей тензорного произведения {tensor-product sur- surfaces). Порции бикубических поверхностей тензорного произведения образуют подмножество порций всех поверхностей, которые содержат члены обоих независимых параметров в степени, не превышающей трех. Они входят в группу так называемых разделяемых поверхностей {separable surfaces), поскольку позволяют работать с параметрами // и v независимо. 10.5. Эрмитова форма представления кривых и поверхностей Методы, которые были рассмотрены применительно к интерполяционным кривым и по- поверхностям, мы используем в этом разделе для анализа кривых и поверхностей других типов. Как уже отмечалось выше, главное отличие между кривыми и поверхностями разных типов состоит в том, как соотносятся опорные точки и формируемая кривая или поверхность. 10.5.1. Форма Эрмита В этом разделе будет рассмотрена форма представления кривых, предложенная Эрмитом (Hermite). Предположим, что анализ сегмента кривой мы начинаем, имея в своем распоряже- распоряжении только точки7 ро и р3, и что по-прежнему сегменту соответствует интервал изменения па- параметра [0, 1], т.е. имеющиеся точки соответствуют значениям параметра г/=0 и и=\. Исполь- Используя те же обозначения, что и раньше, можно записать два условия: Р@) = Ро = с0, Два других условия получим, зная значения производных функции в крайних точках сегмента м=0 им=1. Производная кубического полинома есть квадратный полином d^ du dy_ du dz_ du = c,+2мс, +3m2c3 . Р'№), Обозначим известные значения производных в конечных точ- точках р'о и р'3. Тогда два дополнительных условия примут вид (рис. 10.12) = с,+2с2+3с3. Эти же уравнения можно записать и в матричной форме: р@) Рис. 10.12. К определению формы Эрмита для кубической кривой Ро Ря Ро Р'з 1 1 0 0 0 1 1 1 0 1 0 2 0 1 0 3 с. 7Для единообразия мы здесь используем принятую ранее нумерацию опорных точек. Эта же нумерация будет использоваться и при анализе кривых Безье в разделе 10.6. 432 Глава 10. Кривые и криволинейные поверхности
Обозначив через q матрицу имеющихся в нашем распоряжении данных q= можно записать решение уравнения в виде где \н называется базисной матрицей Эрмита (Hermite geometry matrix): 10 0 0 0 0 10 -3 3 2 -2 1 -2 -1 1 1 В результате получим представление полиномиальной кривой в форме Эрмита: р(м) = urM#q. Мы будем использовать форму Эрмита для представления сегментов составной кривой, как показано на рис. 10.13. Точка сопряжения является общей для обоих сегментов, и, кроме то- того, производные к кривой в точке сопряжения для обоих сегментов также равны. В результа- результате получаем составную кривую, непрерывную по первой производной на всем протяжении. Возможность получения более гладких кривых при исполь- использовании формы представления по Эрмиту можно обосновать математически следующим образом. Запишем полином в виде где новые функции смешивания имеют вид Рис 10.13. Применение фор- формы представления по Эрмиту к стыковке сегментов = q'|O) и3 -2и2 +и Нули этих четырех полиномов расположены вне интервала [0, 1], а потому функции смешива- смешивания являются гораздо более гладкими, чем для интерполяционных полиномов (см. упр. 10.16). Воспользовавшись представленными выше функциями смешивания, можно следующим образом определить порцию поверхности в форме Эрмита: i=0 j=0 где Q=[q,/] — набор данных, представляющих порцию поверхности аналогично тому, как q представляет сегмент кривой. Но пока что эти уравнения являются для нас чисто формаль- формальными выражениями. Совершенно неясно, как связаны между собой элементы Q и производ- производные функции р(м, v). Четыре элемента Q представляют собой значения функции p(w, v) в уг- угловых точках порции поверхности, а четыре других должны представлять производные к по- поверхности в этих угловых точках. В интерактивных приложениях пользователю желательно специфицировать не данные о производных, а координаты точек, и, следовательно, не сфор- сформулировав аналитические выражения для этих данных, мы не сможем получить производные. 10.5. Эрмитова форма представления кривых и поверхностей 433
10.5.2. Геометрическая и параметрическая непрерывность Перед тем как мы перейдем к обсуждению форм представления Безье и сплайнов, кратко рассмотрим проблему непрерывности представления и его производных в общем виде. На рис. 10.14 показаны два последовательных сегмента составной параметрической кривой. Обозначим полином левого сегмента р(м), а полином правого — q(w). Сформулируем разные условия непрерывности, сопоставляя значения полиномов и их производных в точке сопря- сопряжения и=\ для р(м) и и=0 для q(u). Если желательно, чтобы составная кривая была непрерыв- непрерывной, необходимо в точке сопряжения обеспечить выполнение условия = q@) = ЧуФ) В точке сопряжения значения всех трех параметрических компо- компонент векторов р и q должны быть равны. Кривые, в которых такие условия удовлетворяются, назовем кривыми, обладающими пара- параметрической непрерывностью (parametric continuity) класса С0. Переходя к анализу производных в точке сопряжения, можно сформулировать условие непрерывности по первой производной аналогично тому, как это делалось при обсуждении формы Эрмита: • q(D Рис. 10.14. Непрерывность составной кривой в точке сопряжения р'0) = /\0) = q'@) = Я уф) Кривые, в которых условия непрерывности удовлетворяются и для значения, и для первой производной, назовем кривыми, обладающими параметрической непрерывностью класса С1. Теперь взглянем на проблему непрерывности с точки зрения геометрии. Производная к кривой, заданной параметрически, в некоторой точке есть вектор касательной к этой кривой, направленный в сторону увеличения значения параметра. Предположим, что в точке сопря- сопряжения вместо равенства значений компонент производной к сегментам мы потребуем только соблюдения пропорциональности этих компонент с некоторым коэффициентом к. Если касательные к обоим сегментам пропорциональны, то соответствующие векторы параллельны (имеют одинаковое направление), но имеют разную длину. Будем говорить, что составная кривая, удовлетворяющая этим условиям, обладает геометрической непрерывностью {geometric continuity) класса8 G]. Если необходимо, чтобы касательные к сегментам в точке сопряжения были только пропорциональны, то от нас тре- требуется соблюдение двух условий вместо трех. В результате появляется "лишняя" степень свободы, которую можно использовать для удовлетворения других условий. Эту идею можно распространить и на производные высших порядков, т.е. рассматривать классы параметриче- параметрической непрерывности С" и геометрической непрерывности G". Отметим, что форма кривой, обладающей геометрической непрерывностью класса G\ за- зависит от коэффициента пропорциональности длин касательных к сегментам в точке сопряже- сопряжения. На рис. 10.15 показано, что форма сегментов кривых, совпадающих в конечных точках и имеющих в этих точках пропорциональные векторы касательных, довольно существенно от- 8Класс G0 геометрической непрерывности предусматривает выполнение тех же условий, что и класс С° параметрической непрерывности. 434 Глава 10. Кривые и криволинейные поверхности
личается. Это свойство часто используется в графических программах вычерчивания, в кото- которых пользователю предоставляется возможность регулировать значение коэффициента про- пропорциональности векторов касательных и таким способом настраивать желаемую форму вычерчиваемой кривой. В других приложениях, в част- частности связанных с анимацией, составные кривые задают траекторию движения объектов, и в таком случае геометрическая непрерывность класса G1 оказывается явно недостаточной (см. упр. 10.11). Рис. 10.15. Влияние длины векторов касательных на форму сегмента поли- полиномиальной кривой q'@) Р'@) 10.6. Кривые и поверхности в форме Безье Сравнение кривых в форме Эрмита и в форме интерполяционного полинома чем-то напомина- напоминает сравнение яблока и апельсина. Обе формы представляют кубические полиномиальные кривые, но для их формирования используют разные по характеру наборы данных. Следовательно, нельзя сравнить кривые в этих формах, сформированные на одном и том же наборе данных. Попробуем использовать один и тот же ансамбль опорных точек и для определения интерполяционного поли- полинома, и для косвенного задания производных в форме Эрмита. В результате получим кривую в форме Безье (Bezier), которая является очень хорошим приближением кривой в форме Эрмита и которую можно сравнивать с интерполяционным полиномом, сформированным на том же ансамб- ансамбле опорных точек. Кроме того, поскольку определение кривой в форме Безье не требует задания производных, такая процедура идеально подходит для интерактивного построения криволинейных объектов в системах компьютерной графики и автоматизации проектирования. 10.6.1. Кривые Безье Рассмотрим ансамбль из четырех опорных точек р0, рь р2 и р3. Зададимся условием, что конечные точки формируемой кривой р(м) должны совпадать с опорными точками р0 и р3: Ро= Р@)> Рз = Р(О- Безье предложил использовать две другие опорные точки р, и р2 не для интерполяции, а для задания производных в крайних точках сегмента н=0 и н=1. Мы воспользуемся для этого линейной аппроксимацией в пространстве параметра (рис. 10.16): p'@)s=PLZPi = 3(Pl-p0), /3 Ро* .р3 Применив эту аппроксимацию к касательным в двух крайних точках к па- =uy раметрической полиномиальной кривой p(w)=uyc, получим два условия: Рис. 10.16. Аппрок- Аппроксимация век- векторов каса- касательных Зрз-3р2 = с,+2с2+3с3. Добавим их к уже имеющимся условиям совпадения кривой в конечных точках с р0 и р3: Ро = с0, Рз = Со+с,+с2+с3. 10.6. Кривые и поверхности в форме Безье 435
Итак, мы снова получили три набора по четыре уравнения относительно четырех неизвест- неизвестных в каждом. Решая их по той же методике, что и в разделе 10.5.1, получим с = Мдр, где Мв называется базисной матрицей Безье (Bezier geometry matrix)9. Матрица Мй имеет вид 10 0 0" -3 3 -1 3 -6 3 0 3 -3 0 0 1 Следовательно, кубический полином Безье определяется соотношением p(u) = u'Mfip. Эту формулу можно использовать точно так же, как и аналогичную формулу для составной кривой, сегменты которой являются интерполяционными полиномами. Если имеется ансамбль опорных точек po,.-.,P;i, то первую четверку р0, рь р2 и р3 будем использовать для определения пер- первого сегмента составной кривой, конечную точку первого сегмента р3 и следующие три опорные точки р4, р5 и р6 — для определения второго сегмента и т.д. Очевидно, что составная кривая, по- построенная по методу Безье на произвольном ансамбле опорных точек, относится к классу С0 пара- параметрических непрерывных кривых, но требованиям класса С1 она не удовлетворяет, поскольку ка- касательные справа и слева от точки сопряжения аппроксимируются по разным формулам. Важные свойства кривых Безье станут более понятными после анализа функций смешива- смешивания для этого класса кривых (рис. 10.17). Можно представить кривую в виде р{и) = Ь(мOр, где ЗмA-мJ Зи2A-«) Эти четыре полинома являются частными случаями полиномов Бернштейна {Bernstein polynomials): bu{u)= ,d- ч икA-и)"~к. u k\(d-k)\ K ' Полиномы Бернштейна обладают рядом интересных свойств. Во-первых, все нули полинома располагаются либо в точках и=0, либо в точках и=\. Следовательно, каждая полиномиальная функция смешивания удовлетворяет условию 0 < bjj(u)npn 0<м<1. Поскольку полиномиаль- полиномиальные функции не имеют нулей на интервале представления параметра, все они достаточно гладкие. Несложно показать, что на этом интервале полиномы Бернштейна удовлетворяют условиям (см. упр. 10.4) Иногда в литературе можно встретить название этой матрицы 'базисная матрица кубической кри- кривой Безье ". — Прим. ред. 436 Глава 10. Кривые и криволинейные поверхности
Учитывая эти свойства полиномов Бернштейна, можно утверждать, что форма Безье кубиче- кубической полиномиальной кривой является выпуклой суммой: 0.8 0.6 0.4 0.2 0.2 0.4 0.6 0.8 Рис. 10.17. Полиномиальные функции смешивания для кривых в форме Безье Следовательно, кривая р(м) должна лежать внутри выпуклой много- многоугольной оболочки, образованной четырьмя заданными опорными точ- точками, как показано на рис. 10.18. Таким образом, оказывается, что хотя полиномиальная кривая Безье и не проходит через все заданные опорные точки, она никогда не выходит за пределы области, ограниченной этими точками. Эти два свойства кривых Безье, к которым следует еще присо- присовокупить и возможность работать при конструировании таких кривых только с координатами опорных точек, определили широкое распро- распространение этого класса кривых в практических приложениях компью- компьютерной графики и в системах автоматизации проектирования. Пользова- Пользователь может задать расположение четырех опорных точек, получить эс- эскиз кривой, построенной на их основе, а затем, перемещая опорные точки, "подгонять" форму кривой. 10.6.2. Порции поверхности в форме Безье Порции поверхностей Безье (Bezier surface patches) можно сформировать с помощью функций смешивания. Если Р-[р/;] — массив опорных точек с размерами 4x4, то соответст- соответствующая порция поверхности в форме Безье описывается соотношением Рис 10.18. Выпук- Выпуклая оболочка и по- полиномиальная кри- кривая Безье Порция поверхности проходит через угловые точки р00, рОз, Рзо и Рзз и не выходит за пределы выпуклого многогранника, вершинами которого являются опорные точки (рис. 10.19). Двена- Двенадцать опорных точек из 16 можно интерпретировать как данные, определяющие направление производных по разным параметрам в угловых точках формируемой порции поверхности. Рассмотрим, например, угол порции поверхности, соответствующий значениям парамет- параметров m=v=0. Можно рассчитать функцию р(м, 0) и первые частные производные от р(м, v) в этой точке следующим образом: 10.6. Кривые и поверхности в форме Безье 437
р@,0) = Ро |E(O,O) = 3(plo-Poe), dudv = 9(роо-рО1+р1О-р11). Первые три соотношения являются очевидными расширениями граничных условий, которые были сформулированы при анализе кривых Безье. Четвертое соотношение можно рассматри- рассматривать как меру оценки тенденции порции поверхности в этой угловой точке к изгибу, отклоне- отклонению от плоской формы — кручению (ftvist). Рассматривая четырехугольник, вершинами ко- которого являются р00, рю, р,, и роь придем к выводу, что кручение будет равно нулю только в том случае, если все точки лежат в одной плоскости. На ил. 8 цветной вклейки показано изо- изображение рельефа, которое сформировано с использованием порций поверхностей Безье, по- построенных на основании массива данных об уровнях опорных точек рельефа. Рис. 10.19. Порция поверхности Безье Рис. 10.20. Кручение в угловой точке порции поверхности Безье 10.7. Кубические В-сплайны Кубические кривые Безье и порции кубических поверхностей Безье довольно широко ис- используются в задачах компьютерной графики и автоматизации проектирования. Но объекты этого класса имеют одно принципиальное ограничение. В точках сопряжения составных кри- кривых или поверхностей обеспечивается выполнение условий параметрической непрерывности только класса С0. Если, например, использовать составную кривую в форме Безье для по- построения шпангоутов фюзеляжа планера (эту задачу мы рассматривали в разделе 10.2), вряд ли ее гладкость удовлетворит конструктора. Создается впечатление, что мы достигли предела возможностей бикубического представ- представления кривых и поверхностей и дальнейшие перспективы связаны либо с повышением поряд- порядка полиномов, либо с более частым размещением опорных точек. Но, оказывается, существу- существует и третий путь совершенствования методики построения кубических кривых— нужно только отказаться от требования, чтобы формируемая кривая проходила через опорные точ- точки, и согласиться, чтобы она проходила близко к ним. При этих условиях оказывается до- довольно просто обеспечить непрерывность не только самой составной кривой, но и ее первой и второй производных в точках сопряжения сегментов. 10.7.1. Кубические В-сплайны (пример) В этом разделе мы рассмотрим один из частных случаев в В-сплайновой кривой и пока- покажем, как обеспечить непрерывность класса С2 в точках сопряжения сегментов. В разделе 10.8 438 Глава 10. Кривые и криволинейные поверхности
будет рассмотрен более общий подход к формированию сплайновых кривых, который вклю- включает кривые Безье как частный случай. Рассмотрим четыре последовательные опорные точки {р,_2, рм, р„ р,+|'}, "выдернутые" из достаточно длинной последовательности. Раньше мы формировали на основе четырех точек такую кубическую кривую, которая при изменении значения параметра и от 0 до 1 "пробегала" расстояние от р,_2 до р,+ь причем проходила через точки р,_2 и р,+,. Теперь же ос- оставим только первое условие — по мере того как параметр // изменяется от 0 до 1, мы долж- должны сформировать участок кривой между промежуточными точками, соответствующими двум средним из заданного набора (рис. 10.21). По этому же принципу при обработке набора {р/3, р,-?, Рм, Р/} строится сегмент между р,_2 и р,_ь а при обработке набора {р,-ь р„ Р/+ь Р/+г} — между точками р, и p/+t. Пусть р(м) — сегмент со- составной кривой между точками p,_i и р„ a q(w) — сегмент кри- кривой слева от него, между точками р/_2 и рм. Можно сопоста- сопоставить характеристики кривой в точке р@) с характеристиками в точке q(l). Используя принятые ранее обозначения, будем ис- искать матрицу М, которая обеспечит формирование искомого полинома в форме р(г/) = и7Мр, где р есть матрица опорных точек: Р-2 Р-1 Р, Рис. 10.21. Четыре опор- опорные точки, опреде- определяющие сегмент В- сплайна Р = Аналогичное соотношение можно записать и для сегмента q(«): q(//) = u Mq, где Р-з Р, 2 Р, -I Р- В принципе можно было бы выписать набор ограничений, накладываемых на характеристики точки р(О') при их сопоставлении с характеристиками точки q(l), и такие же ограничения на характеристики точки р( 1) при их сопоставлении с начальной точкой сегмента справа от не- него. Например, ограничение p(O) = q(D обеспечивает непрерывность составной кривой в точке сопряжения, но не требует, чтобы со- составная кривая проходила через какую-либо опорную точку. Это ограничение представляет собой одно из уравнений системы, решением которой являются элементы матрицы М. Оче- Очевидно, что можно придумать довольно много разных ограничений и получить в результате избыточную систему уравнений. Выбирая то или иное множество уравнений из этой избы- избыточной системы, можно вычислить разные значения элементов матрицы М. Можно ограничить пространство перебора и вычислить элементы матрицы для наибо- наиболее распространенного сочетания ограничений. Этот вариант строится исходя из того, что 10.7. Кубические В-сплайны 439
нам нужно использовать симметричную аппроксимацию в точке сопряжения сегментов. Следовательно, любая формулировка ограничений на характеристики в точке q(l) не мо- может использовать опорную точку р^з, поскольку она не может появиться в уравнении, ко- которые мы выпишем для р(м). Точно так же, нельзя использовать р,+, в любом уравнении, составленном для характеристик точки р@). Этим условиям симметричности будут соот- соответствовать два уравнения: p@) = q(l)=4(p(._,+4pbI+p,.), о р'@)=ч'A)Л(р,-р,_2). Если выразить р(и) через массив коэффициентов с в виде р(м) = и'с, то сформулированные условия примут вид Со=т(Р,-2+4р,.,+р,.)) о с.=-(Р,--Р/-2)- Симметричные условия можно сформулировать для точки рA): рA) = с0 +с, +с2 +с3 =-(р,_, +4р, +р/+1), о р'A) = с1+2с:+Зс3=^(р,+1-р,.1). Теперь в нашем распоряжении имеется система четырех уравнений для коэффициентов с, кото- которую можно решить, зная матрицу Ms— базисную матрицу В-сплайна (B-spline geometry matrix): 14 10' 1 ~6 -3 3 -1 0 -6 3 3 3 -3 0 0 1 Значения элементов матрицы M.s определяют несколь- несколько интересных свойств полинома. Их несложно вывес- вывести, обратившись опять к полиномиальным функциям смешивания: A-йK 4-6м2+Зм3 Ь,(и) Ъ(и) = Графики этих функций показаны на рис. 10.22. Как и в риСш jo.22. Функции смешивания для случае с формой Безье, полиномиальные функции сме- кубических В-сплайнов шивания обладают следующими свойствами: и 0 < bj{u) < 1 на интервале 0 < и < 1. 440 Глава 10. Кривые и криволинейные поверхности
Следовательно, сформированный сегмент кривой должен лежать в границах, заданных выпуклой оболочкой из опорных точек, как показано на рис. 10.23. Обратите внимание, что сегмент перекрывает только часть диапазона изменения параметра в пределах оболочки. Мы формулировали ограничения таким образом, чтобы составная кривая принадлежала к классу непрерывности С1, но фактически она относится к классу непрерывности10 С2. Это несложно проверить, вычислив значение р"(м) в точках и=0 и и=\. Вы увидите, что эти значения одина- одинаковы, а следовательно, сегменты справа и слева от точки сопряжения имеют разные значения вторых производных. Это свойство сплайновых кривых делает их особенно привлекательны- привлекательными для задач конструирования в интерактивном режиме. Если по одному и тому же ансамблю опорных точек формиро- формировать сплайновую кривую, кривую Безье и составную интерполя- интерполяционную кривую, то в первом случае приходится выполнить поч- почти в три раза больше вычислений, чем в двух других. Это объяс- объясняется тем, что за один цикл вычислений при аппроксимации кривой сплайном формируется сегмент длиной, соответствующей расстоянию между соседними опорными точками, а во втором и третьем случаях длина сегмента в три раза больше. Рз 10.7.2. В-сплайны и базисные функции Рис. 10.23. Выпуклая обо- оболочка сегмента сплайновой кривой Попробуем взглянуть на формируемую кривую с несколько иной точки зрения — какое влияние оказывает на форму кривой каждая отдельная опорная точка. Каждая опорная точка принимается во внимание при формировании четырех последовательных сегментов состав- составной В-сплайновой кривой. Это свойство гарантирует локальный характер кривых этого клас- класса, т.е. изменение положения отдельной точки влияет на поведение составной кривой в дос- достаточно малой окрестности этой точки. Рассмотрим опорную точку pj. На интервале между н=0 и и=\ координаты этой точки являются весовыми коэффициентами для полиномиальной функции смешивания Ь2{и). Они также учитываются и при формировании сегмента q(u) сле- слева, где эти координаты являются весовыми коэффициентами функции смешивания b^u+l), — для этого сегмента нам придется сдвинуть значение параметра и на 1. Общий взнос отдельной опорной точки можно выразить в виде 5,(")Р» где В,{и) имеет вид 0 bo(u + 2) 0((м + 1) bj\M) Ьъ(и-\) 0 при при при при при при и < i - 2, |-2<и<1-21 / -1 < и < /, / < и < i +1, / +1 < м < / + 2, и > i + 2. График этой функции показан на рис. 10.24. Задавшись множеством опорных точек р0,...,р можно выразить составной сплайн в виде единого выражения": Из этого выражения следует, что каждый член множества функций B(u-i) является сдвину- сдвинутым образом единственной функции и что такое множество образует базис для всех кубиче- 10Если вы посчитаете условия класса С2 слишком жесткими, то можно обеспечить выполнение условий только класса G2 и получить дополнительную степень свободы при конструировании кривых такого типа. Подробнее этот вопрос рассматривается в работе Барского (Barsky) [Bar83]. "Условия для начального и конечного сегментов составного сплайна мы рассмотрим в разделе 10.8. 10.7. Кубические В-сплайны 441
ских В-сплайновых кривых. Задавшись ансамблем опорных точек, мы формируем кусочно- полиномиальную кривую р(м) на всем интервале представления параметра в виде линейной комбинации отдельных базисных функций. На рис. 10.25 показаны кривая р(и) и базисные функции, которые входят в состав взвешенной суммы. В теории сплайнов общего вида, кото- которую мы кратко рассмотрим в разделе 10.8, эта идея распространяется на полиномы более вы- высокой степени и вариант аппроксимации, когда разные сегменты составной сплайн-кривой представлены полиномами разной степени. Р/-2 /—2 / — 1 / / +1 / + 2 Рис. 10.25. Аппроксимация кривой взве- взвешенной суммой базисных функций / - 2 / - 1 i / + 1 i + 2 Рис. 10.24. Базисная функция сплайна 10.7.3. Сплайновые поверхности В-сплайновая поверхность является таким же расширением В-сплайновых кривых, как поверхность Безье является расширением кривых Безье. Используя функции смешивания В- сплайновых кривых, получим уже знакомое вам описание порции поверхности: ;=0 j=0 Отличие его от аналогичного соотношения для поверхности Безье заключается в том, что оно справедливо только для порции, расположенной около центрального четырехугольника массива опорных точек (рис. 10.26). На расчет поверхности, соответствующей всему массиву опорных точек, уйдет в 9 раз больше времени, чем в случае Безье- поверхности. Но зато на том же множестве опорных то- чек удается построить гораздо более плавную поверх- поверхность, чем при использовании формы Безье. Рис. 10.26. Порция сплайновой поверхности 10.8. Обобщенные В-сплайны Предположим, что имеется ансамбль опорных точек р0,.-, р«,- В общем виде задача ап- аппроксимации формулируется как поиск функции р(и)=[х(и) у\и) z(u)}', определенной на ин- интервале мт1п < и < мтах, которая является достаточно гладкой и проходит в определенном смысле достаточно близко к опорным точкам. Предположим, что имеется множество значе- значений {ик}, называемых узлами (knots), такое, что Wmin = W0 ^ "I ^ ... ? U,, = Wmax. 442 Глава 10. Кривые и криволинейные поверхности
Последовательность и0, ии..., и„ будем называть массивом узловп. При использовании ап- аппроксимации сплайнами функция р(м) имеет вид полинома степени d на интервале между со- соседними узлами: Следовательно, для того чтобы определить сплайн степени d, потребуется определить n(d+\) трехмерных коэффициентов с,*. Необходимые для этого уравнения можно получить, рассмат- рассматривая разного рода ограничения, связанные с непрерывностью функции и критерием близо- близости к опорным точкам. Например, если d-З, то мы имеем дело с кубическим полиномом на каждом интервале, т.е. при данном п нужно сформулировать An ограничений. Существует п-\ внутренний узел, и, формулируя ограничения, связанные с непрерывностью класса С2 в этих узлах, получим Зя-3 уравнения. Если же требуется, чтобы функция еще и проходила через п+\ опорную точку, то общее число уравнений составит 4п-2. Остается сформулировать еще два огра- ограничения (получить еще два уравнения). Их можно получить, задавшись, например, опреде- определенным направлением касательной к формируемой кривой в начальной и конечной точках. Такой подход к формированию сплайна является глобальным — нужно решить систему из An уравнений относительно An неизвестных, а значит, каждый полученный коэффициент бу- будет зависеть от всех опорных точек. Поэтому, хотя такая методика определения коэффициен- коэффициентов сплайна и обеспечит получение гладкой кривой, проходящей через заданные опорные точки, она не очень хорошо согласуется со спецификой задач компьютерной графики и авто- автоматизации проектирования. 10.8.1. Рекурсивно определенные В-сплайны Подход, который выбран для формирования В-сплайнов, состоит в том, чтобы определять сплайн в терминах множества базисных функций (или, что то же самое, функций смешива- смешивания), каждая из которых отлична от нуля только на интервале в несколько узлов. Следова- Следовательно, можно записать функцию р(м) в виде где каждая функция B,j(u) есть полином степени d на интервале в несколько узлов и равна нулю за пределами интервала (и, min, щ тах). Само название В-сплайн происходит от basis spline (базисный сплайн), т.е. отражает тот факт, что множество функций {Bi(j(u)} образуют базис для данной последовательности узлов и заданной степени полинома. Существует много способов определения базисных функций, но особое место принадлежит одному из них, который получил название метода рекурсивных функций Кокса-де Бура (Cox-deBoor recursion)^: [l, если uk <//<Mi+1, 0 - в противном случае; 12Во многих работах эта последовательность именуется вектором узлов ficnot vector/ но использование в данной книге такой терминологии может внести определенную путаницу, поскольку ранее мы договорились называть вектором направленный отрезок. ИЭта формула также известна как рекурсивная формула де Кастельо fdeCasteljau recursion/ 10.8. Обобщенные В-сплайны 443
Каждая функция из первого множества, В^, постоянна на одном интервале и равна нулю за его пределами; каждая функция из второго множества, Вки линейна на двух интервалах и равна нулю за их пределами; каждая функция из третьего множества, Вк2, имеет вид квадра- квадратичной кривой на трех интервалах и равна нулю за их пределами и т.д. (рис. 10.27). В общем случае функция из множества Bkd имеет отличное от нуля значение на d+1 интервалах между щ и uk+d+\ и представляет собой полином степени d на каждом из этих интервалов, причем в узлах обеспечивается параметрическая непрерывность класса С7. Сплайн-кривая, образо- образованная как взвешенная сумма таких базисных функций, будет лежать внутри выпуклой мно- многоугольной оболочки, поскольку для базисных функций выполняется условие Но поскольку каждая функция Bid отлична от нуля только на d+\ интервалах, то каждая опор- опорная точка оказывает влияние только на тот участок суммарной кривой, который лежит внутри оболочки, образованной d+\ опорной точкой. Рис. 10.27. Три первые базисные функции Кокса-де Бура Множество базисных функций В-сплайна определяется заданной степенью сплайна и мас- массивом узлов. Учтите, что, поскольку для определения сплайна в диапазоне от и0 до м,м необ- необходимо располагать рекурсивными функциями, отличными от нуля в узлах от м0 ДО ч„ л нам понадобятся d-\ "дополнительных" значений в узлах. Эти дополнительные значения опреде- определяются ограничениями, которые можно наложить на характеристики результирующей В- сплайновой кривой в начальной и конечной точках. Обращаю также ваше внимание и на то, что мы не накладываем никаких ограничений на значения в узлах, кроме как ик < ик+1. Если считать, что результат деления 0/0 при вычислении по рекурсивным формулам равен 1, то мы можем иметь повторяющиеся, или кратные, узлы. При постоянном шаге между узлами сформированный В-сплайн называется равномерным (uniform spline), но можно обеспечить большую гибкость при формировании кривых, если позволить не только неравномерное размещение узлов, но и наличие кратных (ик = м*+1) узлов. Кратко проанализируем открывающиеся при этом возможности. 10.8.2. Равномерные В-сплайны Рассмотрим последовательность равноотстоящих узлов {0,1,2,...,/?}. Кубический В- сплаин, который был рассмотрен в разделе 10.7, можно построить на основе формул Кокса- де Бура на равноотстоящих узлах. Будем использовать нумерацию узлов, принятую в форму- формулах Кокса-де Бура. Участок сплайн-кривой между узлами к и к+\ будем формировать по подмножеству опорных точек р*_ь рк, рк+], р*+2. Следовательно, получим кривую, определен- определенную только на интервале от и=\ до и-п-\. Для опорных точек, расположенных так, как пока- показано на рис. 10.28, мы построим кривую, которая не пересекает узлы. В некоторых случаях (один из них показан на рис. 10.29) удается использовать периодичность в расположении опорных точек и построить сплайн, который проходит через эти точки. Такой равномерный периодический В-сплайн {uniform periodic B-spline) обладает тем свойством, что каждая ба- базисная функция представляет собой сдвинутый образ единственной функции. 444 Глава 10. Кривые и криволинейные поверхности
•Pi /ft 28. Равномерный В-сплайн Рис 10.29. Периодиче- Периодический равномерный В-сплайн 10.8.3. Неравномерные В-сплайны При повторении узлов (кратности узлов) проявляется эффект приближения сформирован- сформированного В-сплайна к опорной точке, ассоциированной с этими узлами. Если кратность узла равна d+1, то В-сплайн степени d будет проходить через соответствующую опорную точку. Следо- Следовательно, один из методов решения проблемы недостатка данных для формирования сплайна состоит в том, чтобы повысить кратность узлов, соответствующих начальной и конечной точкам, и тем самым "заставить" результирующую кривую пройти через эти точки. При этом остальные, внутренние, узлы остаются равноотстоящими. Такие В-сплайны называются от- открытыми (open splines). Для формирования кубических В-сплайнов часто используется массив узлов {0,0,0,0, \,2,...,п-\,п,п,п,п}. Особый интерес представляет массив узлов {0,0,0,0, 1,1,1,1}, который превращает В-сплайн в кривую Безье. В общем случае можно делать кратными и внут- внутренние узлы, а также размещать их неравномерно. 10.8.4. NURBS - неравномерный рациональный В-сплайн Анализируя В-сплайны, мы полагали, что р(м) представляет собой массив триад [х{и) у(и) z(u)]T. При работе в двухмерном пространстве триада заменяется парой [х(и) у(и)]', но все выведенные соотношения остаются неизменными. Точно так же соотно- соотношения не изменятся и в том случае, если мы перейдем к четырехмерным В-сплайнам. Трех- Трехмерные опорные точки р,=[дг/ yt z,] можно следующим образом представить в пространстве однородных координат: xi V, 1 Идея состоит в том, чтобы использовать коэффициенты w, для увеличения или уменьшения "веса" конкретной опорной точки в ансамбле. Эти взвешенные опорные точки можно исполь- использовать при формировании четырехмерного В-сплайна. Первые три компонента полученного сплайна будут представлять собой обычное В-сплайновое представления взвешенных опор- опорных точек: х(и) У(и) 10.8. Обобщенные В-сплайны 445
Компонент w представляет собой скалярный полиномиальный В-сплайн, сформированный по множеству значений весовых коэффициентов: При использовании четырех параметрических сплайнов в качестве составляющих набора од- однородных координат значение м> может оказаться не равным 1, и, следовательно, нужно будет при переходе к трехмерному представлению точек использовать перспективное деление: () 00 ^ р(/<) Я00 ^• ;=0 Каждый компонент функции р(«) представляет собой рациональную функцию параметра и, и по- поскольку мы не накладывали никаких офаничений на расположение узлов, то эта функция относит- относится к классу неравномерных рациональных В-стайнов (NURBS nonuniform rational B-spline). NURBS-кривые сохраняют все свойства, присущие трехмерным В-сплайнам, в частности Офаниченность выпуклой оболочкой и непрерывность, но дополнительно к ним обладают и еще двумя уникальными свойствами, которые представляют особый интерес для задач ком- компьютерной графики и автоматизации проектирования. Если по отношению к В-сплайновой кривой или поверхности применить аффинное пре- преобразование, то получим ту же функцию, что и В-сплайн, сформированный по исходному ан- ансамблю опорных точек, подвергшихся тому же преобразованию. Поскольку перспективное преобразование не относится к классу аффинных, большинство геометрических объектов, со- состоящих из В-сплайновых кривых, будет искажаться при их выполнении, но как раз NURBS- кривые будут обрабатываться при таких преобразованиях корректно. Квадратичные поверхности обычно определяются аналитически в неявной форме. Используя нерациональные сплайны, можно только приближенно представить такие по- поверхности. Однако можно показать, что аналитические квадратичные кривые являются частным случаем квадратичных NURBS-кривых. Следовательно, можно использовать единый метод моделирования, основанный на формировании NURBS-кривых, для соз- создания практически всех распространенных типов криволинейных геометрических объек- объектов (см. упр. 10.14 и 10.15). 10.9. Построение кривых и поверхностей Если уж в состав сцены включены объекты, состоящие из кривых и поверхностей, нам не обойтись без методов их воспроизведения. Существует несколько подходов к выполнению подобной процедуры. Первый подход— можно вычислить точки пересечения с таким объек- объектом лучей, исходящих из центра проецирования и проходящих через определенные пиксели картинной плоскости. Однако вычисление пересечений с криволинейными объектами, если только они не представляют собой квадратичные поверхности (о них речь пойдет в разде- разделе 10.11), требует решения нелинейных уравнений, что не так-то просто сделать, учитывая, что все вычисления следует выполнять в реальном масштабе времени. Второй подход состо- состоит в том, чтобы вычислить массив вершин, принадлежащих криволинейному объекту, и по- построить на основе этого массива приближение криволинейного объекта множеством плоских из стандартного набора. Мы сосредоточим внимание именно на втором подходе и рассмот- рассмотрим, как на его основе воспроизводить в графической системе параметрические полиноми- полиномиальные кривые и поверхности. 446 Глава 10. Кривые и криволинейные поверхности
10.9.1. Методы вычисления полиномов Рассмотрим полиномиальную форму задания кривой на стандартном интервале: Можно вычислить значения р(г/) для некоторого множества значений параметра {и*} и затем использовать кусочно-линейную аппроксимацию кривой (в OpenGL это делается с помощью примитива типа GL_LINE_STRIP). Вместо того чтобы вычислять независимо каждый член, со- содержащий и*, их можно сгруппировать следующим образом: р(м) = со+м(с,+м(с2+м(...+с„м))). После такой группировки для вычисления значения полинома р(ик) потребуется только п ум- умножений. Этот алгоритм известен под названием схемы Горнера {Homer's method). Типовой кубический полином р(м) группируется по этой схеме следующим образом: P(m) = Co+w(c1+m(c2+wc3)). Если отсчеты значения параметра {«,} распределены равномерно на заданном интервале представления полинома, то для вычисления р(м*) можно использовать метод правых разно- разностей {forward differences), который требует О(п) сложений и ни единого умножения. Этот ме- метод реализуется следующей итерационной процедурой: АП)Р("*) Если uit+\-uirh , то несложно показать, что для полинома р(м) степени п значение Д(';)р(м*) будет постоянным при любом к. Из этого результата следует вычислительная схема, вариант которой для скалярного кубического полинома представлен на рис. 10.30: р{и)= \+Зи+2и2+и\ Для вычислений по этой схеме значения А^р(и0) требуется знать первые п+\ значений функции p(Uk). Располагая значением Д(//)р(м0), можно скопировать его в ячейки таблицы и ра- работать дальше по схеме, представленной на рис. 10.31. Последующие значения р(ик) вычис- вычисляются по рекуррентной формуле, полученной перестановкой членов в приведенном выше соотношении: Этот метод довольно эффективен, но, к сожалению, его можно применять только на равно- равномерной сетке, и он склонен к накоплению ошибок вычислений. t p A% 4<2>p A™P 0 1 1 6 10 6 1 7 у 1 16 У I 16 у 1 6 2 23 / 1 32 / 1 22 // 1 6 3 55 у 1 / 1 28^ 4 109 у \ у 5 191 Рис. 10.30. Построение таблицы правых раз- разностей t Р Л% А^р 0 1 6 10 6— 1 7 16 16— —¦б — 2 23 32 > у* — 22 - —*6 3 55 — г54 — г28 4 ¦ 109—> —¦ 82 5 191 jt Рис. 10.31. Вычислительная схема, использую- использующая таблицу правых разностей 10.9. Построение кривых и поверхностей 447
10.9.2. Рекурсивное разбиение кривых Безье Пожалуй, наибольшее распространение в компьютерной графике получил метод рекурсив- рекурсивного разбиения полиномиальных кривых Безье. В основе метода лежит использование выпуклой многоугольной оболочки, причем в процессе вычислений по этому методу никогда не прихо- приходится в явном виде вычислять значения полиномов. Пусть имеется описание кубической кривой Безье (метод пригоден и для обработки кривых Безье более высокой степени). Известно, что кривая должна быть расположена в области, ограниченной выпуклой многоугольной оболочкой, построенной на опорных точках, как на вершинах. Разделим кривую на два сегмента 1(и) и г(м). Каждый сегмент имеет область определения, равную половине исходного интервала. Поскольку исходная кривая кубическая, то и каждый из сегментов является кубической кривой. Учтите, что поскольку каждый сегмент имеет область определения, равную половине исходного интервала, то придется изменить масштаб представления параметра и для 1 и г таким образом, чтобы на каждом сегменте и изменялся в диапазоне @, 1). Сегмент 1(и) представляет левую половину р(м), а сегмент г(м)— правую. Каждый из сегментов имеет четыре опорные точки, которые, во- первых, определяют его форму, а во-вторых, служат вершинами выпуклой многоугольной обо- оболочки. Обозначим эти два множества опорных точек {10,1Ь 12,13} и {г0, г,, г2, г3}. Исходная кри- кривая р(м) имеет ансамбль опорных точек {р0, рь р2, р3}. Все три ансамбля и кривая р(м), разбитая на сегменты \(и) и г(м), показаны на рис. 10.32. Обратите внимание на то, что выпуклые оболочки для 1 и г всегда будут лежать внутри выпуклой оболочки для р, что является следствием из свойст- свойства уменьшения вариации (variation-diminishing property), которым обладают кривые Безье. Рассмотрим левый сегмент. Можно выяс- выяснить, насколько многоугольник оболочки яв- является выпуклым, измерив расстояние между точками 1| и 12 и отрезком, связывающим 10 и 13. Если расстояние не превышает некоторой наперед заданной величины, то вместо криво- криволинейного сегмента l(w) можно провести пря- прямую. В противном случае можно разделить сегмент 1(и) еще на пару сегментов и проана- проанализировать их выпуклость. В результате полу- получилась рекурсивная процедура, которая на первый взгляд не требует вычисления значений полинома. Но у читателей, естественно, должен возникнуть вопрос, а откуда мы возьмем дан- данные о положении опорных точек {10,1ь 12,13} и {г0, гь г2, г3} сегментов кривой после разбие- разбиения, которые одновременно являются и вершинами многоугольных выпуклых оболочек этих сегментов. Ниже будет показано, как отыскать эти вершины для левого сегмента 1(г/), а про- процедура для правого сегмента будет симметричной (в смысле индексов точек). Начнем с пред- представления кривой Безье, знакомого нам по разделу 10.6.1: Ро Рис. 10.32. Выпуклые оболочки и опорные точ- точки кривой Безье и ее сегментов р(н)=игМв Ро р, LP3J где 1 0 0 0 -3300 3-630 -13-3 1 448 Глава 10. Кривые и криволинейные поверхности
Полином 1(м) должен проходить через точки р@) и рA/2). Следовательно, КО) = 1о=Ро, При м=0 касательная к 1 должна повторять касательную к р, но поскольку параметр и для кривой 1 имеет измененный масштаб по сравнению с параметром и~ кривой р, то, подставляя и~=2и, придем к выводу, что отношение между производными должно быть скорректировано с учетом соотношения dii~=2du. Следовательно, для производных в начальной точке сегмента справедливо соотношение Г(О)=3A1-1о) = р'(О)=|(р,-Ро). Рассуждая аналогично, выведем и соотношение для производных в средней точке р(и~) (она же последняя точка сегмента 1(и)): 1'A) = A3 -1, ) = pji) = |(-Po - р, + р2 + р3) р2 Ро Рис. 10.33. Геометрическая интерпретация процесса разбиения кривой Безье В результате получается четыре уравнения, которые можно решить аналитически. Но су- существует и более наглядная геометрическая интерпретация процедуры разбиения и поиска новых опорных точек (рис. 10.33). Она позво- позволяет параллельно формировать опорные точки для левого и правого сегментов. Сначала вы- выпишем соотношения для крайних опорных то- чек, которые должны совпадать в исходной кривой и сегментах I и г: •о = Ро, г3 = Рз Опорные точки lt и г2 определяются направлением касательных к начальной точке 1 и конеч- конечной точке г: 1,=-(Ро+Р,)' Г2=-(Р:+Рз)- Учет направления касательных в точке сопряжения сегментов определяет еще две опорные ТОЧКИ 12 И Г\\ И, наконец, для точки сопряжения сегментов получим 10.9. Построение кривых и поверхностей 449
Преимущество такого метода определения опорных точек сегментов заключается в том, что он требует только операций суммирования и сдвига (для деления пополам), причем в единой процедуре определяются опорные точки обоих сегментов. 10.9.3. Построение других типов полиномиальных кривых методом разбиения Подобрав подходящий ансамбль опорных точек, любую полиномиальную кривую можно представить не только в форме Безье, но и в форме интерполяционного полино- полинома, В-сплайна и т.д. Эффективность метода разбиения кривых Безье привела к тому, что обычно стараются любые кривые преобразовать в форму Безье. Алгоритмы преобразо- преобразования можно сформулировать, анализируя процесс формирования кривой. Рассмотрим кубическую кривую в форме Безье. Ее можно описать следующим образом с помощью базисной матрицы Безье Мв: р(и) = и7Млр, где р есть матрица ансамбля опорных точек для формирования формы Безье. Ту же самую полиномиальную кривую можно представить и в виде р(н) * u7Mq, где М есть базисная матрица другой формы кривой, a q — матрица соответствующего ан- ансамбля опорных точек. Если полиномы идентичны (речь ведь идет об изменении только формы представления кривой), то, приравняв правые части соотношений, получим В частности, для преобразования из формы интерполяционного полинома в форму Безье матрица пересчета должна иметь вид м-;м, = Для выполнения преобразования из формы кубического В-сплайна в форму Безье необходимо использовать матрицу пересчета в виде 4 10" 0 4 2 0 1 -Уь Уг 0 0 3 -% 0 0 -% 3 0 0 Уг 1 о о 4 2 2 4 О 1 4 На рис. 10.34 показаны четыре опорные точки и построенные по ним кривые в форме Бе- Безье, в форме интерполяционного полинома и в форме В-сплайна. Все три кривые сформи- сформированы средствами построения кривых Безье, имеющимися в составе графической системы OpenGL. Интерполяционная кривая и В-сплайн сформированы как кривые Безье с исполь- использованием матриц пересчета МД"'М/ и М/Г'М.у. Обратите внимание на то, что кривая в фор- форме В-сплайна сформирована только на интервале между двумя средними опорными точка- точками исходного ансамбля. 450 Глава 10. Кривые и криволинейные поверхности
б) Рис. 10.34. Кубические полиномиальные кривые, сформированные как кривые Безье с преобразо- преобразованием исходного ансамбля опорных точек: а — в форме Безье; б — в форме интерполя- интерполяционного полинома; в — в форме В-сплайна. 10.9.4. Разбиение поверхности Безье Описанный выше алгоритм разбиения можно распространить и на построение поверхно- поверхностей в форме Безье. Порция поверхности Безье, представленная на рис. 10.35, сформирована по 16 опорным точкам. Каждые четыре точки в одном и том же столбце или строке задают сегмент кривой Безье, и, следовательно, к ним можно применить алгоритм разбиения. Но применительно к порции поверхности алгоритм должен разбивать ее не на две, а на четыре порции, поэтому модифицируем исходный алгоритм и преобразуем его в двухэтапный. Сначала применим исходный вариант алгоритма к четырем сегментам, заданным шестна- шестнадцатью опорными точками, причем каждый сегмент соответствует определенному значению параметра и. В результате для значений м=0, 1/3, 2/3, 1 сформируются две группы по четыре опорные точки в каждой, причем средние точки будут принадлежать обеим группам. Таким образом, получим новые ансамбли по семь опорных точек вдоль каждого из четырех сегмен- сегментов. На рис. 10.36 эти точки отмечены темными кружочками. Как видно на этом рисунке, все имеющиеся опорные точки (и прежние, и новые) можно разделить на три категории: исход- исходные опорные точки, которые сохранились в новом ансамбле (они залиты серым цветом), но- новые опорные точки, которые появились в результате выполнения разбиения (они отмечены черными кружочками), и те точки исходного ансамбля, которые после разбиения отброшены (они отмечены белыми кружочками). После этого перейдем ко второму этапу. Выполним разбиение семи кривых, образованных новым ансамблем опорных точек, для семи фиксированных значений пара- параметра v. Каждая кривая характеризуется четырьмя опор- опорными точками, каурые в результате применения алго- алгоритма разбиения превращаются в семь (средняя точка ис- используется совместно двумя новыми сегментами). Весь ансамбль опорных точек, образовавшийся после второго этапа выполнения алгоритма, показан на рис. 10.37. Раз- Разделив этот ансамбль на четыре группы по 16 точек в каждой A2 средних точек попадают в две группы, а одна — центральная — во все четыре), получим четыре новые порции поверх- поверхности Безье (рис. 10.38). Рис. 10.35. Порция кубической по- поверхности Безье Рзо * Новые опорные точки, сформированные в результате разбиения О Исходные опорные точки, которые после разбиения отброшены • Исходные опорные точки, которые сохранились в новом ансамбле Рис. 10.36. Первый этап разбиения порции поверхности 10.9. Построение кривых и поверхностей 451
• Новые опорные точки, сформированные в результате разбиения О Исходные опорные точки, которые после разбиения отброшены ® Исходные опорные точки, которые сохранились в новом ансамбле Рис. 10.37. Опорные точки после второго этапа разбиения порции поверхности При выполнении такого двухэтапного алгоритма раз- разбиения анализ "плоскостности" образовавшейся новой порции поверхности будет более сложным, чем при работе с кривой. Поэтому во многих системах разработчики про- просто фиксируют количество циклов разбиения, позволяя пользователю настраивать его программно. Если от систе- системы требуется особо высокое качество построения образа поверхности на экране, разбиение выполняется до тех пор, пока размеры проекции порции поверхности на картинную плоскость не окажутся сравнимы с размерами пикселя. Рис. 10.38. Квадрант порции по- поверхности, образовавшийся после разбиения 10.10. Пример: формирование изображения чайника Анализ методов работы с поверхностями, заданными в параметрической форме, завершим рассмотрением примера использования алгоритма рекурсивного разбиения для формирования изображения объекта с явно выраженной криволинейностью формы. В качестве такового возь- возьмем чайник, который проф. М. Ньюэлл (М. Newell) из университета штата Юта еще лет 20 назад начал использовать дня тестирования эффективности различных алгоритмов построения изо- изображения криволинейных объектов. Этот объект приобрел известность в кругах специалистов и даже получил имя собственное— чайник Юта (Utah teapot). Исходное описание поверхности состоит из опорных точек для построения 32 порций бикубической поверхности Безье. Опорные точки образуют массив из 306 элементов, пронумерованных от 1 до 306. Первые 12 порций об- образуют основной объем чайника, следующие четыре — ручку, а еще четыре — носик. Подроб- Подробнее всего проработана крышка — на нее "ушло" восемь порций, а оставшиеся четыре образуют донышко. Все эти данные широко распространены среди специалистов и часто используются при проверке работоспособности алгоритмов и программ тонирования. Мы будем использовать этот объект для демонстрации применения метода рекурсивного разбиения, пользуясь уже известными вам средствами графической си^^мы OpenGL. Каж- Каждую порцию поверхности будем "пропускать" через п циклов разбиения и после этого фор- формировать изображение по сформированному массиву вершин, используя либо отрезки пря- прямых, либо многоугольники, проведенные через четыре угловые точки сформированной "микропорции". Следовательно, формирование на экране изображения микропорции можно выполнить с помощью следующей функции: void draw_patch(point3 p[4][4j) { glBegin(GL_QUADS); glVertex3fv(p[0][0]); glVertex3fv(p[3][0]); glVertex3fv(p[3][3]); glVertex3fv(p[0][3]); glEnd(); 452 Глава 10. Кривые и криволинейные поверхности
Функцию разбиения порции образуем из функции разбиения сегмента кубической кривой Безье с, в которой используется ранее определенный тип данных point3. void divide_curve(point3 c[4], point3 r[4], point3 1[4]) { int i; point3 t; for(i=0;i<3;i++) Рекурсивную программу разбиения порции поверхности несложно построить в виде по- последовательности вызовов функции divide_curve () и предполагая, что в нашем распоряже- распоряжении имеется функция транспонирования матрицы transpose(). Текст этой программы при- приведен ниже. void divide_patch(point3 р[4][4], int n) { point3 q[4][4], r[4][4], s[4][4], t[4][4]j point3 a[4][4], b[4][4]; int i,j, k; if(n==0) draw patch(p); else for(k=0; k<4; k++) divide_curve(p[k], a[k] transpose(a); transpose(b); for(k=0; k<4; { divide_curve(a[k], q[k], divide_curve(b[k], s[k], } divide_patch(q, n-1); divide_patch(r, n-1); divide_patch(s, n-1); divide_patch(t, n-1); Полный текст программы формирования тонированного изображения чайника Юта вы можете найти на ftp-сервере по адресу ftp.cs.unm.edu в каталоге pub/angel /BOOK. В текст программы включен и массив координат исходных опорных точек, описывающих тесто- тестовый объект. 10.10. Пример: формирование изображения чайника 453
Чайник как программный объект включен в библиотеки GLUT и aux. Но сейчас он нас интере- суег только как средство демонстрации работоспособности и практической реализации алгоритма рекурсивного разбиения. На рис. 10.39 показаны "проволочное" изображение чайника, полученное после выполнения трех циклов разбиения, и закрашенное изображение, сформированное методом заливки каждого многоугольника серым цветом постоянной интенсивности. Обратите внимание на то, что порции имеют различный размер и кривизну. Поэтому использовать одно и то же количест- количество циклов разбиения для разных участков поверхности нерационально — это порождает множест- множество маленьких многоугольников, в которых совершенно нет необходимости. Желательно так орга- организовать процесс разбиения, чтобы для формирования участков с большей кривизной выполнялось больше циклов разбиения, а для относительно плоских — меньше. Рис. 10.39. Изображение чайника Юта Системные средства работы с кривыми Безье в OpenGL, которые мы подробно рассмот- рассмотрим в разделе 10.12, позволяют формировать порции поверхностей при минимальном коли- количестве вызовов функций API. Они выполняют рекурсивное разбиение значительно более эф- эффективно, чем та "доморощенная" программа, которая была приведена выше. 10.11. Алгебраические поверхности Хотя квадратичные кривые и поверхности можно рассматривать как частный случай NURBS-кривых и поверхностей, этот класс криволинейных объектов настолько часто ис- используется в практике автоматизации проектирования и в задачах компьютерной графики, что имеет смысл выделить пару страниц для их анализа. 10.11.1. Квадратичные поверхности Квадратичные поверхности (quadric surfaces) описываются алгебраическими уравне- уравнениями в неявном виде, в которых для каждого члена полинома дг'Уг* выполняется условие i+j+k < 2. Любая квадратичная поверхность может быть описана уравнением вида q(x, y,z) 222 с = 0 . Этот класс поверхности включает эллипсоиды, параболоиды и гиперболоиды. Общее уравнение поверхности можно записать в виде квадратичной матричной формы {quadratic form), используя трехкомпонентную матрицу-столбец р=[х у -]7: p'Ap+b'p+c = О, где А = «II «12 0.3 «2. «22 а,. «3t «32 «33 , b = W b2 A 454 Глава 10. Кривые и криволинейные поверхности
Десять независимых коэффициентов в A, b и с определяют конкретный вид квадратичной поверхности. Однако для классификации можно воспользоваться последовательностью пре- преобразований поворота и сдвига, в результате которой частная поверхность будет приведена к каноническому (стандартному) виду. В трехмерном пространстве такие преобразования мож- можно представить в виде р' = Mp+d. Матрица М представляет собой матрицу поворота, которая преобразует А в диагональную матрицу D=M7AM. Например, уравнение эллипсоида в каноническом виде выглядит так: Все коэффициенты в этом уравнении положительны. Обращаю ваше внимание на то, что по- поскольку при приведении уравнений поверхностей к каноническому виду используются аф- аффинные преобразования, то свойства квадратичных поверхностей сохраняются такими пре- преобразованиями, что хорошо сочетается со свойствами примитивов других типов. 10.11.2. Вычисление точек на квадратичной поверхности методом приведения лучей Вычислить координаты точек на квадратичной поверхности в процессе формирования ее изображения можно с помощью метода приведения лучей. Представим в параметрической форме луч, исходящий из точки р0 в направлении d: р = Подставляя его в уравнение квадратичной поверхности, получим скалярное уравнение отно- относительно переменной а: a2d/Ad+ad7(b+2Apo)+Po/Apo+b7d+c = 0. Это уравнение может не иметь ни одного действительного корня, один или два действитель- действительных корня. Результат решения уравнения можно использовать либо для заполнения буфера кадра, либо как промежуточный результат при выполнении тонирования по методу трасси- трассировки лучей. Кроме того, можно к любой точке квадратичной поверхности применить стан- стандартный алгоритм закрашивания, поскольку, располагая аналитическим выражением поверх- поверхности, несложно вычислить компоненты нормали: п = к Эл- к Эу .эГ. =2Ap-b Такой метод построения может быть распространен на все виды алгебраических поверх- поверхностей. Пусть имеется уравнение поверхности в виде При конвейерной организации процесса тонирования будет последовательно выполняться приведение луча, исходящего из центра проецирования, ко всем пикселям картинной плоско- плоскости. Уравнение каждого луча можно записать в параметрическом виде: p(a) * 10.11. Алгебраические поверхности 455
После подстановки этого выражения в уравнение для q получим неявное полиномиальное уравнение относительно неизвестной а: <7(Р(а)) = 0. Это уравнение можно решить подходящим численным методом или для квадратичных по- поверхностей — аналитически. Если в уравнении имеются члены вида x'yh , то получим не бо- более i+j+k точек пересечения. Отбор среди них единственной может привести к тому, что то- тонирование займет довольно много времени. 10.12. Кривые и поверхности в OpenGL В составе OpenGL имеются средства поддержки работы с кривыми и поверхностями Бе- зье — Безье-вычислитель (evaluator), которые позволяют вычислять значения полиномов Бе- зье любого порядка. Безье-вычислитель не требует, чтобы опорные точки размешались с по- постоянным шагом, а потому позволяет преобразовывать в форму Безье кривые, определенные любым другим из описанных ранее способов. Безье-вычислитель можно использовать для ра- работы с полиномами от одной, двух, трех и четырех переменных. При построении кривых Бе- Безье-вычислитель вычисляет значения полинома одной переменной, что необходимо при фор- формировании карт цветов или траектории движения во временной области персонажей в анима- анимационных приложениях. В библиотеке GLU Безье-вычислитель используется в четырехмерном варианте для поддержки методов обработки NURBS-кривых. Вариант Безье-вычислитель, работающий с полиномами двух переменных, используется в основном при формировании поверхностей. Помимо обработки кривых и поверхностей Безье-вычислитель в OpenGL обес- обеспечивает вычисление цветов, нормалей и координат текстур. 10.12.1. Кривые Безье Функция обработки полинома одной переменной настраивается в процессе инициализа- инициализации OpenGL-программы посредством вызова функции glMaplf(type, u_min, u_max, stride, order, point_array) Аргумент type задает тип объекта, который будет представлен полиномом Безье. Можно назначать в качестве значения этого аргумента константы, задающие трех- и четырехмерные геометрические точки, цвет в формате RGBA, нормали, индексированные цвета и координа- координаты текстур (от одно- до четырехмерных). Указатель на массив опорных точек полинома пе- передается функции через аргумент point_array. Аргументы u_min и u_max определяют об- область существования параметра полинома. Аргумент stride представляет собой количество значений параметра между сегментами кривой. Например, для кубического В-сплайна прихо- приходятся три точки на каждый сегмент кривой. Значение аргумента order должно быть на еди- единицу больше степени полинома. Для формирования кубической трехмерной кривой в форме В-сплайна, определенной на интервале @, 1), функции glMaplf () следует передать такой на- набор аргументов: point data[]={...} glMaplf(GL_MAP_VERTEX_3, 0.0, 1.0, 3, 4, data); Можно в одной и той же программе инициализировать несколько функций расчета поли- полиномов, каждую со своим набором аргументов. Например, могут параллельно работать функ- функции расчета полиномиальной кривой и нормали, аппроксимированной полиномом. После на- настройки функция активизируется посредством вызова glEnable(type); 456 Глава 10. Кривые и криволинейные поверхности
Если функция расчета активизирована, то можно получить от нее значение полинома, вы- вызвав функцию glEvalCoordlf(и); Таким образом, обращение к glEvalCoordlf () может заменить обращение к функциям glVertex(), glColor() и glNormal(). Все зависит от того, как она была настроена при ини- инициализации. Пусть, например, функция расчета настроена на формирование кривой Безье на интервале @, 10) по некоторому массиву опорных точек. Набор из 100 точек кривой, равно- равномерно отстоящих на этом интервале, можно получить с помощью такого фрагмента про- программы: glBegin(GL_LINE_STRIP) for(i=0; i<100; i++) glEvalCoordlf( (float)i/100.0); glEnd(); Если значения параметра и распределены равномерно, то для вычисления точек на кривой следует использовать функции glMapGridlf () и glEvalMeshl (), например: glMapGridlfA00,0.0, 10.0); glEvalMeshl(GL_LINE, 0, 100); После вызова glMapGridlf () устанавливается равномерная сетка в 100 отсчетов, а после вы- вызова glEvalMeshl () будет сформирована кривая. 10.12.2. Поверхности Безье Поверхности Безье формируются в OpenGL примерно по той же методике, что и кривые, только роль функции инициализации играет не glMapl* {), a glMap2*(), а для считывания ре- результатов следует обращаться к функции glEvalCoord2*() вместо glEvalCoordl*(). В обеих функциях нужно специфицировать данные, относящиеся к двум независимым параметрам и и v. Например, функция glMap2f() имеет такой формат вызова: glMap2f(type, ujnin, ujnax, u_stride, u_order, vjnin, vjnax, v_stride, v__order, point_array) Настройка функции вычисления на работу с бикубической поверхностью Безье, опреде- определенной на области @, 1)х@, 1), выполняется таким вызовом glMap2f (): glMap2f(GL_MAP_VERTEX_3, 0.0, 1.0, 3, 4, 0.0, 1.0, 12, 4, data); Для обоих независимых параметров нужно задать порядок полинома (аргументы u_order и v_order) и количество значений параметра между сегментами (аргументы u_stride и v_stride), что обеспечивает дополнительную гибкость при формировании поверхности. Об- Обратите внимание на то, что значение v_stride для второго параметра равно 12, поскольку в массиве опорных точек data данные хранятся по строкам. Поэтому для перехода к следую- следующему элементу той же строки нужно "перешагнуть" три числа в формате float, а для перехо- перехода к следующему элементу в этом же столбце нужно "перескочить" через 3x4=12 чисел в формате float. Способ вызова программы расчета зависит от того, какой результат мы хотим получить, — вывести на экран сеть или сформировать многоугольники для последующего раскрашивания. Если ставится задача сформировать на экране сеть, то соответствующий фрагмент программы должен выглядеть примерно так, как показано ниже: for(j=0; { glBegin(GL_LINE_STRIP); 10.12. Кривые и поверхности в OpenGL 457
for(i=0; glEvalCoord2f( (float)i/100.0, (float)j/100.0); glEnd(); glBegin(GL_LINE_STRIP); for(i=0; i<100; i++) glEvalCoord2f( (float)j/100.0, (float)i/100.0); glEnd(); } Если же желательно сформировать множество многоугольников, то фрагмент должен выгля- выглядеть так: for(j=0; j<99; j glBegin(GL_QUAD_STRIP); for(i=0; i<=100; i glEvalCoord2f( (float)i/100.0, (float)j/100.0); glEvalCoord2f( (float)(i+l)/100.0, (float)j/100.0); } glEnd(); } Для работы на равномерной сетке параметров следует использовать функции glMapGrid2*() и glEvalMesh2(). Тогда в самое начало программы, в ту ее часть, которая отвечает за ини- инициализацию, нужно включить такой фрагмент: glMapGrid2fA00, 0.0, 1.0, 100, 0.0, 1.0); В функции отображения display () нужно вызвать glEvalMesh2(): glEvalMesh2(GL_FILL,0,100,0,100); Но существует еще одна проблема, на которую я хочу обратить ваше внимание. До тех пор, пока в программе не будет установлен режим учета освещения, сформированные много- многоугольники будут закрашены практически одинаково, и на экране на этом месте вы увидите темное пятно вместо выпуклой (или вогнутой) поверхности. Можно установить режим учета освещения способом, описанным в главе 6, но для работы алгоритмов тонирования необхо- необходима информация о направлении нормали в каждой обрабатываемой вершине. Эти нормали можно рассчитать и в прикладной программе, но лучше настроить OpenGL таким образом, чтобы система автоматически формировала их своими системными средствами. Для этого при настройке режима учета освещения нужно дополнительно вызвать функцию glEnable(), передав ей в качестве аргумента константу GL_AUTO_NORMAL: glEnable(GL AUTO NORMAL); 10.12.3. Отображение чайника Юта Для отображения на экране чайника Юта воспользуемся системными функциями обра- обработки поверхностей Безье, имеющимися в составе библиотек OpenGL. Предположим, что информация о вершинах и порциях поверхности считана из файла в массив, объявленный следующим оператором: GLfloat data[32][4][4]; 458 Глава 10. Кривые и криволинейные поверхности
Если планируется сформировать проволочную модель чайника, следует инициализировать сетку в пространстве параметров: void myinit() { glEnable(GL_MAP2_VERTEX_3); glMapGrid2fB0, 0.0, 1.0, 20, 0.0, 1.0); Вычерчивание отрезков выполняется в функции отображения display (): for(k=0;k<32;k++) { glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, idata[k][0][0][0]); for (j=0; j <= 8; j glBegin(GL_LINE STRIP); for (i=0; i <= 30; i++) glEvalCoord2f((GLfloat)i/30.0, (GLfloat)j/8.0); glEnd(); glBegin(GL_LINE_STRIP); for (i=0; i <= 30; i++) glEvalCoord2f((GLfloat)j/8.0, (GLfloat)i/30.0); glEnd(); Обратите внимание на то, что для формирования на экране каждой порции поверхно- поверхности нужно отдельно обращаться к функции glMap2f(). Если же планируется сформиро- сформировать закрашенное изображение чайника, процедура инициализации будет выглядеть по- посложнее. В ней потребуется установить режим учета освещения и настроить свойства материала поверхности. void myinit() { glEnable(GL_MAP2_VERTEX_3); glEnable(GL_AUTO_NORMAL); glMapGrid2f(8, 0.0, 1.0, 8, 0.0, 1.0); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glLightfv(GL_LIGHT0, GL_AMBIENT, ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuse); glLightfv(GL_LIGHT0, GL_POSITION, position); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient); 10.12. Кривые и поверхности в OpenGL 459
Собственно отображение организуется двумя операторами в функции display (): for(k=0;k<32;k++) { glMap2f(GL_MAP2_VERTEX_3, 0, 1, 3, 4, 0, 1, 12, 4, &data[k][0][0][0]); glEvalMesh2(GL_FILL,0,8,0,8); 10.12.4. Функции отображения NURBS-кривых и поверхностей Функции вычисления полиномов можно использовать и на неравномерной сетке за- задания параметров при формировании четырехмерных кривых и поверхностей. Посколь- Поскольку любой полиномиальный объект можно привести к форме Безье, подобрав подходя- подходящий ансамбль опорных точек, то в нашем распоряжении имеется полный арсенал средств, необходимых для определения NURBS-кривых и поверхностей. Причем в при- прикладной программе не нужно проходить через все этапы формирования таких объек- объектов — в составе библиотеки утилит OpenGL, GLU имеется набор функций для работы с NURBS-объектами. Эти функции позволяют специфицировать в прикладной программе множество дополнительных параметров, настраивая которые прикладная программа может управлять режимом отображения объекта на экране. Для работы с NURBS- поверхностями имеется пять функций: gluNewNurbsRenderer(); gluNurbsProperty(); gluBeginNurbsSurface(); gluNurbsSurface(); gluEndNurbsSurface(); Две первых функции формируют новый NURBS-объект и задают способ его отображения. Следующие три функции используются для формирования поверхности. При работе с NURBS-кривыми вместо них следует использовать gluBeginNurbsCurve(), gluNurbsCurve() и gluEndNurbsCurve(). В библиотеке GLU есть функции, которые позволяют выполнять операцию выреза- вырезания отверстий криволинейной формы в сформированной поверхности. "Вырезающие кривые" (Trimming curves) представляют со- собой замкнутые криволинейные контуры отвер- отверстий (рис. 10.40). В прикладной программе -Контуры отверстий ВЬ1резаюшая NURBS-кривая формируется с помощью функции gluNurbsCurve(). Можно NURBS-поверхность сформировать и кусочно-линейный контур от- Рис. 10.40. NURBS-поверхность с прорезан- верстия с помощью функции gluPwlCurve(). ными отверстиями Контуры отверстий формируются после вызо- вызова glNurbsSurface(), причем соответствую- соответствующие вершины задаются в операторных скобках — между вызовами функций glBeginTrim() и glEndTrim(). 10.12.5. Квадратичные поверхности В составе библиотеки GLU OpenGL имеются функции формирования и отображения не- нескольких типов распространенных квадратичных поверхностей — дисков, цилиндров и сфер. В прикладной программе такие объекты могут обрабатываться так же, как и все прочие при- 460 Глава 10. Кривые и криволинейные поверхности
митивы, — можно масштаоировать, сдвигать, поворачивать, закрашивать их и накладывать на них текстуру. Функции построения квадратичных поверхностей автоматически формиру- формируют нормали и координаты текстуры. В OpenGL выполняется их аппроксимация многоуголь- многоугольниками, причем в прикладной программе задается, сколько многоугольников должно быть использовано для аппроксимации. Предположим, что в изображении робота, которое мы рассматривали в главе 8, основание робота задумано сделать в форме цилиндра. Для этого сначала определим в программе новый квадратичный объект: GLUquadricObj *p; p=gluNewQuadric(); После этого в программе требуется задать параметры, определяющие вид объекта на эк- экране. Если, например, планируется выводить проволочное изображение и текущий цвет, то следует вызвать функцию gluQuadricDrawStyle(р, GLUJLINE); Далее можно сформировать цилиндр, центр которого находится в начале координат, а об- образующая направлена вдоль оси у: gluCylinder(p, BASE_RADIUS, BASE_RADIUS, BASE_HEIGHT, 5, 5); Первые три аргумента задают радиусы нижнего и верхнего оснований и высоту (т.е. фак- фактически формируется не цилиндр, а усеченный конус частного вида). Два последних пара- параметра задают количество разбиений каждой окружности цилиндра (т.е. сечения при фиксиро- фиксированном значении координаты _>-¦) и количество слоев при разбиении по образующей в направ- направлении 10.13. Резюме Конечно, в этом учебном курсе мы поневоле были вынуждены обойтись крайне поверхно- поверхностным изложением обширной темы использования кривых и поверхностей в системах ком- компьютерной графики. Основное внимание было уделено тем математическим методам, кото- которые реализованы в составе функций API типовой системы, в частности OpenGL. С этой точки зрения наиболее привлекательными выглядят криволинейные объекты в форме Безье. Алго- Алгоритм рекурсивного разбиения кривых и поверхностей в форме Безье позволяет организовать в прикладной программе их отображение с любой степенью точности. Было также показано, что весьма интересные результаты можно получить в приложении, комбинируя методику интерактивного построения кривой в форме В-сплайна с методами ее отображения в форме Безье. В работах, перечисленных в следующем разделе, вы встретите множество вариаций сплайнов, применяемых в системах автоматизации проектирования объ- объектов разных типов. Квадратичные поверхности очень хорошо сочетаются с алгоритмами трассировки лучей, поскольку определение точки пересечения луча с поверхностью требует решения только квадратного уравнения относительно скалярной неизвестной. Анализ, лежит ли точка пересе- пересечения луча с плоскостью многоугольника, внутри этого многоугольника может потребовать больше операций, чем вычисление точки пересечения луча с квадратичной поверхностью. Поэтому во многих системах, реализующих алгоритм трассировки лучей, в набор объектов включаются только бесконечная плоскость, квадратичные поверхности и иногда выпуклые многоугольники. 10.13. Резюме 461
10.14. Рекомендуемая литература Прекрасное введение в геометрию кривых и поверхностей читатель найдет в книге Фарина (Farin) [Far88]. Введение к этой книге написано Безье, и в нем рассказано о том, как он и де Кастельо (de Casteljau) почти одновременно изобрели форму представления поверхностей, которая увековечила затем его имя. К сожалению, исследование де Кастельо не было опубликовано в доступной всем печати (это был внутренний отчет), и потому его имя очень мало известно в научных кругах. Самые разнообразные варианты сплайнов рассматриваются в книгах Роджерса (Rogers) [Rog90], Фоли (Foley) [Fol90J, Бар- тел ьса (Bartels) [Bar87] и Уатта (Watt) fli'a(93J. В книге Фокса (Faux) [Fau80] обсуждается подход к построению кривых и поверхностей, не зависящий от частного вида системы координат. Хотя книга, вышедшая под редакцией Гласснера (Glassner), [GIa89], в первую очередь ка- касается проблематики трассировки лучей, помещенный в ней раздел, написанный Хейнесом (Haines), содержит очень интересный материал, касающийся работы с квадратичными и дру- другими алгебраическими поверхностями. Упражнения ЮЛ. Рассмотрим уравнение алгебраической поверхности J[x,у, г)=0, в котором каждый член может содержать х, у и z в степени не выше т. Каково максимально возмож- возможное количество членов в функции/? 10.2. Рассмотрим уравнения в явном виде у=/[х) и ==g(x). Кривую какого типа они описывают? 10.3. Пусть имеется полином р(и) = ^ски". Найдите другой полином q{\>) = ^dkvk, такой, что для каждой точки р(и) в интервале (а, Ь) значения параметра и существовала бы равная ей точка q(v) в интервале 0 < v < 1. 10.4. Покажите, что если каждой из четырех опорных точек кубической интерполяцион- интерполяционной кривой соответствуют отличающиеся значения параметра и, то базисная ин- интерполяционная матрица существует. 10.5. Покажите, что полиномы Бернштейна на интервале @, 1) должны иметь значения меньше 1. 10.6. Покажите, что кубический сплайн имеет класс непрерывности С2. 10.7. В разделе 10.9 было показано, что кубическую полиномиальную кривую можно представить в форме Безье, подобрав соответствующим образом опорные точки, или, что то же самое, подобрав форму выпуклой оболочки. Покажите, как с уче- учетом этого факта организовать вычисление промежуточных точек интерполяцион- интерполяционной кубической кривой с помощью средств работы с кривыми Безье, которые имеются в составе OpenGL. 10.8. Покажите, как представить квадратичную поверхность в однородных координатах. 10.9. Предположим, что для формирования на экране изображения порции поверхно- поверхности Безье используется алгоритм адаптивного рекурсивного разбиения. При этом каждая окончательная порция имеет разную кратность разбиения. Соблюдается ли при этом непрерывность кривых, ограничивающих стыкующиеся порции? По- Поясните свой ответ. 462 Глава 10. Кривые и криволинейные поверхности
10.10. Разработайте OpenGL-программу, которая будет принимать от пользователя массив опорных точек и формировать на его основе интерполяционную кривую, В-сплаЙн и кривую Безье. 10.11. Предположим, что траектория перемещения некоторого персонажа анимационной программы описывается сплайном, причем в качестве параметра фигурирует время. Как скажется на характере движения персонажа изменение класса непрерывности траектории с С1 на С1? 10.12. Доработайте программу рисования, которая рассматривалась в главе 3, и включите в нее возможность построения кривых Безье. Программа должна предоставлять пользователю возможность настраивать положение опорных точек в интерактив- интерактивном режиме. 10.13. Разработайте простой тест степени кривизны порции поверхности Безье. 10.14. Сформируйте открытый рациональный квадратичный В-сплайн с узлами {0, 0, 0, 0, 1, 1, 1, 1} и весами wo=wz=\ и и,=м'. 10.15. Используя результаты упр. 10.14, покажите, что если и'=г/A-г), 0 < г < 1, то полу- получится кривая конического сечения. Подсказка. Рассмотрите варианты г<1/2 и г>\/2. 10.16. Найдите нули функций смешивания Эрмита. Почему из характера размещения ну- нулей следует, что кривая Эрмита достаточно гладкая на интервале @, 1)? 10.17. Какое отношение существует между опорными точками для порции поверхности Эрмита и производными в угловых точках порции? Упражнения 463
ГЛАВА 11 Процедурные методы До сих пор мы рассматривали геометрические объекты, которые описывались своими поверхностями, причем эти поверхности, как правило, моделировались (или аппрокси- аппроксимировались) множеством плоских выпуклых многоугольников. Использование полиго- полигональных объектов в качестве основных "кирпичиков" диктуется в первую очередь тем, что современные графические системы эффективнее всего работают именно с ними. Успешное применение компьютерной графики для создания приложений самого разного назначения подтверждает целесообразность выбранной модели. Но, тем не менее, хотя полигональная модель успешно справляется с формированием изо- изображения даже в таких сложных приложениях, как системы автоматизации проектирования и симуляторы окружающей обстановки в разного рода динамических тренажерах, и пользова- пользователям, и разработчикам давно известно, что ее возможности отнюдь не безграничны. Нельзя с помощью полигональной модели представить такие физические объекты, как облака, дым или жидкую среду. Невозможно добавить в полигональную модель физические ограничения и воспроизведение сложного поведения объектов. Исследование методов решения подобного рода задач развивается в рамках другого направления компьютерной графики, получившего название процедурного моделирования {procedural models). Отличительная черта этого на- направления — использование алгоритмических моделей для представления физических явле- явлений, лежащих в основе задачи. При этом многоугольники формируются только в том случае, если в них возникает необходимость на стадии вывода изображения. 11.1. Особенности процедурных моделей Если внимательно взглянуть на историю развития компьютерной графики, то видно, что на протяжении всего периода существования этого направления стремление получить более качественное изображение опережало возможности имеющихся аппаратных средств. Хотя на сегодняшнем оборудовании общего применения, доступном практически любому пользова- пользователю, можно обеспечить скорость формирования многоугольников до 1 миллиона в секунду, появились приложения, о которых раньше можно было прочитать только в фантастических романах, — симуляторы окружающей обстановки или приложения, имеющие дело с вирту- виртуальной реальностью, которые требуют воспроизведения свыше 100 миллионов многоуголь- многоугольников в секунду. Более того, по мере повышения скорости работы аппаратуры отображения
возрастает и объем соответствующей базы данных. В новейших приложениях он может пре- превышать 1 миллиард многоугольников. Однако в некоторых приложениях такие непомерные требования являются следствием использования существующих графических систем общего назначения, в которых реализова- реализована совершенно неподходящая для этих приложений парадигма моделирования и организации программного обеспечения. Исследователи и программисты, которые пошли нетрадицион- нетрадиционным путем, не без основания предположили, что нет необходимости формировать так много элементарных многоугольников в описании изображения — достаточно ограничиться теми, которые, во-первых, видимы, а во-вторых, проектируются на область, не меньшую одного пикселя. Процедурные модели охватывают широкий спектр приложений. Эта модель предпо- предполагает алгоритмическое описание объектов, а многоугольники формируются только в том случае, если они необходимы для реализации процесса отображения. В чем-то процедурные методы напоминают способы представления в компьютерах ирра- иррациональных чисел — корней квадратных, синусов, косинусов и т.п. Рассмотрим, например, три способа представления числа V2 . Его можно представить в виде константы 72=1.414..., ограничившись возможностями разрядной сетки компьютера. Есть и другой вариант, более абстрактный: считать, что V2 — это положительное число д;, такое, что х2=2. Но в компьютерах, как правило, V2 представляется как результат выполнения некоторо- некоторого алгоритма, например расчета по методу Ньютона: 2 д; при начальном значении хо=\. С этой точки зрения иррациональное число V2 определено не своим значением, а алгоритмом расчета, т.е. определено программно. Аналогичный подход можно применить и в отношении некоторых объектов, с которыми приходится иметь дело в графических приложениях. Например, сферу, центр которой находится в начале координат, можно определить как математический объект, удовлетворяющий уравнению x2+y2+zW. Сферу можно определить и как результат выполнения алгоритма рекурсивного разбиения тетраэдра, который мы рассматривали в главе 6, или как результат соответствующей про- программы. Потенциальное достоинство такого подхода состоит в том, что изображение сферы будет содержать разное количество многоугольников, в зависимости от того, в каком мас- масштабе она будет представлена на экране. Есть и еще одна проблема, суть которой состоит в том, что полигональное представление объектов в системе компьютерной графики очень сложно увязать с физическими законами, моделируемыми прикладной программой. Хотя и можно построить и "оживить" полигональ- полигональную модель какого-либо реального объекта, но значительно сложнее показать, что этот объ- объект является сплошным, а следовательно, два таких объекта при столкновении не могут про- проникнуть друг в друга. В этой главе мы рассмотрим два подхода к процедурному моделированию. Сначала рас- рассмотрим систему частиц, подчиняющихся законам Ньютона. Будет показано, как можно смо- смоделировать сложное поведение таких частиц, пользуясь математическим аппаратом решения дифференциальных уравнений. Вычисленное положение частицы будет использовано в гра- графической системе в качестве информации для размещения стандартного геометрического объекта в пространстве отображаемой сцены. Второй подход — алгоритмические модели — предполагает замену полигональной моде- модели языковой, описательной моделью, подобно тому, как это делается в обычных языках и 466 Глава 11. Процедурные методы
языках программирования. Модели такого типа позволяют приближенно представить многие типы естественных объектов, опираясь на небольшое множество правил, которые предписы- предписывают формирование соответствующих графических объектов. В комбинации с геометрией фракталов такие модели обеспечивают формирование адекватного изображения, используя при этом минимально необходимое количество многоугольников. 11.2. Физические модели и система частиц Одно из главных достоинств (а может, главных недостатков, это смотря откуда смотреть) технологии моделирования в компьютерной графике состоит в том, что модель можно фор- формировать на основе любых принципов. Тот графический объект, который по заданию при- прикладной программы формирует графическая система, может не иметь никакого отношения к объектам реального мира. Эта гибкость позволяет математикам просматривать такие формы поверхностей, которые вы не встретите ни в каком, пусть даже трижды "затерянном" мире, инженерам — проектировать машины, которые невозможно создать из существующих мате- материалов. Но если понадобится смоделировать поведение такого виртуального объекта в среде, подчиняющейся определенным физическим законам, и просмотреть поведение модели на эк- экране ЭЛТ, то окажется, что эта задача на порядок сложнее задачи формирования объектов. Несложно заставить изображения нескольких объектов двигаться в пространстве сцены, но значительно труднее уследить за тем, не столкнулись ли при этом объекты, и отреагировать на это событие корректно с точки зрения законов физики. Для системы компьютерной графи- графики ничего не стоит "запустить" мяч через закрытое окно, а вот показать, как стекло разлете- разлетелось от удара мячом, — задача на пару порядков сложнее. Даже воспроизвести отскок мяча от обычной стены и то не так просто. В последнее время множество исследований в области компьютерной графики посвящается моделям, основанным на физических законах, т.е. моделям, в которых графические объекты подчиняются определенным, объективно существующим физическим законам. В рамках этого направления можно выделить два подхода. Первый во главу угла ставит физическую модель процесса, исследуемого или воспроизводимого в приложении, а результаты физического моде- моделирования используются для управления графическими объектами. Например, если планируется "запустить" объект в среду, в которой есть множество других объектов, пусть и неподвижных, и показать, как будет двигаться этот объект, отскакивая от других, то, по крайней мере в принци- принципе, можно воспользоваться аппаратом исследования динамики поведения объектов, имеющимся в теоретической механике, составить соответствующие уравнения и решить их. Но, к сожале- сожалению, этот подход слишком сложен для тех, кто только осваивает премудрости компьютерной графики, а потому мы не будем его рассматривать на страницах этой книги. Другой подход состоит в том, чтобы комбинировать базовые физические законы с матема- математическими ограничениями и таким образом моделировать динамику поведения объектов. Имен- Именно этому подходу мы и последуем, рассматривая поведение группы материальных частиц. Система материальных частиц включает множество частиц, как правило, точечных, т.е. вся масса которых сосредоточена в точке, характеризующей текущее положение частицы. Динамика поведения частиц описывается системой дифференциальных уравнений. Системы таких идеальных частиц часто используются в качестве объекта исследования в самых разных областях. Например, в динамике жидкостей с помощью системы материальных частиц иссле- исследуется турбулентность потока. Вместо того чтобы решать уравнения в частных производных, можно смоделировать динамику системы, отслеживая поведение группы частиц, на которых действуют определенные силы и которые подчиняются определенным ограничениям. Идеальные материальные частицы можно использовать и для моделирования явлений в физике твердого тела. В частности, при исследовании деформаций используется представле- представление объекта в виде системы материальных частиц, связанных упругими связями. Под воздей- 11.2. Физические модели и система частиц 467
ствием внешней силы частицы могут смещаться, и их распределение воспроизведет форму деформированного тела. В графических приложениях модель поведения материальных частиц используется при исследовании таких явлений, как распространение огня и волн, поведение птичьих стай в по- полете. В каждом из такого рода приложений модель дает положение частиц в динамике, а гра- графическая система может отображать их не только точками, но и объектами более сложной и наглядной формы. В любом случае мы работаем с группой частиц, вся масса которых сосредоточена в точке. Поведение такой системы частиц описывается уравнениями, которые решаются численными методами, и в результате с определенным тактом на временной шкале мы получаем инфор- информацию о положении всех частиц. А далее уже дело фантазии разработчика программы, как представить частицу на экране — то ли в виде точки определенного цвета, то ли в виде како- какого-либо трафарета или даже объемной фигуры (например, маленьких колпачков — конусов). 11.3. Ньютоновы частицы Рассмотрим множество частиц, подчиняющихся законам Ньютона. Мы остановились именно на этом классе физических объектов, поскольку, анализируя их поведение, можно получить интересные результаты на основании простых физических законов, известных каждому со школьной скамьи. Ньютоновы частицы подчиняются второму закону Ньютона, утверждающему, что произведение массы частицы (т) на ее ускорение (а) равно сумме всех сил (f), действующих на частицу: /?/а = f. Обращаю ваше внимание на то, что ускорение и сила являются векторами, как правило, трех- трехмерными. Из второго закона Ньютона следует, что текущее состояние идеальной частицы с точечной массой (частица, вся масса которой сосредоточена в точке) полностью характеризует- характеризуется ее положением и скоростью. Следовательно, в трехмерном пространстве идеальная частица имеет шесть степеней свободы, а система из и частиц имеет 6/7 переменных состояния — поло- положение и скорости всех частиц системы. В некоторой системе координат состояние /-й частицы описывается двумя трехмерными матрицами-столбцами: матрицей положения Р,= и матрицей скорости Г •  Л, у, = 'dx~ dt dy dt dz dt Учитывая, что ускорение — это производная функции скорости, а скорость — производная функции положения, можно выразить второй закон Ньютона для материальной частицы в ви- виде системы из шести дифференциальных уравнений первого порядка: Р, =v,> v =—f,(/). т, 468 Глава 11. Процедурные методы
Следовательно, динамика системы из п материальных частиц с точечной массой описывается системой 6л обычных дифференциальных уравнений. Помимо текущего состояния, каждая частица характеризуется набором атрибутов, к кото- которым относятся ее масса /я, и набор свойств, способные нести информацию о поведении час- частицы и способах ее визуализации. Например, некоторые атрибуты задают цвет частицы при отображении ее на экране, форму ее образа и свойства поверхности этого образа. Обращаю ваше внимание на то, что хотя в физической модели частица есть идеальный объект нулевого размера, на экране ее образ может иметь вполне различимые размеры и форму, причем и то и другое задается в прикладной программе. Например, в одном приложении частицами моде- моделируются отдельные личности в толпе, в другом — молекулы, участвующие в процессе хи- химического синтеза, а в третьем — кусочки ткани, образующие в совокупности развевающееся на ветру полотнище. В любом случае физическая модель системы частиц описывает положе- положение, скорость и ускорение каждой из них, а располагая этой информацией, можно соответст- соответствующим образом представить моделируемый объект на экране. Множество сил, действующих на частицы, {f,}, определяет поведение системы. Эти силы могут порождаться взаимодействием частиц, которое, в свою очередь, зависит от их текущего состояния, а значит, изменяется во времени. В модель можно включить ос- основные физические явления, которые приводят к возникновению этих сил, например уп- упругость пружины, или некоторые физические ограничения. Можно также моделировать в системе и внешние силы, например воздействие поля гравитации. Тщательно продумав модель сил, действующих на систему, можно воспроизвести в ней те или иные варианты развития событий. Состояние системы в динамике вычисляется численными методами решения дифферен- дифференциальных уравнений, хорошо известными специалистам. При этом весь интервал наблюдения разбивается на такты, и на каждом такте вычисляются силы, действующие на п частиц (силы задаются функциями, определенными пользователем), и новое состояние частиц, которое яв- является следствием приложения этих сил. Последнее как раз и вычисляется методами числен- численного интегрирования дифференциальных уравнений. Конечная процедура каждого цикла — отображение нового состояния частиц на экране с учетом атрибутов соответствующих гра- графических объектов. Следовательно, фрагмент программы на псевдокоде, реализующей один цикл вычислений, будет иметь примерно такой вид: float time, delta; float state[6n], force[3n]; state=get_initial_state(); for(time=tO; time<time_final; time+=delta) { /* Вычисление сил */ force=force_function(state, time); /* Стандартная процедура интегрирования системы дифференциальных уравнений */ state=ode(force, state, time, delta); /* Отображение результатов */ render(state, time); } Основной компонент программы, на который программист тратит больше всего сил при разработке конкретного приложения, — функции вычисления сил, действующих на частицы. 11.3. Ньютоновы частицы 469
11.3.1. Несвязанные частицы Частицы могут взаимодействовать как друг с другом в рамках системы, так и с окружаю- окружающим систему миром, причем каждый вид взаимодействия порождает свои силы, воздейст- воздействующие на элементы системы. Если силы, действующие на определенную частицу, не зависят от других частиц системы, то силу, приложенную к /-й частице, можно представить в виде уравнения 1= f,(p,, v,). Самый простой случай — воздействие на каждую частицу только постоянной силы гравитации: Если сила гравитации направлена вниз, то О -8 О где g— положительное число, а каждая частица движется по дуге параболы. Если в уравне- уравнение добавить член, пропорциональный скорости, то в модель будут включены силы трения. Если в модели менять на протяжении "жизненного цикла" каждой частицы один из ее атри- атрибутов, например цвет, причем протяженность жизненного цикла выбирать случайно, получим на экране имитатор пламени. Рассуждая с более общей точки зрения, внешние силы в системе независимых частиц прикладываются к каждой из них независимо от прочих. Если позволить частицам медленно двигаться в некотором направлении со случайными вариациями, причем представлять частицу на экране графическим объектом достаточно внушительных, но также случайно выбранных размеров, то получим графическую модель группы облаков, медленно плывущих по экрану. 11.3.2. Силы упругости При моделировании системы из п независимых частиц оценка объема вычислений, необ- необходимых для определения сил, имеет вид О{п). В более общем случае, когда нужно учитывать взаимное влияние частиц друг на друга, эта оценка возрастает и имеет вид О(п2). При доста- достаточно большом п этот процесс может серьезно замедлить работу программы. Но в большин- большинстве практических приложений область взаи- взаимовлияния можно свести к ограниченной ок- окрестности частицы, т.е. принимать во внима- внимание только ближайшие частицы. Рассмотрим пример моделирования с по- помощью системы частиц изменения во време- времени формы поверхности куска ткани (полот- (полотнища, развевающегося на ветру, или занаве- занавеса). Положение каждой частицы системы можно рассматривать как вершину (узел) не- некоторой сети из четырехугольных ячеек (рис. 11.1). Форма сети изменяется во времени как из-за действия внешних сил (силы ветра или силы тяжести), так и вследствие сил, стремя- стремящихся сохранить расстояние между соседними частицами. Приближенно их можно пред- представить как силы, действующие только на ближайших соседей каждой конкретной части- частицы. Так, если р;/ — положение частицы, находящейся на пересечении /-й строки и у-го Рис. 11.1. Сеть частиц 470 Глава 11. Процедурные методы
столбца сети, то вычисление сил, действующих на частицу р0, потребует учета сил, связы- связывающих ее с частицами, находящимися в узлах р,ч,/> P/-i,/> Р/,/+ь P/./-I- Таким образом, оцен- оценка объема вычислений будет линейной — О{п). Один из наиболее простых, а потому и распространенных методов моделирования взаим- взаимного влияния частиц в подобных приложениях— включение между соседними частицами упругих "пружин". Рассмотрим две соседние частицы, расположенные в точках р и q и свя- связанные пружиной (рис. 11.2). Обозначим силу, с которой частица q действует на р, через f. Частица р при этом действует на частицу q с силой -f. Когда пружина не деформирована, ее длина равна 5. В отсутствие внешних сил именно на таком расстоянии будут находиться все частицы по истечении достаточно продолжительного пе- периода времени. Если пружина растягивается, то сила упруго- упругости действует в направлении d=p-q, т.е. вдоль прямой, соединяющей точки, в которых нахо- находятся частицы. Эта сила подчиняется закону Гука {Hook's law): Рис. 11.2. Частицы, связан- связанные пружиной р-ц где ks — постоянная, характеризующая упругость пружины. Из этого закона следует, что чем сильнее растягивается пружина, т.е. чем дальше друг от друга отстоят частицы, тем большая сила взаимного притяжения действует на них. И наоборот, если сжимать пружину, появляется сила, стремящаяся оттолкнуть частицы друг от друга. Но в форму- формулировке закона Гука не учитывается фактор демпфирования (или трения) в системе. Система, не обладающая демпфированием, будет колебаться неограниченное время после выхода из состояния покоя под действием внешних сил. Поэтому в закон Гука нужно включить демпфирующий член — "успокоитель"'. Сила демпфирования дей- действует в том же направлении, что и сила упругости, но ее величина зависит от скорости взаимного движения частиц. Коэффициент пропорциональности между скоростью и силой демпфирования за- зависит от величины проекции вектора скорости на вектор, связы- связывающий текущие положения частиц (рис. 11.3). Математически динамический закон Гука формулируется следующим образом: dd (p-q)'(p- q) Рис. 11.3. Вычисление силы демпфирова- демпфирования пружины где kd— постоянная демпфирования, а d=p-q. Описанная таким уравнением система точечных масс и пружин, выведенная однажды из со- состояния равновесия, со временем вновь вернется в него. Четыре изображения, представленные на ил. 16 цветной вклейки, показывают, как с помощью сетки из точечных масс и пружин моделируется воздействие ветра на кузов ав- автомобиля. После вычисления положения узлов сетки на каждом шаге моделирования на получившуюся поверхность накладывается текстура. При этом используются методы, опи- описанные в главе 9. 11.3. Ньютоновы частицы 471
11.3.3. Силы взаимного притяжения и отталкивания В то время как силы упругости стремятся сохранить устойчивое (недеформированное) со- состояние системы частиц, силы взаимного притяжения стремятся собрать все частицы вместе в одной точке, а силы взаимного отталкивания, наоборот, — рассеять по всему свету. В моде- модели силы взаимного отталкивания можно использовать для распределения частиц по некото- некоторой поверхности или, если частицы представляют в физической модели некоторый объемный объект, удерживать такие объекты от столкновений. Без учета сил взаимного притяжения не обойтись в модели солнечной системы, как, впрочем, и в любой другой, описывающей пове- поведение объектов в космосе. Уравнения для сил притяжения и отталкивания, по сути, одни и те же, исключая только знак. Для пары частиц, размещенных в точках р и q, сила отталкивания действует в направле- направлении d=p-q и обратно пропорциональна расстоянию между частицами. Математически это выглядит следующим образом: f = -к Изменив знак выражения и заменив коэффициент кг отношением {niamh)/g, получим уравнение для силы притяжения, действующей между двумя частицами массой та и пц. В этом уравне- уравнении g— гравитационная постоянная. В общем случае, когда на каждую частицу действуют силы притяжения от всех других (/?-1) частиц системы, оценка объема вычислений сил притяжения и отталкивания имеет вид О(п2). Модель, состоящая из сети частиц, связанных силами упругости, воспроизводит поведение объектов, которые стремятся сохранить свою топологию и взаимное положение компонентов. Модель, в которой главенствующее положение занимают частицы, находя- находящиеся под воздействием сил притяжения и отталкивания, характеризуется хаотичным из- изменением взаимного положения компонентов в системе. Поэтому снизить объем вычисле- вычислений в такой модели, уйти от оценки вида О(п') очень сложно. Один из подходов состоит в кластеризации пространства модели — разделения его на отдельные трехмерные ячейки, каждая из которых может содержать сколь угодно много частиц, а может не содержать и не одной (рис. 11.4). Размеры ячеек выбираются с учетом характера взаимодейст- взаимодействия — обратной пропорциональности между величиной силы и квадратом расстояния. При оптимально выбранных размерах ячеек для вычисления сил притяжения и отталкивания, которые действуют на частицу, находящуюся в некоторой ячейке, достаточно анализи- анализировать только частицы, находящиеся в этой же ячейке и в соседних с ней. Если такая кластеризация пространства модели допустима, оценка сложности вычислений будет иметь вид 0{п), а не О(п2). Но, естественно, кластеризация требует накладных расходов. Частицы могут переходить из одной ячейки в другую, а потому время от вре- времени необходимо выполнять их сортировку. Сложность состоит в том, что приходится изобретать подходящую структуру данных для описания ячеек и хранения информации о частицах. Другой подход состоит в том, что взаимодействие частиц друг с другом заменяется учетом взаимодействия частица и силового поля. Например, если необходимо определить силу гравитации, действующую на частицу, находящуюся на поверхности Земли, использу- используется значение гравитационного поля Земли, а не закон притяжения точечных масс, откры- открытый Ньютоном. Правда, положение усложняется, если нужно принимать во внимание еще и влияние Луны. Рис. 11.4. Разделение пространства мо- модели на ячейки 472 Глава 11. Процедурные методы
11.4. Решение системы уравнений Вернемся вновь к системе уравнений, описывающих состояние множества из п материальных частиц с точечной массой. Если ограничиться учетом только самых главных силовых факторов, воздействующих на такую систему, то ее состояние описывается системой из 6п обычных дифференциальных уравнений в виде где и представляет массив из 6п компонентов позиций и скоростей п частиц, a g включает все внешние силы, действующие на частицы. Следовательно, если имеются две частицы а и Ь, связанные силами упругости без демпфирования, то получим ЦГ=[МО И, М, М3 М4 М5 U6 М7 «8 U9 М10 "it] = \ а а а а% a, a b, bK b. b\ b\ b \ При заданных внешних силах g и состоянии системы частиц и в момент времени / можно вычислить gr=[ иг и4 м5 ~kdx -kdy -kd. и9 и10 мп kdx kdy kd. J. Здесь к — постоянная упругости пружины, a dx, dy, d. — компоненты нормированного вектора между а и Ь. Следовательно, сначала необходимо вычислить 1 V(М0 -  )" + (". - Ut )' + (W2 " Ul У м, -и. Программа решения обычных дифференциальных уравнений требует, чтобы мы вычисля- вычисляли функцию g для экстраполяции состояния и на будущее. Можно разработать набор таких программ, основываясь на теореме Тейлора. Простейший метод этой группы известен как метод Эйлера. Предположим, что интегрируется выражение u = g(u,O, на коротком промежутке времени И: j'*'udx = u(/ + h) -u@ = J'+ 'g(u,T)rfT. Если h мало, можно заменить значение g на интервале [/, t+h] значением g в момент t; сле- следовательно, u(/+A) « u(/)+Ag(u(/), t). Из этого выражения следует, что для приближенного вычисления значения u(t+h) использу- используется значение производной в момент времени / (рис. 11.5). Это выражение учитывает два первых члена разложения функции в ряд Тейлора; мы мо- можем записать его в виде Из этого выражения видно, что ошибка аппроксимации пропорциональна квадрату шага ин- интегрирования. Реализовать этот метод не представляет особого труда. Нужно вычислить значения сил (как внешних, так и сил взаимного влияния частиц системы друг на друга) в момент време- времени /, вычислить массив обобщенных сил g, умножить его на h и добавить к массиву теку- 11.4. Решение системы уравнений 473
щего состояния. По этому методу в итерационном цикле вычисляются состояния системы и в последующие моменты времени /+2Л, /+3h,... . Основные вычисления — определение массива обобщенных сил в каждый момент времени. u(t) у у hii(t) ^- t t + h /*ис. /7.5. Приближенное решение дифференциального урав- уравнения по методу Эйлера Применение метода Эйлера требует тщательного выбора шага интегрирования, поскольку от него зависит точность результата и устойчивость итерационной вычислительной процеду- процедуры. Точность вычисления пропорциональна квадрату шага интегрирования. Следовательно, для повышения точности нужно уменьшать шаг, что увеличивает количество циклов итера- итерации и соответственно время решения. Но еще более серьезные опасения вызывает проблема устойчивости вычислительной процедуры. При переходе к очередному циклу у нас появля- появляются ошибки, источниками которых являются, во-первых, использование для приближения только первых членов разложения в ряд Тейлора, а во-вторых, ограниченная разрядность компьютера. Эти ошибки могут либо компенсировать друг друга, либо, наоборот, суммиро- суммироваться и, следовательно, накапливаться. Такое поведение вычислительного алгоритма приня- принято называть вычислительной неустойчивостью. К счастью, в большинстве задач, связанных с анализом состояния системы материальных точек, используются достаточно простые модели сил, а потому при правильном выборе шага интегрирования неустойчивость не отмечается. Потенциально наиболее подверженной вычислительной нестабильности оказывается модель, в которой учитываются силы упругого взаимодействия. Дело в том, что при определенных значениях коэффициентов жесткости, входящих в уравнения Гука, даже точное решение име- имеет колебательный характер, а потому наложение на него ошибок численного интегрирования делает вычислительный алгоритм склонным к "самовозбуждению". Существуют два подхода к решению проблемы вычислительной неустойчивости. Один состоит в том, чтобы отказаться от использования метода Эйлера. Анализ таких методов вы- выходит за рамки нашего курса, и мы отсылаем читателей к специальной литературе по числен- численным методам. Другой подход состоит в том, чтобы сохранить в общих чертах метод Эйлера, но внести в него некоторые изменения, уменьшающие накопление ошибок при переходе к очередному циклу. Мы кратко остановимся на одном из методов такой модификации, кото- который даст читателем общее представление о целом семействе подобных методов. Предположим, что, как и раньше, мы начинаем с интегрирования дифференциальных уравнений на коротком интервале, чтобы получить u(/ + h) = u(/) + Но на этот раз будем использовать при интегрировании усредненное значение на интер- интервале [t, t+h]: 474 Глава 11. Процедурные методы
-( ( ( ) )+ Теперь появляется другая проблема— в нашем распоряжении нет значения g(u(t+h), t+h), мы располагаем только значением g(u(/), /). Для приближенного вычисления g(u(t+h), t+h) вос- воспользуемся все тем же методом Эйлера: g(u(t+h), t+h) ~ g(u(t)+hg(u(t), t), t+h). Этот метод известен под названием усовершенствованного метода Эйлера (improved Eider method) или метода Рунге-Кутта второго порядка (Runge-Kutta method of order 2). Обратите внимание на то, что в одном и том же цикле (при переходе от / к t+h) значение g нужно вы- вычислять дважды. Однако, если проанализировать ошибку согласно теореме Тейлора, то ока- окажется, что она имеет порядок O(h3). Следовательно, хотя на каждом шаге приходится делать больше вычислений, размер шага можно увеличить (а количество циклов соответственно уменьшить) и в итоге даже добиться более высокой производительности. Можно и далее по- повышать порядок формул Рунге-Кутта, увеличивая тем самым точность вычислений на каж- каждом шаге. Наиболее распространенным в настоящее время для решения обычных дифферен- дифференциальных уравнений является метод Рунге-Кутта четвертого порядка, который предполагает четырехкратное уточнение значения g в течение одного шага интегрирования, но обеспечива- обеспечивает при этом точность порядка O(h4). В последнее время разработаны методы этой же группы, которые автоматически подстраивают интервал интегрирования в процессе решения таким образом, чтобы обеспечивалась устойчивость итерационного процесса. 11.5. Ограничения В некоторых приложениях недостаточно сформировать множество частиц, задать их ис- исходное состояние, смоделировать внешнее воздействие и затем смотреть, как ведет себя сис- система "в свободном полете". Особенно это касается приложений, в которых отдельные объек- объекты могут сталкиваться. Хотя поведение объекта, который отскакивает от стены, также под- подчиняется законам Ньютона, построить систему моделирования соударений на таком принципе не удается (по крайней мере на существующих аппаратных средствах общего на- назначения) из-за большого количества необходимых вычислений. Вместо этого гораздо эф- эффективнее ввести в модель ряд ограничений на состояния объектов, например запретить объ- объектам "проникать" друг в друга. Эти ограничения хранятся и обрабатываются отдельно от общих правил поведения частиц, описываемых дифференциальными уравнениями. Применительно к элементам системы частиц можно выделить две группы ограничений. Жесткие ограничения (hard constraints) должны соблюдаться в обязательном порядке. На- Например, мяч должен обязательно отскочить от стены. Ни при каких обстоятельствах он не может "пройти сквозь стену" и выскочить с другой стороны. Мягкие ограничения (soft con- constraints) не являются обязательными — их соблюдение скорее желательно. Например, можно пожелать, чтобы частицы по возможности не приближались друг к другу на расстояние, меньшее заданного, как это реализуется в сетке частиц. 11.5.1. Столкновения Хотя жесткие ограничения общего вида довольно сложно встроить в физическую модель, существуют отдельные классы ограничений, которые можно реализовать на уровне модели системы точечных масс. К таковым, в частности, относятся ограничения, связанные со столкновениями. Задача моделирования столкновений распадается на две подзадачи: обна- обнаружение столкновения и реакция на столкновение. Предположим, что в системе частиц с то- точечной массой между частицами действуют силы отталкивания, а потому частицы друг с дру- 11.5. Ограничения 475
гом не сталкиваются. Возможно только столкновение частиц с внешними геометрическими объектами. Если в системе имеется п частиц и система погружена в среду из т много- многоугольников, то на каждом шаге расчета состояния системы нужно проверять возможность столкновения любой частицы с любым многоугольником. Рис. 11.6. Частица, "пронзившая" многоугольник Рис. 11.7. Отражение частицы Пусть какая-либо частица пронизала многоугольник, как показано на рис. 11.6. Обнару- Обнаружить сам факт столкновения частицы с многоугольником можно, подставив координаты по- положения частицы в уравнение плоскости многоугольника. Если временной шаг интегрирова- интегрирования уравнений состояния частиц достаточно мал, то, полагая, что скорость частицы на после- последовательных шагах изменяется незначительно, можно с помощью линейной интерполяции восстановить момент времени, когда в действительности произошло столкновение. То, что происходит с частицей после столкновения, во многом напоминает то, что проис- происходит со световым пучком после отражения. Если имеет место неупругое столкновение (inelastic collision), то частица не теряет энергии и ее скорость (точнее, модуль скорости) ос- остается неизменной. Но направление движения изменится в соответствии с законом идеально- идеального зеркального отражения. Следовательно, если известны нормаль п к поверхности в точке столкновения s и предыдущая позиция частицы р, то можно вычислить угол идеального зер- зеркального отражения по методике, описанной в главе 6 (рис. 11.7): r=B(p-s)-n)n-{p-s). Частица "пролетит" вдоль вектора отражения п такое же расстояние, как и то, которое она "проле- "пролетит", пронзив многоугольник, если бы столкнове- столкновение не было обнаружено (рис. 11.8). Направление движения изменится, а модуль вектора скорости останется прежним. Следова- Следовательно, та компонента вектора скорости, которая направлена вдоль плоскости многоугольника, ос- остается неизменной, а компонента, параллельная нормали, меняет знак на противоположный. Более сложным является механизм моделиро- моделирования упругого столкновения (elastic collision), при котором частицы теряют часть своей энергии. Рис. 11.8. Положение частицы после Коэффициент восстановления (coefficient of res- столкновения titution) частицы — это та часть нормальной ком- 476 Глава 11. Процедурные методы
поненты вектора скорости, которая сохраняется после столкновения. Следовательно, угол от- отражения вычисляется как и для неупругого столкновения, но нормальная компонента вектора скорости уменьшается в соответствии со значением коэффициента восстановления. Сложнее всего обнаружить в модели столкновения сам факт столкновения. В игровых про- программах вполне достаточно приблизительно определить момент столкновения, и в таком случае можно заменить сложную модель геометрического объекта, состоящую из множества много- многоугольников, на один простой объект, например ограничивающий прямоугольник или паралле- параллелепипед. Обнаружить столкновение с таким каноническим объектом значительно проще. Обращаю ваше внимание на то, что работа с точечными частицами позволяет избежать вы- вычислений, которые обязательно пришлось бы выполнять, если бы анализировалось столкнове- столкновение объекта определенной пространственной формы. Задача обнаружения столкновений во многом напоминает задачу отсечения одного объекта другим — хотя сами по себе формулы и не сложны, но объем вычислений оказывается значительным и на него уходит довольно много времени. Кроме того, при работе с объектами конечной формы нужно учитывать силы инерции, что повышает количество дифференциальных уравнений, описывающих состояние системы1. В компьютерной графике чаще всего используется приближенное решение системы уравнений, описывающей состояние системы, а потом на место идеальных частиц с точечной массой в изо- изображение помещается какой-либо геометрический объект со своей формой и атрибутами. Существует еще один вариант жестких ограничений, который часто включается в физиче- физическую модель, — учет сил контактного взаимодействия {contact forces). Предположим, что в модели имеется частица, на которую действует сила, толкающая ее параллельно какой-либо поверхности (рис. 11.9). Рис. 11.9. Силы контактного взаимодействия С одной стороны, под действием этой силы частица не может "проникнуть" в поверх- поверхность, а с другой — не должна и отрываться от нее. В такой системе следует учитывать толь- только компоненту вектора суммарных сил, касательную к поверхности. Можно пойти дальше и включить в физическую модель еще и учет сил трения при движении вдоль поверхности. 11.5.2. Частицы внутри сферической оболочки Прежде чем перейти к анализу мягких ограничений, остановимся на время еще на од- одном жестком ограничении. Начнем с группы частиц, заключенных в сферическую оболоч- оболочку радиуса /-, центр которой находится в начале координат. Предположим, что на частицы действуют силы взаимного отталкивания, величина которых обратно пропорциональна расстоянию между ними. Соударение частиц с поверхностью оболочки носит характер уп- упругого столкновения. Пусть р,@ и v,@ — положение и скорость /-й частицы в момент вре- времени /. Расстояние с/,, между /-й иу-й частицами равно 'Здесь вряд ли можно согласиться с автором, поскольку учет инерциапьных свойств объектов никак не связан с их размерами, а только с .массой. От размеров .же может зависеть сила сопротивления среды, в которой перемещается моделируемый объект. — Прим. ред. 11.5. Ограничения 477
а отталкивающая сила, действующая на /-ю частицу со стороныу-й частицы вдоль прямой, их соединяющей, равна f,=-*,4 Р-Р, Начав с некоторого случайного положения частиц, можно интегрировать систему дифферен- дифференциальных уравнений, описывающих состояния частиц, применяя численные методы и соот- соответственно подобрав шаг интегрирования. Все будет идти прекрасно до тех пор, пока какая- либо из частиц не попытается проникнуть сквозь сферическую оболочку. Решение, полученное на очередном шаге интегрирования системы уравнений, необходи- необходимо проанализировать на предмет того, не оказалась ли какая-либо из частиц вне сферической оболочки. Пусть положение некоторой частицы в момент времени / было р, а после следую- следующего шага интегрирования, в момент /+/?, стало q. Соответственно скорость частицы в мо- момент / обозначим v, а в момент /+/? — и. Если q находится внутри сферы, то эта точка сохра- сохраняется в качестве текущего положения частицы. Но если точка q вышла за пределы сферы, то дальше выполняется следующая процедура. Сначала отыскивается точка а пересечения сфе- сферы, которая лежит на траектории от точки р до точки q (рис. 11.10). В этой точке должны выполняться соотношения а = A-а)р + aq, |р + a(q-p)| = /•. Отсюда следует простое квадратное уравнение относительно неизвестной а. Вычислив а, по- получим координаты точки пересечения траектории частицы со сферой: а = р + a(p-q). Эту точку в дальнейшем будем рассматривать как точку столкновения частицы с оболоч- оболочкой. Для определения траектории отраженной частицы нам понадобится знать направление нормали к поверхности в точке отражения. Поскольку поверхность сферическая, вектор нор- нормали несложно вычислить по формуле п = -я/г. Направление отражения вычисляется после этого по формуле (рис. 11.11) Рис. 11.10. Пересечение тра- траектории частицы со сферой Рис. 11.11. Отражение час- частицы от сферической оболочки Следовательно, в момент времени t+h частица будет в точке а+ar. Для дальнейших вы- вычислений потребуется также определить и новый вектор скорости \q частицы после отраже- отражения от сферической оболочки. Компонента вектора скорости вдоль направления нормали в точке столкновения равна nv^. Нам нужно изменить направление этой компоненты на проти- противоположное, оставив другие компоненты без изменений. Текст программы, реализующей 478 Глава 11. Процедурные методы
описанную модель системы частиц, заключенных в сферическую оболочку, вы найдете в файле bounce.с. В упражнениях в конце главы вам будет предложено самостоятельно разра- разработать другие варианты подобного рода ограничений. 11.5.3. Мягкие ограничения Включение в физическую модель большинства видов жестких ограничений связано с серьезным увеличением сложности, а значит, и времени вычислений. Если, например, требу- требуется, чтобы скорость всех частиц не превосходила некоторую максимальную или чтобы сум- сумма энергии всех частиц не превышала некоторую константу, то для решения такой задачи в реальном масштабе времени потребуется очень мощный процессор, поскольку обычных дифференциальных уравнений для описания состояния системы будет недостаточно. Но во многих ситуациях можно получить удовлетворительное решение, используя мягкие ограничения в виде минимизации некоторого функционала качества системы. Если, напри- например, желательно, чтобы частица, занимающая в текущем состоянии положение р, не удаля- удалялась слишком далеко от точки р0, то можно включить в модель фунщию штрафа {penalty function) |p-po|2- Чем меньшее значение этой функции будет обеспечено в текущем состоянии, тем точнее соблюдает система сформулированное таким образом ограничение. Эта функция является одним из видов энергетической функции (energy function) системы, величина кото- который является оценкой энергии определенного вида, накопленной в системе. Физически это может быть потенциальная или кинетическая энергия. При использовании энергетической функции в качестве меры состояния системы в модели может решаться задача минимизации или максимизации этой оценки, но описание соответствующих математических методов вы- выходит за рамки данной книги. На ил. 13 цветной вклейки показан результат использования модели системы частиц для ви- визуализации данных научных исследований. В этом исследовании необходимо было построить поверхность уровня определенной функции, основываясь на большом массиве трехмерных дан- данных. В модель были "впрыснуты" частицы, обладающие замечательным свойством, — они стремятся расположиться только на поверхности заданного уровня (изоповерхности). Функция штрафа заставляет частицы стремиться занять место на этой поверхности, а силы взаимного от- отталкивания побуждают их более или менее равномерно распространиться по всей поверхности. а не собираться в отдельных ее областях. Информацию такого типа можно отобразить и с по- помощью другого метода, который будет рассмотрен в главе 12. 11.6. Языковые модели В главе 8 было показано, как с помощью графов двух видов — деревьев и ориентирован- ориентированных ациклических графов— представлять иерархические отношения меду компонентами сложной системы. В этом разделе будет рассказано, как использовать для представления та- таких отношений языковые средства и создавать языковые иерархические модели. Такая мето- методика не только предлагает альтернативный путь описания отношений, но и логически приво- приводит к процедурным методам определения объектов. До сих пор при формировании геометрических объектов мы повсеместно пользовались фиксированной последовательностью операций: обрабатывали модель, а далее формировали все примитивы, независимо от того, будут они видимы или нет, а если будут видимы, то ка- какую часть экрана займут и насколько будут заметны их детали. Вместо этого предлагается определять объекты процедурами или алгоритмами, которые будут формировать примитивы, а не примитивами как таковыми. В результате собственно формирование примитивов — гео- геометрических объектов — откладывается до того этапа процесса, когда наступит очередь ото- отображения. На этом этапе мы будем располагать полной информацией о том, следует ли во- 11.6. Языковые модели 479
обще формировать конкретный примитив, попадет ли он в кадр, не окажется ли так, что при заданных параметрах визуализации, в частности проективного преобразования, размеры при- примитива окажутся меньше размера пикселя и образ примитива просто физически не сможет появиться в изображении. Достоинством такой модели является и возможность встроить в нее генератор случайных чисел и внести "приятное разнообразие" в форму аналогичных объ- объектов на экране — они не будут выглядеть как оловянные солдатики, отлитые по одной фор- форме. Очень удобно применять эту методику при воспроизведении в сцене объектов реального мира — растений или участков земной поверхности. Даже не очень наблюдательный человек скажет вам, что не встречал в своей жизни двух аб- абсолютно одинаковых деревьев. Придумано множество методов, позволяющих создавать при каждом очередном обращении объект, внешне отличающийся от любого из предыдущих, хотя между ними и есть очевидное структурное и типологическое сходство, которое определяется набором достаточно ясных правил. Ниже мы рассмотрим, как можно использовать древовидные структуры данных для формирования объектов, которые выглядят на экране как растения. В информатике древовидные структуры используются повсеместно. Классический пример — грамматический разбор скобочных форм. В основном грамматический разбор в компьютерных и естественных языках выполняется по одной схеме. Разбор предложения компьютерного языка — программы — позволяет уяснить связь между отдельными ее компонентами, а разбор предложе- предложения естественного языка позволяет понять связь между отдельными членами предложения (кто, кого и когда). Дерево, представляющее структуру проанализированного предложения, описывает синтаксис этого предложения. Интерпретация отдельных элементов дерева— слов— придает предложению определенный смысл, т.е. определяет семантику предложения. Если нас интересует только синтаксис языка, то существует прямая корреляция между пра- правилами языка и формой дерева, которое представляет предложение на этом языке. Эту идею можно распространить и на иерархию объектов в графической системе — в той части, которая относится к отношениям между набором правил и древовидной моделью. Подобные системы известны под названием древовидных грамматик (tree grammars). Такая грамматика определя- определяется множеством символов и множеством правил подстановки или порождающих правгп (productions), которые четко предписывают, как можно заменить определенный символом дру- другим символов или комбинацией других символов. Типичное правило представляется в форме А-+ВС, В -> ABA. Располагая набором порождающих правил, можно сформировать бесконечное множество строк символов. В общем случае по отношению к некоторому символу в определенной си- ситуации можно применить более чем одно правило, а потому при случайном выборе фактиче- фактически применяемого правила можно при каждом новом обращении к программе формировать отличающийся вариант строки. Программа может быть написана таким образом, что не толь- только сформирует новую строку, но и, получив строку в качестве аргумента, сможет проанали- проанализировать, является ли она (строка) членом множества строк, сформированных в соответствии с определенным набором правил. Следовательно, можно будет иметь набор правил формиро- формирования объектов определенного класса (скажем, деревьев или кустиков) и отдельно от него программу, которая сможет определить, к какому классу принадлежит тот или иной объект сцены, основываясь на информации о разных грамматиках, порождающих формы объектов. Символы преобразуются в графические объекты в результате интерпретации сформиро- сформированных строк, причем для этого могут быть использованы разные способы. Один из подходов базируется на графической системе черепашьей графики2 (turtle graphics) (см. упр. 2.4). В 2В отечественной литературе по отношению к этому способу формирования штриховых графических объектов иногда используется термин "паучья графика ". — Прим. ред. 480 Глава 11. Процедурные методы
Рис. 11.12. Правило формирования кривой Коха этой графической системе образ объекта формируется в результате манипуляций с переме- перемещением графического курсора— черепахи, который оставляет след на поле изображения. Есть три главные команды управления черепахой — ее можно передвинуть вперед на 1 единицу, повернуть вправо или влево. Угол поворота фиксирован (но в каждой частной системе может иметь место свое значение — например, 30°, 45°, 90° и т.п.). Обозначим три базовые операции символами F, Rh L. Любая строка таких символов име- имеет простую и очевидную графическую интерпретацию. Если, например, в системе используется угол поворота 120°, то интерпретация строки FRFRFR приведет к формированию равностороннего треугольника. Доба- Добавим в набор символов еще два: [— для обозначения операции записи состояния черепахи (ее положения и ориентации) в стек, а 7 — для операции восстановления состояния, извлеченно- извлеченного из стека. Пусть графическая система настроена на угол поворота 60°. Рассмотрим порож- порождающее правило F-+FLFRRFLF, графическая интерпретация которого показана на рис. 11.12. Если применить это правило по- повторно ко всем символам F в новой строке, то получим кривую, показанную на рис. 11.13, а, после повторного применения к этой кривой— замкнутую кривую, показанную на рис. 11.13, б. Эти кривые известны в научном мире под именами собственными кривой Коха (Koch curve) и снежинки Коха {Koch snow/lake) и являются примерами так называемых заполняющих кривых (space-filling curves): при рекурсивном применении правила кривая растяги- растягивается все больше и больше, но при этом нигде не пересекает саму себя. Если на каждом шаге рекурсии уменьшать масштаб условной единицы длины при геометрической интерпретации, то кривая, несмотря на увеличение суммарной длины траекто- траектории, будет все время "вписываться" в определенную прямо- прямоугольную оболочку. Включение в правило операторов записи в стек и извлечения из стека позволяет формировать побочные ответвления образа. Рассмотрим правило F -» F[RF]F[LF]F, для которого установлен угол поворота 27° (рис. 11.14). Учтите, что отрисовка кривой начинается с нижней точки, а угол измеря- измеряется относительно текущего направления "вперед". После применения правила к единственному линейному сег- сегменту получим образ, представленный на рис. 11.14. Далее мож- можно поступать по-разному. Например, применить правило по- повторно ко всем символам Fb последовательности. В этом случае получится образ, показанный на рис. 11.15. Одновременно можно изменить масштаб единич- единичного шага черепахи и таким образом уменьшить длину последующих отростков. После не- нескольких итераций получим нечто, напоминающее сухой кустарник с массой колючек (какой- нибудь саксаул). Все, кажется, прекрасно, но если с помощью этого правила попытаться изо- изобразить на экране заросли саксаула, то получатся не хаотические дебри, а выстроившиеся, как на параде, "ветвистые витязи", ибо "все равны, как на подбор". 6) Рис. II. 13. Заполняющие кривые: а — кривая Коха; б — снежинка Коха 11.6. Языковые модели 481
Рис. 11.14. Кривая, фор- формируемая правилом F -> F/RF/F/LF/F Рис. 11.15. Повторное применение правила F-* F/RF/F/LF/F Рис. 11.16. Включение элемента слу- случайности в повторное приме- применение правила F —> F/RF/F/LF/F Можно устранить это однообразие, так милое людям в погонах, введя случайность в от- отбор тех символов F, по отношению к которым применяется правило на очередном шаге итерации. В результате с помощью одного и того же правила можно получить разные ва- варианты образа саксаула (рис. 11.16). Если ввести в набор еще несколько порождающих правил и случайно выбирать, какое из них применять на очередном цикле итерации, то можно сформировать на экране довольно правдоподобное изображение лесных дебрей. Незначительно модифицировав правила, можно и "приклеить" листочки на концы вет- ветвей — саксаул превратится в сакуру. Привлекательность этой стратегии состоит в том, что обходясь минимумом средств — буквально двумя-тремя правилами и несколькими параметрами, — мы получаем возмож- возможность определить целый класс объектов подобной и в то же время разнообразной формы. Предположим, что для оживления пейзажа или для создания "среды обитания" персонажей в сцену требуется включить деревья. Можно, конечно, просто сформировать в программе такие объекты, используя привычную методику их описания в виде множества примити- примитивов — отрезков, многоугольников и канонических кривых. В некоторых случаях деревьев может потребоваться не единицы, а десятки (а может, и сотни). Но даже не это самое не- неприятное. Как правило, одна и та же сцена "по ходу действия" может рассматриваться в разном ракурсе и в разном масштабе, причем только малая часть из того множества де- деревьев, на формирование которого в программе ушло так много труда, появится в каждом конкретном изображении — виде сцены. Остальные будут либо вне поля зрения, либо иметь такой мелкий масштаб, что ни о какой проработке деталей образа и говорить не приходится. В результате складывается впечатление, что громадная работа по формирова- формированию деревьев ушла впустую. Совсем по-другому складывается ситуация, если для форми- формирования образов деревьев использовать описанный процедурный метод. В программе при- присутствует только алгоритм формирования обобщенного дерева, а собственно создание об- образа происходит только тогда, когда такой образ действительно должен присутствовать в изображении. Причем степенью детализации образа, которая зависит от его масштаба на экране, также можно управлять программно. Грамматику формирования экранных образов можно описать непосредственно в терминах графических форм и аффинных преобразований, в результате чего будет создана грамматика формы (shape grammar). Вернемся вновь к узору Серпинского. Каждый цикл разбиения мож- можно описать в терминах трех аффинных преобразований, каждое из которых уменьшает вдвое масштаб исходного треугольника и размещает его уменьшенную копию в разных точках, как показано на рис. 11.17. Эти правила можно применять в случайном порядке или все три па- параллельно, но в любом случае, в конце концов, получим уже знакомый узор. 482 Глава 11. Процедурные методы
Итак, мы познакомили вас с тремя процедурными методами, которые позволяют форми- формировать как модели реальных физических объектов, так и модели воображаемых математиче- математических объектов. Кривые Коха и узор Серпинского дают вам представление еще об одном аспекте процесса формирования графических образов— методе, кото- который можно применять рекурсивно и который на каж- каждом очередном цикле детализирует ранее созданный образ, сохраняя характерные признаки формы исход- исходного объекта. Этот метод положен в основание одного из направлений современной геометрии — геометрию / \ д фракталов. / \ ^- / \ /А Рис. 11.17. Три правила, используемые при рекурсивном построении узора Серпинского 11.7. Рекурсивные методы и фракталы Языковые процедурные модели — это только один из возможных подходов к формирова- формированию сложных графических объектов с помощью простой программы. Другой подход базиру- базируется на геометрии фракталов {fractal geometry) и использует внутреннюю симметрию, при- присущую многим объектам реального мира. Геометрия фракталов разработана Мандельбротом (Mandelbrot), который положил начало новому направлению в математике. Исследования в этом направлении привели к созданию методов, позволяющих описать очень интересные яв- явления, неподвластные средствам традиционной геометрии. Специалисты по компьютерной графике использовали идеи геометрии фракталов не только для создания графических объек- объектов весьма прихотливой формы, но и для моделирования многих физических явлений, кото- которые другими методами представить не удается. Графические объекты, созданные с помощью методов геометрии фракталов, получили название графталов (graftals). 11.7.1. Масштаб и длина Стройное здание геометрии фракталов зиждется на двух устоях: ¦ зависимость между геометрией формы и масштабом; ¦ внутренняя симметрия (самосимметрия). Чтобы понять смысл этих формулировок, попытаемся ответить на вопрос, который, собст- собственно, и породил это направление: "Что такое длина береговой линии?" Будем считать, что пе- перед нами на столе разложена географическая карта. Поскольку береговая линия извилиста и крайне нерегулярна, нам придется взять сантиметровую ленту (например, такую, которой в до- докомпьютерные времена пользовались портные), выложить ее вдоль изображения береговой ли- линии на карте и затем, используя масштаб, обозначенный на карте, пересчитать измеренную дли- длину3. Однако, если вы потом найдете другую карту того же побережья, более крупномасштабную, и повторите эксперимент, то скорее всего и результат получите другой — больший. Дело в том, что на крупномасштабной карте форма береговой линии станет более изрезанной — появятся мелкие бухты и мысы, которые на мелкомасштабной карте просто не отражены. Еще более под- 3Для подобных измерений чаще всего используется специальный механический прибор — курвиметр. — Прим. ред. 11.7. Рекурсивные методы и фракталы 483
робно будет воспроизведена форма береговой линии на топографической карте. В принципе, этот процесс можно продолжать до бесконечности, например перейти на молекулярный уро- уровень, и каждый раз мы будем видеть все новые и новые детали общей картины. Если мы все-таки хотим получить полезную информацию из проведенных измерений, то сначала нужно согласовать приемлемый масштаб карты или приемлемую единицу разреше- разрешения при измерении. Аналогичная проблема существует и в компьютерной графике при работе с перспективными видами, поскольку степень детализации отдельных объектов зависит от того, на каком расстоянии мы их видим. Понять математическую суть проблемы можно, воспользовавшись рекурсивным приме- применением кривой Коха, описанной в предыдущем разделе. В этой кривой каждый линейный сегмент длиной в 1 единицу заменяется на очередном цикле четырьмя сегментами, каждый из которых имеет длину 1/3 единицы (рис. 11.18). Следовательно, на каждом цикле длина траек- траектории вдоль кривой между одними и теми же точками увеличивается с коэффициентом 4/3. Анализ предела, к которому стремится длина кривой при бесконечном количестве итераций, обязательно подведет нас к вопросу о размерности. Кривую нельзя считать обычной одно- одномерной линией, так как в пределе она имеет бесконечную длину и бесконечное количество разрывов первой производной. Но она и не является двухмерным объектом, поскольку не за- заполняет собой некоторую область на плоскости. Эту проблему можно разрешить, введя поня- понятие дробной размерности {fractional dimension). Рис. 11.18. Удлинение кривой Коха 11.7.2. Размерность фрактала Рассмотрим линейный сегмент единичной длины, единичный квадрат и единичный куб (рис. 11.19). При любой приемлемой формулировке понятия размерность линейный сегмент, квадрат и куб должны оставаться одно-, двух- и трехмерными объектами соответственно. Предположим, в нашем распоряжении есть измеритель с разрешением h, где h=\ln— наи- наименьшая единица длины, воспринимаемая прибором. Положим, что п — целое число. Разде- Разделим объекты на подобные в терминах значения И, как показано на рис. 11.20. 1 ь h ь ь ь ь 1 1 Рис. 11.19. Линейный сегчент, квадрат и куб h h h h h h h h h h" Рис. 11.20. Разбиение объектов при h=l/3 Линейный сегмент разобьем на к=п идентичных сегментов, квадрат — на к=п~ более мел- мелких квадратов, а куб — на к=п* более мелких кубиков. В любом случае можно утверждать, что новый объект сформирован в результате сжатия исходного с коэффициентом h и подста- подстановкой на место исходного к раз. Пусть d— размерность любого из этих объектов. В процес- процессе разбиения сохраняется сумма всех компонентов, и математически для любого объекта это выражается равенством 484 Глава 11. Процедурные методы
Решая это уравнение относительно d, получим выражение для размерности фрактала (fractal dimension): . In* d = . In и Другими словами, размерность фрактала некоторого объекта есть мера оценки количества подобных объектов, формируемых в одном цикле разбиения. Рассмотрим теперь кривую Ко- Коха. При ее разбиении создается четыре подобных объекта, каждый из которых втрое короче. Соответствующее уравнение для размерности фрактала имеет вид d = — = 1.26186. 1пЗ Повторим аналогичные рассуждения в отношении узора Серпинского. Один цикл разбиения представлен графически на рис. 11.21. В каждом цикле стороны треугольника делятся попо- пополам и формируется объект, в который включаются три из четырех образовавшихся треуголь- треугольников. Следовательно, получим </= — = 1.58496. In 2 В обоих случаях видно, что в результате разбиения формируется объект, который занимает больше пространства, чем кривая, но меньше, чем площадь области, ограниченной исходным объектом. Можно сформировать и трехмерный аналог узора Серпинского. Исходным объек- объектом в этом случае будет тетраэдр. В процессе разбиения его грани делятся как объекты двух- двухмерного узора (рис. 11.22). В сформированном объекте остаются четыре малых тетраэдра, а область в середине исходного тетраэдра удаляется. В результате получим значение размерно- размерности фрактала трехмерного узора Серпинского: d 2. In 2 Обратите также внимание на то, что объем объекта после каждого цикла разбиения уменьшается, а площадь поверхности увеличивается. Рассмотрим еще один объект— куб, который разбивается на втрое меньшие кубики, из которых 20 сохраняются в новом объек- объекте, а 7 (шесть в центре каждой грани и один в центре куба) — удаляются (рис. 11.23). Для этого объекта размерность фрактала равна 1пЗ Эти конструкции мы рассматривали здесь только для иллюстрации некоторых положений геометрии фракталов, хотя всякого рода эксперименты с ними весьма интересны и поучи- поучительны. При моделировании явлений реального мира используются несколько другие объек- объекты, в которые, помимо прочего, добавлен феномен случайности. Л Рис 11.21. Разбиение элемента узора Серпинского Рис. 11.22. Разбиение эле- элемента трехмерного узора Серпинского Рис. 11.23. Разбие- Разбиение куба 11.7. Рекурсивные методы и фракталы 485
11.7.3. Разбиение в средней точке и броуновское движение Фрактальные кривые имеют размерность в интервале 1 <d<2. Кривые с меньшим значе- значением размерности фрактала выглядят более гладкими, а по мере увеличения размерности кривая становится все более извилистой. Аналогичное утверждение справедливо и в отноше- отношении поверхностей: фрактальная размерность таких объектов лежит в интервале 2<d<3. В компьютерной графике существует множество приложений, в которых желательно сформи- сформировать объект, имеющий характерные признаки своего класса и в то же время не дублирую- дублирующие форму своих "собратьев". Более того, иногда желательно управлять степенью "резкости" или "мягкости" контура объекта. Например, силуэт горы на экране должен выглядеть более резким, чем линия горизонта в пустынном ландшафте. Следовательно, если и тот и другой создаются с применением технологии фракталов, то в первом случае объект должен иметь более высокую размерность, чем во втором. Кроме того, формирование объекта должно учи- учитывать и требования к разрешению. Степень "проработки" деталей ландшафта очень сильно сказывается на скорости формирования изображения, что существенно для приложений, от которых требуется работа в реальном масштабе времени, например симуляторов. Случайное движение частиц жидкости известно в физике под названием броуновского движения. Моделирование такого движения демонстрирует весьма интересный подход, ко- который с успехом можно использовать при формировании кривых и поверхностей, имеющих довольно естественный вид. Физики моделируют броуновское движение с помощью ломаной линии, в которой каждая следующая точка излома отстоит на случайно выбранном расстоя- расстоянии от предыдущей, а ориентация очередного сегмента также случайна. При воспроизведе- воспроизведении реального физического процесса очень существенную роль играет настройка функции распределения генератора случайных чисел, принимающего участие в построении траекто- траектории. В компьютерной графике мы меньше озабочены обеспечением адекватности модели и физического явления, а значительно важнее для нас скорость вычислений и возможность формировать кривые с настраиваемыми характеристиками "резкости" или "мягкости". По- Поэтому термин броуновское движение мы будем далее использовать для обозначения любых случайных траекторий, может быть, даже и не имеющих отношения к движению частиц жид- жидкости или газа. Конечно, формировать траекторию броуновского движения можно и с помощью последо- последовательности отрезков ломаной, задавая для каждого сегмента случайные значения длины и ориентации, но более эффективно воспользоваться рекурсивной процедурой. Рассмотрим ли- линейный отрезок— сегмент траектории, показанный на рис. 11.24,а. Найдем его среднюю точку и разобьем сегмент на два, причем точку стыковки сегментов сдвинем относительно средней точки по нормали к исходному отрезку на случайно выбранное расстояние (рис. 11.24,6). По- Повторим этот процесс несколько раз, применяя его по отношению ко всем сегментам, построенным в пре- предыдущем цикле. В результате будут построены кривые, примерно такие, как на рис. 11.25. Харак- Характеристики генератора случайных чисел, в частно- а' 6) сти среднеквадратичное отклонение, следует регу- Рис. 11.24. Смещение средней точки: а— лировать на каждом цикле, уменьшая его пример- исходный линейный сегмент; б но в 2 раза, поскольку длины сегментов на каждом сегмент после разбиения цикле также уменьшаются примерно вдвое. Мож- Можно усложнить алгоритм и включить случайный фактор не только в расстояние смещения, но и в его направление. Если отклонение всегда положительно, то будет построена случайная линия горизонта, а если воспользоваться ге- генератором гауссовского центрированного шума со среднеквадратичным отклонением, 486 Глава 11. Процедурные методы
пропорциональным /2B"^, где /— длина разделяемого сегмента, то результирующая кривая будет иметь размерность d. Значение d=\,5 соответствует траектории идеального броунов- броуновского движения. Рис. 11.25. Фрактальные кривые, состоящие из I, 2, 4 и 16 сегментов 11.7.4. Формирование изображения горы с помощью фракталов В задачах компьютерной графики фрактальная технология получила наибольшее распро- распространение при формировании деталей ландшафта, в частности гор. Для этого используется описанный выше метод рекурсивного разбиения в средней точке, но в качестве исходного объекта берется тетраэдр. Рассмотрим одну грань тетраэдра (рис. 11.26). Сначала определя- определяются средние точки каждого из трех ребер грани. Затем каждая средняя точка смещается слу- случайным образом. В результате формируется четыре новые треугольные грани. Управляя ха- характеристиками генератора случайных чисел, задающего смещение, можно добиваться нуж- нужной степени мягкости или резкости рельефа горы. Обращаю ваше внимание на то, что к выбору характеристик генератора нужно отнестись очень внимательно, иначе результат не оправдает ваших ожиданий. Подробно об этом вы сможете узнать в специальной литературе, которая будет рекомендована для самостоятельного изучения в конце этой главы. Рис. 11.26. Разбиение в средней точке граней тетраэдра Алгоритм разбиения в средней точке можно применять по отношению к любой полиго- полигональной сети. Можно начинать с плоской сети из четырехугольных ячеек в плоскости х, г, а далее разбивать каждый четырехугольник на четыре более мелких и смещать все вершины случайным образом вверх (в направлении оси у). На рис. 11.27 показано, как выглядит про- промежуточный результат этого процесса. 11.7. Рекурсивные методы и фракталы 487
Рис. 11.27. Ландшафт, сформированный с помощью фракталов: а— сеть; б—раз- б—разбиение сети со смещением вершин 11.8. Множество Мандельброта Ни одно обсуждение темы фракталов, даже на самом элементарном уровне, не обхо- обходится без упоминания множества Мандельброта {Mandelbrot set). Хотя алгоритм форми- формирования множества Мандельброта довольно прост, при этом формируются образы беско- бесконечно сложной формы. Это множество является прекрасным примером использования таб- таблицы соответствия цветов при генерации изображения. В дальнейшем изложении мы полагаем, что читатели знакомы хотя бы на самом элементарном уровне с арифметикой комплексных чисел. Обозначим точку на комплексной плоскости как z = x+iy, где х— действительная часть, а у— мнимая часть комплексного числа z (рис. 11.28). Если z, = xi+\y] и z2 = х2+\у2 — Два комплексных числа, то операции их сложения и умножения оп- определены следующим образом: Z,Z2 = Число i—мнимая единица— обладает свойством i2=-l. Комплексное число z имеет модуль (или абсолютную величи- величину), которая определяется следующим образом: \z\2=x2+y2. Функция w = F{z) отображает одну точку комплексной плоскости на другую. С помощью такой функции можно опре- определить рекуррентную процедуру построения некоторой траек- траектории на комплексной плоскости (годографа комплексного вектора): z = х + \у Рис. 11.28. Комплексная плос- где Zo = с — заданная исходная точка траектории. Если нанес- кость ти на комплексную плоскость точки zk, то, как видно на рис. 11.29, существует несколько вариантов "развития событий", в зависимости от вида функции F{zk) и начальных условий — положения точки с. В одном случае по мере возраста- возрастания к точки стремятся разойтись все дальше и дальше, в другом — картина периодически по- повторяется, а в третьем — точки стремятся к некоторой фиксированной точке сходимости. 488 Глава 11. Процедурные методы
ад Рассмотрим, например, функцию _ _ 2 z/h-l ~ Z* , где Zq = с. Если с лежит вне окружности единичного радиуса, то последовательность {z*} рас- расходится; если с лежит внутри окружности единичного радиуса, то последовательность {z*} сходится к началу координат комплексной плоскости; если же |с| = 1, то каждое последующее значение гк лежит на окружности единичного радиуса. Более интересный результат дает функция zi+1 = z/+c при начальных условиях щ - 0+Ю. Точка с принадлежит множе- множеству Мандельброта (Mandelbrot set) тогда и только тогда, когда все точки порождаемой ею рекуррентной последовательности являются конечными. Следовательно, комплексную плоскость можно разделить на две зоны, одна из которых представляет со- собой совокупность точек, принадлежащих множеству Мандельб- Мандельброта. Выделив на комплексной плоскости некоторый прямо- прямоугольник, можно графически выделить определенным цветом (например, черным) точки, принадлежащие множеству Ман- Мандельброта, а белым— остальные (рис. 11.30, а). Но на границе Рис. 11.29. Последователь- раздела двух областей картина оказывается довольно сложной, ность точек, формы- поэтому приходится увеличивать коэффициент масштабирова- руемая рекуррентной ния, чтобы рассмотреть подробности. функцией отображения а) б) Рис. 11.30. Множество Мандельброта: а — черно-белая раскраска; б — цветная рас- раскраска Вычисление точек множества Мандельброта может занять довольно много времени, но существует несколько специальных приемов, позволяющих ускорить процесс. Область с цен- центром в точке с = -О.5+Ю.О представляет наибольший интерес, хотя, возможно, вам и захочет- захочется изменить размер и положение окна просмотра. Как правило, уже после нескольких итераций можно довольно уверенно предсказать, уй- уйдут ли точки рекуррентной последовательности в бесконечность или будут стремиться к точ- точке сходимости. Например, если |z*| > 4, то по мере возрастания номера итерации точки все больше расходятся и можно прекращать процесс. Сложнее предсказать поведение последова- 11,8. Множество Мандельброта 489
тельности, когда исходная точка находится вблизи границы зоны раздела. Поэтому обычно используется аппроксимация множества, причем выполняется она следующим образом. Мы фиксируем максимальное количество итераций. Если при данном с последовательность то- точек, сформированная на заданном количестве итераций, обнаруживает тенденцию к расхож- расхождению, последовательность считается расходящейся и точка с окрашивается белым цветом. Если после выполнения заданного количества итераций значение \zk\ не превышает некоторо- некоторого порога, считается, что точка с принадлежит множеству Мандельброта и на изображении комплексной плоскости она окрашивается в черный цвет. Можно присваивать цвет точке с и соответственно значению \zk\ после выполнения последней итерации. На ftp-сервере по адресу ftp.cs.unm.edu в каталоге /pub/angel/BOOK находится про- программа Mandelbrot.с, которая формирует множество Мандельброта по этому приближенно- приближенному алгоритму. Пользователь этой программы имеет возможность устанавливать размеры и положение обозреваемой зоны комплексной плоскости, а также максимальное количество итераций для анализа расходимости рекуррентной последовательности. Комплексные числа ък масштабируются таким образом, чтобы их абсолютная величина находилась в интервале от О до 255. Программа формирует однобайтовый массив image размером пхши заполняет его значениями гь полученными после выполнения заданного количества итераций. Каждый элемент массива определяет затем цвет соответствующего пикселя картинной плоскости. Процесс отображения включает две стадии. На первой стадии формируется карта цветов, это происходит на этапе инициализации программы. Программа демонстрирует пользователю представление значений яркости в диапазоне @.0, 1.0). Пользователь определяет карту соот- соответствия интенсивности красного цвета таким образом, что минимальной яркости @.0) будет соответствовать отсутствие красной составляющей в сумме цветов, а максимальной яркости A.0)— насыщенный красный цвет. Между этими крайними значениями выполняется линей- линейная интерполяция. Для синей составляющей формируется обратное соответствие — нулевой яркости соответствует насыщенный синий цвет, а максимальной яркости — отсутствие синей составляющей. Зеленая составляющая выбирается случайным образом. Такой способ уста- установки значения зеленой составляющей позволяет лучше передать в изображении области с малым перепадом интенсивности (рис. 11.30,6). Программный код заполнения карты соот- соответствия цветов приведен ниже. for(i = redmap[i]=i/255.; greenmap[i]=rand()%RAND_MAX; bluemap[i]=1.0-i/255.; glPixelMapfv(GL_PIXEL_MAP_I_TO_R, 256, redmap); glPixelMapfv(GLJ>IXEL_MAP_I~TO_G, 256, greenmap); glPixelMapfv(GL_PIXEL_MAP_I~TO_B, 256, bluemap); Если посчитаете нужным, можете самостоятельно включить в программу другой алгоритм формирования карты соответствия цветов. Функция отображения включает всего две опера- операции — очистку экрана и передачу в буфер кадра значения кодов засветки из массива image. void display() { glClear(GL_COLOR_BUFFER BIT); glDrawPixels(n,m,GL_COLOR_INDEX, GL_UNSIGNED_BYTE, image); 490 Глава 11. Процедурные методы
11.9. Резюме В этой главе мы кратко познакомились с двумя новыми подходами к моделированию — физической моделью системы материальных частиц и языковыми методами моделирования графических образов. В обоих случаях важное место отводится технологии рекурсивного вы- выполнения процедур, которая позволяет формировать графические образы с контролируемой степенью проработки деталей. Основное достоинство процедурных методов формирования графических объектов — возможность управлять процессом создания примитивов для отображения конкретного набо- набора объектов из всех имеющихся в сцене с учетом их характеристик именно в данной ситуа- ситуации. Процедурные методы хорошо сочетаются с объектно-ориентированным подходом к соз- созданию моделей реальных объектов и явлений. Поэтому следует ожидать интенсивное разви- развитие этих методов уже в самом ближайшем будущем. Объединение в единой системе информации о физической природе явлений с технологией компьютерной графики порождает совершенно новые технологии создания таких популяр- популярных в последнее время приложений, как компьютерная анимация в самых различных облас- областях — от видеофильмов до научных исследований. Та система материальных частиц, о моде- моделировании которой шла речь в этой главе, — лишь пример использования физических моде- моделей в задачах компьютерной графики. Она позволила продемонстрировать, насколько широкими возможностями обладает подобная технология. Те технологии, с которыми вы познакомились в данной главе, будут использованы в гла- главе 12 при построении системы визуализации результатов научных исследований. 11.10. Рекомендуемая литература Системы материальных частиц впервые были внедрены в практику компьютерной графики Ривсом (Reeves) [Ree83J. С тех пор они стали использоваться довольно широко для исследова- исследования и визуализации самых различных физических и иных явлений — см. [Rey87], [WH94, а]. В данной книге мы следовали подходу, предложенному Уиткином (Witkin) [Wit94, b]. Существует огромный массив специальной литературы, посвященной фракталам и связан- связанным с ними методам. Впервые методику формирования рельефа с помощью геометрии фракта- фракталов описал Фурнье (Fournier) в статье [Fou82]. Более глубокое освещение теории фракталов можно найти в книгах Мандельброта (Mandelbrot) [Мап82] и Петгена (Peitgen) [Pei88]. Имеется также довольно много работ, посвященных использованию грамматик графов в самых разных формах, — [Pni90, Smi84, Lin68]. В работах Хилла (Hill) [HU90] и Прузинкевича (Prusinkiewicz) [Рги90] описаны интересные кривые заполнения пространства. Система итерационных функций Барнсли (Barnsley), описанная в работе [Ваг93], демонстрирует подход к использованию свой- свойства самосимметрии фракталов в задаче сжатия изображения. Упражнения 11.1. Разработайте набор порождающих правил для процедуры формирования узора Серпинского, которая начинается с единственного равностороннего треугольника. 11.2. Как определить размерность фрактала береговой линии? Как можно удостовериться в том, что береговая линия в действительности обладает свойствами фрактала? 11.3. Преобразуйте программу разбиения тетраэдра для аппроксимации сферы, которую мы рассматривали в главе 6, в программу формирования графического образа воз- возвышенности с помощью технологии фракталов. Упражнения 491
11.4. Бинарное дерево, которое используется в задачах поиска, обычно описывается связным списком. Напишите OpenGL-программу, которая по этому списку сможет сформировать на экране изображение графа. 11.5. Разработайте программу моделирования состояния системы материальных частиц, соединенных упругими связями. Программа должна формировать динамическое изображение сети из четырехугольных ячеек, представляющей текущее состояние системы частиц. Включите в программу средства, позволяющие пользователю вос- восстановить исходное состояние системы. 11.6. Добавьте в программу, разработанную при выполнении предыдущего упражнения, возможность моделирования внешних сил. Сформируйте с ее помощью изображе- изображение развевающегося на ветру флага. 11.7. Разработайте программы фрактализации сети. Для задания исходного положения вершин сети постарайтесь использовать реальные данные. 11.8. Разработайте программу, которая, получив на входе описания двух многоугольни- многоугольников с равным количеством вершин, покажет последовательность фаз преобразова- преобразования одного многоугольника в другой. 11.9. Разработайте физическую модель системы материальных частиц, которая будет имитировать искры при электросварке. 11.10. Доработайте модель, разработанную при выполнении предыдущего упражнения, таким образом, чтобы она позволяла имитировать взрыв полигонального объекта и разлет осколков. 11.11. Разработайте программу, которая будет формировать изображение облаков, ис- используя механизм альфа-канала (глава 9), алгоритм рекурсивной аппроксимации сферы (глава 6) и технологию фракталов. 11.12. Разработайте программу, которая будет формировать изображение поверхности виртуальной планеты с помощью технологии фракталов. На изображении должны быть отчетливо видны океаны и континенты. 492 Глава 11. Процедурные методы
ГЛАВА 12 Визуализация данных научных исследований В заключительной главе будет показано, как использовать все описанные ранее техноло- технологии в одном из самых сложных приложений компьютерной графики — системах визуа- визуализации результатов научных исследований. В предыдущих главах был описан богатый инструментарий, которым располагает современная компьютерная графика, — средства формирования геометрических объектов, средства управления их раскраской, анимацией, из- изменением ориентации и точки наблюдения. Средства работы с внутренними буферами гра- графической системы позволяют выполнять маскирование, удаление невидимых линий и по- поверхностей, комбинировать несколько изображений в одном. Применение этого богатого ар- арсенала позволяет выбрать наиболее подходящий для конкретного случая метод визуализации имеющегося набора данных. О том, как это делается, и будет рассказано в данной главе. 12.1. Данные+геометрия В любой вычислительной системе данные— это, прежде всего, числа. В чистом виде данные невозможно представить пользователю иначе, как в виде последовательности цифр, и нет для пользователя более нудной и неблагодарной работы, чем пытаться извлечь полезную информацию из мириады цифр. А ведь именно так использовались компьютеры еще не так уж давно — всего 30-40 лет назад. Собственно и компьютерная графика начала интенсивно развиваться в период, когда объем такой числовой информации стал настолько велик, что дальнейшее повышение мощности вычислительной техники теряло всякий смысл, — пользо- пользователи не успевали "переварить" все, что насчитали компьютеры. Графическое представле- представление данных позволяет наглядно представить огромные массивы числовой информации, вы- выявить в этих массивах наиболее интересные фрагменты и сосредоточить именно на них даль- дальнейшие исследования. Формирование графического представления и составляет суть процесса визуализации данных. Рассмотрим знакомое со школьной скамьи представление чи- числовых данных в виде двухмерных графиков. Сначала необходимо преобразовать имеющийся массив чисел в линии, прямоугольники, маркеры и другие графические объекты. В научных приложениях данные могут поступать из множества источников — это могут быть результа-
ты измерений, математического моделирования, имитационного моделирования и т.д. Проше всего определить понятие визуализация научных данных следующим образом: преобразова- преобразование данных в графические образы посредством технологии компьютерной графики. Чаще всего перед исследователем стоит задача оценки распределения некоторой харак- характеристики процесса по объему, причем данные для этой задачи представляют собой либо массив значений, полученных в отдельный момент процесса, либо совокупность массивов, соответствующих множеству моментов протекания процесса. Мы рассмотрим три вида по- подобных задач: скалярная визуализация — исследуемая характеристика представляет собой скалярную величину, например плотность излучения; векторная визуализация — исследуемая характеристика представляет собой векторную величину, например скорость течения жидкости в каждой точке потока; тензорная визуализация — исследуемая характеристика в каждой точке пространства имеет вид матрицы, например напряжения в механической конструкции. На пути решения задачи оценки распределения нам предстоит преодолеть довольно много сложностей, главная из которых — большой объем массива первичных ("сырых") данных. Данные считываются в точках интересующего нас объема с определенным про- пространственным шагом, кроме того, поскольку большинство процессов динамические, нуж- нужно повторять процесс съема первичных данных еще и с некотором тактом по времени. На- Например, при накоплении данных магниторезонансного исследования в биологии и медици- медицине они собираются в 512 х 512 х 200 точках. Примерно такой же объем первичных данных дают и другие новейшие методики медицинских исследований. Следовательно, любая (даже самая простая на первый взгляд), операция при выполнении ее на таком множестве данных перестает быть "проходной" и ей нужно уделять достаточно серьезное внимание. Вторая сложность состоит в том, что первичные данные представляют собой просто числа и им еще предстоит стать графическими объектами, которые система сможет предъявить пользователю. Третья сложность состоит в том, что во многих случаях нам придется иметь дело с многомерными выборками, т.е. состояние исследуемой системы в данной точке «и в данный момент времени определяется не одним, а множеством чисел. Если, например, ре- регистрируется процесс течения жидкости, то в каждой точке потока измеряются компонен- компоненты трехмерного вектора скорости. Если измеряются напряжения или деформации в точке твердого тела, то в каждой точке нужно регистрировать матрицу данных размером 3x3. Стратегия визуализации и определяет, как эти данные трансформируются в графические объекты, и набор их отображаемых атрибутов. 12.2. Поля превышений и линии уровня При выполнении различных исследований очень часто возникает необходимость наглядно представить те или иные математические функции. Способ визуализации функций во многом определяется формой ее задания — явной, неявной, параметрической — и количеством неза- независимых переменных. В этом разделе мы рассмотрим методы визуализации функций двух видов. Сначала остановимся на функциях двух переменных, заданных в явном виде: Функция такого вида описывает одну или несколько поверхностей в трехмерном пространст- пространстве. Часто их называют полями превышений (height fields), поскольку значение зависимой пе- переменной i графически представляется как высота соответствующей точки над уровнем ко- координатной плоскости ху. В такой форме представляются данные топографической съемки, причем в качестве независимых переменных х и у выступают географические координаты 494 Глава 12. Визуализация данных научных исследований
точки на местности (долгота и широта), а в качестве значения z — высота над уровнем моря. При визуализации функции /формируется изображение рельефа местности. Такого же рода изображение формируется и при отображении двухмерных данных {г, х}, которые изменяют- изменяются во времени. Заменив у переменной времени /, можно наглядно, в виде поверхности, пред- представить характер изменения данных во времени. Задав прямоугольную область изменения х и у, получим представление функции/в виде порции поверхности, как показано на рис. 12.1. Далее рассмотрим неявно заданные функции двух переменных вида g{x,y) = c, где с — константа. Такое уравнение описывает одну или несколько двухмерных кривых на плоскости х,у. Может случиться и так, что пары значений х,у, которая удовлетворяла бы этому соотношению, и не существует. Как отмечалось в главе 10, хотя функция такого вида иногда может быть представлена аналитически, но общего метода выражения одной пере- переменной через другую не существует. Знакомые всем примеры неявно заданных функций двух переменных — обобщенное уравнение квадратичной кривой (кривой конического сечения): ax2+bxy+cy2+dx+ey+d = 0 и уравнение плоскости ax+by+cz+d=0. Более сложный пример — уравнение овалов Кассини (Ovals o/Cassini): g(x,y) = (x2+y2+a2J-Aa2x2-b\ В зависимости от значений коэффициентов а и b это уравнение описывает одну или две замк- замкнутые кривые. Методы визуализации обоих видов функции во многом связаны, поскольку при заданном значении с функция g описывает кривую, которая соответствует линии пересечения поверх- поверхности/плоскостью z - с, как показано на рис. 12.2. Такие кривые часто называются линиями уровня с (contour curveI. Рис. 12. L Порция поверхности Рис. 12.2. Порция поверхности с z = f(x, у) нанесенными линиями уровня 12.2.1. Сети Самый общий способ визуализации функции вида J(x, у) — отображение поверхности. Процедура построения поверхности, которую мы рассматривали в главе 10, начиналась с оп- определения множества опорных точек. Если значения функции известны только в отдельных точках (т.е. отсутствует аналитическое описание функции), то в нашем распоряжении массив выборок или результатов измерения: 'В топографии и картографии линии уровня принято называть горизонталями. — Прим. ред. 12.2. Поля превышений и линии уровня 495
Можно предполагать, что точки выборки расположены с постоянным шагом на плоскости ху: х, = хо+/Длг, i = 0,..., N, УГУо+jAy, j = 0,...,M, где Ах и Ау — шаг между выборками в направлении хну. Если известен аналитический вид функции/, то значения в массиве формируются вычислением значения функции в точках х„ у. Но гораздо чаще приходится иметь дело с результатами измерений. Проще всего сформировать поверхности, воспользовавшись форматом описания сети, со- состоящей либо из треугольных, либо из четырехугольных ячеек. Взяв четыре выборки =,,, г,+| у, г,+1 /+1 и г,/+ь расположенные в соседних элементах массива, можно сформировать ли- либо одну четырехугольную, либо две треугольные ячейки сети. Таким образом, массив данных позволяет сформировать сеть, состоящую либо из NM четырехугольных ячеек, либо из 2NM треугольных. Разработать соответствующую программу в системе OpenGL не представляет большого труда. На рис. 12.3 показана сеть из четырехугольных ячеек, построенная по дан- данным топографической съемки участка острова Гонолулу в группе Гавайских островов. Рис. 12.3. Сеть, построенная по данным топографической съемки участка острова Гонолулу Остановимся на некоторых аспектах формирования и модификации такой сети в OpenGL- программе. Во-первых, если использовать для формирования сети весь набор имеющихся дан- данных, то в ней окажется очень много ячеек маленького размера. В результате плотность линий в изображении будет очень большой, что может привести к появлению муара. Следовательно, нужно каким-то образом отобрать из всего массива некоторый подмассив. Очевидное реше- решение — брать не все выборки подряд, а, например, каждую k-ю, задавшись определенным значе- значением к. Другой вариант — выполнить усреднение соседних выборок на какой-то базе усредне- усреднения. В любом случае должен быть сформирован массив меньшей размерности, что соответст- соответственно уменьшит размерность сети. Во-вторых, при формировании изображения, показанного на рис. 12.3, черным цветом вычерчивались линии контура ячеек, а белым цветом заливались мно- многоугольники ячеек. Линии контура ячеек позволяют передать в изображении характер рельефа, а вычерчивать и заливать многоугольники нужно для того, чтобы удалить невидимые наблюдате- наблюдателю участки сети. Можно было бы воспользоваться алгоритмом удаления невидимых поверхно- поверхностей, который имеется в составе системных средств OpenGL. Но в данном случае этого делать не стоит, поскольку данные, описывающие рельеф, уже упорядочены. Если формировать ячейки сети в порядке от более удаленных к ближним, то при заливке многоугольников, ближе распо- расположенных к наблюдателю, автоматически удалятся контуры закрываемых ими ранее сформиро- сформированных ячеек, которые таким образом становятся невидимыми для наблюдателя. Если в графи- графической системе стандартные средства удаления невидимых поверхностей реализованы про- 496 Глава 12. Визуализация данных научных исследований
граммно, то отказ от их использования в данном случае позволит повысить скорость формиро- формирования изображения. Данные, организованные таким образом, получили название множества данных размерностью 2.5 B.5 dimensional data sets), поскольку, хотя они и представляют точки трехмерного пространства, специфическая организация данных позволяет использовать эффек- эффективные алгоритмы их отображения. Но учтите, подобная структура данных не позволяет ото- отображать трехмерные поверхности произвольного вида. При формировании изображения, представленного на рис. 12.3, использован еще один специфический прием, который вы сможете в дальнейшем применять и в собственной прак- практике. Одновременное вычерчивание многоугольника и контура ячейки выполняется следую- следующим фрагментом программы: glColor3fA.0, 1.0, 1.0); glBegin(GL_QUADS) glVertex3i(i, z[i][j],j); glVertex3i(i+l, z[i+l][j],j); glVertex3i(i+l, glVertex3i(i, glEnd(); glColor3f@.0, 0.0, 0.0); glBegin(GL_LINE_LOOP) glVertex3i(i, z[i][j],j); glVertex3i(i+l, z[i+l][j],j); glVertex3i(i+l, glVertex3i(i, glEnd(); Следовательно, и контур ячейки, и многоугольник лежат в одной и той же плоскости. Хотя контур ячейки вычерчивается и после отображения многоугольника, неточность вычислений может привести к тому, что часть линий контура окажется перекрытой заливкой соседнего мно- многоугольника, частично использующего те же вершины. В OpenGL отсутствует режим совме- совмещенного отображения контура многоугольника и его внутренней области разными цветами. Но тот же эффект можно получить, воспользовавшись режимом смещения многоугольника (polygon offset mode). Для этого следует предварительно установить параметры смещения: glEnable(GL_POLYGON_OFFSET_FILL); glPolygonOffsetA.0, 0.5); Первый параметр функции glPolygonOf f set() задает смещение, а второй — зависимое от реализации значение масштабного коэффициента. В результате изображение контура много- многоугольника будет несколько придвинуто к наблюдателю по сравнению с изображением внут- внутренней области многоугольника, а потому обязательно будет видимо. Изображение сети можно модифицировать множеством способов, добиваясь того или иного изобразительного эффекта. Если в изображение сети включены только многоуголь- многоугольники (т.е. не формируется изображение контуров ячеек), то можно включить в сцену ис- источники света и выполнить тонирование. В результате получим полутоновое изображение рельефа, хорошо передающее его объемность. Управляя положением источников света, можно даже моделировать вид рельефа в разное время суток. Для вычисления нормалей, которые необходимы для выполнения тонирования по описанным в главе 6 алгоритмам, используются координаты вершин ячеек сети. Еще более интересный эффект дает наложе- наложение правильно выбранной текстуры. Если количество исходных данных достаточно велико, то при их отображении часто ис- используется какой-либо алгоритм сглаживания или сплайн-аппроксимации из тех, что были описаны в главе 10. 12.2. Поля превышений и линии уровня 497
12.2.2. Вычерчивание линий уровня Трехмерное изображение поверхности, представленной в виде сети, очень наглядно, но, к сожалению, не дает возможности количественно оценить значения функции в отдельных точ- точках или в окрестности точки. Кроме того, часть поверхности бывает скрыта от наблюдателя (находится на "обратном склоне"). Оценить поведение исследуемой функции количественно позволит ее графическое представление на двухмерной плоскости линиями уровня — кривы- кривыми, соответствующими фиксированному значению функции. На раннем этапе развития компьютерной графики, когда отсутствовали растровые системы отображения и не было достаточно развитого программного обеспечения для формирования трех- трехмерного изображения, представление функции линиями уровня было основным. Этот метод при- пришел в компьютерную графику из топографии, где именно таким способом на топографической карте представляется рельеф земной поверхности. Этот метод достаточно распространен до сих пор, хотя сейчас он используется в качестве дополнения к трехмерному изображению рельефа. Если функция/задана аналитически, то для построения линии уровня необходимо найти решение уравнения при определенном значении параметра с. Чаще всего на одном изображении выводится не- несколько линий уровня, соответствующих разным значениям с. Таким образом, все сводится к решению уравнения, заданного в неявной форме: g(x,y)=J{x,y)-c = 0. Например, если Ах,у) = х2+у\ то при положительном с каждая линия уровня есть окружность радиуса 4с . Если в нашем распоряжении имеется не аналитическая формула дляДдг,^), а массив выбо- выборок, то мы сталкиваемся с аналогичной проблемой, но решать ее необходимо на основе ап- аппроксимации массива выборок. К слову сказать, даже при аналитическом задании функции fix, у) получить аналитическое решение уравнения fix,y)-c = 0 удается чрезвычайно редко. На практике чаще всего в этом случае формируется все тот же массив выборок {/#}, а уже по не- нему строятся линии уровня. Эту задачу мы будем решать с помощью метода маркированных квадратов, который является частным случаем метода маркированных кубиков (этот метод будет подробно рассмотрен в разделе 12.4). 12.2.3. Метод маркированных квадратов Предположим, что имеется массив {А/у-Л*/»^)} выборок функции fix, у), сформирован- сформированный на регулярной сетке (или решетке) по независимым переменным хиу: х, = хо+/Дх, ; = 0, 1,...,jV-1, у} - yo+j&y, j = о, 1,..., м-\, где Ах и Ду — шаг между выборками в направлении хиу. Постоянный шаг выборок нам ну- нужен только для упрощения рассуждений. Массив выборок может быть получен и путем непо- непосредственного измерения некоторой физической величины с постоянным шагом по про- пространству и во времени, а если речь идет об интенсивности какого-либо излучения, то шаг определяется расположением элементарных ячеек в матрице приемника излучения. Предположим, что необходимо найти точки на кривой, неявно заданной уравнением z =fix,y) при z = c. Для определенного значения с функция fix, у) может вообще не иметь ли- линии уровня, иметь единственную линию или множество линий. Стратегия построения кусоч- кусочно-линейной аппроксимации линии уровня состоит в следующем. 498 Глава 12. Визуализация данных научных исследований
Процесс начинается с некоторой прямоугольной ячейки, определенной четырьмя узлами сетки выборок (xhy,), (xi+byj), (хм,у/+]), (Xi,yl+i), как показано на рис. 12.4. В общем случае может оказаться так, что линия уровня проходит через ячейку, хотя ни одно из значений функции в углах ячеек (соответствующих узлах сети) не будет равно с. Рассмотрим простой случай, когда одно из значений в углах ячейки, скажем/,, оказывает- оказывается больше с, а значения в других углах — меньше с: Эту ситуацию можно схематически представить либо так, как на рис. 12.5,а, где соотно- соотношения для каждого узла выведены в виде надписей, либо так, как на рис. 12.5,6, где использу- используется раскраска (маркировка) углов ячейки: белым цветом маркируется угол, в котором значе- значение больше заданного, а черным — углы, в которых значения меньше заданного. Очевидно, что в этом случае кривая линии уровня должна пересечь два ребра ячейки, прилегающих к "белому" углу. Это же должно произойти и в том случае, когда значение в одном углу ячейки будет меньше с, а в соседних с ним — больше. Такая ситуация показана на рис. 12.6,а. '/./¦! <С '!./¦! <с О (*,-./,) (х,+ 1,у() а) Рис 12.4. Прямоугольная ячейка Рис. 12.5. Маркировка углов ячейки б) На рис. 12.6,а показано единственное пересечение ячейки линией уровня. Но возможен вариант, когда линия уровня пересекает ребро ячейки три раза (рис. 12.6,6), а в общем слу- случае — любое нечетное число раз. Но мы всегда будем интерпретировать переход линии уров- уровня через ребро ячейки как однократное, следуя одному из принципов Питера: если нечто можно объяснить по-разному, то самое простое объяснение будет и самым вероятным. О а) б) Рис. 12.6. Пересечение ячейки линией уровня: а — однократное; б — многократное 12.2. Поля превышений и линии уровня 499
Если мы сможем оценить, в каких именно точках ребер линия уровня пересекает ячей- ячейку, то сможем соединить эти точки отрезком прямой и далее сразу вывести его на экран, поскольку анализ отдельных ячеек выполняется независимо. Но где установить точки пе- пересечения? Тут возможны две стратегии. Можно просто установить точку пересечения на середине ребра между "белой" и "черной" вершинами. Но если значение а функции в вер- вершине (х,,^) лишь незначительно больше с, а значение b в соседней вершине (xl+],y>j) значи- значительно меньше с, то вполне резонно именно в таком отношении разделить ребро точкой пересечения. По такому принципу интерполируется положение точки пересечения с реб- ребром. Рассмотрим, например, две соседние вершины ячейки, имеющие противоположную маркировку, т.е. значение функции в одной вершине больше с, а в другой — меньше с: J[xn у,) = а, а>с, Ax»uyJ) = b1 b<c. Если шаг независимой переменной х равен Ах, то можно вычислить положение точки пересе- пересечения, линейно интерполируя перепад между значениями а и Ъ на интервале Ах (рис. 12.7). о— С С L А """ А/с. /2.7. Определение точки пересечения линии уровня с ребром ячейки В результате получим: аАх X = .Y Н . а-Ь Точку пересечения на втором ребре найдем аналогично, а затем соединим эти точки отрезком и получим один из сегментов кусочно-линейной аппроксимации линии уровня. До сих пор мы анализировали только один из возможных вариантов маркировки вершин ячейки— одна вершина окрашена в белый цвет, а остальные— в черный. Всего возможно 24 = 16 вариантов маркировки. Все они представлены на рис. 12.8. Там же вы увидите и совмес- совместимый с такой маркировкой вариант пересечения ячейки линией уровня. Внимательный анализ этих вариантов показывает, что в них имеются два типа симметрии. Один тип — симметрия от- относительно поворота. Все варианты, которые могут быть преобразованы к единому путем пово- поворота, как, например, варианты 1 и 2, характеризуются тем, что линия уровня "отсекает" один из углов ячейки. Существует еще один тип симметрии, который образуется, если инвертировать маркировку вершин, — белые сделать черными, а черные — белыми, как, например, в вариан- вариантах 0 и 15. Если принять во внимание подобные типы симметрии, то 16 возможных вариантов сводятся к четырем. Эти варианты показаны на рис. 12.9. Следовательно, нам нужно разрабо- разработать программу, которая сможет вычерчивать сегмент линии уровня в этих четырех случаях, и с ее помощью можно будет отобразить все 16 возможных вариантов. Первый вариант тривиальный, поскольку в этом случае линия уровня не пересекает ячейку. Второй вариант мы уже рассмотрели чуть выше. Третий вариант также доволь- довольно прост — нужно вычертить сегмент, который пересекает ячейку от одного ребра до противоположного. 500 Глава 12. Визуализация данных научных исследований
^—о о Рис. 12.8. 16 вариантов пересечения ячейки линией контура О Рис. 12.9. Четыре уникальных варианта пересечения ячейки линией контура Последний, четвертый, вариант наиболее сложен и наиболее интересен — его можно интер- интерпретировать по-разному (рис. 12.10), и нам предстоит выбрать одну из возможных интерпретаций. При отсутствии дополнительной информации нельзя отдать предпочтение ни одному из представ- представленных вариантов, а потому следует выбрать любой из них наудачу, случайно. Можно и заранее выбрать один способ интерпретации, а о другом "забыть навсегда". Но на рис. 12.11 показано, что результаты могут существенно отличаться, если предпочесть один вариант другому. Чтобы разрешить неопределенность, придется выполнить дополнительные расчеты. Раз- Разделим "подозрительную" ячейку на четыре части (рис. 12.12) и рассчитаем значение функции в центральной точке. Если функция задана аналитически, это делается очень просто, а если в 12.2. Поля превышений и линии уровня 501
нашем распоряжении только экспериментальные данные, то можно получить "воображае- "воображаемое" значение, интерполируя по какому-либо закону уже имеющиеся данные в вершинах ис- исходной ячейки. Скорее всего, что ситуация в новых ячейках будет однозначной. Если это не так, то "подозрительную" четвертушку придется опять разделить. О ' Рис. 12.10. Неоднозначная интерпретация маркировки вершин ячейки Рис. 12.11. Пример разной интерпретации одного и того же вариан- варианта маркировки вершин ячейки Рис. 12.12. Разбиение ячейки Программный код, реализующий этот метод, весьма незамысловат. В программе органи- организуется цикл по всем ячейкам. Функция cell(), текст которой приведен ниже, определяет тип ячейки — один из шестнадцати. Аргументы функции — а, Ь, с и d — значения выборок, со- соответствующих вершинам ячейки. int cell(double a, double b, double с , double d) { /* THRESHOLD = значение уровня для формируемой линии */ int n=0; if(a>THRESHOLD) n+=l; if(b>THRESHOLD) n+=8; if(OTHRESHOLD) n+=4; 502 Глава 12. Визуализация данных научных исследований
if(d>THRESHOLD) n+=2; return n; } Теперь нужно отнести ячейку к одному из четырех уникальных типов. Это выполняется оператором логического анализа switch (): switch(n) { case 1: case 2: case 4: case 7: case 8: case 11: case 13: case 14: /* Линия уровня отсекает один угол */ draw_one(num, i,j,a,b,c,d); break; case 3: case 6: case 9: case 12: /* Линия уровня пересекает ячейку от одного ребра до противоположного */ draw_adjacent(num,i,j,a,b,c,d); break; case 5: case 10: /* Неоднозначный вариант */ draw_opposite(nuin, i, j,a,b,c,d); break; case 0: case 15: /* Линия не пересекает ячейку */ break; Теперь разработаем три функции, которые будут вычер- вычерчивать сегмент, — каждая в своем варианте. Например, функция draw_one() будет вычерчивать сегмент, проходя- проходящий между двумя соседними ребрами ячейки. Переменные s_x и s_y — размеры ячейки в направлении осей х и у соот- соответственно, а переменные ох и оу — координаты левого нижнего угла ячейки (рис. 12.13). Программный код этой функции представлен ниже. void draw_one(int num, int i, int j, double a, double b, double c, double d) [ox +sx, oy+sy) (°x + sx, oy) Рис. 12.13. Вычерчивание ли- линейного сегмента /* num = тип ячейки */ /* i, j = индексы в массиве ячеек */ /* a, b, с, d = значения функции в четырех вершинах ячейки */ /* s_x, s_y = длины ребер ячейки */ /* Здесь необходимо вычислить координаты левого нижнего угла ячейки по значениям индексов i и j */ switch(num) /* тип варианта */ 12.2. Поля превышений и линии уровня 503
case 1: case 14: xl=ox; yl=oy+s_y; x2=ox+s_x; y2=oy; break; /* Другие варианты */ } /* Вычертить сегмент */ glBegin(GL_LINES); glVertex2d(xl, yl); glVertex2d(x2, y2); glEnd(); } Полный текст программы вы найдете в файле contour.с, который находится на ftp-сер- ftp-сервере по адресу ftp.cs.unm.edu в каталоге /pub/angel/BOOK. На рис. 12.14 показаны две ли- линии уровня для функции овалов Кассини j{x, у) = {x2+y2+a2J-4a2x2-b\ рассчитанные при значениях я=0,49 и 6=0,5. В том изображении, которое показано на рисун- рисунке слева, использовался простейший вариант деления ребра ячейки пополам, а справа показа- показано, как сглаживается линия уровня, если применяется линейная интерполяция. Функ- Функция была представлена массивом выборок размером 50x50. На рис. 12.15 показаны линии уровня (топографические горизонта- горизонтали), построенные по данным топографиче- топографической съемки острова Гонолулу в группе Га- Гавайских островов. Рис. 12.14. Линии уровня овала Кассини: а — построенная с помощью алгоритма деления ребра пополам; б — построенная с помощью интерполяции выборок Рис. 12.15. Топографические горизонтали на кар- карте острова Гонолулу Существуют и другие методы построения линий уровня. Один из них состоит в том, что сначала отыскивается любая ячейка, через которую проходит линия, а затем отслеживается ее траектория, целенаправленным переходом из текущей ячейки в ту, куда "ушла" линия конту- 504 Глава 12. Визуализация данных научных исследовании
pa. Этот метод кажется более производительным, поскольку анализируются не все ячейки подряд, а только те из них, через которые обязательно проходит линия уровня (раз уж она "нырнула" в ячейку, то обязательно должна из нее и "вынырнуть"), но он таит опасность упустить один из контуров линии уровня. Кроме того, достоинство рассмотренного выше ме- метода в том, что анализ одной ячейки не зависит от другой, а следовательно, при наличии со- соответствующих средств ячейки можно анализировать параллельно. Кроме того, рассмотрен- рассмотренный метод без особого труда переносится в трехмерное пространство, о чем вы узнаете в раз- разделе 12.4. С некоторыми вариантами этого метода вы сможете познакомиться и в литературе, рекомендуемой в конце главы для самостоятельной проработки. 12.3. Визуализация поверхностей и скалярных полей Задача отображения значения функции двух переменных и линий уровня, которую мы рассматривали в предыдущем разделе, можно расширить на случай трех переменных. Рас- Рассмотрим функцию/ определенную в пространстве трех независимых переменных. В каждой точке пространства функция характеризуется некоторым скалярным значением/(дг, у, г), и мы говорим, что функция/определяет скалярное поле {scalar field). Например, функция /может характеризовать плотность вещества в каждой точке пространства или интенсивность погло- поглощения рентгеновских лучей тканями тела человека, которая измеряется методами компью- компьютерной томографии, или коэффициент прозрачности в каждой точке стеклянной отливки. Ви- Визуализация скалярного поля — задача намного более сложная, чем визуализация функции двух переменных, и на то есть две основные причины. Во-первых, скалярное поле характери- характеризуется намного большим массивом данных, следовательно, для выполнения даже рутинных операций вроде чтения файла или канонических преобразований приходится изобретать спе- специальные ускоренные методы. Во-вторых, при визуализации функции двух переменных мож- можно использовать третью координату трехмерного пространства для представления значения функции. В результате получаем наглядное изображение поведения функции на области из- изменения независимых переменных в виде рельефа. При работе с функцией трех переменных руки у нас связаны — все три координаты трехмерного пространства уже заняты тремя неза- независимыми переменными. Тем не менее можно распространить рассмотренные ранее методы и на визуализацию скалярных полей. 12.3.1. Объемное множество данных Как и в рассмотренной задаче визуализации функции двух переменных, будем считать, что в нашем распоряжении имеется массив значений функции, сформированный либо по результатам измерений реального процесса, либо вычислением аналитически заданной функции в точках {xhyh zk}. Такой массив будем называть объемным множеством данных {volumetric data set). Как и прежде, будем считать, что выборки сняты на равномерной сетке по координатам х,у, z, но на сей раз трехмерной (рис. 12.16). Предположение о равномерности сетки не явля- является принципиальным, но оно упростит понимание сути предлагаемого метода. Итак, имеем //* =Axh УР Zk), где х, = Хо+/'Лх, yj=yo+jAy, Каждое значение/^ можно считать результатом усреднения скалярного поля в правильном параллелепипеде со сторонами Ах, Ау, Ас, центр которого находится в точке {х„ур zk). Такой 12.3. Визуализация поверхностей и скалярных полей 505
Ay Л / 'Az Дх Рис. 12.16. Объемное множество данных элементарный параллелепипед будем называть векселем {voxel) по аналогии с пикселем — элементарной областью двухмерной плоскости. Трехмерный массив значений скалярного поля, соответствующих вокселям, называется структурированным множеством данных {structured data set), поскольку при его хранении нет смысла хранить еще и информацию о размещении ка- каждой выборки в пространстве. Термины структу- структурированное множество данных и множество вокселей {set of voxels) зачастую используют как синонимы. Данные, снятые в произвольных точках про- пространства, должны храниться совместно с коорди- координатами соответствующих точек, а потому их приня- принято называть неструктурированным множеством данных. Визуализация неструктурированного мно- множества данных выполняется несколько сложнее, но, в конце концов, все сводится к тем же самым мето- методам. Поэтому эту проблему мы специально исследо- исследовать не будем. Способов наглядного представления скалярного поля можно придумать даже больше, чем способов отображения функции двух переменных. Однако из всего множества наибольшую популярность завоевали два — непосредственное отображение объема и построение изоповерхностей. При непосредственном отображении объема в фор- формировании изображения "принимает участие" каждый воксель, а при построении изоповерх- изоповерхностей в конкретном изображении используется только подмножество вокселей. Для функ- функции Дх, у, z) изоповерхность определяется уравнением в неявной форме: Лх, у,=) = с. Значение константы с — уровень изоповерхности. Наша задача — отыскать приемлемый способ аппроксимации изоповерхности по заданному множеству вокселей. 12.3.2. Визуализация функций, заданных в неявной форме Задача визуализации изоповерхностей функции трех переменных является естественным расширением задачи построения линий уровня функции двух переменных. Рассмотрим функ- функцию трех переменных, заданную в неявной форме: g(x,y,=) = 0, где функция g представляет собой некоторое аналитическое выражение. Множество точек, удовлетворяющих этому уравнению, образуют одну или несколько трехмерных поверхно- поверхностей. Общеизвестными примерами являются сфера, плоскость, квадратичные поверхности, тор радиуса г, имеющий радиус поперечного сечения а: {х2 + у2 + :2- г2 - а2J - 4а V - ~2) = 0. Хотя функция g и позволяет выяснить, принадлежит ли точка {х, у, z) заданной поверхности, при построении поверхности она может играть только вспомогательную роль. Нам придется самостоятельно изобрести метод построения поверхности. Один из способов основывается на упрощенной форме трассировки лучей, который полу- получил название приведения луча {ray casting). На рис. 12.17 показаны наблюдатель, поверх- поверхность, соответствующая функции, и картинная плоскость. Любой проецирующий луч можно описать параметрическим уравнением р@ = ро + /d. 506 Глава 12. Визуализация данных научных исследований
Перепишем это уравнение в виде системы уравнений для компонентов: y z{t) = г0 + td:. Подставив их в неявное уравнение поверхности, получим скалярное уравнение относительно неизвестной /: g(x0 + tdx, у0 + tdy, z0 + td.) = u(t) = 0. Решением уравнения является точка пересечения проецирующего луча с поверхностью, при- причем решение может быть не единственным. Последнее означает, что луч пересекает поверх- поверхность многократно. Если g— простая функция, например уравнение квадратичной поверхно- поверхности или тора, то уравнение u(t) можно решить аналитически. Если g представляет квадратич- квадратичную поверхность, то и является квадратным уравнением относительно неизвестной / и имеет не больше двух действительных корней, которые находятся по известному алгебраическому выражению, знакомому всем со школьной парты. Если же функция g имеет более сложный вид, например полинома более высокой степени, или включает тригонометрические или экс- экспоненциальные функции, то решать уравнение придется численными методами. у g(x,y,z)=O Рис. 12.17. Приведение луча при отображении поверх- поверхности, заданной функцией в неявном виде Вычислив координаты точки (точек) пересечения проецирующего луча с поверхностью, мы можем применить к этой поверхности одну из моделей закрашивания. Нужные для этого компоненты вектора нормали можно найти с помощью частных производных функции g в точке пересечения: дх ду dg(x,y,z) Обычно при отображении таких поверхностей нас не очень заботит проблема глобального освещения. Следовательно, нет необходимости вычислять параметры теней, т.е. опреде- 12.3. Визуализация поверхностей и скалярных полей 507
лять, освещается ли данная точка поверхности от определенного источника, или парамет- параметры отраженных лучей. Для сцен, состоящих из простых квадратичных поверхностей, при- приведение лучей является не только способом отображения, но и позволяет проанализиро- проанализировать видимость поверхности и часто используется в сочетании с моделями, созданными в рамках конструктивной геометрии тел. Но для функций, более сложных, чем квадратич- квадратичные, объем вычислений, требуемых для определения точки пересечения проецирующего луча с поверхностью, становится основным лимитирующим фактором, и приходится ис- искать альтернативные методы отображения. Сформулируем более общую постановку задачи — отображать не поверхности, а объемы, т.е. рассматривать не поверхность, описываемую уравнением g(x, у, г) = 0, а скалярное поле J(x, у, z), определенное в каждой точке пространства. Если нас интересует только определен- определенное значение функции/, скажем значение с, то в этом случае мы имеем дело с задачей ото- отображения изоповерхности скалярного поля: g(x, у, г) =Дх, у, z) - с = 0. Иногда вполне достаточно сформировать изображение одной изоповерхности. В частности, это относится к приложениям компьютерной томографии, где пользователя интересуют области с определенным уровнем поглощения тканями рентгеновских лучей. Но существуют и приложе- приложения, в которых необходимо выводить на экран изображения нескольких изоповерхностеи. Ниже мы рассмотрим метод построения изоповерхности скалярного поля, заданного объ- объемным множеством данных — выборок, измеренных на равномерной сетке. Этот метод по- получил название метода маркированных кубиков и является расширением метода маркиро- маркированного квадрата на трехмерное пространство независимых переменных. 12.4. Изоповерхности и метод маркированных кубиков Пусть имеется массив выборок {fl/k} скалярного поля J{x, у, z)— множество вокселей. Требуется по этим выборкам построить изоповерхность в форме полигональной сети. Для за- заданного значения с может существовать единственная изоповерхность, ни одной изоповерх- изоповерхности или несколько. Учитывая, что графическая система лучше всего справляется с задачей отображения трехмерных треугольников, разработаем метод, который позволит отыскать вершины изоповерхности и аппроксимировать ее множеством треугольников. Этот метод по- получил наименование метода маркированных кубиков. Будем считать, что выборки сняты на равномер'ной сетке, узлы которой совпадают с цен- центрами вокселей. Если это не так, то всегда можно использовать ту или иную процедуру ин- интерполяции и получить из исходного массива неструктурированных данных множество во- вокселей (см. упр. 12.7). Восемь соседних узлов про- пространственной сетки образуют одну ячейку в форме кубика (рис. 12.18). Воксель (i,j, к) в углу этой ячейки имеет значение/у*. Теперь на основе имеющихся зна- значений вокселей следует проанализировать, проходит ли изоповерхность через данную ячейку, и если про- проходит, то отыскать координаты вершин соответст- соответствующего участка. Для данного значения уровня изоповерхности с можно промаркировать черным и белым цветом вершины ячейки, в зависимости от того, превышает значение соответствующего вокселя с или нет. Су- Существует 28=256 возможных вариантов маркировки отдельной ячейки. Но приняв во внимание имеющуюся симметрию отдельных вариантов, все разнообразие сводится к 1.У+1, ¦1J, fc Рис. 12.18. Ячейка вокселей 508 Глава 12. Визуализация данных научных исследований
14 уникальным случаям (рис. 12.19) . Точки пересечения поверхности с ребрами ячейки можно вычислить, используя тот же метод интерполяции, который применялся при об- обработке квадратных ячеек в методе маркированных квадратов (см. раздел 12.2). После определения координат точек пересечения формируется участок изоповерхности в виде одного или нескольких треугольников (рис. 12.20). 7 8 9 10 11 12 13 Рис 12.19. Варианты маркировки ячеек вокселей 8 10 11 12 13 Рис. 12.20. Элементарные участки изоповерхности в ячейках векселей Как и квадратные ячейки, которые раньше использовались для формирования линий уровня, кубические ячейки вокселей можно обрабатывать независимо друг от друга. Значение каждого внутреннего вокселя (т.е. вокселя, не расположенного по краям области существова- существования скалярного поля) влияет на расположение участков изоповерхности в восьми соседних ячейках. Самый простой алгоритм — обрабатывать ячейки последовательно, строка за стро- строкой, слой за слоем. После обработки очередной ячейки сформированный в ней участок изоповерхности в виде одного или нескольких треугольников передается в конвейер визуализации графической систе- системы. Поскольку этот алгоритм довольно просто можно распараллелить, метод маркированных квадратиков довольно широко используется для визуализации трехмерных данных. Этот метод одновременно служит и для сжатия информации, имеющейся в исходном массиве вокселей, и для моделирования. И имитационные программы, и программы отображения способны форми- формировать множества вокселей объемом от 107 до 109 выборок. При таком объеме данных даже простые операции (считывание данных с диска, масштабирование или иные подобные преобра- 2 В статье авторов этого метода Лоренсена (Lorensen) и Клайна (Cline) [Lor87] и во множестве ста- статей их последователей упоминается 15 уникальных вариантов, но в приведенном ими наборе два варианта все-таки симметричны. 12.4. Изоповерхности и метод маркированных кубиков 509
б) в) Рис. 12.21. Проблема неоднозначности при анализе маркированной про- пространственной ячейки: а — марки- маркированная ячейка; бив — два вари- варианта интерпретации зования) становятся весьма обременительными для системы в смысле как временных, так и ап- аппаратных ресурсов. Но в большинстве приложений после анализа множества вокселей и по- построения изоповерхности объем данных сокращается до 103-104 трехмерных треугольников. С таким количеством графических объектов современная система компьютерной графики справ- справляется без особого труда. Построенную поверхность можно в реальном времени поворачивать, масштабировать, тонировать и т.д. В общем случае только небольшая часть вокселей принимает участие в формировании определенной изоповерхности, что и объясняет высокий коэффициент сжатия информации. Изображение омара на ил. 12 цветной вклейки сформировано описанным методом. Изображение изоповерхности тонировано, что позволило наглядно передать мельчайшие детали ее рельефа. При работе с маркированными кубиками возникает та же проблема неоднозначности, которую мы рассматривали выше применительно к маркированным квадратам. Неодно- Неоднозначность появляется в том случае, когда воксели с одинаковой маркировкой расположены по диа- диагонали грани ячейки. Рассмотрим вариант марки- маркировки ячейки, показанный на рис. 12.21,а. На рис. 12.21, б,в показаны возможные варианты ин- интерпретации такой маркировки. Совершенно оче- очевидно, что эти варианты приводят к разному виду изоповерхности в районе этой ячейки. Неверный выбор проявится в виде разрыва изоповерхности, которая в других местах будет выглядеть доволь- довольно гладкой. Удовлетворительное решение этой задачи, которое одинаково хорошо срабатывало бы во всех возможных случаях, до сих пор не найдено. Как и при работе с маркированны- маркированными квадратами, для ее решения требуется дополнительная информация, которая отсутству- отсутствует в исходном наборе выборок. 12.5. Непосредственное отображение объема Представление скалярного поля набором изоповерхностей имеет недостаток, который яв- является "продолжением достоинств" этой методики, — при построении изоповерхностей зна- значительная часть информации, содержащейся во множестве вокселей, утрачивается (страницей выше мы отмечали это как достоинство метода, поскольку оно позволяет "сжимать информацию"). Следовательно, не исключается возможность того, что наиболее интересная часть информации ускользнет от внимательного глаза исследователя только по- потому, что соответствующий участок поля попал "между" изоповерхностями. Метод непосред- непосредственного отображения объема {direct volume rendering) позволяет сформировать изобра- изображение, в котором "принимают участие" все воксели исходного множества. Эта методика яв- является развитием методов создания совмещенного изображения, о которых шла речь в главе 9. Поскольку, как правило, воксели образуют регулярную пространственную сетку, то при известном положении наблюдателя всегда можно отыскать такой порядок просмотра во- вокселей, который позволит формировать изображение либо от ближайших объектов к более дальним, либо в обратном порядке — от более дальних к ближайшим. В самых первых разработках методов этого типа воксель рассматривался как маленький кубик, который может быть либо совершенно прозрачным, либо абсолютно непрозрачным (поглощающим свет). Если изображение формировалось в порядке от ближних объектов к дальним, то прослеживалась траектория лучей, проходящих через каждый пиксель картин- картинной плоскости, пока на пути луча не возникал непрозрачный воксель, соответствующий 510 Глава 12. Визуализация данных научных исследований
пиксель окрашивался в черный цвет. Если на пути луча нигде не встречался непрозрачный воксель, пиксель окрашивался в белый цвет. Если же изображение формировалось в по- порядке от дальних объектов к ближним, то алгоритм "маляра" закрашивал на экране только области, соответствующие непрозрачным вокселям. В любом варианте качество сформи- сформированного изображения было весьма далеко от идеального. Существенное повышение ка- качества было достигнуто только после применения палитры цветов и варьируемого коэф- коэффициента прозрачности. 12.5.1. Управление цветом и коэффициентом прозрачности Описание методов непосредственного отображения объемов начнем с анализа методов управления цветом и коэффициентом прозрачности вокселей. В качестве примера рассмот- рассмотрим данные, полученные при компьютерной томографии головы человека. Значению меры поглощения рентгеновского излучения поставим в соответствие определенные цвета. Мягкие ткани, которые характеризуются низкой поглощающей способностью, могут быть окрашены в красный цвет, ткани со средней поглощающей способностью — в синий, а ткани с высоким поглощением — в белый. Ткани, прозрачные для рентгеновского излучения, окрашиваются в черный цвет. Чаще всего соответствие цветов значениям интенсивности скалярного поля ус- устанавливается в результате анализа гистограммы распределения интенсивностей. Гистограм- Гистограмма, представленная на рис. 12.22, имеет че- четыре пика. Каждому из пиковых значений можно назначить определенный цвет и, ис- используя механизм индексирования цветов, | сформировать таблицу соответствия цвета. >\ Интенсивность каждого из основных цве- ; тов — красного, синего и зеленого — мо- >,! жет линейно изменяться в каком-либо ин- интервале значений плотности скалярного поля, как показано на рис. 12.23. Если вы- выборки скалярного поля получены с помо- помощью компьютерной томографии головы, то, скорее всего, костям черепа будут соот- „ ветствовать значения интенсивности более [ низкого пика в левой части гистограммы и 'I им мы назначим белый цвет, а пустым про- i странствам — значения/крайнего правого " пика, и им мы назначим черный цвет. Интенсивность поля Интенсивность поля U Интенсивность поля Интенсивность поля Рис. 12.22. Гистограмма данных, полученных Рис. 12.23. Кривые интенсивности основных цве- при компьютерной томографии тов для цветового представления интенсив- интенсивности скалярного поля 12.5. Непосредственное отображение объема 511
Значения коэффициентов прозрачности присваиваются исходя из того, какие воксели жела- желательно подчеркнуть в изображении поля. Если желательно выделить в изображении ткани моз- мозга, а кости черепа сделать "прозрачными", то следует назначить наибольшее значение коэффи- коэффициента прозрачности именно значениям интенсивности, характерным для костных тканей. В принципе назначение цветов и коэффициентов прозрачности выполняется на основе методов, исследуемых в теории распознавания образов, а потому в данной книге мы не будем в них уг- углубляться. В графических системах разработчики выходят из положения следующим образом — в распоряжение пользователя предоставляются средства ручной настройки таблиц соответствия цветов и коэффициента прозрачности. Это позволяет ему на свой вкус и на основании собствен- собственного опыта настраивать систему отображения. Нас же больше интересует, как сформировать изображение после того, как подобная настройка уже каким-то способом выполнена. 12.5.2. Отображение скалярного поля с помощью отпечатков После того как со значениями интенсивности поля будут ассоциированы цвета и коэффи- коэффициенты прозрачности, нам нужно ассоциировать с каждым вокселем геометрический объект определенной формы и далее применить методы совмещения изображений, описанные в гла- главе 9. Один из них основывается на методике отображения объектов от дальнего к ближнему (алгоритм "маляра"). Рассмотрим группу вокселей, показанную на рис. 12.24. Обычно, когда мы говорим ближний, имеется в виду ближний по отношению к наблюдателю. Для трехмер- трехмерного скалярного поля, если уж мы расположили наблюдателя относительно осей этого поля, термин ближний будет определять порядок обработки массива вокселей. Эффективный меха- механизм хранения данных о множестве вокселей, который позволяет изменять порядок обработ- обработки в зависимости от положения наблюдателя, предоставляет в наше распоряжение 8-арные деревья (octrees). Их мы рассматривали в главе 8. Порядок обхода такого 8-арного дерева оп- определяется размещением наблюдателя. Проще всего сформировать изображение, наглядно передающее информацию о фор- форме скалярного поля, с помощью отпечатков (footprint). Каждый воксель представляется в виде простой геометрической фигуры, и эти фигуры проецируются на картинную плос- плоскость. На рис. 12.25 показаны воксель в виде сферы и его отпечаток на картинной плос- плоскости. Обращаю ваше внимание на то, что если используется параллельная проекция и все воксели представлены одинаковыми геометрическими объектами, то их отпечатки будут отличаться только цветом и прозрачностью. Следовательно, можно обойтись без проецирования каждого вокселя, а заменить эту операцию наложением растровых обра- образов отпечатков на изображение, формируемое в буфере кадра. Воксель s s У У Рис 12.24. Массив вокселей 512 Рис. 12.25. Отпечаток вокселя на картинной плоскости Глава 12. Визуализация данных научных исследований
Форма отпечатка выбирается исходя из требований теории дискретных выборок, подоб- подобных тем, которые мы рассматривали в главе 9. Если процесс выборки данных считать иде- идеальным, то каждый отпечаток должен иметь вид трехмерной функции sine. Использование гексагональных или эллипсоидных отпечатков основано на аппроксимации вокселей парал- параллелепипедами или эллипсоидами. Лучший результат можно получить, если использовать ап- аппроксимацию отпечатками в виде функции Гаусса, которые получаются при проецировании трехмерного геометрического объекта в форме функции sine. Ключевым при формировании изображения, состоящего из отпечатков отдельных воксе- вокселей, является метод наложения отпечатков друг на друга с учетом их прозрачности. Посколь- Поскольку исходные данные представляют собой выборки на регулярной сетке, они изначально от- отсортированы по расстоянию до наблюдателя или до картинной плоскости. Воксели можно обрабатывать в порядке от дальних к ближним, добавляя "взнос" каждого в формируемое изображение посредством наложения его отпечатка. Сначала формируется фоновое изобра- изображение, а затем в работу включается механизм альфа-смешивания отпечатков. Рассмотрим формулу смешивания полупрозрачных пикселя-источника и пикселя-приемника: a,. Если обработка вокселей идет в порядке от дальних к ближним и пиксель-источник не- непрозрачен (а, = 1), то пиксель-приемник также становится непрозрачным и ему присваивает- присваивается цвет пикселя-источника. Если же пиксель-источник прозрачен, то цвет и коэффициент прозрачности пикселя-приемника сохраняются. Для реализации такого метода формирования изображения можно воспользоваться функцией смешивания OpenGL, заменив значение цвета С каждого вокселя значением ссС и используя в качестве коэффициентов смешивания пиксе- пикселя-источника и пикселя-приемника соответственно значения 1 и l-av. Основное преимущест- преимущество метода формирования изображения от дальних вокселей к ближним состоит в том, что можно обрабатывать данные послойно, как бы накладывая друг на друга двухмерные изобра- изображения. При этом даже нет нужды держать весь массив вокселей в оперативной памяти в про- процессе формирования изображения. Правда, "эта медаль" имеет и обратную сторону — при послойной обработке утрачивается внутренняя связь между соседними вокселями, располо- расположенными в разных слоях, а учет этих связей потенциально мог бы несколько ускорить про- процесс. Если, например, ближайшие к наблюдателю слои непрозрачны, то только они и будут присутствовать в окончательном изображении, а время, затраченное на обработку дальних слоев, оказывается потраченным впустую. 12.5.3. Трассировка лучей в скалярном поле Альтернативой описанному методу является формирование изображения в порядке от ближних вокселей к дальним посредством трассировки лучей (рис. 12.26). Пользуясь теми же формулами смешивания, можно определить момент, когда трассируемый луч "упрется" в непрозрачный воксель, и прекратить после этого дальнейшую трассировку данного луча. Недостаток алгоритма в том, что трассировка каждого луча требует просмотра массива во- вокселей "в глубину", а следовательно, приходится держать весь массив исходных данных в оперативной памяти. Выбор между методом обработки вокселей в порядке от дальних к ближним или от ближ- ближних к дальним во многом напоминает выбор между методами отображения, опирающимися на пространство изображения или на пространство объектов. Фактически мы просто добави- добавили в этот процесс учет прозрачности. Следовательно, алгоритм трассировки лучей в объеме скалярного поля способен на основании того же массива данных создавать изображение, в котором наглядно передается объемность. Но за это приходится платить увеличением коли- 12.5. Непосредственное отображение объема 513
чества вычислительных операций, поскольку при любом изменении параметров наблюдателя необходимо полностью повторить все расчеты. Поэтому такой алгоритм не применяется в приложениях, предполагающих интенсивный диалог с пользователем. б) Рис. 12.26. Приведение лучей: а — трехмерный вид; б — вид сверху 12.5.4. Наложение текстуры на объем Технология наложения текстур играет весьма важную роль в приложениях, использующих анимацию, а потому современные графические системы комплектуются специальной памя- памятью довольно большого объема для хранения и обработки текстур. Если наложение текстуры выполняется в основном на аппаратном уровне, то это практически не сказывается на скоро- скорости формирования окончательного изображения. Если же речь идет о программной реализации этого процес- процесса, то в версию OpenGL 1.2 включены средства под- поддержки наложения трехмерных текстур. Предположим, что в графической системе доста- достаточно памяти для работы с текстурами, что позволяет разместить в ней весь массив данных скалярного поля. В таком случае можно определить множество плоско- плоскостей, параллельных картинной и рассекающих объем скалярного поля, и отобразить координаты текстуры на мировые координаты таким образом, что эти плос- плоскости образуют множество параллельных многоуголь- многоугольников (рис. 12.27). После этого можно накладывать текстуры на образовавшиеся многоугольники. По- Поскольку количество многоугольников невелико — не более нескольких сотен, то процесс наложения текстур на них не слишком загрузит специализированные аппаратные средства. В отличие от других методов визуализации скалярных полей, метод с применением наложения текстур реализует- реализуется достаточно быстро, и его можно использовать в приложениях, ориентированных на интен- интенсивный диалог с пользователем. Рис 12.27. Формирование многоугольни- многоугольников в трехмерной памяти текстур 514 Глава 12. Визуализация данных научных исследований
12.6. Визуализация векторных полей Векторное поле отличается от скалярного тем, что в каждой его точке определено не от- отдельное значение, а л-мерный вектор. В результате имеем трехмерный объем, в каждой точке которого определена «-мерная вектор-функция f(x, у, z). Чаще всего исследуются трехмерные векторные поля, в которых каждая точка характеризуется трехмерным вектором. Примером может служить задача исследования распределения скоростей в потоке жидкости. При этом в каждой точке потока измеряется скорость — трехмерный вектор. В двухмерном случае можно рассматривать вектор-функцию f(x, у), определенную на не- некоторой поверхности. Например, спутниковые системы наблюдений измеряют набор пара- параметров в каждой точке земной поверхности. В частности, многоканальный спектро- анализатор Landsat Multispectral Sensor фиксирует интенсивность излучения в 13 спектраль- спектральных диапазонах. В этом случае аргументами вектор-функции f являются две координаты точ- точки на поверхности Земли. Такие данные, как правило, наглядно представляются в виде цвет- цветного двухмерного изображения. Значения данных сопоставляются с определенным цветом, по которому исследователь может оценить информацию, содержащуюся в данных измерений. Методику цветового кодирования мы рассмотрим в разделе 12.6.3. Для визуализации трехмерных векторных полей используются две технологии: значение поля в каждой точке передается либо цветом, либо с помощью геометрических объектов. 12.6.1. Отрезки переменной длины Будем считать, что векторное поле определено в объеме, имеющем форму параллелепи- параллелепипеда. В каждой точке этого объема известно значение трехмерного вектора данных. Наиболее очевидным решением кажется представление вектора в виде трехмерного ко- короткого отрезка, ориентация и длина которого пропорциональны соответствующим характе- характеристикам вектора. Эти отрезки получили название hedgehog (в дословном переводе — ко- колючки, иголки ежа). Сформировать изображение, состоящее из таких отрезков, не представ- представляет особого труда, если обратить внимание на некоторые нюансы процесса. Во-первых, поскольку трехмерные отрезки произвольной ориентации проецируются на двухмерную картинную плоскость, то длина штрихов в сформированном изображе- изображении зависит не только от исходных данных, но и от ориентации картинной плоскости (т.е. от положения наблюдателя). С этой проблемой можно справиться, применив цвето- цветовое кодирование. Например, можно сопоставить осям х, у, г цвета вершин RGB-куба и соответственно закрашивать проекции штрихов. Кроме того, графическая система должна позволить пользователю легко менять направление визирования объема и рас- рассматривать его с разных точек зрения. Во-вторых, если формировать отрезки в каждой точке объема, то штрихи в результирую- результирующем изображении наложатся друг на друга и разобраться в этой картинке будет невозможно. Эту проблему можно решить, либо выбирая для отображения точки пространства с каким-то шагом (например, выбирая каждую к-ю точку), либо отбирая только те точки, в которых мо- модуль вектор-функции превышает определенный порог. И в-третьих, нужно продумать, как масштабировать модуль отображаемой вектор- функции. Здесь потребуется отыскать "золотую середину" — штрихи на экране не должны быть ни слишком короткими, ни слишком длинными. Если в графической системе имеется достаточно памяти для хранения всего массива данных, решить эту проблему не представ- представляет особого труда, но она может доставить много хлопот, если весь массив не вмещается в имеющуюся память, а требуется визуализация динамического процесса в реальном мас- масштабе времени. 12.6. Визуализация векторных полей 515
I-1 На рис. 12.28 показано, как выглядит на экране изо- изображение векторного поля тока крови в сонной артерии человека. Исходные данные были собраны посредством измерения в точках трехмерного объема. В большинстве точек модуль измеренного вектора мал, поскольку через них не проходят кровеносные сосуды. Поэтому пороговая селекция позволяет исключить эти точки из результи- результирующего изображения. Кроме того, для отображения вы- выбираются не все точки объема, а каждая пятая. Рис. 12.28. Изображение векторного поля тока крови в сон- сонной артерии человека, сформированное с помощью от- отрезков регулируемой длины 12.6.2. Бусинки Рис. 12.29. Использование бусинок в форме конуса для визуализа- визуализации векторного поля потока жидкости Можно представлять векторы в каждой точке поля не в виде штрихов, а небольшими объемными геомет- геометрическими объектами — бусинками (glyphs). Как пра- правило, форма бусинок выбирается такой, чтобы с ее помощью можно было наглядно передать ориентацию вектора, а модуль вектора отображается масштабом изображения бусинки. Примером может быть конус — колпачок (рис. 12.29). Центр основания конуса помещается в точку вы- выборки данных, а ориентация оси конуса соответствует ориентации измеренного вектора. Довольно часто в качестве бусинок используются разные варианты изо- изображения стрелок. 12.6.3. Цвет Цвет при визуализации векторных полей используется либо как основное средство на- наглядного представления, либо как дополнение к другим средствам — отрезкам или бусинкам. Чаще всего с помощью цвета передается информация о модуле вектор-функции в той или иной точке поля, но иногда он используется и для того, чтобы привлечь внимание наблюда- наблюдателя к определенным данным. Можно использовать и кодирование компонент трехмерной вектор-функции тремя основными цветами и таким образом передать цветом ориентацию вектора. Правда, как показывает практика, наблюдателю довольно сложно интерпрети- интерпретировать такое представление ориентации. При использовании индексируемых цветов нам потребуется сначала сформировать таблицу соответ- соответствия цветов. Индексом таблицы может служить значение модуля вектор-функции в заданном интер- интервале. Затем каждому значению индекса нужно при- присвоить определенные значения интенсивности ос- основных цветов (рис. 12.30). Индекс Красный Зеленый Синий 0 1 255 0.0 0.0 1.0 1.0 0.9 1.0 0.0 0.1 1.0 Рис 12.30. Заполнение таблицы со- соответствия цветов 516 Глава 12. Визуализация данных научных исследований
Проще всего при назначении цветов отдельным строкам таблицы соответствия восполь- воспользоваться термальной шкалой. Человеку свойственно ассоциировать холодные тона — синий и зеленый — с данными, не несущими особой информации, а теплые тона — красный, желтый и белый — с данными, представляющими определенный интерес. Следовательно, если харак- характер исследуемого процесса таков, что на векторы большей длины следует обратить особое внимание исследователя, то имеет смысл в начальных строках таблицы разместить максимально интенсивный холодный цвет— зеленый, а по мере возрастания ч значения индекса переходить к голубому, | синему, фиолетовому, красному, желтому ^ и завершить белым. Такая функция рас- распределения цветов наглядно представляет- представляется цветовым кубом. Нужно стремиться по возможности назначать цвета, соответст- соответствующие ребрам цветового куба. На рис. 12.31 показана последовательность об- обхода ребер цветового куба при заполнении таблицы, а на рис. 12.32— функции соот- ч ветствия интенсивности отдельных цве- « товых компонентов и модуля отображае- <у мой вектор-функции. Модуль вектор-функции 3 \ ж i Модуль вектор-функции U Ф Рис. 12.31. Последовательность на- назначения цветов при формирова- формировании термальной цветовой шкалы Модуль вектор-функции Рис. 12.32. Соответствие интенсивности от- отдельных цветовых компонентов и модуля отображаемой вектор-функции Если при визуализации векторного поля используются бусинки, то их можно раскрасить с помощью такой таблицы соответствия цветов, что позволит более наглядно представить на- наблюдателю характер исследуемого поля. 12.6.4. Треки частиц и линии потока Одно из наиболее распространенных приложений методов визуализации векторных по- полей — визуализация разного рода потоков частиц в жидкостях и газах. В таких приложениях исходный массив данных представляет собой выборки компонент вектора скорости частиц в каждой точке исследуемого объема, причем это может быть единственный массив, характе- 12.6. Визуализация векторных полей 517
ризующий поле скоростей в определенный момент времени, либо несколько массивов дан- данных, снятых в разные моменты. Как правило, при исследовании потоков пользователя интересует не столько распределе- распределение скоростей, сколько траектории частиц в таком потоке. Эту информацию можно наглядно представить двумя способами. Треки частиц {particle track) представляют собой траектории частиц, перемещающихся в потоке жидкости. Следовательно, для визуализации трека нужно поместить частицу в вирту- виртуальный объем и использовать имеющиеся данные наблюдений для трассировки ее траектории по мере протекания вещества (жидкости или газа) через этот объем. В общем случае для ре- решения этой задачи потребуются массивы данных, снятые в разные моменты времени, но если поток является стационарным, можно обойтись и данными о векторном поле скоростей, за- зафиксированными в какой-то один момент времени. Линия потока {streamline) представляет собой кривую, полученную интегрированием скоростей при заданных начальных условиях. В стационарном потоке частицы будут пе- перемещаться вдоль этих кривых. Технология отображения треков частиц и линий потока одна и та же, а отличие существует только в алгоритме обработки данных первичных измерений. Предположим, что в нашем распоряжении имеется векторное поле распределения скоро- скоростей в определенном объеме, снятое в момент времени /. Выберем произвольную точку в этом объеме: v(/) z(n В исходном массиве данных имеются компоненты вектора скорости в этой точке: 4@" v(r) = Если поместить мысленно в эту точку материальную частицу, то через небольшой интервал времени, т.е. в момент / + И, эта частица будет примерно в точке р(/ + И) = р(/) + hx(t), как показано на рис. 12.33. Эта точка— следующая на траектории движения частицы. Если поток стационарный, то эта точка также принадлежит линии потока р(/). Новая точка будет лежать в одной из ячеек, образованных сеткой дискретизации первичных измерений. Следующую точку траектории частицы можно вычислить по той же фор- формуле, но подставив в нее данные из элемента исходного мас- массива, ближайшего к только что сформированной точке, либо воспользовавшись билинейной интерполяцией данных пер- первичных измерений. Если известно, что поток нестационарный, то интервал h выбирается равным интервалу съема первичных данных и для вычисления следующей точки траектории берутся данные из очередного массива либо, если интервал между очередными Рис. 12.33. Вычисление точек измерениями не равен И, значение в момент t+h формируется линии потока с помощью линейной интерполяции. 518 Глава 12. Визуачизация данных научных исследований
12.7. Визуализация тензорных полей Данные, характеризующие состояние тензорного поля в каждой точке объема, также яв- являются распределенными в некоторой окрестности этой точки. Например, можно измерять напряжения в каждой точке конструкции, которые вызываются внешними силами. Напряже- Напряжение в точке (х, у, :) описывается матрицей размера 3x3 (тензором ранга 2): М = т.. т.... о. Диагональные члены матрицы представляют нормальные напряжения (силы, действующие на единицу площади), которые либо сжимают, либо растягивают материал по нормали к граням элементарного куба в данной точке. Члены матрицы, лежащие по обе стороны от диагонали, представляют силы, которые стремятся "перекосить" элементарный куб, превратить его в па- параллелепипед, как показано на рис. 12.34. а) 6) Рис. 12.34. Напряжения, действующие на элементарный куб конструк- конструкции: а — элементарный куб; б — нормальные напряжения; в — скашивающие напряжения Замена матрицы 3x3 матрицей 1x9 (тензором ранга 1) приведет к утере части информа- информации, заключенной во внутренней структуре массива 3x3; следовательно, для визуализации та- такого тензорного поля нельзя применить те методы, которые мы использовали для визуализа- визуализации векторных полей. Поле напряжений в конструкции можно наглядно отобразить, напри- например, с помощью маленьких кубиков, каждый из которых "перекошен" под воздействием локальных напряжений в соответствии со значениями элементов матрицы напряжений 3x3. Другой метод — отобразить на экране свойства локальных матриц напряжения в каждой точ- точке объема. Если, например, матрица симметрическая — именно так обстоит дело в большин- большинстве случаев, — то ее можно разложить на собственные числа и собственные векторы, т.е. можно представить матрицу М в виде М = VyAV, где матрица Л является диагональной, а V есть матрица вращения, т.е. Vr= V. Три диаго- диагональных элемента матрицы Л — Хи Х2 и А3 — это собственные числа матрицы М, а столбцы матрицы V — ее собственные векторы. Собственные числа являются одной из главных ха- характеристик матрицы. С тремя собственными числами матрицы можно ассоциировать эллипсоид. Изоповерхно- сти квадратичной формы х'Мх сами по себе являются квадратичными, поскольку 12.7. Визуализация тензорных попей 519
Подстановка у = Vx вместо х изменяет вид квадратичной формы: х'Мх = yrV'MVy = у'Лу = ^ХУ; . Если все три собственных числа матрицы положительны, то квадратичная поверхность явля- является эллипсоидом и матрица V поворачивает исходный фрейм таким образом, что его оси становятся параллельны главным осям эллипсоида. Собственные числа характеризуют длины трех главных осей эллипсоида. Поскольку форма эллипсоида однозначно соответствует виду матрицы тензора, то для визуализации такого тензорного поля удобно использовать бусинки в форме эллипсоида, которые наглядно представят информацию о характеристиках матрицы напряжения в каждой точке объема. Собственные векторы определяют ориентацию эллип- эллипсоида в пространстве. 12.8. Резюме В этой главе мы сосредоточили внимание на задачах визуализации больших массивов данных, получаемых при исследованиях в самых разных областях науки и техники. Нагляд- Наглядное представление таких данных требует применения всего арсенала средств компьютерной графики, рассмотренных в предыдущих главах этой книги. Пользуясь средствами таких API, как OpenGL, не так уж сложно сформировать изображе- изображения поверхностей и линий уровня при визуализации функций двух переменных. Данные, характеризующие скалярное поле в определенном объеме, можно наглядно представить и с помощью изоповерхностей или тонированием объема. В первом случае на экран выводятся трехмерные поверхности, объединяющие точки объема с равными значе- значениями характеристической скалярной величины. Такие трехмерные поверхности отобра- отображаются с помощью полигональной сети, которая эффективно отображается существую- существующими аппаратными и программными средствами стандартных систем компьютерной гра- графики. При тонировании объема скалярного поля характеристики цвета и прозрачности отдельных вокселей ставятся в соответствие величине напряженности скалярного поля, но такой метод затрудняет организацию интерактивного режима просмотра, поскольку со- сопряжен с большим числом вычислений. Методы визуализации векторных полей играют большую роль при отображении потоков жидкостей и газов. В общих чертах были описаны разнообразные методы представления век- векторов отрезками переменной длины, простейшими геометрическими телами — бусинками, линиями потока и треками частиц. Наибольшие сложности вызывает визуализация тензорных полей, причем эти сложности являются следствием ограниченных возможностей объемной графики для представления многокомпонентных тензоров состояния. 12.9. Рекомендуемая литература Метод маркированных квадратов и кубиков был разработан и внедрен в практику нагляд- наглядного представления результатов научных исследований Лоренсоном (Lorenson) и Клайном (Kline) в работе [Lor87]. В дальнейшем метод неоднократно модифицировался различными исследователями. Проблемы неоднозначности интерпретации результатов маркировки обсу- обсуждаются в работе [Van94]. О первых работах, посвященных визуализации данных о распре- распределении скалярных полей в трехмерном объеме, сообщалось в конце 70-х годов в статьях Германа (Herman) [Her79] и Фуча (Fuchs) [Fuc77]. Метод трассировки лучей при визуализа- визуализации скалярных полей впервые описан Левоем (Levoy) в работе [Lev88]. Другие методы рас- 520 Глава 12. Визуализация данных научных исследований
смотрены в работах [Wes90], [Wit94,a] и [Сго97]. Достаточно много информации о совре- современных методах визуализации читатель найдет в работах [Gal95, Nie97J и особенно на стра- страницах журнала IEEE Transactions on Visualization and Computer Graphics. Упражнения 12.1. Разработайте программу отображения массива значений {/J,} в виде • мозаики разноцветных прямоугольников; • сети прямоугольных ячеек; • линий уровня. 12.2. Модифицируйте программу отображения сети таким образом, чтобы она формиро- формировала тонированную поверхность. 12.3. Доработайте программу отображения сети и включите в нее возможность наложе- наложения текстуры на сформированное изображение поверхности. 12.4. Используйте при формировании изображения сети полиномиальные поверхности третьего порядка, описанные в главе 9. 12.5. Сформируйте таблицу соответствия цветов, которая позволит передать разными цветами 16 вариантов маркировки прямоугольной ячейки при прохождении через нее линии уровня. 12.6. Существует 256 вариантов маркировки элементарного куба при пересечении его изоповерхностью. После учета симметрии это множество вариантов сводится к 14 уникальным. Сколько вариантов из исходного множества соответствует каждому из уникальных? 12.7. Реализуйте программно метод маркированного куба. В вариантах маркировки, до- допускающих неоднозначную интерпретацию, используйте интерпретацию по собст- собственному выбору. 12.8. Трехмерные данные можно наглядно представить с помощью локальных нормалей к каждой ячейке или группе соседних ячеек. Разработайте программу, которая бу- будет по заданному множеству линий уровня формировать нормаль к участку поверх- поверхности, проходящему через заданную ячейку. 12.9. Имеется массив значений функции в восьми вершинах правильного параллелепипе- параллелепипеда. Разработайте метод трилинейной интерполяции, который позволит получить значение функции в любой внутренней точке объема, ограниченного параллелепи- параллелепипедом, подобно тому, как метод билинейной интерполяции позволяет получить значение функции внутри прямоугольной области. 12.10. Разработайте метод построения изоповерхности, аналогичный методу маркирован- маркированного квадрата, но в качестве элементарного объема используйте тетраэдр. 12.11. Разработайте программу визуализации потока жидкости, которая будет выводить на экран траектории линий потока в виде трубок разного цвета с наложением теней. 12.12. Разработайте программу формирования собственных чисел и собственных векторов матрицы 3 х 3 и отображения их в виде эллипсоида. 12.13. Разработайте программу визуализации скалярного поля, использующую в качестве отображаемого примитива только точки. Плотность точек в определенной области должна соответствовать усредненному значению напряженности поля. Упражнения 521
ПРИЛОЖЕНИЕ А Демонстрационные программы В приложении А представлены исходные тексты некоторых программ, которые кратко рассматривались в разных главах этой книги. Эти и другие программы можно так- также загрузить с ftp-сервера ftp.cs.unm.edu из каталога /pub/angel/BOOK. Инфор- Информацию об изменениях, внесенных в тексты программ, можно получить на Web-сервере http//www.cs.unm.edu/"angel. Компиляцию можно выполнять в большинстве реализаций опе- операционной системы UNIX, а соответствующие файлы управления компиляцией можно загру- загрузить с того же сервера. Тексты других учебных программ, в том числе и тех, которые входят в состав руководства OpenGL Programmer's Guide, а также библиотеки GLUT для разных версий операционных систем можно загрузить с Web-сервера SGI (http://www.sgi.com/software/ opengl) или с Web-сервера ассоциации пользователей OpenGL (http://www.opengl.org). Версии OpenGL существуют для всех типов графических станций Silicon Graphics и для операционных систем семейства Microsoft Windows (98 и NT). Информация о версиях OpenGL для других операционных систем представлена на Web-сервере ассоциации пользо- пользователей OpenGL по адресу http://www.opengl.org. Существует также свободно распростра- распространяемый комплект API, полностью совместимый с OpenGL, — программный пакет MESA, ко- который можно загрузить с Web-сервера http://www.ssec.wisc.edu/~brianp/Mesa.html. Па- Пакет работает под управлением большинства существующих операционных систем, в том числе и Linux. Существует также коммерческая версия пакета для Linux. Приведенные ниже программы взаимодействуют с операционной системой через биб- библиотеку GLUT, которая доступна по сети Internet. Имена функций соответствуют руково- руководствам OpenGL Programmer's Guide и GLUT Users Guide. Поскольку некоторые фрагменты "кочуют" из одной программы в другую, мы снабдили комментариями только первые вер- версии этих фрагментов. Мы отбирали для включения в книгу программы, руководствуясь, прежде всего, их дос- доступностью и читабельностью программного кода, принося подчас в жертву эффективность и производительность программы. В процессе самостоятельной работы вы сможете усовер- усовершенствовать их и добиться большей эффективности.
В данное приложение включены следующие программы: ¦ программа формирования 5000 точек двухмерного узора Серпинского (глава 2); ¦ программа формирования двухмерного узора Серпинского, использующая рекурсив- рекурсивный алгоритм (глава 2); ¦ программа формирования точек трехмерного узора Серпинского (глава 2); ¦ программа формирования трехмерного узора Серпинского, использующая рекурсив- рекурсивный алгоритм (глава 2); ¦ программа, демонстрирующая методику использования библиотеки GLUT для взаи- взаимодействия с операционной системой (глава 3); ¦ программа рисования (глава 3); ¦ программа формирования изображения вращающегося квадрата, использующая оди- одинарную и двойную буферизацию (глава 3); ¦ программа формирования изображения вращающегося куба (глава 4); ¦ программа формирования изображения вращающегося куба, использующая массив вершин (глава 4); ¦ программа формирования изображения вращающегося куба, использующая для управ- управления вращением виртуальный трекбол (глава 4); ¦ программа, демонстрирующая управление положением наблюдателя (глава 5); ¦ программа аппроксимации сферы методом рекурсивного разбиения тетраэдра (глава 6). А.1. Двухмерный узор Серпинского /* Построение точек двухмерного узора Серпинского способом деления пополам отрезков между произвольно выбранными вершинами */ iinclude <GL/glut.h> /* Оператор включения файла <glut.h> зависит от размещения файлов библиотек на конкретном компьютере */ /* Включение файла glut.h автоматически включает в программу gl.h и glu.h */ void myinit() { /* Атрибуты */ /* Цвет фона - белый */ glClearColorA.0, 1.0, 1.0, 1.0); /* Цвет переднего плана - красный */ glColor3fA.0, 0.0, 0.0); /* Установка параметров отображения */ /* Окно 500 х 500 */ /* Начало экранных координат в левом нижнем углу */ 524 Приложение А. Демонстрационные программы
glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D@.0, 500.0, 0.0, 500.0); glMatrixMode(GL_MODELVIEW); /* -- */ void display() { /* Определение типа данных, представляющего в программе */ /* точку двухмерного пространства */ typedef GLfloat point2[2]; /* Треугольник */ point2 vertices[3]={{0.0,0.0}, {250.0,500.0}, {500.0,0.0}}; int i, j, k; int rand(); /* Стандартный генератор случайных чисел */ point2 p ={75.0,50.0}; /* Произвольно выбранная исходная */ /* точка внутри треугольника */ glClear(GL COLOR BUFFER BIT); /* Очистить окно */ /* Вычислить и вывести на экран 5000 точек узора */ for( k=0; k<5000; k++) { j=rand()%3; /* Выбрать случайно одну из вершин */ /* Вычислить точку на полпути между выбранной */ /* вершиной и прежней точкой */ р[0] = (p[0]+vertices[j][0])/2.0; = (p[l]+vertices[j][l])/2.0; /* Вычертить новую точку */ glBegin(GL_POINTS); glVertex2fv(p); glEnd(); } glFlush(); /* Очистить буферы */ A.I. Двухмерны и узор Серпинского 525
/* */ void main(int argc, char** argv) /* Стандартная процедура инициализации библиотеки GLUT */ glutlnit(&argc,argv); /* Дублируются */ /* установки по умолчанию. */ /* Эти операторы можно опустить. */ glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutInitWindowSizeE00,500); /* Окно 500 х 500 пикселей */ glutInitWindowPosition@,0); /* Начало координат */ glutCreateWindow("Sierpinski Gasket"); /* Заголовок окна */ /* Регистрация функции отображения. */ /* Функция будет вызвана при открытии окна. */ glutDisplayFunc(display); myinit(); /* Установка атрибутов */ glutMainLoop(); /* Запустить цикл обработки событий */ /* Конец файла */ А.2. Рекурсивный алгоритм построения узора Серпинского /* Построение двухмерного узора Серпинского рекурсивным разбиением треугольника */ /* Количество циклов задается в командной строке при вызове программы */ linclude <GL/glut.h> typedef float point2[2]; /* Исходный треугольник */ point2 v[]={{-1.0, -0.58}, {1.0, -0.58}, {0.0, 1.15}}; int n; /* */ void triangle( point2 a, point2 b, point2 c) /* Отобразить треугольник */ glBegin(GLJL'RIANGLES); 526 Приложение А. Демонстрационные программы
glVertex2fv(a); glVertex2fv(b); glVertex2fv(c); glEnd(); /* */ void divide_triangle(point2 a, point2 b, point2 c, int m) { /* Разбиение треугольника */ point2 vO, vl, v2; int j; if(m>0) for(j=0; j<2; j++) v0[j]= for(j=0; j<2; j++) vl[j]=(a[j]+c[j])/2; for(j=0; j<2; j++) v2[j]=(b[j]+c[jJ)/2; divide_triangle(a, vO, vl, m-1); divide_triangle(c, vl, v2, m-1); divide_triangle(b, v2, vO, m-1); } else(triangle(a,b,c)); /* Вычертить треугольник */ /* */ void display() { glClear(GL_COLOR_BUFFER_BIT); divide_triangle(v[O], v[l], v[2], n); glFlush(); /* */ void myinit() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluOrtho2D(-2.0, 2.0, -2.0, 2.0); glMatrixMode(GL_MODELVIEW); glClearColor A.0, 1.0, 1.0, 1.0); glColor3f@.0,0.0,0.0); /* */ void main(int argc, char **argv) { n=atoi(argv[l]); glutlnit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB ); A.2. Рекурсивный алгоритм построения узора Серпинского 527
glutInitWindowSizeE00, 500); glutCreateWindow("Sierpinski Gasket"); glutDisplayFunc(display); myinitj); glutMainLoop(); Конец файла */ А.З. Трехмерный узор Серпинского /* Программа формирования трехмерного узора Серпинского */ iinclude <GL/gl.h> iinclude <GL/glu.h> ¦include <GL/glx.h> linclude <GL/glut.h> /* Определение типа данных, представляющего точку в трехмерном пространстве */ typedef struct { float x,y,z;} point3; /* Исходный тетраэдр */ point3 vertices[4] = {{0,0,0},{250,500,100}, {500,250,250},{250,100,250}}; int j; point3 new, old={250,100,250}; /* */ void clear() { glClear(GL_COLOR_BUFFER_BIT); /* __„ */ /* Вычисление и вывод на экран новой точки узора */ void display() { long rand(); int i; j=rand()%4; /* Выбор вершины случайным образом */ /* Вычисление координат точки, стоящей на середине пути между вершиной и прежней точкой узора */ new.x = (old.x+vertices[j].x)/2; new.у = (old.y+vertices[j].y)/2; new.z = (old.z+vertices[j].z)/2; /* Вывод точки на экран */ glBegin(GL_POINTS); glColor3fA.0-new.z/250.,new.z/250.,0.)i glVertex3f(new.x, new.y,new.z); 528 Приложение А. Демонстрационные программы
glEnd(); /* Заменить прежнюю точку новой */ old.x=new.x; old.y=new.y; old.z=new.z; glFlush(); /* */ void mouse(int btn, int state, int x, int y) { if(btn==GLUT_LEFT_BUTTON&state==GLUT_DOWN) glutldleFunc(display); if(btn==GLUT_MIDDLE_BUTTON&state==GLUT_DOWN) glutldleFunc(NULL); if(btn==GLUT_RIGHT_BUTTON&state==GLUT DOWN) exit(); /* */ int main(int argc, char** argv) { glutlnit(barge,argv); glutlnitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutInitWindowSizeE00,500); glutlnitWindowPosition@,0); glutCreateWindow("Sierpinski Gasket"); glutldleFunc (display); glutMouseFunc (mouse); glClearColorA.0, 1.0, 1.0, 0.0); /* Цвет фона - белый */ glColor3fA.0, 0.0, 0.0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho@.0, 500.0, 0.0, 500.0, -500.0, 500.0); glMatrixMode(GL_MODELVIEW); glutDisplayFunc(clear); glutMainLoop(); } /* Конец файла */ A.4. Рекурсивный алгоритм построения трехмерного узора Серпинского /* Построение трехмерного узора Серпинского рекурсивным разбиением тетраэдра */ /* Количество циклов задается в командной строке А. 4. Рекурсивный ачгоритм построения трехмерного узора Серпинского 529
при вызове программы */ finclude <stdlib.h> linclude <GL/glut.h> typedef float point3[3]; /* Исходный тетраэдр */ point3 v[]={{0.0, 0.0, 1.0}, {0.0, 0.942809, -0.33333}, {-0.816497, -0.471405, -0.333333}, {0.816497, -0.471405, -0.333333}}; static GLfloat theta[] = {0.0,0.0,0.0}; int n; /* */ /* Отображение треугольника. В режиме проволочного изображения вычерчивается контур из трех отрезков. Для закрашивания фиксированным цветом используется нормаль в одной точке. Для закрашивания изменяющимся цветом используются три нормали в вершинах. */ void triangle( point3 a, point3 b, point3 с) { glBegin(GL_POLYGON); glNormal3fv(a); glVertex3fv(a); glVertex3fv(b); glVertex3fv(c); glEnd(); /* */ /* Разбиение треугольника. */ void divide triangle(point3 a, point3 b, point3 c, int m) { point3 vl, v2, v3; int j; if(m>0) for(j=0; j<3; j for(j=0; j<3; j++) v2[j]= for(j=0; j<3; j++) v3[j]= divide_triangle(a, vl, v2, m-1); divide triangle(c, v2, v3, m-1); divide triangle(b, v3, vl, m-1); } else(triangle(a,b,c)); /* Вычертить треугольник */ 530 Приложение А. Демонстрационные программы
/* */ /* Применение алгоритма разбиения треугольника к граням тетраэдра */ void tetrahedron( int m) { glColor3f(l.О,0.0,0.0); divide triangle(v[0], v[l], v[2], m); glColor3f@.0,1.0,0.0); divide triangle(v[3], v[2], v[l], m); glColor3f@.0,0.0,1.0); divide triangle(v[0], v[3], v[l], m); glColor3f@.0,0.0,0.0); divide triangle(v[0], v[2], v[3], m); /* */ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity(); tetrahedron(n); glFlush(); /* */ void myReshape(int w, int h) { glviewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-2.0, 2.0, -2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho(-2.0*(GLfloat)w/(GLfloat)h, 2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glutPostRedisplay(); /* - */ void main(int argc, char **argv) { n=atoi(argv[l]); glutlnit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSizeE00, 500); A.4. Рекурсивный алгоритм построения трехмерного узора Серпинского 531
glutCreateWindow(D Gasket"); glutReshapeFunc(myReshape); glutDisplayFunc(display); glEnable(GL_DEPTH_TEST); glClearColor A.0, 1.0, 1.0, 1.0); glutMainLoop(); Конец файла */ A.5. Программа вычерчивания квадрата /* Программа демонстрирует методику использования функций из библиотеки GLUT для взаимодействия с операционной системой управления окнами */ /* Программа открывает окно, очищает его - устанавливает черный цвет фона, а затем вычерчивает квадрат в том месте, где пользователь щелкнет левой кнопкой мыши. Щелчок правой кнопкой мыши завершает выполнение программы. При перемещении окна и изменении его размеров программа очищает окно. */ ¦include <GL/glut.h> /* Глобальные переменные */ GLsizei wh = 500, ww = 500; /* исходные размеры окна */ GLfloat size =3.0; /* половина длины стороны квадрата */ /* */ void drawSquare(int x, int у) { y=wh-y; glColor3ub( (char) rand()%256, (char) rand(I256, (char) rand()%256); glBegin(GL_POLYGON); glVertex2f(x+size, y+size); glVertex2f(x-size, y+size); glVertex2f(x-size, y-size); glVertex2f(x+size, y-size); glEnd(); glFlush(); /* */ /* Перерисовка изображения в окне. Программа вызывается при изменении положения или размеров окна. */ void myReshape(GLsizei w, GLsizei h) { /* Настройка рамки отсечения */ 532 Приложение А. Демонстрационные программы
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho@.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); /* Настройка параметров видового окна и очистка окна */ glviewport@,0,w,h); glClearColor @.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); /* Установка глобальных переменных размеров окна. Переменные будут использоваться подпрограммой отображения */ ww = w; wh = h; /* */ void myinit() { glviewport@,0,ww,wh); /* Установка размеров отсекающей рамки в соответствии с размерами окна. Такая настройка позволяет избежать масштабирования координат объектов при изменении размеров окна. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho@.0, (GLdouble)ww , 0.0, (GLdouble)wh , -1.0, 1.0); /* Установка черного цвета фона и очистка окна */ glClearColor @.0, 0.0, 0.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); /* Регистрация функции обработки события изменения размеров окна */ glutReshapeFunc(myReshape); /* */ void mouse(int btn, int state, int x, int y) { if(btn==GLUT_RIGHT_BUTTON && state==GLUT_DOWN) exit(); A.5. Программа вычерчивания квадрата 533
/* - — */ /* Функция отображения. Библиотека GLUT 3.0 требует, чтобы такая функция была обязательно определена в программе.*/ void display() /* */ int main(int argc, char** argv) { glutlnit(&argc,argv); glutlnitDisplayMode (GLUTJINGLE | GLUT_RGB); glutCreateWindow("square"J; myinit (); glutReshapeFunc (myReshape); glutMouseFunc (mouse); glutMotionFunc(drawSquare); glutDisplayFunc(display); glutMainLoop(); } /* Конец файла */ A.6. Программа рисования /* Простая программа, формирующая на экране текст, отрезки прямых, треугольники, прямоугольники и точки */ «define NULL О «define LINE 1 «define RECTANGLE 2 «define TRIANGLE 3 «define POINTS 4 «define TEXT 5 «include <GL/glut.h> void mouse(int, int, int, int); void key(unsigned char, int, int); void display(); void drawSquare(int, int); void myReshape(GLsizei, GLsizei); void myinit(); void screen_box(int, int, int); void rightjnenu(int); void middlejnenu(int); void color_menu(int); 534 Приложение А. Демонстрационные программы
void pixeljnenu(int); void filljnenu(int); int pick(int, int); /* Глобальные переменные */ GLsizei wh = 500, ww = 500; /* исходные размеры окна */ GLfloat size=3.0; /* половина длины стороны квадрата */ int drawjnode =0; /* режим рисования */ int rx, ry; /* позиция растра */ GLfloat r = 1.0, g = 1.0, b = 1.0; /* цвет переднего плана */ int fill = 0; /* флаг заливки */ /* */ void drawSquare(int x, int у) { y=wh-y; glColor3ub( (char) rand()%256, (char) rand()%256, (char) rand()%256); glBegin(GL_POLYGON); glVertex2f(x+size, y+size); glVertex2f(x-size, y+size); glVertex2f(x-size, y-size); glVertex2f(x+size, y-size); glEnd(); /* */ /* Программа перерисовки при изменении размеров или положения окна */ void myReshape(GLsizei w, GLsizei h) { /* adjust clipping box */ Настройка рамки отсечения */ glMatrixMode(GL_PROJECTION); glLoadIdentity(J; glOrtho@.0, (GLdouble)w, 0.0, (GLdouble)h, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(J; /* Настройка видового окна и его очистка */ glViewport@,0,w,h); glClearColor @.8, 0.8, 0.8, 1.0); glClear(GL_COLOR_BUFFER_BIT); displayoT glFlush(); A.6. Программа рисования 535
/* Установка глобальных размеров окна. Эти данные потребуются программе вывода изображения */ ww = w; wh = h; /* */ void myinit() { glViewport@,0,ww, wh); /* Установка размеров отсекающей рамки в соответствии с размерами окна. Такая настройка позволяет избежать масштабирования координат объектов при изменении размеров окна. */ glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho@.0, (GLdouble)ww , 0.0, (GLdouble)wh , -1.0, 1.0); /* Установка черного цвета фона и очистка окна */ glClearColor @.8, 0.8, 0.8, 1.0); glClear(GL_COLOR_BUFFER_BIT); glFlush(); /* */ void mouse(int btn, int state, int x, int y) { static int count; int where; static int xp[2],yp[2]; if(btn==GLUT_LEFT_BUTTON && state==GLUT_DOWN) { glPushAttrib(GL_ALL_ATTRIB_BITS); where = pick(x,y); glColor3f(r, g, b); if(where != 0) { count =0; drawjnode = where; } else switch(drawjnode) { case(LINE): if(count==0) 536 Приложение А. Демонстрационные программы
count++; xp[O] = x; yp[O] = y; else glBegin(GL_LINES); glVertex2i(x,wh-y); glVertex2i(xp[0],wh-yp[0]); glEnd(); draw_mode=0; count=0; } break; case(RECTANGLE): if(count == 0) { count++; xp[0] = x; yp[0] = y; } else { if(fill) glBegin(GL_POLYGON); else glBegin(GL_LINE_LOOP); glVertex2i(x,wh-y); glVertex2i(x/wh-yp[0]); glVertex2i(xp[0],wh-yp[0]); glVertex2i(xp[0],wh-y); glEnd(); draw_mode=0; count=0; } break; case (TRIANGLE): switch(count) { case@): count++; xp[0] = x; УР[0] = у; break; case(l): count++; xp[l] = x; yp[i] = y; break; caseB): A. 6. Программа рисования 537
if(fill) glBegin(GL_POLYGON); else glBegin(GL_LINE_LOOP); glVertex2i(xp[0 ],wh-yp[0]); glVertex2i(xp[1],wh-yp[1]); glVertex2i(x,wh-y); glEnd(); draw_mode=0; count=0; break; case(POINTS): { drawSquare(x,y); count++; } break; case(TEXT): { rx=x; ry=wh-y; glRasterPos2i(rx,ry); count=0; glPopAttrib( glFlush(); /* */ int pick(int x, int y) { у = wh - y; if(y < wh-ww/10) return 0; else if(x < ww/10) return LINE; else if(x < ww/5) return RECTANGLE; else if(x < 3*ww/10) return TRIANGLE; else if(x < 2*ww/5) return POINTS; else if(x < ww/2) return TEXT; else return 0; /* - -— */ void screen_box(int x, int y, int s ) { glBegin(GL_QUADS); glVertex2i(x, y); glVertex2i(x+s, y); 538 Приложение А. Демонстрационные программы
glVertex2i(x+s, y+s); glVertex2i(x, y+s); glEnd(); /* void right_menu(int id) { if(id == 1) exit(); else display(); /* */ void middle_menu(int id) {} /* */ void color menu(int id) { if(id == 1) {r = 1.0; g = 0.0; b = 0.0;} else if(id == 2) {r = 0.0; g = 1.0; b = 0.0;} else if(id == 3) {r = 0.0; g = 0.0; b = 1.0;} else if(id == 4) {r = 0.0; g = 1.0; b = 1.0;} else if(id == 5) {r = 1.0; g = 0.0; b = 1.0;} else if (id == 6) {r = 1.0; g = 1.0; b = 0.0;} else if(id == 7) {r = 1.0; g = 1.0; b = 1.0;} else if(id == 8) {r = 0.0; g = 0.0; b = 0.0;} /* void pixel_menu(int id) if (id == 1) size = 2 * size; else if (size > 1) size = size/2; /* - void fill_menu(int id) { if (id == 1) fill = 1; else fill = 0; /* - */ void key(unsigned char k, int xx, int yy) { if(draw_mode!=TEXT) return; glColor3f@.0,0.0,0.0); glRasterPos2i(rx,ry); glutBitmapCharacter(GLUT_BITMAP_9_BY_15, k); A. 6. Программа рисования 539
/*glutStrokeCharacter(GLUT_STROKE_ROMAN,i); */ rx+=glutBitmapWidth(GLUT_BITMAP_9_BY_15,к); /* */ void display() { int shift=0; glPushAttrib(GL_ALL_ATTRIB_BITS); glClearColor @.8, 0.8, 0.8, 1.0); glClear(GL_COLOR_BUFFER_BIT); glColor3fA.0, 1.0, 1.0); screen_box@,wh-ww/10,ww/10); glColor3fA.0, 0.0, 0.0); screen_box(ww/10,wh-ww/10,ww/10); glColor3f@.0, 1.0, 0.0); screen_box(ww/5,wh-ww/10,ww/10); glColor3f@.0, 0.0, 1.0); screen_boxC*ww/10,wh-ww/10,ww/10); glColor3fA.0, 1.0, 0.0); screen_boxB*ww/5,wh-ww/10,ww/10); glColor3f@.0, 0.0, 0.0); glBegin(GL_LINES); glVertex2i(wh/40,wh-ww/20); glVertex2i(wh/40+ww/20,wh-ww/20); glEnd(); glBegin(GL_TRIANGLES); glVertex2i(ww/5+ww/40,wh-ww/10+ww/40); glVertex2i(ww/5+ww/20,wh-ww/40); glVertex2i(ww/5+3*ww/40,wh-ww/10+ww/40); glEnd(); glPointSizeC.0); glBegin(GL_POINTS); glVertex2iC*ww/10+ww/20, wh-ww/20); glEnd(); glRasterPos2iB*ww/5,wh-ww/20); glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'A'); shift=glutBitmapWidth(GLUT_BITMAP_9_BY_15, 'A'); glRasterPos2iB*ww/5+shift,wh-ww/20); glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'В'); shift+=glutBitmapWidth(GLUT_BITMAP_9_BY_15, 'В'); glRasterPos2iB*ww/5+shift,wh-ww/20); glutBitmapCharacter(GLUT_BITMAP_9_BY_15, 'С'); glFlush(); glPopAttrib(); 540 Приложен ие А. Демонстрационны е программы
/* — */ int main(int argc, char** argv) { int cjnenu, pjnenu, f_menu; glutlnit(&argc,argv); glutlnitDisplayMode (GLUT_SINGLE | GLUT_RGB); glutCreateWindow("square"J; glutDisplayFunc(display); cjnenu = glutCreateMenu(color jnenu); glutAddMenuEntry("Red", 1); glutAddMenuEntry("Green", 2); glutAddMenuEntry("Blue", 3); glutAddMenuEntry("Cyan", 4); glutAddMenuEntry("Magenta",5); glutAddMenuEntry("Yellow",6); glutAddMenuEntry("White", 7); glutAddMenuEntry("Black",8); pjnenu = glutCreateMenu(pixeljnenu); glutAddMenuEntry("increase pixel size", 1); glutAddMenuEntry("decrease pixel size", 2); fjnenu = glutCreateMenu(filljnenu); glutAddMenuEntry("fill on", 1); glutAddMenuEntry("fill off", 2); glutCreateMenu(rightjnenu); glutAddMenuEntry("quit",1); glutAddMenuEntry("clear",2); glutAttachMenu(GLUT_RIGHT_BUTTON); glutCreateMenu(middle_menu); glutAddSubMenu("Colors", c_menu); glutAddSubMenu("Pixel Size", pjnenu); glutAddSubMenu("Fill", fjnenu); glutAttachMenu(GLUT_MIDDLE_BUTTON); myinit (); glutReshapeFunc (myReshape); glutKeyboardFunc(key); glutMouseFunc (mouse); glutMainLoop(); Конец файла */ A.7. Программа отображения с двойной буферизацией /* * single double.с * Программа демонстрирует методику использования одинарной и * двойной буферизации при создании динамического изображения. А. 7. Программа отображения с двойной буферизацией 541
* Щелчок левой кнопкой мыши запускает движение квадрата по * экрану, а щелчок средней кнопкой прекращает движение. */ finclude <GL/glut.h> iinclude <stdlib.h> static GLfloat spin =0.0; int singleb, doubleb; /* -— */ void displayd() { glClear (GL_COLOR_BUFFER_BIT); glRectf (-25.0, -25.0, 25.0, 25.0); glutSwapBuffers (); /* */ void displays() { glClear (GL COLOR BUFFER BIT); glRectf (-25.0, -25.0, 25.0, 25.0); glFlush(); /* */ void spinDisplay () { spin = spin + 2.0; if (spin > 360.0) spin = spin - 360.0; glutSetWindow(singleb); glLoadldentityO; glRotatef (spin, 0.0, 0.0, 1.0); glutPostRedisplay(); glutSetWindow(doubleb); glLoadldentityO; glRotatef (spin, 0.0, 0.0, 1.0); glutPostRedisplay(); /* */ void myinit () { glClearColor @.0, 0.0, 0.0, 1.0); glColor3f A.0, 1.0, 1.0); glShadeModel (GL FLAT); } /* */ void mouse(int btn, int state, int x, int y) 542 Приложение А. Демонстрационные программы
if(btn==GLUT_LEFT_BUTTON && state==GLUTJ)OWN) glutldleFunc(spinDisplay); if(btn==GLUT MIDDLE BUTTON && state«GLUT DOWN) glutldleFunc(NULL); /* */ void myReshape(int w, int h) { glviewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w, 50.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0); else glOrtho (-50.0*(GLfloat)w/(GLfloat)h, 50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -1.0, 1.0); glMatrixMode(GL_MODELVIEW); glLoadldentity "[); /* Основной цикл */ int main(int argc, char** argv) { glutlnit(&argc,argv); glutlnitDisplayMode (GLUTJINGLE | GLUT__RGB); singleb=glutCreateWindow(~single buffered"); myinit (); glutDisplayFunc(displays); glutReshapeFunc (myReshape); glutldleFunc (spinDisplay); glutMouseFunc (mouse); glutlnitDisplayMode (GLUT_DOUBLE | GLUT_RGB); doubleb=glutCreateWindow("double buffered"); myinit (); glutDisplayFunc(displayd); glutReshapeFunc (myReshape); glutldleFunc (spinDisplay); glutMouseFunc (mouse); glutMainLoop(); Конец файла */ A. 7. Программа отображения с двойной буферизацией 543
А.8. Программа отображения вращающегося куба /* Вращающийся куб. * При раскраске граней используется линейная * интерполяция цвета */ /* Программа демонстрирует использование преобразований * однородных координат и структуру данных, описывающую * топологию пространственного объекта. Подробно эта * структура данных рассматривается в главе 4. */ /* С каждой вершиной ассоциирован вектор нормали и цвет. */ /* Центр куба находится в начале координат, а потому * ненормированные компоненты векторов нормалей * совпадают с координатами соответствующих вершин */ ¦include <stdlib.h> ¦include <GL/glut.h> GLfloat vertices[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat normals[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat colors[][3] = {{0.0,0.0,0.0},{1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}}; /* */ void polygon(int a, int b, int с , int d) { /* Вычертить многоугольник, используя список вершин */ glBegin(GL_POLYGON); glColor3fv(colors[a]); glNormal3fv(normals[a]); glVertex3fv(vertices[a]); glColor3fv(colors[b]); glNormal3fv(normals[b]); glVertex3fv(vertices[b]); glColor3fv(colors[c]); glNormal3fv(normals[c]); glVertex3fv(vertices[c]); 544 Приложение А. Демонстрационные программы
glColor3fv(colors[d]); glNormal3fv(normals[d]); glVertex3fv(vertices[d]) glEnd(); /* — */ void colorcube() { /* Связь вершин с гранями */ polygon@,3,2,1); polygonB,3,7,6); polygon@,4,7,3); polygon(l,2,6,5); polygonD,5,6,7); polygon@,1,5,4); static GLfloat theta[] = {0.0,0.0,0.0}; static GLint axis = 2; /* */ void display() { /* Программа отображения очищает буфер кадра и z-буфер, * поворачивает куб, вычерчивает куб в новой * ориентации и переключает буферы. */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[l], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); colorcube(); glFlush(); glutSwapBuffers(); /* */ void spinCube() { /* Программа обработки простоя. * Программа поворачивает куб на 2 градуса * вокруг заданной оси. */ А.8. Программа отображения вращающегося куба 545
theta[axis] += 2.0; if( theta[axis] > 360.0 ) theta[axis] -= 360.0; /* display(); */ glutPostRedisplay(); /* */ void mouse(int btn, int state, int x, int y) { /* Программа обработки событий мыши. * Программа выбирает ось, вокруг которой * выполняется поворот. */ if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; /* */ void myReshape(int w, int h) { glViewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-2.0, 2.0, -2.0*(GLfloat)h/FLfloat)w, 2.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho(-2.0*(GLfloat)w/(GLfloat)h, 2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0, -10.0, 10.0); glMatrixMode(GL MODELVIEW); /* */ void main(int argc, char **argv) { glutlnit(&argc, argv); /* Задается режим двойной буферизации и обработки z-буфера */ glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUTJJEPTH)? glutInitWindowSizeE00, 500); glutCreateWindow("colorcube"); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutldleFunc(spinCube); glutMouseFunc(mouse); glEnable(GL_DEPTH_TEST); /* Разрешается режим удаления */ 546 Приложение А. Демонстрационные программы
/* невидимых поверхностей */ glutMainLoop(); } /* Конец файла */ А.9. Вращение куба с использованием массива вершин /* Вращающийся куб. * При раскраске граней используется линейная * интерполяция цвета */ /* Программа демонстрирует использование преобразований * однородных координат и структуру данных, описывающую * топологию пространственного объекта. Подробно эта * структура данных рассматривается в главе 4. */ /* С каждой вершиной ассоциирован вектор нормали и цвет. */ /* Центр куба находится в начале координат, а потому * ненормированные компоненты векторов нормалей * совпадают с координатами соответствующих вершин */ ¦include <stdlib.h> iinclude <GL/glut.h> GLfloat vertices[] = {-1.0,-1.0,-1.0,1.0,-1.0,-1.0, 1.0,1.0,-1.0, -1.0,1.0,-1.0, -1.0,-1.0,1.0, 1.0,-1.0,1.0, 1.0,1.0,1.0, -1.0,1.0,1.0}; GLfloat normals[] = {-1.0,-1.0,-1.0,1.0,-1.0,-1.0, 1.0,1.0,-1.0, -1.0,1.0,-1.0, -1.0,-1.0,1.0, 1.0,-1.0,1.0, 1.0,1.0,1.0, -1.0,1.0,1.0}; GLfloat colors[] * {0.0,0.0,0.0,1.0,0.0,0.0, 1.0,1.0,0.0, 0.0,1.0,0.0, 0.0,0.0,1.0, 1.0,0.0,1.0, 1.0,1.0,1.0, 0.0,1.0,1.0}; GLubyte cubelndices[]={0,3,2,1,2,3,7,6,0, 4,7,3,1,2,6,5,4,5,6,7,0,1,5,4}; static GLfloat theta[] * {0.0,0.0,0.0}; static GLint axis = 2; /* */ void display() { /* Программа отображения очищает буфер кадра и z-буфер, А. 9. Вращение куба с использованием массива вершин 547
* поворачивает куб, вычерчивает куб в новой * ориентации и переключает буферы. */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glRotatef(theta[0]f 1.0, 0.0, 0.0); glRotatef(theta[l], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, cubelndices); glBegin(GL_LINES); glVertex3f@.0,0.0,0.0); glVertex3fA.0,1.0,1.0); glEnd(); glFlush(); glutSwapBuffers(); /* */ void spinCube() /* Программа обработки простоя. * Программа поворачивает куб на 2 градуса * вокруг заданной оси. */ theta[axis] += 2.0; if( theta[axis] > 360.0 ) theta[axis] -= 360.0; glutPostRedisplay(); /* */ void mouse(int btn, int state, int x, int y) /* Программа обработки событий мыши. * Программа выбирает ось, вокруг которой * выполняется поворот. */ if(btn==GLUT_LEFT_BUTTON && state == GLUT_DOWN) axis = 0; if(btn==GLUT_MIDDLE_BUTTON && state == GLUT_DOWN) axis = 1; if(btn==GLUT_RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; /* */ void myReshape(int w, int h) 548 Приложение А. Демонстрационные программы
glviewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho(-2.0, 2.0, -2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)wf -10.0, 10.0); else glOrtho(-2.0*(GLfloat)w/(GLfloat)h, 2.0*(GLfloat)w/(GLfloat)h, -2.0, 2.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); /* _ */ void main(int argc, char **argv) { /* Задается режим двойной буферизации и обработки z-буфера */ glutlnit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSizeE00, 500); glutCreateWindow("colorcube"); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutldleFunc(spinCube); glutMouseFunc(mouse); glEnable(GL_DEPTH_TEST); /* Разрешается режим удаления */ /* невидимых поверхностей */ glEnableClientState(GL_COLOR_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); glVertexPointerC, GL_FLOAT, 0, vertices); glColorPointerC,GL_FLOAT, 0, colors); glNormalPointer(GL_FLOAT;0, normals); glColor3f(l.0,1.0,1.0); glutMainLoop(); } /* Конец файла */ A. 10. Вращающийся куб, управляемый трекболом /* Программа демонстрирует использование трекбола для управления вращением куба */ tinclude <math.h> ¦include <GL/glut.h> idefine bool int tdefine false 0 A. 10. Вращающийся куб, управляемый трекболом 549
tdefine true 1 int winWidth, winHeight; float angle =0.0, axis[3], trans[3]; bool trackingMouse = false; bool redrawContinue = false; bool trackballMove = false; /* */ /* Вычерчивание куба */ GLfloat vertices[][3] = { {-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0} GLfloat colors[][3] = { {0.0,0.0,0.0},{1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0} /* */ void polygon(int a, int b, int с , int d, int face) { /* Вычерчивание многоугольника с помощью списка вершин */ glBegin(GL_POLYGON); glColor3fv(colors[a]); glVertex3fv(vertices[a]); glColor3fv(colors[b]); glVertex3fv(vertices[b]); glColor3fv(colors[c]); glVertex3fv(vertices[c]); glColor3fv(colors[d]); glVertex3fv(vertices[d]); glEnd(); /* _ */ void colorcube() /* Связь вершин с гранями */ polygon(l,0,3,2,0); polygonC,7,6,2,1); polygonG,3,0,4,2); polygonB,6,5,l,3); polygonD,5,6,7,4); 550 Приложение А. Демонстрационные программы
polygonE,4,0,l,5); /* */ /* ** Управление движением с помощью трекбола. */ float lastPos[3] = {0.0F, 0.0Ff 0.0F}; int curx, cury; int startX, startY; /* _ */ void trackball_ptov(int x, int y, int width, int height, float v[3]) { float d, a; /* Проекция х,у на полусферу */ v[0] = B.0F*x - width) / width; v[l] = (height - 2.0F*y) / height; d = (float) sqrt(v[0]*v[0] + v[l]*v[l]); v[2] = (float) cos((M_PI/2.0F) * ((d < 1.0F) ? d : 1.0F)); a = 1.0F / (float) sqrt(v[0]*v[0] + v[l]*v[l] + v[2]*v[2]); v[0] *= a; v[l] *= a; v[2] *= a; /* */ void mouseMotion(int x, int y) { float curPos[3], dx, dy, dz; trackball_ptov(x, y, winWidth, winHeight, curPos); if(trackingMouse) { dx = curPos[0] - lastPos[0]; dy = curPos[l] - lastPos[l]; dz = curPos[2] - lastPos[2]; if (dx || dy || dz) { angle = 90.OF * sqrt(dx*dx + dy*dy + dz*dz); axis[0] = lastPos[l]*curPos[2] - lastPos[2]*curPos[l]; axis[l] = lastPos[2]*curPos[0] - lastPos[0]*curPos[2]; axis[2] = lastPos[0]*curPos[l] - lastPos[l]*curPos[0]; lastPos[0] = curPos[0]; lastPos[l] = curPos[l]; lastPos[2] = curPos[2]; A.JO. Вращающийся куб, управляемый трекболом 551
} } glutPostRedisplay(); } /* void startMotion(int x, int y) trackingMouse = true; redrawContinue = false; startX = x; startY = y; curx = x; cury = y; trackball_ptov(x, y, winWidth, winHeight, lastPos); trackballMove=true; /* void stopMotion(int x, int y) trackingMouse = false; if (startX != x || startY != y) { redrawContinue = true; } else { angle = 0.0F; redrawContinue = false; trackballMove = false; /* __ */ void display() { glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); /* Преобразование вида */ if (trackballMove) { glRotatef(angle, axis[0], axis[l], axis[2]); } colorcube(); glutSwapBuffers(); } /* */ void mouseButton(int button, int state, int x, int y) { if(button==GLUT_RIGHT_BUTTON) exit(); if(button==GLUT_LEFT_BUTTON) 552 Приложение А. Демонстрационные программы
switch(state) { case GLUT_DOWN: y=winHeight-y; startMotion( x,y); break; case GLUT_UP: stopMotion( x,y); break; /* -— */ void myReshape(int w, int h) { glViewport@, 0, w, h); winWidth = w; winHeight = h; /* */ void spinCube() { if (redrawContinue) glutPostRedisplay(); /* */ void main(int argc, char **argv) { glutlnit(Sargc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSizeE00, 500); glutCreateWindow("colorcube"); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutldleFunc(spinCube); glutMouseFunc(mouseButton); glutMotionFunc(mouseMotion); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-2.0, 2.0, -2.0, 2.0, -2.0, 2.0); glMatrixMode(GL_MODELVIEW); glutMainLoop(); } /* Конец файла */ A. 10. Вращающийся куб, управляемый трекболом 553
А.П. Изменение положения наблюдателя /* Вращение куба с изменением положения наблюдателя. * Программа рассматривалась в главе 5. * Структура данных, описывающая куб, и программа * отображения те же, что и в предыдущем примере. */ iinclude <stdlib.h> iinclude <GL/glut.h> GLfloat vertices[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat normals[][3] = {{-1.0,-1.0,-1.0},{1.0,-1.0,-1.0}, {1.0,1.0,-1.0}, {-1.0,1.0,-1.0}, {-1.0,-1.0,1.0}, {1.0,-1.0,1.0}, {1.0,1.0,1.0}, {-1.0,1.0,1.0}}; GLfloat colors[][3] = {{0.0,0.0,0.0},{1.0,0.0,0.0}, {1.0,1.0,0.0}, {0.0,1.0,0.0}, {0.0,0.0,1.0}, {1.0,0.0,1.0}, {1.0,1.0,1.0}, {0.0,1.0,1.0}}; /* */ void polygon(int a, int b, int с , int d) { glBegin(GL_POLYGON); glColor3fv(colors[a]); glNormal3fv(normals[a]); glVertex3fv(vertices[a]); glColor3fv(colors[b]); glNormal3fv(normals[b]); glVertex3fv(vertices[b]); glColor3fv(colors[c]); glNormal3fv(normals[c]); glVertex3fv(vertices[c]); glColor3fv(colors[d]); glNormal3fv(normals[d]); glVertex3fv(vertices[d]); glEnd(); /* */ void colorcube() { polygon@,3,2,1); polygonB,3,7,6); polygon@,4,7,3); polygon(l,2,6,5); polygonD,5,6,7); 554 Приложение А. Демонстрационные программы
polygon@,l,5,4); static GLfloat theta[] = {0.0,0.0,0.0}; static GLint axis = 2; static GLdouble viewer[]= {0.0, 0.0, 5.0}; /* Исходное положение наблюдателя */ /* - */ void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* Обновить положение наблюдателя */ glLoadIdentity(); gluLookAt(viewer[0],viewer[l],viewer[2], 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); /* Поворот куба */ glRotatef(theta[0], 1.0, 0.0, 0.0); glRotatef(theta[l], 0.0, 1.0, 0.0); glRotatef(theta[2], 0.0, 0.0, 1.0); colorcube(); glFlush(); glutSwapBuffers(); /* */ void mouse(int btn, int state, int x, int y) { if(btn==GLUT_LEFT BUTTON && state « GLUT DOWN) axis = 0; if(btn==GLUT MIDDLE BUTTON && state == GLUT DOWN) axis = 1; if(btn«GLUT~RIGHT_BUTTON && state == GLUT_DOWN) axis = 2; theta[axis] += 2.0; if( theta[axis] > 360.0 ) theta[axis] -= 360.0; display(); /* -- */ void keys(unsigned char key, int x, int y) { /* Положением наблюдателя управляют клавиши х, X, у, Y, z, Z */ if(key == 'x') viewer[0]-= 1.0; if(key == 'X') viewer[0]+= 1.0; A.ll. Изменение положения наблюдателя 555
if(key == 'у') viewer[l]-= 1.0; if(key == 'Y') viewer[l]+= 1.0; if(key == 'z') viewer[2]-= 1.0; if(key == 'Z') viewer[2]+= 1.0; display!)j /* __ */ void myReshape(int w, int h) { glViewport@, 0, w, h); /* Режим перспективной проекции */ glMatrixMode(GL_PROJECTION); glLoadldentity(); if(w<=h) glFrustum(-2.0, 2.0, -2.0*(GLfloat)h/(GLfloat)w, 2.0*(GLfloat)h/(GLfloat)w, 2.0, 20.0); else glFrustum(-2.0, 2.0, -2.0*(GLfloat)w/(GLfloat)h, 2.0*(GLfloat)w/(GLfloat)h, 2.0, 20.0); /* Можно использовать и функцию gluPerspective() */ /* gluPerspectiveD5.0, w/h, 1.0, 10.0); */ glMatrixMode(GL_MODELVIEW); /* */ void main(int argc, char **argv) { glutlnit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSizeE00, 500); glutCreateWindow("colorcube"); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutMouseFunc(mouse); glutKeyboardFunc(keys); glEnable(GL_DEPTH_TEST); glutMainLoop(); } /* Конец файла */ A. 12. Построение сферы /* Построение сферы рекурсивным разбиением тетраэдра. Программа подробно рассматривалась в главе 6. */ /* Режим 0 - каркасное изображение, Режим 1 - постоянная закраска граней, 556 Приложение А. Демонстрационные программы
Режим 3 - закраска граней с интерполяцией цвета */ linclude <stdlib.h> ¦include <GL/glut.h> typedef float point[4]; /* Исходный тетраэдр */ point3 v[]={{0.0, 0.0, 1.0}, {0.0, 0.942809, -0.33333}, {-0.816497, -0.471405, -0.333333}, {0.816497, -0.471405, -0.333333}}; static GLfloat theta[] = {0.0,0.0,0.0}; int n; int mode; /* */ void triangle( point3 a, point3 b, point3 c) /* Отображение одного треугольника */ { if (mode==0) glBegin(GL_LINE_LOOP); else glBegin(GL_POLYGON); if(mode==l) glNormal3fv(a); if(mode==2) glNormal3fv(a); glVertex3fv(a); if(mode==2) glNormal3fv(b); glVertex3fv(b); if(mode==2) glNormal3fv(c); glVertex3fv(c); glEnd(); /* */ void normal(point3 p) { /* Нормализация вектора */ double sqrt(); float d =0.0; int i; for(i=0; i<3; i++) d+=p[i]*p[i]; d=sqrt(d); if(d>0.0) for(i=0; i<3; i++) p[i]/=d; /* */ void divide_triangle(point3 a, point3 b, point3 c, int m) A. 12. Построение сферы 557
/* Разбиение треугольника */ point3 vl, v2, v3; int j; if(m>0) for(j=0; j<3; j++ normal(vl); for(j=0; j<3; j++ normal(v2); for(j=0; j<3; j++ ) vl[j]= ) v2[j]= ) v3[j]= normal(v3); divide_triangle(a, vl, v2, m-1); divide_triangle(c, v2, v3, m-1); divide_triangle(b, v3, vl, m-1); divide_triangle(vl, v3, v2, m-1); } else(triangle(a,b,c)); /* draw triangle at end of recursion */ /* */ void tetrahedron( int m) { /* Разбиение граней тетраэдра с помощью программы разбиения треугольников */ divide_triangle(v[O], v[l], v[2], m); divide_triangle(v[3], v[2], v[l], m); divide_triangle(v[O], v[3], v[l], m); divide~triangle(v[O], v[2], v[3], m); /* */ void display() { /* Отображение объекта в трех видах */ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) glLoadIdentity(); mode=0; tetrahedron(n); mode=l; glTranslatef(-2.0, 0.0,0.0); tetrahedron(n); mode=2; glTranslatef( 4.0, 0.0,0.0); tetrahedron(n); 558 Приложен ие А. Демонстрационные программы
glFlush(); } /* */ void myReshape(int w, int h) { glviewport@, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(J; if (w <= h) glOrtho(-4.0, 4.0, -4.0*(GLfloat)h/(GLfloat)wf 4.0*(GLfloat)h/(GLfloat)w, -10.0, 10.0); else glOrtho(-4.0*(GLfloat)w/(GLfloat)h, 4.0*(GLfloat)w/(GLfloat)h, -4.0, 4.0, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); display(); /* */ void myinit() { GLfloat mat_specular[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_diffuse[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_ambient[]={1.0, 1.0, 1.0, 1.0}; GLfloat mat_shininess={100.0}; GLfloat light ambient[]={0.0, 0.0, 0.0, 1.0}; GLfloat light~diffuse[]={1.0, 1.0, 1.0, 1.0}; GLfloat light_specular[]={1.0, 1.0, 1.0, 1.0}; /* Установка компонентов источника 0 */ glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL~LIGHT0, GL_SPECULAR, light_specular); /* Определение свойств материала */ glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_AMBIENT, mat ambient); glMaterialfv(GL FRONT, GL_DIFFUSE, mat~diffuse); glMaterialf(GL_FRONT, GL_SHININESS, mat_shininess); glEnable(GL_SMOOTH); /* режим плавного закрашивания */ glEnable(GL_LIGHTING); /* учитывать источники света */ glEnable(GL_LIGHT0); /* включить источник 0 */ glEnable(GL_DEPTH_TEST); /* разрешить работу с z-буфером */ glClearColor A.0, 1.0, 1.0, 1.0); glColor3f @.0, 0.0, 0.0); A.I2. Построение сферы 559
/* */ void main(int argc, char **argv) { n=atoi(argv[l]); glutlnit(&argc, argv); glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB | GLUT_DEPTH); glutInitWindowSizeE00, 500); glutCreateWindow("sphere"); myinit(); glutReshapeFunc(myReshape); glutDisplayFunc(display); glutMainLoop(); } /* Конец файла */ 560 Приложение А. Демонстрационные программы
ПРИЛОЖЕНИЕ Б Абстрактные пространства в компьютерной графике Математическое обеспечение систем компьютерной графики — это совокупность ме- методов представления и манипулирования множеством геометрических элементов, главными из которых являются точки и отрезки прямых. Эти методы базируются на свойствах нескольких типов абстрактных пространств. В приложение Б мы включили мате- материал об основных свойствах тех типов пространств, которые наиболее широко используются в средствах формирования и трансформации геометрических объектов в системах компью- компьютерной графики, — линейного (векторного), аффинного и евклидового. Линейное (векторное) пространство содержит объекты только двух типов: скаляры (действительные числа) и векто- векторы. В аффинном пространстве к ним добавляется третий тип объектов — точки. В евклидо- евклидовом пространстве вводится концепция расстояния. В математическом обеспечении компьютерной графики понятие вектор трактуется как направленный отрезок прямой, представленный совокупностью п чисел. В приложении В бу- будут представлены основные сведения из матричной алгебры — базового математического ин- инструмента, с помощью которого можно выполнять различные преобразования векторов. В данном приложении основное внимание будет сосредоточено на главных свойствах трех пе- перечисленных типов пространств и имеющихся в них правилах манипулирования объектами. Вероятно, наиболее удобно считать эти объекты (скаляры, векторы и точки) абстрактными типами данных, а аксиомы, характеризующие свойства пространств, как множество опера- операций, допустимых при работе с этими типами данных. Б.1. Скаляры Множество вещественных чисел и операций над ними представляет собой скачярное поле. Рассмотрим множество S скалярных элементов, которые обозначим строчными буквами гре- греческого алфавита а, Р, ... . Над скалярами определены две основные операции — сложение, которое обозначается знаком (+), и умножение, которое обозначается знаком (•)'. Следова- 'Иногда, если это не может быть истолковано неоднозначно, мы будем обозначать операцию умноже- умножения в виде аE вместо а ¦ Д
тельно, если Va,C e S, то a+C е S, а • C е 5. Эти операции обладают свойствами ассоциатив- ассоциативности, коммутативности и дистрибутивности2, т.е. если Va,C,Y ? -S", то Существуют два специальных скаляра (универсальные тождественные элементы) — числа 0 и 1, таких, что прибавление первого и умножение на второе не меняют ни одно- одного числа, т.е. для Va e 5 Каждый элемент а скалярного множества имеет соответствующие ему аддитивный инверс- инверсный элемент, который обозначается -а, и мультипликативный инверсный (обратный) эле- элемент, который обозначается а, такие, что a + (-a) = 0, a-a"' =1. -a.of'e 5. Множество действительных чисел в совокупности с определенными выше операциями сло- сложения и умножения образуют действительное скалярное поле. Скалярные поля других типов могут быть образованы множеством комплексных чисел в совокупности с операциями ком- комплексного сложения и комплексного умножения и множеством рациональных функций (отношений двух полиномов). Б.2. Векторное пространство Векторное пространство помимо скаляров содержит и элементы другого типа— векто- векторы. Над векторами определены две операции: сложение вектора с вектором и умножение вектора на скаляр. Пусть и, v, w — векторы, определенные в векторном пространстве V. Опе- Операция сложения векторов определяется как замкнутая (z/+v e V, Vh,v € V), коммутативная (u+v = v+м) и ассоциативная («+(v+ii') = (;/+v)+ir). В векторном пространстве существует спе- специальный вектор — нуль-вектор, который обозначается 0, такой, что, если Vm e V, то м + 0 = и. Каждый вектор и имеет соответствующий ему аддитивный инверсный вектор, который обо- обозначается -и,такой, что Операция умножения вектора на скаляр определена таким образом, что для любого скаляра a и любого вектора и произведение аи есть также вектор в пространстве V. Операция умноже- умножения вектора на скаляр подчиняется дистрибутивному закону, т.е. 'Иногда говорят, что эти операции подчиняются ассоциативному, коммутативному и дистрибутивному законам ачгебры. — Прим. рея 562 Пршожение Б. Абстрактные пространства в компьютерной графике
Примерами векторных пространств являются пространство геометрических векторов (направленных отрезков прямой) и пространство алгебраических векторов — совокупностей и действительных чисел. Рассмотрим множество направленных отрезков, которые графиче- графически представлены на рис. Б.1. Если скаляры в векторном пространстве есть действительные числа, то операция умножения вектора на скаляр изменяет модуль (абсолютную величину или длину) вектора, но не меняет его ориентацию (рис. Б.2). Рис. Б. 1. Направленные от- отрезки прямой Рис. Б.2. Умножение векто- вектора на скаляр Рис Б.З. Правило сло- сложения векторов Сложение вектора с вектором определяется в векторном пространстве правилом сочлене- сочленения начала с концом (head-to-tail rule), которое имеет довольно наглядное представление в пространстве направленных отрезков. Вектор суммы u+v образуется после сочленения начала вектора и с концом вектора v (рис. Б.З). Можете самостоятельно проверить, что все правила, определенные на пространстве векторов, удовлетворяются и для суммы векторов. Второй пример векторного пространства — это пространство алгебраических векторов, т.е. совокупностей и скаляров. Как правило, скалярами являются действительные или ком- комплексные числа. В таком пространстве вектор определятся как Операции умножения вектора на скаляр и сложения векторов определяются соотношениями av = (av,,av2 сс»>и). Такое векторное поостранство принято обозначать R", и для манипуляций с векторами в нем используется математический аппарат матричной алгебры, который будет кратко описан в приложении В. Базовой концепцией векторного пространства является линейная независимость векторов. Линейной комбинацией п векторов щ, и,..., и„ называется вектор вида Если соотношение а,м, + а,н, +• •• + «»"« =0 удовлетворяется только единственным множеством скаляров а, = а, =...аи =0. то векторы и\, и,..., и„ являются линейно-независимыми. Наибольшее количество линейно- независимых векторов, которое может быть найдено в данном векторном пространстве, оп- определяет размерность этого пространства. В «-мерном векторном пространстве любое мно- Б.2. Векторное пространство 563
жество п линейно-независимых векторов образует базис этого пространства. Если v,, is, ..., v,, есть базис пространства V, то произвольный вектор v можно единственным способом пред- представить в виде линейной комбинации векторов базиса: Множество скаляров (ЗЛ называется разложением вектора v по базису vb v;, ..., v,, (часто также используется термин представление вектора v в базисе vb v2, ..., v,,). Если множество v'j, v':, ..., v',, представляет собой другой базис в этом же пространстве (количество векто- векторов в базисе для данного пространства фиксировано), то существует разложение вектора v по этому базису: Разложения вектора по разным базисам «-мерного векторного пространства связаны матри- матрицей М размера их п, такой, что р; р; р; = м ¦р,' Рз р„ Свойства матриц М мы рассмотрим в приложении В. Такая матрица позволяет изменять представление вектора с помощью линейного преобразования, которое реализуется с помо- помощью только вычислительных операций над скалярами в процессе умножения матриц. Обоб- Обобщая, можно утверждать, что если известен базис (или базисы) векторного пространства, то далее все манипуляции происходят только с разложениями векторов по базису (или по бази- базисам) этого пространства. Если скаляры в данном пространстве являются вещественными чис- числами, то все операции над /7-мерными векторами (наборами п чисел) можно свести к опера- операциям матричной алгебры. Б.З. Аффинное пространство Строго говоря, в векторном пространстве отсутствуют такие понятия, как положение и расстояние. Если в качестве векторного пространства для решения геометрических задач ис- использовать пространство отрезков прямых, то мы встретимся со множеством сложностей, поскольку векторы в нем являются сво- свободными, т.е. как и физические векторные величины, имеют мо- модуль (длину) и направление, но не имеют тонки приложения. Век- Векторы, показанные на рис. Б.4, являются в векторном пространстве идентичными объектами. Система базисных векторов, которая позволяет представить любой вектор в данном пространстве в виде набора коэффициен- коэффициентов разложения по этому базису, образует систему координат пространства. На рис. Б.5,а показаны три базисных вектора, исхо- исходящие из определенной точки пространства, называемой началом этой системы координат. Те векторы, которые показаны на рис. Б.5,б, идентичны базисным векторам, поскольку в векторном пространстве все векторы являются свободными. В результате в векторном пространстве отсутствует возможность определить эту особую точку, к которой должны быть "привязаны" базисные векторы. Рис. Б.4. Идентичные свободные векторы 564 Приложение Б. Абстрактные пространства в компьютерной графике
а) 6} Рис. Б. 5. Система координат: а — базисные векторы, "привязанные" к точке начала координат; б — свободные базисные векторы Эта проблема разрешается вводом в векторное пространство еще одного типа объектов — точки. Такое "дополненное" векторное пространство получило название аффинного. В аф- аффинном пространстве определено множество точек (Р, Q, R,...), и единственная операция над ними— вычитание точек, результатом которой является вектор. Если Р и Q— две произ- произвольные точки, то результат операции вычитания есть вектор в пространстве V. Следовательно, для любого вектора v и любой точки Р можно отыскать такую точку Q , для которой будет справедливо приведенное выше соотношение. Эта операция, производная от базовой операции вычитания точек, называется операцией сложения вектора и точки P-R P-Q Следствием из правила сложения векторов является утверждение, что для любых трех то- точек Р, Q, R выполняется соотношение Если отображать вектор P-Q в виде отрезка прямой, проведен- проведенного от точки Q к точке Р, причем для обозначения направле- направления отрезка использовать стрелки, то правило сложения векто- векторов может быть наглядно представлено так, как на рис. Б.6. Геометрические объекты в аффинном пространстве обла- обладают множеством интересных свойств. Пожалуй, наиболее важное из них состоит в том, что если использовать для представления объектов фрейм, а не систему координат, то можно единообразно представлять и векторы, и точки. Фрейм включает набор базисных векторов v,, v2,..., v,, и точку начала координат Ро (точку отсчета), из которой исходят эти базисные векторы. Если в аффинном пространстве определен фрейм, то любой вектор можно представить в этом фрейме единственным образом: Q Q-R Рис Б.6. Наглядное представ- представление операции сложения векторов в аффинном пространстве v = a,v, Б.З. Аффинное пространство 565
Любая точка также может быть единственным образом представлена в заданном фрейме: Два множества из п скаляров, {а,,..., а,,} и {Р,,..., Р„} — соответственно представление век- вектора и точки в аффинном пространстве относительно заданного фрейма. Если точка отсчета фрейма не изменяется, то следует учитывать только изменение сис- системы базисных векторов. Но в задачах компьютерной графики нам чаще всего приходится иметь дело с изменением всех компонентов фрейма и представлением одних и тех же гео- геометрических объектов в разных фреймах. Например, чаще всего геометрический объект определяется в некотором "физическом" фрейме. В терминах этого же фрейма определяет- определяется и положение камеры (или, что то же самое, — наблюдателя), а уже в процессе формиро- формирования изображения появляется необходимость сформировать представление объекта по отношению к фрейму камеры. Точка отсчета этого фрейма, как правило, совпадает с цен- центром проецирования камеры. Б.4. Евклидово пространство Хотя аффинное пространство и содержит все элементы, необходимые для построения геометрических моделей, в нем отсутствует понятие расстояния между двумя точками, а со- соответственно и понятие длины вектора. Аффинное пространство, дополненное понятием рас- расстояния (меры длины), превращается в евклидово. Евклидово пространство Е содержит скаляры (а, Р, у,...) и векторы (//, v, iv,...) причем скаляры представляют собой действительные числа. В евклидовом пространстве вводится новая операция — скалярное произведение, операндами которой являются векторы, а резуль- результатом — скаляр. Операция скалярного произведения обладает следующими свойствами: для любых трех векторов и, v, ir и двух скаляров а, Р выполняются соотношения и v = vu. (аи + Pv) • w = аи ¦ w + Pi' • w. v- v>0, если \'Ф 0. 0 0 = 0. Если // • v = 0, то векторы и и v ортогональны. Модуль (длина) вектора в евклидовом пространстве опреде- определяется соотношением Поскольку евклидово пространство унаследовало от аффинного, кроме векторов и скаляров, еще и точки, то в нем можно определить и понятие расстояния между точками Р и Q, как производное от длины вектора P-Q : Операция скалярного произведения дает нам и меру угла между двумя векторами: m-v = |h||v|cos9. Несложно показать, что определенное по этой формуле значение cosG лежит в интервале (-1, +1) и равно нулю в том случае, если векторы ортогональны. Если векторы парал- параллельны (и = ccv), то |cos9| = 1. 566 Приложение Б. Абстрактные пространства в компьютерной графике
Б.5. Проекции вектора Из свойства ортогональности двух векторов следует несколько важных для практики поня- понятий. Понятие проекции возникло при решении задачи определения кратчайшего расстояния ме- между точкой и прямой (или плоскостью). Пусть заданы два непа- непараллельных вектора v и w. Один из них, ir, можно представить в виде суммы двух векторов, один из которых параллелен v, а дру- другой — ортогонален ему (рис. Б.7): Вектор ocv, первое слагаемое, параллелен v, и вектор и, второе слагаемое, — ортогонален v. Следовательно, и ¦ v = О, W ¦ V — (XV • V + U • V = ОСУ • V. Рис. Б. 7. Проекция вектора на другой вектор Тогда V-V Вектор ccv называется проекцией вектора w на вектор v. Второе слагаемое разложения векто- вектора м' определяется соотношением w-v и = w v. V V Полученный результат можно распространить на разложение вектора по ортогональным век- векторам произвольного базиса. Б.6. Ортогонализация Грамо-Шмидта Если в /7-мерном пространстве определено множество векторов базиса аи аг,..., а„у то в нем можно определить другой базис, b\, b2,..., Ь„, который будет ортонормированным, т.е. каждый вектор этого базиса имеет единичную длину и ортогонален всем другим векторам ба- базиса. Математически это выражается следующим образом: [1, если / = j, [О в противном случае. Следовательно, при использовании ортогональной (декартовой) системы координат общ- общность рассуждений не теряется. Будем искать вектор вида Ьг =а, который можно сделать ортогональным Ьь выбрав соответствующим образом значение а. Рассмотрим скалярное произведение Ъг bx =0 = a, bx +ab, bx. Из этого соотношения следует, что а2 6, о, =а, —=—-о,. ¦ - ь,ьх ' Таким образом, мы построили ортогональный вектор, удалив компоненту, параллельную Ь\, т.е. проекцию а2 на Ь\. Б. 6. Ортогонализация Грача-Шмидта 567
Обобщенный итерационный цикл состоит в том, чтобы найти вектор который будет ортогонален векторам Ьи ..., Ь),-\. Существует к-\ условий ортогональности, которые позволяют нам отыскать коэффициенты я,: ак ¦ b а, =—-—'-. Ъ,Ь, Каждый вектор можно нормализовать либо перед завершением процедуры, заменив Ъ, ча- частным b,l\b\, либо, что более эффективно, нормализуя каждый вектор Ъ, сразу же после его определения. Б.7. Рекомендуемая литература Имеется масса прекрасной учебной литературы по линейной алгебре и векторным про- пространствам. Для тех, кто занимается компьютерной графикой, я бы рекомендовал сначала по- познакомиться с основными концепциями векторного пространства, а затем с линейной алгеб- алгеброй, которую следует рассматривать в качестве математического инструментария для мани- манипулирования объектами обобщенного векторного пространства. К сожалению, в большинстве книг по линейной алгебре основное внимание уделяется только «-мерным евклидовым про- пространствам R". Я бы рекомендовал в первую очередь познакомиться с книгами Бойера (Bowyer) и Вудворка (Woodwark) [Bow83] и Банхофа (Banchoff) и Вернера (Werner) [Ban83]. Для знакомства с аффинным пространством я рекомендую книгу Фоли (Foley) [Fo/90], в которой основной акцент сделан на геометрических свойствах. Упражнения Б.1. Докажите, что множество комплексных чисел образует скалярное поле. Какой вид в этом поле будут иметь универсальные тождественные элементы для операций сло- сложения и умножения? Б.2. Докажите, что множество рациональных функций образует скалярное поле. Б.З. Докажите, что множество рациональных функций с действительными коэффициен- коэффициентами образует векторное пространство. Б.4. Докажите, что количество элементов базиса определенного векторного пространст- пространства является фиксированным. Б.5. Рассмотрите множество из и действительных функций {f,(x)}, /=1,..., п. Покажите, как сформировать векторное пространство с этими элементами. Дайте определение базису и размерности этого пространства. Б.6. Покажите, что множество полиномов степени не выше п образует /7-мерное вектор- векторное пространство. Б.7. Дайте определение операциям сложения векторов и умножения вектора на ска- скаляр в w-мерном евклидовом пространстве R". Что представляет собой операция скалярного произведения в R"? Б.8. Предположим, что в евклидовом пространстве R3 заданы три вектора. Как прове- проверить, образуют ли они базис этого пространства? Б.9. В евклидовом пространстве R3 заданы три вектора: A, 0, 0), A, 1, 0) и A, 1, 1). По- Покажите, что эти векторы линейно-независимы. Постройте на этих векторах ортого- ортогональный базис, начав с A, 0, 0). 568 Приложение Б. Абстрактные пространства в компьютерной графике
ПРИЛОЖЕНИЕ В Матрицы Основная роль матриц в математическом обеспечении систем компьютерной графики со- состоит в выполнении операций преобразования геометрических объектов при изменении систем координат или фреймов. Термин вектор в матричной алгебре имеет трактовку, несколько отличную от той, которая используется в векторном анализе и линейной алгебре. Чтобы не вносить излишней путаницы, при описании операций матричной алгебры мы будем пользоваться вместо общепринятого термина вектор терминами матрица-строка и матрица- столбец. Вектором же будем называть направленный отрезок прямой и иногда, как в приложе- приложении Б, абстрактный тип данных, представляющий элемент векторного пространства. В этом приложении собраны основные сведения из матричной алгебры, которые необхо- необходимы для понимания сути операций преобразования геометрических объектов. Практически везде будут использоваться матрицы размера 4x4. Поэтому мы опустили в этом приложении те разделы матричной алгебры, которые относятся к операциям с матрицами общего вида, в частности методы обращения произвольных квадратных матриц. В большинстве современ- современных графических систем обращение матриц размера 4x4 выполняется системными аппарат- аппаратными или программными средствами. В.1. Основные определения Матрицей размера их т называется массив скаляров, организованный в виде таблицы из п строк и т столбцов. Часто параметры п и т называют размерностями матрицы по строкам и столбцам. Если т=п, то матрица называется квадратной размера (или порядка) п. В качест- качестве скаляров мы будем использовать только действительные числа, хотя большинство пред- представленных результатов в равной мере справедливы и в отношении комплексных чисел. Эле- Элементы матрицы А представляют собой скаляры {а,,}, /=1,..., n,j=l,..., т. Запишем матрицу А в терминах ее элементов в виде Операция транспонирования матрицы А размером пхт формирует новую матрицу разме- размером т х и, в которой строки исходной матрицы заменяются столбцами с тем же номером. Полученная в результате матрица называется транспонированной и обозначается Ау:
Специально выделяются матрицы, состоящие из одной строки (матрица размера 1 х т) и одного столбца (матрица размера п х 1). Первая называется матрицей-строкой, а вторая — матрицей-столбцом. Матрицы-столбцы в дальнейшем будем обозначать строчными буквами: b = [*,]. В результате транспонирования матрицы-строки образуется матрица-столбец, которую будем обозначать b'. В.2. Операции над матрицами Существуют три базовые операции над матрицами: умножение матрицы на скаляр, сло- сложение матрицы с матрицей и перемножение двух матриц. Мы полагаем, что скаляры пред- представляют собой действительные числа, но это не обязательно. Главное, чтобы типы элемен- элементов матрицы и скалярного множителя были совместимы. Операция умножения матрицы на скаляр определена для матриц А любого размера. Матрица результата образуется в результате поэлементного перемножения исходной матри- матрицы на скалярный множитель а: аА=[сха,;]. Операция сложения матриц определена только для пар матриц равного размера. Элемен- Элементы новой матрицы — суммы матриц — являются суммами одноименных элементов матриц- слагаемых: Операция умножения матрицы А размера п х / на матрицу В размера / х т формирует матрицу произведения размером п х т: где Операция перемножения имеет смысл только для квадратных матриц-сомножителей равного размера или для прямоугольных матриц, у которых количество столбцов матрицы множимо- множимого А равно количеству строк матрицы множителя В. Будем говорить, что матрица А умножа- умножается справа на матрицу В или что матрица В умножается слева на матрицу А. Операция умножения матрицы на скаляр подчиняется простым правилам, которые спра- справедливы для любых матриц А и скаляров а и C: сфА=(ЗаА. Эти правила вытекают из того, что операция умножения матрицы на скаляр сводится к опе- операциям над скалярными элементами матрицы и скалярным множителем. Операция сложения матриц является коммутативной, т.е. для любых матриц А и В размера п х т выполняется соотношение А + В = В + А. Эта операция обладает также свойством ассоциативности, т.е. для любых трех матриц А, В и С размера п х т справедливо равенство = (А + В)+С. 570 Приложение В. Матрицы
Свойством ассоциативности обладает и операция умножения матрицы на матрицу: А(ВС) = (АВ)С, но эта операция не является коммутативной. При этом не только отличаются произведе- произведения, т.е. АВ Ф ВА, но и вполне вероятно, что одно из произведений существует, а вто- второе — нет. Из этого утверждения следует, что в графических приложениях, где матрицы представляют преобразования объектов (сдвиг, поворот и т.д.), порядок выполнения от- отдельных преобразований имеет существенное значение. Поворот, за которым последует сдвиг, даст результат, совершенно отличный от того, который получится при выполне- выполнении сдвига, а затем поворота. Единичная матрица I представляет собой квадратную матрицу, элементы главной диаго- диагонали которой равны 1, а остальные элементы равны 0: ? г 1 Р' ес 1= а- , ау =\ L J [Ов противном случае. Если матрицы А. В и I имеют одинаковые размеры, то выполняются соотношения А1 = А, 1В = В В.З. Матрицы-строки и матрицы-столбцы Особый интерес для задач компьютерной графики представляют матрицы-строки размера 1 х п и матрицы-столбцы размера их 1. Вектор и точку трехмерного пространства1 по отно- отношению к произвольному фрейму можно представить в виде матрицы-столбца: Р = Для обозначения матриц-столбцов будем в дальнейшем использовать строчные буквы. Ре- Результатом транспонирования матрицы р будет матрица-строка р7=Ф у 4 Поскольку произведение матрицы размера п х / на матрицу размера / х т представляет собой матрицу размера пх т, то произведение квадратной матрицы размера п на матрицу- столбец размера п х 1 даст новую матрицу-столбец размера п х 1. Стандартный способ гео- геометрических преобразований в графических системах основан на использовании матриц- столбцов размером 2, 3 или 4 для представления точек или векторов и квадратных матриц для представления операторов преобразований. Следовательно, выражение р' = Ар описывает отдельное преобразование точки (или вектора), а выражение р' = АВСр описывает последовательность или суперпозицию преобразований. Обращаю ваше внимание на то, что поскольку операция умножения матриц обладает свойством ассоциативности, в этом выражении нет необходимости использовать скобки. При использовании однородных координат, которые мы рассматривали в главе 4. векторы и точки представляются по-разному. В.З. Матрицы-строки и матрицы-столбцы 571
В некоторых книгах по компьютерной графике авторы предпочитают описывать точки и векторы матрицами-строками. В таком случае, учитывая, что транспонирование произведе- произведения есть произведение транспонированных сомножителей в обратном порядке, т.е. (AB)r=BrAf, придем к выводу, что суперпозиция трех преобразований может быть записана для матрицы строки в следующем виде: рг=ртСгЪтАг. Достоинство такой формы описания в том, что матрицы отдельных преобразований в комбинированном преобразовании перечисляются слева направо в том порядке, в котором элементарные преобразования реализуются в системе, т.е. сначала преобразование С, затем В, а потом А. Но в большинстве серьезных научных, технических и математических работ используется традиционная форма представления точек и векторов в виде матриц-столбцов. Поэтому и в данной книге мы отдали предпочтение этой форме. Хотя принципиального зна- значения выбор той или иной формы и не имеет, при освоении нового API обязательно обратите внимание, какая именно форма представления в нем использована, поскольку от этого зави- зависит не только порядок перечисления матриц в составном выражении, но и сами матрицы пре- преобразований должны быть транспонированы. В.4. Ранг матрицы В математическом обеспечении систем компьютерной графики матрицы используются в первую очередь для представления точек, векторов и геометрических преобразований этих объектов. Часто возникает задача определить, является ли преобразование, описанное квад- квадратной матрицей, обратимым. Так, если имеется преобразование q = Ap, желательно знать, существует ли квадратная матрица В, такая, что p = Bq. Подставляя исходное выражение для q, получим р = Bq = ВАр = 1р = р, из чего следует, что ВА = 1 Если матрица В существует, то она называется обратной матрице А, а матрица А является невырожденной {неособенной). Матрица А, для которой не существует обратная, называется вырожденной {особенной). Матрица, обратная А, обозначается А. Фундаментальное утверждение относительно обратных матриц гласит следующее: Матрица, обратная заданной квадратной матрице, существует тогда и только то- тогда, когда определитель этой матрицы неравен нулю. Вычисление определителя матрицы А (он обозначается |А|) для матриц порядка более вы- высокого, чем второй, требует практически столько же вычислительных операций, сколько и вычислений обратной матрицы. Оценка сложности вычислений для матрицы порядка п имеет вид О{п3). Для матриц второго, третьего и четвертого порядков, которые преимущественно используются в математическом обеспечении систем компьютерной графики, можно доволь- довольно быстро вычислить определитель, воспользовавшись правилом Крамера. Получив значение 572 Приложение В. Матрицы
определителя, можно выполнить обращение матрицы. Но обращение матриц преобразования можно провести и на основе геометрических соображений. Например, матрица, обратная матрице сдвига, задает сдвиг на то же расстояние, но в обратном направлении. Подобные ва- варианты мы рассматривали в главе 4. Для прямоугольных матриц общего вида особое значение имеет понятие ранга матрицы. Квадратную матрицу можно рассматривать как матрицу-строку, элементами которой являют- являются матрицы-столбцы, или как матрицу-столбец, элементами которой являются матрицы- строки. В терминах векторного пространства, которое мы рассматривали в приложении Б, строки матрицы размера п х т есть элементы евклидова пространства R"', в то время как столбцы есть элементы пространства R". Можно проанализировать, сколько строк (или столбцов) матрицы являются линейно-независимыми. Ранг матрицы по строкам (по столбцам) есть максимальное количество линейно-независимых строк (столбцов). Следовательно, для любой матрицы размера пх п ранг по строкам равен рангу по столбцам и матрица является неособенной тогда и только тогда, когда ее ранг равен п. Таким образом, справедливо утвер- утверждение, что матрицу можно обратить в том и только в том случае, если ее строки (и столбцы) являются линейно-независимыми. В.5. Изменение представления С помощью матриц можно представить изменение базиса любого множества векторов, удовлетворяющих правилам, сформулированным в Приложении Б. Предположим, что имеет- имеется «-мерное векторное пространство. Пусть {ии и2,..., и,,} и {vb v2,..., v,,} —два базиса в этом векторном пространстве. Тогда некоторый вектор v можно представить в виде v = a,//, +а2иг +... + а„м„ или в виде v = P,v, +P:v2 + ... + P,,v,(. Следовательно, (a,, a2,..., а„) и (Рь p2,..., Р„) — два представления вектора v, причем каждое из них можно выразить в виде матрицы-столбца размера п х 1. Но работая с таким представ- представлением, нужно очень внимательно относиться к обозначениям, которые должны однозначно отображать разницу в представлениях. Представление v можно записать либо в виде v = [a, a, ... a,(] , либо в виде V = [3, Р; .- Р,,]'- Разница в обозначениях соответствует разнице базисов, в которых представляется вектор. Теперь рассмотрим, каким образом можно перейти от представления v к представле- представлению v1. Векторы базиса {v,, v2, ..., v,,} можно выразить в базисе {иь и2, ..., и,,}. Следователь- Следовательно, существует множество скаляров у,,, таких, что 14, =Y,il't + Y,2l':> +-" + У,Л' ' =1 "• Это выражение для всех и{ можно записать в матричной форме следующим образом: м2 = А В.5. Изменение представления 573
где матрица А является квадратной матрицей порядка /?: Оба представления — и v, и v' — можно выразить через матрицы-столбцы где ¦=[«,]• Можно следующим образом определить Ь: b = [ft] и выразить v' в виде u=b7 Матрица А связывает два базиса, и прямой подстановкой получим ЬЛ=а'А. Таким образом, матрица А позволяет выполнять преобразование представления векторов в одном базисе в представление в другом базисе. В.6. Векторное произведение Для двух непараллельных векторов и и v в трехмерном пространстве векторное произве- произведение определяет третий вектор и, ортогональный двум первым. Независимо от формы пред- представления векторов, справедливо равенство w • и = w • v = 0. Одну компоненту вектора w можно установить произвольно, поскольку для нас имеет значе- значение направление этого вектора, а не его длина. В результате остаются три условия, связы- связывающие три компоненты вектора м>. Если в определенной системе координат вектор и имеет компоненты аь а2, сх3, а вектор v— компоненты р(, C2, рз, то в этой системе координат опе- операция векторного произведения определяется следующим образом: w=uxv= а3Р, -а Обращаю ваше внимание на то, что вектор w полностью определяется векторами и и v; их представление мы используем только тогда, когда необходимо вычислить компоненты векто- вектора м> в конкретной системе координат. В правосторонней системе координат векторное про- произведение хх у ортов осей х и у есть вектор, параллельный орту третьей оси г. 574 Приложение В. Матрицы
В.7. Рекомендуемая литература Наиболее распространенными учебниками по линейной алгебре и теории матриц являют- являются книги Штранга (Strang) [Str93], Банхофа (Banchoff) и Вернера (Werner) [Ban83]. Также обратите внимание на книгу Роджерса (Rogers) и Адамса (Adams) [Rog90] и выпуски серии Graphics Gems [Gra90—Gra92, Gra94, Gra95]. Дискуссия о том, какая форма представления векторов предпочтительнее — матрицами- строками или матрицами-столбцами, — имеет давнюю историю. В первых книгах по компь- компьютерной графике, в частности в книге Ньюмена (Newman) и Спрулла (Sproull) [New73], ис- использовались матрицы-строки. Но в современной литературе четко прослеживается тенден- тенденция использования матриц-столбцов (см., например [Fol9Q]), хотя в некоторых книгах, на- например в [Wat93], авторы по-прежнему отдают предпочтение матрицам-строкам. При работе с конкретным API отнюдь не всегда можно выяснить из документации, какое именно пред- представление использовано, поскольку квадратная матрица в программе часто имеет вид линей- линейного массива из п2 элементов. Некоторые API, в частности OpenGL, позволяют выполнять только умножение справа системной матрицы на матрицу, определенную в прикладной про- программе. В других API, например в PHIGS, поддерживается как умножение справа, так и ум- умножение слева. Упражнения 8.1. Рассмотрите два базиса в трехмерном пространстве R3—{A,0,0), A,1,0), A, 1, 1)} и {A, 0, 0), @, 1, 0), @, 0, 1)}. Определите матрицы, преобразующие пред- представление вектора в одном базисе в представление в другом. Покажите, что эти матрицы взаимно обратные. 8.2. Рассмотрим векторное пространство полиномов степени не выше второй. Покажи- Покажите, что множества полиномов {1,дг, х2} и {1, 1+дг, \+х+ х2} являются базисами этого пространства, отыщите представление \+2х+Зх2 в обоих базисах. Определите мат- матрицу преобразования из одного базиса в другой. 8.3. Пусть i, j и к — единичные векторы, параллельные осям координат х, у и г в трех- трехмерном евклидовом пространстве R3. Покажите, что векторное произведение лю- любой пары векторов и х v задается матрицей uxv = 1 J и, и,. V, В.4. Покажите, что в трехмерном евклидовом пространстве R3 выполняется соотноше- соотношение \и х v| = |w||v|sin0, где 0 — угол между векторами и и v. Упражнения 575
Литература Ado85 Adobe Systems Incorporated. PostScript Language Reference Manual.— Addison- Wesley, Reading, MA, 1985. Ake88 Akeley K., JermolukT. High Performance Polygon Rendering. Computer Graphics, 22D), 1988, p. 239-246. Ake93 Akeley K. Reality Engine Graphics. Computer Graphics, 1993, p. 109-116. Ang90 Angel E. Computer Graphics. — Addison-Wesley, Reading, MA, 1990. ANSI85 American National Standards Institute (ANSI), American National Standard for Information Processing Systems-Computer Graphics-Graphical Kernel System (GKS) Functional Description. — ANSI, X3.124-1985, ANSI, New York, 1985. ANSI88 American National Standards Institute (ANSI). American National Standard for Information Processing Systems—Programmer's Hierarchical Interactive Graphics System (PHIGS). — ANSI, X3.144-1988, ANSI, New York, 1988. App68 Appel A. Some Techniques for Shading Machine Renderings of Solids. Spring Joint Computer Conference, 1968, p. 37^5. Am96 Arnold K., Gosling J. The Java Programming Language. — Addison-Wesley, Reading, MA, 1996. Ban83 Banchoff Т., Werner J. Linear Algebra Through Geometry. — Springer-Verlag, New York, 1983. Bar93 Barnsley M. Fractals Everywhere, Second Edition. — Academic Press, San Diego, CA, 1993. Bar83 Barsky B. A., Beatty C. Local Control of Bias and Tension in Beta-Splines. ACM Transactions on Graphics, 2B), 1983, p. 109-134. Bar87 Bartels, Beatty C, Barsky B. A. An Introduction to Splines for use in Computer Graphics and Geometric Modeling. — Morgan-Kaufman, Los Altos, CA, 1987. BH76 Blinn J. F., Newell M. E. Texture and Reflection in Computer Generated Images. — CACM, 19A0), 1976, p. 542-547. BH77 Blinn J. F. Models of Light Reflection for Computer-Synthesized Pictures. Computer Graphics, 11B), 1977, p. 192-198. Bom'83 Bowyer A., Woodwark J. A Programmer's Geometry. — Butterworth, 1983. Bre65 Bresenham J. E. Algorithm for Computer Control of a Digital Plotter. IBM Systems Journal, January, 1965, p. 25-30. Bre87 Bresenham J. E. Ambiguities in Incremental Line Rastering. IEEE Computer Graphics and Applications, May, 1987, p. 31-43. Car78 Carlbom I., Paciorek J. Geometric Projection and Viewing Transformations. Computing Surveys, 1D), 1978, p. 465-502. Cas96 Castleman К. С Digital Image Processing. — Prentice-Hall, Englewood Cliffs, NJ, 1996. Cat75 Catmull E. A Hidden-Surface Algorithm with Antialiasing. Computer Graphics, 12C), 1975, p. 6-11.
Cha98 Chan P., Lee R. The Java Class Libraries: Java.Applet, Java.Awt, Java.Beans (Vol 2). — Addison-Wesley, Reading, MA, 1998. Cla82 Clark J. E. The Geometry Engine: A VLSI Geometry System for Graphics. Computer Graphics, 16, 1982, p. 127-133. Coh85 Cohen M. F., Greenberg D. P. The Hemi-Cube: A Radiosity Solution for Complex Environments. Computer Graphics, 19C), 1985, p. 31-^0. Coh88 Cohen M. F., Chen S. E., Wallace J. R., Greenberg D. P. A Progressive Refinement Approach to Fast Rasiosity Image Generation. Computer Graphics, 22D), 1988, p. 75-84. Coo82 Cook R. L., Torrance К. Е. A Reflectance Model for Computer Graphics. ACM Transactions on Graphics, 1A), 1982, p. 7-24. Coo87 Cook R. L., Carpenter L., Catmull E. The Reyes Image Rendering Architecture. Computer Graphics, 21D), July, 1987, p. 95-102. Cro81 Crow F. С A Comparison of Antialiasing Techniques. IEEE Computer Graphics and Applications, 1A), 1981, p. 40-48. Der88 DeRose T. D. A Coordinate Free Approach to Geometric Programming. — SIGGRAPH Course Notes, SIGGRAPH, 1988. Der89 DeRose T. D. A Coordinate Free Approach to Geometric Programming. Theory and Practice of Geometric Modeling. W. Strasser and H.P. Seidel (Eds.)— Springer- Verlag, Berlin, 1989. Dre88 Drebin R. A., Carpenter L., Hanrahan P. Volume Rendering. Computer Graphics, 22D), 1988, p. 65-74. Ebe94 Ebert D. S. (Ed) Texturing and Modeling: A Procedural Approach. — Academic Press, New York, 1994. End84 Enderle G., Kansy K., Pfaff G. Computer Graphics Programming: GKS-The Graphics Standard.— Springer-Verlag, Berlin, 1984. (Имеется русский перевод: Эндерле Г., Канси К., Пфафф Г. Программные средства машинной графики. Международный стандарт GKS. — М.: Радио и связь, 1988.) Eng68 Engelbart D. С, English W. К. A Research Center for Augmenting Human Intellect. Fall Joint Computer Conference. — Thompson Books, Washington, DC, 1968. Far88 Farin G. Curves and Surfaces for Computer Aided Geometric Design. — Academic Press, New York, 1988. Fau80 Faux I. D., Pratt M. J. Computational Geometry for Design and Manufacturing. — Halsted, Chichester, England, 1980. (Имеется русский перевод: Фокс А., ПраттМ. Вычислительная геометрия. Применение в проектировании и на производстве. — М: Мир, 1982.) Fol90 Foley J. D., van Dam A., Feiner S. K., Hughes J. F. Computer Graphics, Second Edition.— Addison-Wesley, Reading, MA, 1990 (C Version 1996). (Имеется рус- русский перевод первого издания: Фоли Дж., ван Дэм А. Основы интерактивной ма- машинной графики: В 2-х книгах. — М.: Мир, 1985.) FoI94 Foley J. D., van Dam A., Feiner S. K., Hughes J. F., Phillips R. Introduction to Computer Graphics. — Addison-Wesley, Reading, MA, 1994. Fos96 Fosner R. OpenGL Programming for Windows 95 and Windows NT.— Addison- Wesley, Reading, MA, 1996. Fou82 Foumier A., Fussell D., Carpenter L. Computer Rendering of Stochastic Models. CACM, 25F), 1982, p. 371-384. Gla89 Glassner A. S. (Ed.) An Introduction to Ray Tracing. — Academic Press, New York, 1989. Gla95 Glassner A. S. Principles of Digital Image Synthesis.— Morgan-Kaufmann, New York, 1995. 578 Литература
Gol83 Goldberg A., Robson D. Smalltalk-80: The Language and Its Implementation. — Addison-Wesley, Reading, MA, 1983. Gon87 Gonzalez R., Wintz P. Digital Image Processing, Second Edition. — Addison-Wesley, Reading, MA, 1987. Gor84 Goral С M., Torrance K. E., Greenberg D. P., Battaile B. Modeling the Interaction of Light Between Diffuse Surfaces. Computer Graphics (SIGGRAPH 84), 18C), 1984, p. 213-222. Gou71 Gouraud H. Computer Display of Curved Surfaces. IEEE Trans. Computers, C-20, 1971, p. 623-628. Gra90 Graphics Gems I, Glassner A. S. (Ed.) — Academic Press, San Diego, CA, 1990. Gra91 Graphics Gems II, Arvo J. (Ed.) — Academic Press, San Diego, CA, 1991. Gra92 Graphics Gems III, Kirk D. (Ed.) —Academic Press, San Diego, CA, 1992. Gra94 Graphics Gems IV, Heckbert P. (Ed.) — Academic Press, San Diego, CA, 1994. Gra95 Graphics Gems V, Paeth A. (Ed.) — Academic Press, San Diego, CA, 1995. НЫ89 Hall R. Illumination and Color in Computer Generated Imagery. — Springer-Verlag, New York, 1989. Har96 Hartman J., Wemecke J. The VRML 2.0 Handbook. — Addison-Wesley, Reading, MA, 1996. Hea94 Hearn D., Baker M. P. Computer Graphics, Second Edition. — Prentice-Hall, Englewood Cliffs, NJ, 1994. Hec84 Heckbert P. S., Hanrahan P. Beam Tracing Polygonal Objects. Computer Graphics, 18C), 1984, p. 119-127. Hek86 Heckbert P. S. Survey of Texture Mapping. IEEE Computer Graphics and Applications, 6A1), 1986, p. 56-67. HU90 Hill F. S. Computer Graphics. — MacMillan, New York, 1990. Hop83 Hopgood F. R. A., Duce D. A., Gallop J. A., Sutcliffe D. С Introduction to the Graphical Kernel System: GKS. — Academic Press, London, 1983. Hop91 Hopgood F. R. A., Duce D. A. A Primer for PHIGS. — John Wiley Sons, Chichester, England, 1991. ISO88 International Standards Organization, International Standard Information Processing Systems—Computer Graphics—Graphical Kernel System for Three Dimensions (GKS- 3D), ISO Document Number 8805:1988(E). — American National Standards Institute, New York, 1988. Jar76 Jarivs J. F., Judice C.N., Ninke W. H. A Survey of Techniques for the Image Display of Continuous Tone Pictures on Bilevel Displays. Computer Graphics and Image Processing, 5A), 1976, p. 13^0. Joy>88 Joy K. 1., Grant С W., Max N. L., Hatfield L. Computer Graphics: Image Synthesis. — Computer Society Press, Washington, DC, 1988. Kaj86 Kajiya J. T. The Rendering Equation. Computer Graphics, 20D), 1986, p. 143-150. KH94,a Kilgard M. J. OpenGL and X, Part 3: Integrated OpenGL with Motif. The X Journal, SIGS Publications, July/August 1994. Kil94,b Kilgard M. J. An OpenGL Toolkit. The X Journal, SIGS Publications, November/December 1994. KU96 Kilgard M. OpenGL Programming for the X Windows System. — Addison-Wesley, Reading, MA, 1996. Knu87 Knuth D. E. Digital Halftones by Dot Diffusion. ACM Transactions on Graphics, 6D0), 1987, p. 245-273. Kov97 Kovatch P. J. The Awesome Power of Direct3D/DirectX. — Manning Publications Company, 1997. Литература 579
Las87 Lasseter J. Principles of Traditional Animation Applied to 3D Computer Animation. Computer Graphics, 21D), 1987, p. 33-44. Lev88 Levoy M. Display of Surface from Volume Data. IEEE Computer Graphics and Applications, 8C), 1988, p. 29-37. Lia84 Liang Y., Barsky B. A New Concept and Method for Line Clipping. ACM Transactions on Graphics, 3A), 1984, p. 1-22. Lin68 Lindenmayer A. Mathematical Models for Cellular Interactions in Biology. J. Theoretical Biology, 18, 1968, p. 280-315. Lor87 Lorensen W. E., Cline H. E. Marching Cubes: A High Resolution 3D Surface Construction Algorithm. Computer Graphics, 21D), 1987, p. 163-169. Mag85 Magnenat-Thalmann N., Thalmann D. Computer Animation: Theory and Practice. — Springer-Verlag, Tokyo, 1985. Man82 Mandelbrot B. The Fractal Geometry of Nature. — Freeman Press, New York, 1982. Mai95 The Math Works. Student Edition of MatLab Version 4, Users Guide. — Prentice-Hall, Englewood Cliffs, NJ, 1995. Max51 Maxwell E. A. General Homogeneous Coordinates in Space of Three Dimensions.— Cambridge University Press, Cambridge, England, 1951. Nen-73 Newman W. M., Sproull R. F. Principles of Interactive Computer Graphics.— McGraw-Hill, New York, 1973. (Имеется русский перевод: Ньюмен У., Спрулл Р. Основы интерактивной машинной графики. — М.: Мир, 1976.) .\ie94 Nielson J. Usability Engineering. — Academic Press,.New York, 1994. ОреЯЗ.а OpenGL Architecture Review Board. OpenGL Programming Guide. — Addison- Wesley, Reading, MA, 1993. Ope93,b OpenGL Architecture Review Board. OpenGL Reference Manual. — Addison-Wesley, Reading, MA, 1993. OSF89 Open Software Foundation. OSF/Motif Style Guide. — Prentice-Hall, Englewood Cliffs, NJ, 1989. Ost94 Osterhout J. Tel and the Tk Toolkit. — Addision-Wesley, Reading, MA, 1994. PapSl Papert S. LOGO: A Language for Learning. — Creative Computer Press, Middletown, NJ, 1981. Pav95 Pavlidis T. Interactive Computer Graphics in X. — PWS Publishing, 1995. Per89 Perlin K., Hoffert E. Hypertexture. Computer Graphics, 23C), 1989, p. 253-262. PH1G88 PHIGS+ Committee. PHIGS+ Functional Description, Revision 3.0. Computer Graphics, 22C), July, 1988, p. 125-218. Pho75 Phong В. Т. Illumination for Computer Generated Scenes. Communications of the ACM, 18F), 1975, p. 311-317. Pie88 Peitgen H. O., Saupe S. (Ed.) The Science of Fractal Images. — Springer-Verlag, New York, 1988. Pik84 Pike R., Guibas L., Ingalls D. Bitmap Graphics. Computer Graphics, 18C), 1984, p. 135-160. Por84 Porter Т., Duff Т. Compositing Digital Images. Computer Graphics, 18C), 1984, p. 253-259. Pra78 Pratt W. K. Digital Image Processing. — Wiley, New York, 1978. Pru90 Prusinkiewicz P., Lindenmayer A. The Algorithmic Beauty of Plants. — Springer- Verlag, Berlin, 1990. Rie81 Riesenfeld R. F. Homogeneous Coordinates and Projective Planes in Computer Graphics. IEEE Computer Graphics and Applications, 1A), 1981, p. 50-56. Rev83 Reeves W. T. Particle Systems - A Technique for Modeling a Class of Fuzzy Obejcts. Computer Graphics, 17C), 1983, p. 359-376. 580 Литература
Rey87 Reynolds C.W. Flocks, Herds, and Schools: A Distributed Behavioral Model. Computer Graphics, 21D), 1987, p. 25-34. Rog85 Rogers D. F. Procedural Elements for Computer Graphics, Second Edition. — McGraw-Hill, New York, 1998. Rog90 Rogers D. F., Adams J. A. Mathematical Elements for Computer Graphics. — McGraw-Hill, New York, 1990. (Имеется русский первод: Роджерс Д., Адаме Дж. Математические основы машинной графики. — М.: Мир, 2001.) Rns97 Rush К., Deering M., Sowizral H. A. The Java 3D API Specification.— Addison- Wesley, Reading, MA, 1997. Sch88 Schiefler R. W., Gettys J., Newman R. X-Window System. — Digital Press, 1988. Schi97 Schneiderman B. Designing the User Interface: Strategies for Effective Human- Computer Interaction, Third Edition. — Addison-Wesley, Reading, MA, 1997. Sch85 Shoemaker K. Animating Rotation with Quaterion Curves. Computer Graphics, 19C), 1985, p.245-254. Schr98 Schroeder W., Martin K., Lorensen B. The Visualization Toolkit. — Prentice-Hall, 1998. Seg92 Segal M., Akeley K. The OpenGL Graphics System: A Specification, Version 1.0.— Silicon Graphics, 1992. Sie81 Siegel R., Howell J. Thermal Radiation Heat Transfer. — Hemisphere, Washington, DC, 1981. SH89 Sillion F. X., Puech С A General Two-Pass Method Integrating Specular and Diffuse Reflection. Computer Graphics, 22C), 1989, p. 335-344. Smi84 Smith A. R. Plants, Fractals and Formal Languages. Computer Graphics, 18C), 1984, p. 1-10. Str93 Strang G. Introduction to Linear Alegbra. — Wellesley-Cambridge Press, 1993. Sut63 Sutherland I. E. Sketchpad, A Man-Machine Graphical Communication System, SJCC, 329. — Spartan Books, Baltimore, MD, 1963 Sut74,a Sutherland I. E., Hodgeman G. W. Reentrant Polygon Clipping. Communications of the ACM, 17, 1974, p. 32-42. Sut74,b Sutherland I. E., Sproull R. F., Schumacker R. A. A Characterization of Ten Hidden- Surface Algorithms. Computer Surveys, 6A), 1974, p. 1-55. Tor67 Torrance K. E., Sparrow E. M. Theory for Off-Specular Reflection from Roughened Surfaces. J. Optical Society of America, 57(9), 1967, p. 1105-1114. Tu/83 Tufte E. R. The Visual Display of Quantitative Information. — Graphics Press, Cheshire, CN, 1983. Tit/90 Tufte E. R. Envisioning Information. — Graphics Press, Cheshire, CN, 1990. TuJ97 Tufte E. R. Visual Explanations. — Graphics Press, Cheshire, CN, 1997. Ups89 Upstill S. The Renderman Companion: A Programmer's Guide to Realistic Computer Graphics. — Addison-Wesley, Reading, MA, 1989. Van94 Van Gelder A., Wilhelms J. Topological Considerations in lsosurface Generation. ACM Transactions on Graphics, 13D), 1994, p. 337-375. Wai92 Watt A., Watt M. Advanced Animation and Rendering Techniques. — Addision- Wesley, Wokingham, England, 1992. Wat93 Watt A. 3D Computer Graphics, Second Edition. — Addison-Wesley, Wokingham, England, 1993. Wer94 Wernecke J. The Inventor Mentor. — Addison-Wesley, Reading, MA, 1994. Wes90 Westover L. Footprint Evaluation for Volume Rendering. Computer Graphics, 24D), 1990, p. 367-376. Whi80 Whitted T. An Improved Illumination Model for Shaded Display. Communications of ACM, 23F), 1980,p. 343-348. Литература 581
Wit94,a Witkin A. P., Heckbert P. S. Using Particles to Sample and Control Implicit Surfaces. Computer Graphics, 28C), 1994, p. 269-277. Wit94,b Witken A. (Ed.). An Introduction to Physically Based Modelling.— Course Notes, SIGGRAPH 94. Wol9l Wolfram S. Mathematica. — Addison-Wesley, Reading, MA, 1991. Wh97 Wright R. S. Jr., Sweet M. OpenGL Superbible. — Wite Group Press, Corte Madera, CA, 1997. Wys=82 Wyszecki G., Stiles W. S. Color Science. — Wiley, New York, 1982. 582 Литература
Предметный указатель D Direct 3D. графическая система. 373 FTP (File Transfer Protocol), протокол передачи файлов. 370 API. 44 атрибуты.72 буферы, 395 визуализация. 211-16 кривые. 456 операции над пискелями, 397 поверхности, 457 положение камеры, 211-16 цвет. 77 GKS (Graphical Kernel System). 108: 216 GLU (GL Utility) library'- 88 GLUT (GL Utility toolkit). 84; 88; 104 H HTML (Hypertext Markup Language), язык разметки гипертекста. 370 PHIGS (Programmer's Hierarchical Interactive Graphics System). 44: 108; 216; 348 PostScript, 70 R Rcnderman, 47; 373 I Internet. 369 Internet Explorer, 29 Sketchpad, проект. 103 M Macintosh. 104 Microsoft Windows, 84 TCP/IP (Transmission Control Protocol/Internet Protocol). 369 N Netscape. 29 NTSC ("National Television Systems Committee), 327 О Open Inventor, надстройка над OpenGL. 361; 371 OpenGL. 26 Предметный указатель VRML (Virtual Reality Modeling Language), язык моделирования виртуальной реальности. 44; 371 w World Wide Web, 370 583
z-буфер. 227 Абстрактный тип данных. 150: 166 Автоматизация проектирования. 27 Аддитивная цветовая модель. 74 Алгебра множеств. 362 Альфа-канал. 78; 400 Анимация. 135-39:352 Аплет. 372 Архитектура графических систем. 47-52 конвейерная. 49 Атрибуты размер точки. 80 цвет отображения точек. 80 цвет чистого экрана, 80 Аффинное преобразование. 164 пространство. 150 сложение. 150: 153 Базис. 157 Базисная функция сплайна. 442 Ьернштейна полином. 436 Битовая плоскость, 376 Буфер. 376-77 глубины (г). 395: 403 источник. 391 кадра. 29 глубина. 30 полноцветный. 30 рабочий. 137 фоновый. 137 маски. 395 накопитель. 395: 406 приемник. 391 рабочий. 395 фоновый. 395 цвета. 395 В Ввода устройства. 105-12 джойстик. 107 клавиатура. 105 логические. 105: 108 мышь, 106 планшет. 106 спейсбол, 108 трекбол, 106 виртуальный, 199 указания. 105 физические, 105 Вектор, 148 вертикали вида, 216 вертикали картинной плоскости, 217 градиента. 262 компоненты. 157 координаты. 157 нормаль к картинной плоскости, 216 нормаль к поверхности. 261 нулевой длины. 151 половинного направления. 264 сложение с вектором. 152 сложение с точкой. 152 сочленение начала с концом, 148 Векторное произведение. 154 пространство, 150 Векторный дисплей. 31 Вершина. 34 список. 171 Видовое окно, 86: 293 Визуализация. 80 данных научных исследований. 493-520 зона видимости, 81 перспектива. 210 двухточечная. 54: 211 одноточечная, 54: 211 точка схода, 54: 211 трехточечная. 211 прямоугольник видимости. 81 функции OpenGL. 64 Виртуальная реальность. 28 Вокссль. 506 Восстановление непрерывной функции. 413 Выпуклая оболочка. 154 Выпуклость. 153 Гамма-коррекция. 330 Геометрическая оптика. 35 Геометрические преобразования. 50 Геометрический процессор, 50 Геометрия, 171 Геометрия фракталов. 483 Гистограмма распределения яркости. 399 Гладкость кривой. 425 584 Предметный указатель
Глаз человека, 38 Глубина резкости. 41 Горнера схема вычисления полинома. 447 Грамматика формы. 482 Граф. 340 Графика, инвариантная к устройствам, 62 Графический сервер. 112: 369 Гуро (Gouraud H.). 268 Д Двойная буферизация. 137; 395 Деление перспективное, 293 Дерево 4-арное. 367 8-арное. 367 БРП (бинарное, разделения пространства), 365 динамический узел, 351 обход, 343; 363 Дискретизация в пространственной области, 324 Дисплейная память. 114; 326 Дисплейный процессор, 48; 114 список. 115: 369 файл. 113 Диффузная поверхность. 37 Диффузное отражение, 250; 257 идеальное. 257 Зависимые переменные. 420 Закрашивание. 247; 289 метод Гуро, 268 метод Фонга. 269 многоугольников, 266 плоское, 266 Зеркальное отражение. 250 Зона видимости, 224: 288 каноническая, 231 пирамида, 224 И Изоповерхность, 506 Интерполяция билинейная, 173; 269 вдоль строк растра, 173 Интерфейс прикладного программирования. 44:63 К Камера спецификация параметров. 45: 211-16 фрейм. 212 Камера-обскура, 40 Карта микрорельефа. 377 Карта отражения. 388 Карта среды. 388 Картинная плоскость. 42; 206 Касательная плоскость. 262 Кассини овалы. 495 Кватернионы, 202 Класс параметрической непрерывности, 434 Клиент/сервер, архитектура. 112; 369 Кокса-де Бура рекурсивные функции, 443 Компьютерная графика. 25 области применения. 26 Конвейерная архитектура, 49 Конструктивная геометрия тел (КГТ). 157; 362 Коха снежинка. 99: 481 Коэффициент поглощения. 400 прозрачности. 401 резкости бликов. 259; 276 смешивания. 402 формы, 282 Кривая В-сплайн, 438 NURBS (неравномерный рациональный). 446:460 базисная матрица. 440 неравномерный. 445 открытый. 445 равномерный. 444 функции смешивания, 440 геометрическая непрерывность. 434 заполнения пространства. 99 заполняющая, 481 параметрическая непрерывность. 434 полиномиальная. 72 форма Безье, 435: 445; 456 разбиение, 448 форма Эрмита, 432 Кривизна, 420 Предметный указатель 585
л Ламберта закон. 257 Линейная фильтрация. 407 Линейная функция. 177 Линия потока. 518 Линия уровня. 495 Локатор. 109 Луч. 36 трассировка, 38; 279-282 м Максвелла треугольник. 101 Мандельброта множество. 488 Маркированный квадрат. 498 Матрица вида. 83; 167; 292 масштабирования, 183 перспективной нормализации, 238 поворота, 184 проецирования. 83: 231 сдвига, 183 скоса. 185 Меню. 127-128 Метаметрическая пара, 75 Метод анализа излучательности. 282 Многоугольник. 67 выпуклый. 68 закрашивание внутренней области, 321 общего вида. 319 плоский, 68 простой. 68 разбиение, 319 самопересекающийся. 318 тест на четность-нечетность, 318 тест охватывания. 318 Моделирование. 288; 337-51 Модуль вектора, 151 Морфинг, 353 н Наблюдатель. 34 Найквиста теорема о выборках, 411; 413 Наложение изображения окружающих предметов, 388 микрорельефа, 377: 390 проективной текстуры, 377-388 Направление проецирования, 206 Направляющие косинусы,191 углы, 191 Независимые переменные, 420 Нелицевые грани. 307 Немедленное отображение, режим, 73; 114 Нормализация проецирования. 230: 304 Нормаль, вектор, 261 Нуль-вектор. 151 Ньютона второй закон. 468 О Обратная задача кинематики, 352 Объектно-ориентированное программирование, 355 Объемное множество данных. 505 Овалы Кассини. 495 Однородные координаты. 162 преобразования. 182 Ориентированный ациклический граф, 341 Ортогональность, 154 Освещение глобальное, 248 локальное, 248 Отображение. 326-31 от ближнего к дальнему. 310 от дальнего к ближнему, 310 Отображение с сохранением, режим, 115 Отсечение. 49: 50: 289: 293 алгоритм Коэна-Сазерленда. 294; 303 алгоритм Лианга-Барского. 296: 303 алгоритм Сазерленда-Ходжмена, 298 в трехмерном пространстве. 303 конвейер. 300 многоугольников, 298 отрезков, 294 пирамида видимости, 224 рамка, 50; 81 п Первичные цвета. 39 Пересылка битового блока. 140; 391 Перспективное деление, 223: 231 Перспективные сокращения, 210 Пиксель, 29: 376 Пирамида видимости, 224 Плоскость, 155 проекции, 42 светоприемника, 46 Поверхность алгебраическая. 421; 454 внутренняя сторона, 170; 275 586 Предметный указатель
идеально отражающая. 250 квадратичная. 421; 454; 460 Ламбертова. 257 неявная форма представления, 421 порция. 424 разделяемая. 432 тензорного произведения. 432 форма Безье, 437; 457 разбиение. 451 форма Эрмита, 433 шероховатая, 257 Поворот вокруг произвольной оси, 188 матрица, 184 фиксированная точка, 187 Поглощение света, 78; 400 Позиция растра, 71; 395 Позиция растра, параметр состояния, 120 Показания. 109 Поле зрения. 41 Полигональная сеть, 100 Полиномиальные кривые, 72 Полное внутреннее отражение, 265 Полутона, 330 Поля превышений, 494 Порция смешивания. 431 Порядок полинома, 423 Правило правой руки, 171 Преломление, 37; 250 Преломления показатель, 265 Преобразование. 176 аффинное. 176-182 BOpenGL, 193 изометрическое, 180 масштабирование, 181 матрица масштабирования, 183 матрица сдвига, 182 нормализация, 289 обобщенное, 182 отражение, 181 перспективное, 221 перспективной нормализации, 236; 289; 305; 311 поворот, 179; 187-193 сдвиг. 179 скос, 185 суперпозиция, 186 экземпляра, 189; 338 Примитивы, 66-72 замкнутая ломанная, 67 кривая, 72 ломанная линия, 67 многоугольник, 67 отрезок, 67 полоса из треугольников, 69;267;319 из четырехугольников. 69; 267 розетка из треугольников, 70; 319 точка. 60 треугольник. 69 четырехугольник, 69 Проективное преобразование. 51; 220 Проекция аксонометрическая. 208 диметрическая. 208 изометрическая, 208 триметрическая, 208 косоугольная, 209; 233: 304 ортогональная. 82; 208 параллельная, 208-210 перспективная, 210; 221; 236 плоская геометрическая, 206 Проецирующий луч, 42; 206 Прозрачность, 78 Прямая, 152 неявная форма. 421 Прямоугольная оболочка, 301; 310 Псевдоцвет. 398 Разбиение рекурсивное, 270 Разрешающая способность. 39 Рассеивающая поверхность, 37 Растр. 29 позиция, 395 прогрессивная развертка. 32 чересстрочная развертка. 32 Растровое преобразование. 30; 51; 289; 313 алгоритм Брезенхэма, 315 алгоритм ЦДА, 314 Регенерация изображения, 31 Режим побитовой записи, 393 Режим работа с матрицами. 83 Режимы ввода выборок, 111 обработки событий, 111; 121-27 по запросу, ПО Резиновая линия, 140 Рекурсивная формула де Кастельо, 443 Рекурсивное разбиение, 90 Рефракция, 37 Предметный указатель 587
Сазерленд A. (Sutherland I.). 27; 53; 103 САПР. 27 СБИС. 49 Свертка. 408 Свет. 35-36 диффузное отражение, 250; 257 зеркальное отражение, 250; 258 источник. 250 прожектор, 253; 274 точечный. 252 удаленный, 254 монохраматический, 36 преломление, 250: 265 фоновый. 252: 257 функция излучения. 251 Световое перо, 107 Связывание атрибутов, 73 Сглаживание усреднением по области, 325 Серпинского узор, 56; 89-96; 482 Сигнал синхронизации, 109 Символ, 338 Симулятор. 28; 219 Синтезированная камера, 42 Система координат, 62: 158 мировая, 62: 292 наблюдателя. 292 нормализованная устройства отображения, 293; 321 объекта, 292 окна OpenGL. 231 отсечения, 292 правосторонняя. 155 прикладная. 62 текстуры, 378 экрана, 231; 293 Скаляр, 148 Скалярное поле. 505 Скалярное произведение, 154 Снелля закон. 265 Событие, 121-27 изменение размеров окна, 124 очередь. 111 Соотношение сторон. 85 Стек. 116: 196; 345 Степень полинома, 423 Степень свободы, 108; 177; 468 Стереопара, 395 Структурированное множество данных, 506 Тексель, 378 Текст, 70; 117-121:396 Тени, 240-242 Термальная шкала. 398 Тип объекта GL_LINE_LOOP. 67 GL_LINE_STRIP, 67 GLLINES. 67 GL_POINTS. 60 GL_POLYGON, 69 GL_QUAD_STRIP. 69 GL_QUADS, 69 GL_TRIANGLE_FAN, 69 GL_TRIANGLE_STR1P. 69 GL_TR1ANGLES, 69 Тонирование, 47 глобальное, 278 локальное, 247 Топология, 171 Точка, 148 вычитание точек, 151 опорная полиномиальной кривой, 425 привязки вида. 216 сложение с вектором. 152 сопряжения полиномиальных сегментов, 424 Трассировка лучей, метод. 38; 279-82 Треки частиц, 518 Угол азимута. 220 возвышения. 220 зрения, 41; 224 крен, 220 кручения. 220 отражения, 263 падения, 263 половинного направления, 264 рыскание, 220 тангаж, 220 Эйлера, 201 Удаление невидимых поверхностей, 95; 227; 289; 305 алгоритм z-буфера, 308; 320 алгоритм маляра, 310 алгоритм построчного сканирования. 312 анализ пространства изображения, 306 анализ пространства объектов. 305 588 Предметный указатель
отбраковка нелицевых граней, 307 оценка сложности, 306 сортировка по глубине. 310 Узел дерева динамический. 351 корневой. 340 терминальный, 340 Указание объектов. 129 Универсальный локатор ресурса (Uniform Resource Locator— URL), 370 Уравнение в неявной форме. 262: 421 в параметрической форме. 262; 296: 422 в явной форме. 420 Уровень изоповерхности, 506: 508 Устройства указания. 33 Ф Физические модели. 467-472 Фиксированная точка преобразования. 180 Фокусное расстояние. 41: 45: 53 Фонг (Phong B.T.). 248 метод закрашивания, 269 модель отражения. 255; 260 Фракталы. 98: 483-90 графтал. 483 Фрейм. 159 камеры.167;206 мировой. 167: 338 модели. 338 Функция OpenGL g!Accum(). 409 glBegin(). 59:66 glBeginTrim(). 460 glBitmapO, 396 glBlendFunc(). 402 glCallLisK). 116 glCallListsO. 120 glClearQ. 137:228:406 glClearColorQ. 78: 80 glColor3f(). 77: 80 glColorMateriak). 276 glColorPointerQ. 175 glCopyPi\els(). 398 glCullFaceQ. 308 glDepthFuncQ. 309 glDepthMask(). 403 glDrawElements(). 175: 176 glDrawPixelsQ. 398 glEnableQ. 96: 228: 331: 384: 402 glEnableClientState(). 175 glEndQ. 59: 66 glEndList(). 115; 119: 121 glEndTrim(). 460 glEvalCoordlf(), 457 glEvalCoord2*(). 457 glEvalMesh 1(X457 glEvalMesh2(), 458 glFlush(). 61 glFrustumO. 225; 229; 409 glGenListsO, 117; 119 glHint(). 387 gllndexiQ. 79 glLightf(). 273: 274 glLightfvQ. 273 glLightModcH). 275 g!ListBase(). 120 glLoadIdeniily(). 195 glLoadMatri\(). 169:220 glLoadMatrixf(). 195; 198 glLogicOp(). 394 glMaplf(). 456 glMap2f(). 457: 459 glMapGridlf(). 457 gIMapGrid2*(). 458 glMaterialf(). 275 glMaterialfvQ. 275 g!Matri\Mode(). 83: 193: 195: 346 glMultMatrixfQ. 195 glNewLisU). 115: 119: 121 glNormal3f(). 263 glNormal3fv(). 263 glOrthoQ. 82: 86; 214: 226; 231; 407 glPersectiveQ. 407 glPixelMapQ. 398 glPointQ. 99 gIPointSize(). 80 glPolygonModeO. 69 glPolygonOffsetQ. 497 glPopAttrib(). 117:347 g!PopMatrix(), 117: 120: 196; 346; 360 glPushAttrib(). 117:347 glPushMatrix(). 117; 120: 196; 346: 350: 360 glRasterPos*(). 120:395 glRasterPos3f(). 395 glReadPixels(). 398 glRotatef(). 195 glScalef(). 195 glShadeModclQ. 267: 268 gITexCoord2f(). 384 glTcxEnvQ. 387 glTexGenfv(). 389 g!Tc.\Image2D(). 383 glTexParameterQ. 385 glTranslate(), 118 Предметный указатель 589
glTransIatef(). 119: 195 gluBeginNurbsCurve(), 460 gluBeginNurbsSurface(). 460 gluBuild2DMipmaps(). 386 gluCylinderQ, 461 gluEndNurbsCurve(). 460 gluEndNurbsSurface(), 460 gluLookAt().219;228:229 gluNewNurbsRendererQ, 460 gluNe\vQuadric(). 461 gluNe\\Tess(). 319 gluNurbsCurve(). 460 gIuNurbsProperty(). 460 gluNurbsSurface(), 460 gluOrtho2D(), 82 gluPerspective(). 225 gluP\vlCurve(), 460 gluQuadricDra\\Style(). 461 glutBitmapCharacter(). 71: 120 glutBitmapWidth(). 121 glutCreateWindow(). 85: 127 glutDisplayFuncQ. 88: 126 glutldleFunc(), 136 glutlnit(). 85 glutlnitDisplayModeQ. 85; 96: 127; 137; 228 glutMotionFunc(). 126 glutMouscFunc(). 122 glutPostDisplay(), 126 glutPostRedisplayQ. 136 glutSetColor(). 19 glutSetWindovv(). 127 glutStrokeCharacter(), 120 glutS\vapBufTers(). 137 glVertex(), 212 gIVertex*, 59 glVertexPointerQ, 175 glVie\vport(). 86 gMultMatrixf(). 198 ввода информации, 65 визуализации, 64 геометрические преобразования, 64 задания атрибутов. 64 построения примитива. 64 Функция с обратным вызовом, 112 движение мыши. 126 отображение. 126 перерисовка, 124 простой. 127 X Характеристический код области, 294 Хроматические координаты. 328 ц Цвет. 73-80 RGB. 30: 77 RGBA. 78 гамма, 76 дополняющие цвета (CMY), 76 индексируемый, 78; 516 палитра, 78 первичный. 39 смешивание. 401 таблица соответствия. 79: 398; 516 цветовой куб, 76 четырехкомпонентная система, 78 ЦДА (цифровой дифференциальный анализатор). 314 Центр проецирования, 42 Чайник Юта. 452: 458 Черепашья графика. 99: 480 Численное интегрирование Рунге-Кутта метод, 475 Эйлера метод, 473 ш Шрифт. 70 растровый. 71: 396 штриховой. 70; 396 Эйлера углы. 201 Экземпляр. 189: 338 ЭЛТ. 31 теневая маска, 32 триада. 32 Я Язык C++. 355 HTML. 370 JAVA. 372 JAVA-3D, 372: 373 Renderman Shading Language, 373 VRML, 371 Яркость. 39 590 Предметный указатель
Научно-популярное издание Эдвард Эйнджел Интерактивная компьютерная графика. Вводный курс на базе OpenGL, 2 изд. Литературный редактор И.А. Попова Верстка М.А. Удалое Художественный редактор С. А. Чернокозинский Технический редактор Г.Н. Горобец Корректоры Л.А. Гордиенко, Т.А. Корзун, Л. В. Коровкина, О.В. Мишутина Издательский дом "Вильяме". 101509, Москва, ул. Лесная, д. 43, стр. 1. Изд. лиц. ЛР № 090230 от 23.06.99 Госкомитета РФ по печати. Подписано в печать 09.10.2001. Формат 70x100/16. Гарнитура Times. Печать офсетная. Усл. печ. л. 47,73. Уч.-изд. л. 39,93. Тираж 5000 экз. Заказ № 1875. Отпечатано с диапозитивов в ФГУП "Печатный двор" Министерства РФ по делам печати, телерадиовещания и средств массовых коммуникаций. 197110, Санкт-Петербург, Чкаловский пр., 15.
Ил. 1 Трехмерный узор Серпинского, рассчитанный по точкам. (С любезного разрешения университета Ныо-Мексико.) Ил. 2 Трехмерный узор Серпинского, рассчитанный методом рекурсивного разбиения. (С любезного разрешения университета Нью-Мексико.)
Ил. 3 Аксономет- Аксонометрический вид здания. (С любезного разрешения архитектора Ричарда Нордхауза (Richard Nodrhaus), Альбукерк, шт. Нью-Мексико.) Ил. 4 Перспек- Перспективный вид интерьера здания. (С любезного разрешения архитектора Ричарда Нордхауза (Richard Nodrhaus), Альбукерк, шт. Нью-Мексико.)
Ил. 5 Тень, отбрасываемая кубом на горизонтальную плоскость. (С любезного разрешения университета Нью- Мексико; авторы Филип Экенрот (Philip Eckenroth), Майкл Тайпинг (Michael Tipping), Мерили Падилла (Marilee Padilla), Джой Мадригал (Joey Madrigal).) Ил. 6 Цветовой куб в системе RGB. (С любезного разрешения университета Нью-Мексико.)
Ил. 7 Модель распределения температуры воды в океане во время урагана "Эль-Ниньо". При построении изображения карты использовано наложение текстуры на поверхность материков. (С любезного разрешения Национальной лаборатории в Лос-Аламосе; автор Аллен Мак-Ферсон (Allen McPherson).) Ил. 8 Пространственное изображение рельефа о. Гонолулу в группе Гавайских островов, полученное с помощью поверхности в форме Безье. (С любезного разрешения университета Нью-Мексико и Национальной лаборатории Сандиа; автор Брайан Уайли (Brian Wylie).) Ил. 9 Модель динамики жидкой магмы в мантии Земли. В изображении для окрашивания изотермальных поверхностей использованы псевдоцвета. (С любезного разрешения Национальной лаборатории в Лос-Аламосе; автор Джемс Пейнтер (James Painter).)
Ил. 10 Результат тонирования по методу трассировки лучей. (С любезного разрешения Национальной лаборатории в Лос- Аламосе и университета Нью-Мексико; автор Патрик Мак-Кормик (Patrick McCormick.) Ил. 11 Изображения чайников Юта, в которых использованы материалы с разными свойствами. (С любезного разрешения Silicon Graphics, Inc.) | ЯШ/
Ил. 12 Изоповерхности, сформированные методом маркированного куба на основании набора данных о строении омара A20х120х34вокселей). (С любезного разрешения университета Нью-Мексико и Национальной лаборатории Сандиа; автор Патриция Кроссно (Patricia Crossno).) Ил. 13 Изоноверхность, сформированная с помощью модели системы 40000 материальных частиц на основании набора данных о строении омара. (С любезного разрешения университета Нью- Мексико и Национальной лаборатории Сандиа; автор Патриция Кроссно (Patricia Crossno).)
Ил. 14 Кадр из игровой программы Geri's Game, в котором на стеклышках очков персонажа видно отражение окружающего пейзажа и преломление световых лучей. (С любезного разрешения Pixar Animation Studios.) Ил. 15 Карта отражения среды, которая была использована для наложения при формировании изображения стеклышек очков персонажа игровой программы Geri's Game. (С любезного разрешения Pixar Animation Studios.)
Интерактивная компьютерная графика Вводный курс на базе Open Данная книга написана с учетом того, что студенты значительно быстрее усваивают основы компьютерной графики, если сразу же включаются в разработку программ. Такой подход с самых первых страниц книги во- вовлекает читателя в процесс разработки графических программ. В каждой главе представлены нетривиальные приложения, на практике демонстрирующие применение рассмотренных методов. Книга включает все основ- основные разделы компьютерной графики — архитектуру графических систем, геометрические преобразования, взаимодействие света и материала, тонирование изображения, графическое моделирование, построение кри- кривых и криволинейных поверхностей, наложение текстур, особенности современных аппаратных средств гра- графических систем. Весь практический материал, приведенный в книге, базируется на графической системе OpenGL, с которой читатель познакомится уже в первых главах. Эта система доступна тем, кто работает в операционной среде Microsoft Windows (как 98, так и NT), а пользователей компьютеров Apple фирма-изготовитель известила о том, что OpenGL будет включена в качестве базового компонента в будущие версии операционной системы. Пользовательский интерфейс OpenGL и знание основ- основных методик программирования на языке С позволят чи- читателям создавать собственные достаточно сложные гра- графические приложения, использующие технологию визуа- визуализации трехмерных объектов и обработку событий для управления ходом выполнения программы. Во второе издание книги внесены значительные измене- изменения и дополнения. В частности, существенно дополнен материал, касающийся построения иерархических моде- моделей, использования в компьютерной графике объектно- ориентированного подхода, новых методов визуализации и взаимодействия пользователя с программой. В книге освещены новые возможности, которые предоставляет пользователям версия системы OpenGL 1.1, в том числе и работа с массивами вершин. Дополнительный материал к этой книге, включая тексты программ и ссылки на ресурсы, касающиеся OpenGL, чи- читатель найдет на Web-узле по адресу http://www.awl.com/ cseng/angel. взяты п.i компьютерного фильма mac КраСНОЙ смерти", КОТОРЫЙ СОЗДаЛИ С1\'Де п.1 университета Нью Мексико: Макс Ха 1слрш (мах Hazelrigg), Кристофер Джор i.iii (( hristophei Ionian), lllei.ii Мораии (Shezatl Morani), Джим Прейеп (Jim Prewett), X.i i (M.niep (llal Sniyer) u Хью Уокер (I lue Walker). < 'цены были смодели- смоделированы с помощью пакета Maya, .1 для м> нирования ниш п, к им |ся пакет Blue Moon Rendering TtK)ls. Ну |ата, пока данная на пер вой и последней страницах обложки, in ни, одна и 11 емп комнат, и кото|>ых рая во 1ЬЯМС" Эдвард Эйнджел — профессор по специальностям "Инфор- "Информатика" и "Электротехника и вычислительная техника" уни- университета Нью-Мексико, в котором он также является пред- председателем совета отделения и консультирует аспирантов. В университете Нью-Мексико он был первым, кого удостои- удостоили Президентского отличия воспитателю юношества (Presidential Teaching Fellow). Эйнджел получил степень ба- бакалавра в Калифорнийском технологическом институте, а степень доктора философии — в университете Южной Ка- Калифорнии. ISBN 5-8459-0209-6 О 1079 9 785845 902092