Перейти к содержимому

Иерархическая окклюзия z буфера что это

  • автор:

Иерархический Z-буфер для отбора окклюзии

Я читаю раздел Occlusion Culling в Real-time Rendering 3rd Edition, и я не мог понять, как это работает. Некоторые вопросы:

Как влияет наличие Z-пирамиды? Зачем нам нужно несколько разрешений Z-буфера? В книге это отображается следующим образом (левая сторона): введите описание изображения здесь

Является ли структура Octree той же самой Octree, которая используется для общего отбора и рендеринга усеченного конуса? Или это специализированный Octree, созданный специально для техники отбора окклюзии?

Более общий вопрос: в предыдущем разделе (а также здесь) термин «запрос окклюзии» описывается как «рендеринг упрощенного ограничивающего объема объекта и сравнение его результатов глубины с Z-буфером, возвращая количество пикселей, которые являются видимый «. Какие функции в OpenGL связаны с этой концепцией Occlusion Query?

Является ли этот метод стандартом для отбора окклюзии в играх с открытым миром?

0 ответов

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

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

Да. Это те же октреи. Но это не обязательно должно быть октри. Любая иерархическая структура данных пространственного индексирования будет работать как для иерархического Z-буфера, так и для выбора усеченного конуса.

Чтобы извлечь выгоду из иерархического Z-буфера, нам нужно визуализировать пейзаж, начиная с ближайших объектов. Для этого можно использовать такие приемы, как октреи или BSP.

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

Часть OpenGL, которая отвечает за запросы окклюзии: glBeginQuery , glEndQuery а также glGetQueryObject , Смотрите Query Objects для деталей.

Иерархические Z-буферы были реализованы аппаратно на некоторых ранних Radeon. Однако я не слышал об этом в настоящее время.

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

Иерархическая окклюзия z буфера что это satisfactory

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

Вместимость

Объём

Накопители позволяют сохранять электрическую энергию. Каждый накопитель может хранить до 100 МВтч (360 000 МДж). Поскольку он позволяет подключать 2 источника, несколько накопителей могут быть соединены в цепочку для хранения большого количества энергии.

Изъяны этого кода

Во-первых, в случае буферов глубин полного разрешения, у которых одна размерность в два с лишним раза больше другой размерности, индексы вызовов texelFetch могут выйти за пределы u_depthBuffer . (В таких случаях меньшая размерность превращается в 1 прежде, чем другая.) Я хотел использовать в этом примере texelFetch (использующий целочисленные координаты), чтобы происходящее было как можно более понятным, и не сталкивался лично с такими особо широкими/высокими буферами глубин. Если у вас возникнут такие проблемы, то можно ограничить ( clamp ) передаваемые texelFetch координаты или использовать texture и нормализованные координаты текстуры (в сэмплере задав ограничение по краю). При вычислении min или max стоит всегда рассматривать один тексел несколько раз на наличие пограничных случаев.

Во-вторых, несмотря на то, что первые четыре вызова texelFetch можно заменить одним textureGather , это всё усложняет (так как textureGather не может указывать mip-уровень); кроме того, я не заметил повышения скорости работы при использовании textureGather .

Краткий обзор

Иерархический буфер глубин — это многоуровневый буфер глуби (Z-буфер), используемый как ускоряющая структура (acceleration structure) для запросов глубин. Как и в случае mip-цепочек текстур, размеры каждого уровня обычно являются результатами деления на степени двойки размеров буфера полного разрешения. В этой статье я расскажу о двух способах генерации иерархического буфера глубин из буфера полного разрешения.

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

Затем для случаев, в которых требуется только один уровень даунсэмплинга, я продемонстрирую, как сгенерировать этот уровень при помощи одного вызова вычислительного (compute) шейдера, использующего атомарные операции в общей памяти рабочей группы. Для моего приложения, где требуется только разрешение 1/16 x 1/16 (mip-уровень 4), способ с вычислительным шейдером в 2-3 раза быстрее, чем обычный подход с даунсэмплингом mip-цепочки в несколько проходов.

Энергия

Потребляет

Введение

Иерархические глубины (также называемые Hi-Z) — это часто используемая в 3D-графике техника. Она используется для ускорения усечения невидимой геометрии (occlusion culling) (в CPU, а также в GPU), вычислений отражений в экранном пространстве, объёмного тумана и многого другого.

Кроме того, в GPU Hi-Z часто реализуется как часть конвейера растеризации. Быстрые операции поиска Hi-Z в кэшах на чипе позволяют целиком пропускать тайлы фрагментов, если они полностью закрыты ранее отрендеренными примитивами.

Базовая идея Hi-Z заключается в ускорении операций запросов глубин благодаря считыванию из буферов меньшего разрешения. Это быстрее, чем считывание из буфера глубин полного разрешения, по двум причинам:

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

Буферы Hi-Z чаще всего запрашиваются почти сразу на выходе, чтобы избежать дальнейшей обработки и выполнения более точных операций поиска в буфере полного разрешения. Например, если мы храним значения max для неинвертированного буфера глубин (в которых чем больше значение глубины, тем дальше находится объект), то можем быстро совершенно точно определить, перекрыта ли конкретная позиция в экранном пространстве буфером глубин (если её координата Z > значения (max), сохранённого в какой-то более высоком уровне (то есть с пониженным разрешением) буфера Hi-Z).

Обратите внимание на то, что я использовал фразу «совершенно точно»: если координата Z <= полученному значению (max), то неизвестно, перекрывает ли её буфер. В некоторых областях применения в случаях неопределённости может потребоваться поиск в буфере глубин полного разрешения; в других случаях этого не требуется (например, если на кону стоят только лишние вычисления, а не правильность рендеринга).

Интерфейс [ ]

Интерфейс накопителя.jpg

Левый индикатор — это уровень заряда выбранного накопителя. Здесь же выводятся и числовые показатели текущего уровня накопленной энергии, скорости зарядки и оценка времени, необходимого до полной зарядки, Правый индикатор отображает уровень суммарного заряда всех накопителей в этом сегменте сети.

Внешние индикаторы [ ]

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

Альтернативный способ генерации mip-цепочки

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

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

В этом альтернативном способе при даунсэмплинге уровня с нечётной шириной (или высотой) вместо добавления для каждого выходного тексела дополнительного столбца (или строки) текселов из предыдущего (нижнего) уровня, мы выполняем эту операцию только для выходных текселов с максимальными индексами («крайних» текселов). Единственное, что это изменяет в представленном выше фрагментном шейдере — задание значений shouldIncludeExtraColumnFromPreviousLevel и shouldIncludeExtraRowFromPreviousLevel :

Из-за этого крайние текселы с наибольшим индексом становятся очень «толстыми», так как каждое деление на 2 нечётной размерности приводит к тому, что они занимают пропорционально всё больший интервал нормализованного пространства координат текстуры.

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

Показанный ниже фрагмент кода выполняет перенос из пространства NDC в координаты текселов на mip-уровне higherMipLevel :

Моё приложение: рендеринг частиц в вычислительном шейдере

Я столкнулся с необходимостью использования Hi-Z при реализации рендеринга частиц в вычислительном шейдере в движке моего VR-приложения PARTICULATE. Поскольку эта техника рендеринга не использует растеризацию фиксированными функциями, ей нужно применять собственную проверку глубин для каждой частицы размером один пиксель. А так как частицы никак не сортируются, доступ к буферу глубин оказывается (в худшем из случаев) практически случайным.

Операции поиска по полноэкранной текстуре с произвольным доступом — путь к низкой производительности. Чтобы снизить нагрузку, я сначала выполняют поиск глубин в уменьшенном буфере глубин разрешением 1/16 x 1/16 от исходного. Этот буфер содержит значения глубин min , что позволяет вычислительному шейдеру рендеринга для подавляющего большинства видимых частиц пропускать тест глубин полного разрешения. (Если глубина частицы < минимальной глубины, хранящейся в буфере пониженного разрешения, то мы знаем, что она совершенно точно видна. Если она >= min, то нам нужно проверить буфер глубин полного разрешения.)

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

Благодаря тому, что поиск сначала выполняется в буфере глубин пониженного разрешения (как сказано выше), время рендеринга частиц снижается максимум на 35% по сравнению со случаем, когда поиск выполняется только в буфере полного разрешения. Поэтому для моего приложения Hi-Z очень выгоден.

Теперь мы рассмотрим две техники генерирования иерархического буфера глубин.

Производительность

Я использовал приведённый выше вычислительный шейдер для обработки буфера глубин полного разрешения с теми же размерами, которые использовались для генерации полной mip-цепочки (буферы 1648×1776 для каждого глаза). Он выполняется за 0,12 мс на NVIDIA GTX 980 и за 0,08 мс на AMD R9 290. Если сравнивать со временем генерации только mip-уровней 1–4 (0,22 мс на NVIDIA, 0,25 мс AMD), то решение с вычислительным шейдером оказалось на 87% быстрее у GPU NVIDIA и на 197% быстрее, чем у GPU AMD.

В абсолютных величинах ускорение не такое уж и большое, но важны каждые 0,1 мс, особенно в VR 🙂

Зарядка [ ]

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

Максимальная отбираемая мощность для каждого накопителя составляет 100 МВт, поэтому для полной зарядки пустого накопителя потребуется не менее часа, или больше, если избыточная мощность меньше, чем нужно для удовлетворения всех накопителей в сети.

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

Несколько не полностью заряженных накопителей разделяют запасную мощность поровну.

Накопители не заряжают друг друга.

Игровой процесс

Качество сети

Определяет максимальную полосу пропускания сети, которую игра может использовать для игры в режиме мультиплеера.

Покачивание головой

Регулирует степень покачивания головы пионера при ходьбе.

Сила оптического эффекта при движении камеры (%)
Интервал автосохранений в минутах

Задаёт частоту автоматических сохранений. Задайте нулевое значение, если хотите отключить автосохранения. Количество слотов для автоматического сохранения можно изменить только в конфигурационных файлах.

Уведомления об автосохранении

Включает уведомление с таймером обратного отсчёта за 10 секунд до начала автоматического сохранения.

Отправлять данные об игре

Разрешает отправку внутриигровой диагностики в Coffee Stain Studios.

Режим арахнофобии
Уведомления о перерыве

Должно выдавать предупреждение после двух часов непрерывной игры. Это функциональность перестала работать после патча 0.3.3.0

Ставить опоры при отпускании кнопки мыши

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

Автоматическое соблюдение отступов при строительстве

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

Выполнять наклон опоры по оси Х

Использование [ ]

Если в сети образуется нехватка энергии (потребление превышает производство), все подключенные накопители начинают отдавать накопленную энергию.

Переключение между режимами работы накопителя происходит автоматически и мгновенно.

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

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

Техника 1: генерация полной Mip-цепочки

Во многих областях использования Hi-Z требуется создание полной mip-цепочки буфера глубин. Например, при выполнении occlusion culling с помощью Hi-Z ограничивающий объём проецируется в экранное пространство и спроецированный размер используется для выбора подходящего mip-уровня (чтобы в каждой проверке перекрытия участвовало фиксированное количество текселов).

Генерирование mip-цепочки из буфера глубин полного разрешения обычно является простой задачей — для каждого тексела на уровне N мы берём max (или min , или оба значения) соответствующих 4 текселов в ранее сгенерированном уровне N-1. Выполняем последовательные проходы (каждый раз вдвое уменьшая размеры), пока не получим последний mip-уровень размером 1×1.

Однако в случае буферов глубин, размеры которых не соответствуют степеням двойки, всё сложнее. Так как Hi-Z для буферов глубин часто строится из стандартных разрешений экрана (которые редко являются степенями двойки), нам нужно найти надёжное решение этой задачи.

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

Итак, что же конкретно будет означать получаемое нами значение отдельного тексела на mip-уровне N? Это должно быть минимальное значение ( min ) из всех текселов полноэкранного буфера глубин, занимающего то же пространство в (нормализованном) пространстве координат текстуры.

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

Если это соответствие гарантируется, то мы будем уверены, что операция поиска на высоких mip-уровнях никогда не вернёт значение глубины > значения тексела в той же координате текстуры, соответствующей буферу полного разрешения (уровень 0). В случае отдельного N, эта гарантия сохраняется для всех уровней ниже него (< N).

Для чётных размерностей (а в случае буферов полного разрешения, являющихся степенями двойки, чётными будут размерности на каждом уровне до самого последнего, где размеры становятся равными 1) это сделать легко. В одномерном случае для тексела с индексом на уровне N нам нужно взять текселы на уровне N-1 с индексами и и найти их значение min . То есть . Мы можем напрямую сопоставить текселы в отношении «2 к 1» (а значит, и размеры координат текстур), ведь размер каждого уровня ровно в два раза меньше предыдущего.

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

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

Это означает, что теперь у нас нет чёткого сопоставления «2 к 1» текселов уровня N-1 к текселам уровня N. Теперь размер координаты текстуры каждого тексела на уровне N накладывается на размер 3 текселов на уровне N-1.

Пример нечётных размеров уровня: 7 текселов этого уровня уменьшаются до 3 текселов на следующем уровне. Размеры координаты текстуры трёх текселов высокого уровня накладываются на размеры трёх текселов из нижнего уровня.

Следовательно . Это означает, что один тексел на уровне N-1 иногда влияет на значение min , вычисленное для 2 текселов на уровне N. Это необходимо для сохранения описанного выше сопоставления.

Описание выше для простоты было представлено всего в одном измерении. В двух измерениях, если обе размерности уровня N-1 являются чётными, то область текселов 2×2 на уровне N-1 сопоставляется с одним текселом на уровне N. Если одна из размерностей нечётна, то область 2×3 или 3×2 на уровне N-1 сопоставляется с одним текселом на уровне N. Если нечётны обе размерности, то следует также учитывать «угловой» тексел, то есть область 3×3 на уровне N-1 сопоставляется с одним текселом на уровне N.

Размеры

Длина
Ширина
Высота

Техника 2: генерация одного уровня Hi-Z при помощи вычислительного шейдера

Генерация полной mip-цепочки выполняется довольно быстро, но меня немного напрягало то, что моё приложение генерирует все эти уровни, а использует только один из них (уровень 4). Кроме устранения этой небольшой неэффективности, мне также хотелось посмотреть, насколько можно всё ускорить, если использовать для генерации только нужного мне уровня один вызов вычислительного шейдера (compute shader). (Стоит заметить, что моё приложение может останавливаться на уровне 4 при использовании решения с многопроходным фрагментным шейдером, поэтому в конце этого раздела я использовал его как основу для сравнения времени выполнения.)

В большинстве случаев использования Hi-Z требуется только один уровень глубин, поэтому я считаю, что такая ситуация встречается часто. Я написал вычислительный шейдер под собственные специфические требования (генерирование уровня 4, имеющего разрешение 1/16 x 1/16 от исходного). Похожий код можно использовать для генерации разных уровней.

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

Я попробовал два основных решения на основе вычислительных шейдеров. В первом каждый поток вызывал atomicMin для одной переменной общей памяти.

Учтите, что поскольку программисты не могут (без расширений под «железо» конкретного производителя) выполнять атомарные операции над нецелочисленными значениями (а мои глубины хранятся как float ), здесь необходим какой-то трюк. Поскольку неотрицательные значения с плавающей запятой стандарта IEEE 754 сохраняют свой порядок, когда их биты обрабатываются как беззнаковые целочисленные значения, мы можем использовать floatBitsToUint , чтобы привести (при помощи reinterpret cast) значения глубин float к uint , а потом вызвать atomicMin (чтобы затем выполнить uintBitsToFloat для готового минимального значения uint ).

Наиболее очевидным решением с atomicMin стало бы создание 16×16 групп потоков, в которых каждый поток получает один тексел, а затем выполняет его atomicMin со значением в общей памяти. Я сравнил такой подход с использованием блоков потоков меньшего размера (8×8, 4×8, 4×4, 2×4, 2×2), в которых каждый поток получает область текселов и вычисляет собственный локальный минимум, а затем вызывает atomicMin .

Самым быстрым из всех этих протестированных решений с atomicMin и на NVIDIA, и на AMD оказалось решение с блоками потоков 4×4 (при котором каждый поток сам получает область текселов размером 4×4). Я не совсем понимаю, почему этот вариант оказался самым быстрым, но возможно, он отражает компромисс между конкуренцией атомарных операций и вычислениями в независимых потоках. Стоит также заметить, что размер рабочей группы 4×4 использует всего 16 потоков на warp/wave (а возможно использовать ещё и 32 или 64), что любопытно. В показанном ниже примере реализован этот подход.

В качестве альтернативы использованию atomicMin я попытался выполнять параллельное уменьшение при помощи техник, использованных в этой активно цитируемой презентации NVIDIA. (Основная идея заключается в использовании массива общей памяти того же размера, что и количество потоков в рабочей группе, а также проходов для последовательного совместного вычисления min минимумов каждого потока, пока не будет получен окончательный минимум всей рабочей группы.)

Я попробовал это решение со всеми теми же размерами рабочих групп, что и в решении с atomicMin . Даже со всеми оптимизациями, описанными в презентации NVIDIA, решение с параллельным уменьшением слегка медленнее (на обоих GPU в пределах десятка процентов), чем решение с atomicMin , к которому пришёл я. Кроме того, решение с atomicMin значительно проще с точки зрения кода.

Содержание

Накопитель электроэнергии

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

Становится доступным

Пример кода

При таком способе проще всего не стараться поддерживать соответствие в нормализованном пространстве координат текстуры между текселами уменьшенных буферов и полного разрешения. Можно просто выполнять преобразования из координат текселов полного разрешения в координаты текселов уменьшенного разрешения:

В моём случае (генерирование эквивалента mip-уровня 4) downscalingFactor равен 16.

Как сказано выше, этот вычислительный шейдер GLSL реализует решение с atomicMin с размером рабочих групп 4×4, где каждый поток получает из буфера полного разрешения область текселов размером 4×4. Получившийся уменьшенный буфер глубин значений min равен 1/16 x 1/16 от размера буфера полного разрешения (с округлением вверх, когда размеры полного разрешения не делятся на 16 нацело).

Производительность

Я использовал приведённый выше фрагментный шейдер для генерации двух полных mip-цепочек для двух (по одному на каждый глаз) буферов глубин в своём VR-движке. В тесте разрешение для каждого глаза составляло 1648×1776, что привело к созданию 10 дополнительных уменьшенных mip-уровней (а значит, 10 проходов). На генерацию полной цепочки для обоих глаз потребовалось 0,25 мс на NVIDIA GTX 980 и 0,30 мс на AMD R9 290.

Mip-уровни 4, 5 и 6, сгенерированные из буфера глубин, соответствующего показанному выше цветовому буферу. (Стоит учесть, что бОльшая часть сцены прозрачна, а значит, не влияет на буфер глубин.) Mip-уровень 4 — первый, в котором размеры (103×111) не делятся нацело на 2.

Что такое Z-буфер?(ликбез).

Трехмерный объект задается:

— координатами его вершин в пространстве сцены,

— локальными координатами в пространстве текстурной карты,

— смещением и прочими изменениями в течение времени в соответствии с замыслом разработчиков.

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

Вершины. Задают местоположение объекта в трехмерном пространстве; само их положение задается координатами X, Y и Z .

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

Текстуры. Это двухмерные изображения, или поверхности, налагаемые на примитивы. Точки текстуры называются текселами.

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

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

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

Z-буферизация — эта технология изначально применялась в системах САПР (системах автоматизированного проектирования). В двумерном мире объекты не могут располагаться впереди или позади друг друга, поэтому не было проблем с перекрытием. Но в трехмерном мире один объект может находиться впереди другого. Обычно световые лучи не проникают через непрозрачные объекты, поэтому мы видим все, что находится впереди, и не видим того, что позади.

Когда два объекта перекрываются , нужно выяснить, какой из них находится впереди, чтобы знать, какие пиксели объекта нужно показать на дисплее. Область, в которой пересекаются две фигуры , можно описать , указав для каждого пиксела фигур величину расстояния от него до условного заднего плана . Если дополнить обычную видеопамять картой этих расстояний для каждого пикселя , то будет всегда известно, нужно ли закрашивать конкретный пиксель: если значение расстояния (или значение Z ) у пикселя меньше, значит, он позади и его не нужно закрашивать.

Эту идею можно реализовать аппаратно . Решение, состоит в создании параллельно с памятью дисплея другого массива памяти, называемого Z-буфером . Каждый раз при записи пикселя вычисляется его значение Z . При этом записываются только пиксели с большими значениями Z и обновляются расстояния в Z-буфере. Все остальные пикселы игнорируются. Таким образом, в каждой ячейке Z -буфера хранится расстояние по оси Z (вглубь экрана) для рисуемого пиксела, поэтому легко проверить, затенен ли новый записываемый пиксель или нет. Z-буфер требует дополнительной памяти, и, чем большая точность нужна для значений Z , тем больше памяти нужно для запоминания значений Z . Если используется разрешающая способность 640х400 и значения Z в виде 16-разрядных (двухбайтовых) чисел, то нужно иметь 0,5 мегабайта памяти только для Z-буфера. С помощью Z -буфера можно легко решить, какие объекты расположены на переднем плане, но при этом понадобится вдвое больший объем видеопамяти. Почти все современные 3 D -ускорители имеют 24-х или 32-битную Z -буферизацию, что в значительной мере повышает разрешающую способность и , как следствие, качество рендеринга .

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

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

Пример кода

Показанный ниже код шейдера на GLSL реализует описанный нами алгоритм. Он должен выполняться для каждого последующего mip-начиная с уровня 1 (уровень 0 — это уровень полного разрешения).

иерархический Z-буффер

Нужно прикрутить к octree иерархический z-буффер. Для эффективной работы в indoor. Принцип работы — если узел дерева на основе данных из z-буфера невидим — отбрасывается вся геометрия куба. Планируется использование z-пирамиды вроде mip-levels. Вопросик: есть ли где-нибудь в Сети примерчик с реализацией этой вещички? Если кто-то знает дайте, плз, ссылку.

ЗЫ. Воду не давать — воды я уже начитался. Мне бы код.

Э. Гм, не советую. Для того, чтобы заполнять иерархический Z буфер, нужно будет софтверно рисовать какую-то геометрию (возможно, более грубую). А потом тестировать. Бр. Неизвестны примеры, когда подход удалось бы довести до конца. То, что удалось сделать с s-буфером (Unreal) производит сейчас жалкое впечатление. На сколь-нибудь сложных сценах все проседает нафиг.

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

IronPeter
Медленно? У-у-у, а я так надеялся.
>Для того, чтобы заполнять иерархический Z буфер, нужно будет софтверно рисовать >какую-то геометрию (возможно, более грубую).

Чего-то я не понял. Например, рисуем мы небольшой коридорчик. Стена коридорчика закрывает пол-экрана. Мы используем грубое основание пирамиды, например, 8х8 пикселей. Я думаю делать так: определяем, входит ли тайлик полностью в проекцию треугольника, и если так — записываем в z тайла максимальное z этого треугольника. И так далее. Например, для разрешения 800х600 у нас будет 7500 тайлов в основании пирамиды — не так уж и мало. Пирамиду надо обновлять каждый раз до вершины, что тоже хлопотно. Но я думал, что преимущество раскроется после того, как мы построим пирамиду — потом просто сравниваем z полигонов, из которых состоят фантомные кубы octree-дерева, со значением в вершине пирамиды (одна проверка!) и весь куб — тю-тю, отбрасывается, возможно, даже с дочерними кубами, если это не лист.

>Дело в том, что на cовременных картах иерархический буфер (или что-то подобное) уже >существует. Так что использовать надо именно его (occlusion test и компания).
Это про ATI Radeon? Там какая-то тайловая технология. Но, прости, как это прикрутить к octree, не представляю, особенно на GF2MX.

ЗЫ. А помему тогда пишут, что ИЗБ — это мощное и элегантное решение? И вообще, хотел бы я посмотреть, как можно применить PVS для гигантской модели — скажем сотня домов в городе, в каждом доме сотня комнат, в каждой комнате сотня вещичек и т. д. Octree + ИЗБ даст здесь фору любому другому алгоритму. Или я неправ?

Нееее тут надо тебе решить, лидо ты будеш сам запонять Z buffer либо доверить это дело карте. Но тут тоже есть свои загогулины. Предложеный тобой алгоритм пропрёт вперед с реактивной скоростью, если ты отладишь использование Z buffer’а на карте в купе с OctTree. У меня есть много задумок на ету тему, но тут тебе надо наладить связь с Z на карте, чтобы при рендеренге отслеживать будет ли виден куб или нет.

Джо
Вот тут и вступает в дело иеархия.
Сначало определяешь видимость домов(или в начале районов), потом комнат в домах и тока потом вешей.

Тут вообще лучше по мне так использовать не z-buffer а обычные оклудеры.
в общем рей трейсинг, иеархический и через octtree.

Надо быть проще. Чем дальше дом, тем темнее в его комнатах, и, начиная с какого-то расстояния, внутренности не рисуются а проёмы затягиваются чёрными плоскостями. Видел в СерьёзномСэме2, выглядит совершенно естественно.

В готике2 также. Но выглядит ужасно.

Ну я не знаю, может случайно выйти быстро 😉

По поводу 8×8 — думаю, что нельзя останавливаться на этом уровне. Основная проблема в стыкующихся полигонах. Вот есть два полигона. И существуют две возможности — или они стыкуются, или между ними есть щель. Тогда сквозь эту щель можно что-то видеть. И эту разницу в несколько пикселей нельзя поймать на уровне 8×8.

То есть Z буфер должен быть примерно экранного разрешения, если мы хотим иметь хоть какую-то точность. То есть мы должны софтово рисовать в Z буфер полигоны. И при этом сохранять какую-то иерархию. Мне кажется, что это мучительно медленно.

Дешевле просто рисовать это octree дерево. От ближнего к дальнему. Железо сейчас весьма оптимизовано, и оно не чихнет на трансформации лишних вершин. А ранний Z тест доделает работу. Я не против определения видимости, но я против уж совсем тупого дублирования отрисовки.

>>Это про ATI Radeon? Там какая-то тайловая технология. Но, прости, как это прикрутить к octree, не представляю, особенно на GF2MX.

Как прикрутить? Что тебе надо? Определить, виден ли кубик относительно Z буфера? Пожалуйтса. Есть такая возможность. На хороших картах, естественно.

Ладно, поищем про окклудеры. А что это такое, хотя бы в двух словах, скажите.

Насчёт Сэма — это не из той оперы. Я планирую крутой тесный indoor, но чтобы движок позволял иногда выходить и на открытые полянки или в космос, вроде как в DOOM III на поверхность Марса.

IronPeter
Нестыковка полигонов. А это в каком случае? Я думал так: если тайл НЕ ПОЛНОСТЬЮ входит в проекцию прямоугольника, работу с ним не ведём. Если я правильно нарисовал у себя на бумажке, то в месте зазора мы оставим незаполненную полосу тайлов:

Не знаю, как получится 🙁

В том-то и дело, что планируется взять основание пирамиды погрубее, и не доводить его до пиксельного уровня. Мы не определяем видимость самой геометрии в листьях дерева — пускай это делает карточка. Мы лишь берём координаты вершин этой геометрии, составляем проекции граней на плоскость и обновляем всю пирамиду от основания до вершины. Чем грубее пирамида — тем быстрее. При грубом основании, конечно, будут ошибки — узел не виден, а мы просчитались и решили его отрисовать, но это терпимо.

Я больше боюсь другого — если мы вышли, например, на просторы здорового помещения. Дальняя стена и предметы возле неё настолько малы, что толком не закрывают полностью ни один тайл размеров 8х8 на основании. В этом случае алгоритм будет работать вхолостую, и мы никак не сможем отсечь геометрию дальше этой далёкой стены. Да и вообще, множество мелких полигонов может свести на нет работу алгоритма.

Можно ещё попробовать вычислять размеры проеции полигонов на плоскость, и если они достаточно малы, работать на пиксельном уровне, а потом объединять в ячейки 8х8. Но это уже страшненько.

>>Я думал так: если тайл НЕ ПОЛНОСТЬЮ входит в проекцию прямоугольника, работу с ним не ведём.

Если такой тайл не берем, то появятся щели, сквозь которые будет просвечивать заведомо невидимая геометрия. Да, кстати, а почему прямоугольника?

>>Да и вообще, множество мелких полигонов может свести на нет работу алгоритма

Да-да, это и есть проблема.

IronPeter
>Если такой тайл не берем, то появятся щели, сквозь которые будет просвечивать заведомо >невидимая геометрия. Да, кстати, а почему прямоугольника?

Как раз я об этом и говорил:
>Чем грубее пирамида — тем быстрее. При грубом основании, конечно, будут ошибки — узел >не виден, а мы просчитались и решили его отрисовать, но это терпимо.

Что поделать, скорость требует жертв. Оговорился — конечно же, проекцию треугольника.

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

Но что же мне тогда применить? 🙁 Octree я уже полностью отладил и скоростью работы доволен, но не рисовать же все объекты во viewing frustum? Читый Octree бы сошёл, если бы мы гуляли по полянке или смотрели на полянку сверху (тут даже лучше quadtree), но для шутера нужно отсекать задние объекты. И с железом, которого у меня нету, связываться не хочется. Не хочу применять BSP & PVS.

Под occlusion test имеются в виду HP_occlusion_test и NV_occlusion_query? Ну нет у меня GF3+, ну что я сделаю? Да и не дело это — отсекать одним махом половину старых карточек от движочка ;-(

А может, попробовать считывать в массив содержимое буфера глубины (стенсил-буфера, правильно?). Он ведь формируется аппаратно:

glReadPixel( 0, 0, viewport_width, viewport_height, GL_DEPTH_COMPONENT, GL_FLOAT, zbuf );
Ну и просчитывать, виден узел octree или нет.
Вывели узел — считали в буффер. Проверили, вывели-не вывели. Вывели — снова считали и т. д. Практически, получаем пиксельное основание. Их такого буфера легко построить z-пирамиду.

Джо
Да усе правильно . Все нормально , отличный алгоритм! Единственная проблемма это непосредственно заполнять твой Z буфер. Почему проблемма ? потому что если будешь работать непосредственно с треугольниками то производительности не хватит(представь 50000 видимых трианглов ). Работать можно с заведомо просчитанными OCCLUDERS . Это объекты которые приближенно описывают геометрию , например домик с едренной геометрией можно описать кубиком или шариком(ми) который полностью поглощен домиком. В некотором смысле порталы это тоже окюдеры. (поправьте меня если что )
Короче если у тебя есть статическая геометрия тогда совсем просто, все можно заранее подготовить.
Так вот работа с упрощенной геометрией таки даст едренную экономию

IronPeter
Проблем не будет , потому что отбрасываются только СОВЕРШЕННО точно невидимые полигоны , а те которые хрен знает оставляются на съедение карточке.

All
К сожалению, это все теория, не встречал я готового решения такого плана и сам не делал 🙁
( а вот судя по статье на IXBT на некоторых карточках ранняя отсечка не работает)

Удаление невидимых граней. Алгоритм z-буфера

Это один из простейших алгоритмов удаления невидимых поверхностей. Впервые он был предложен Кэтмулом. Работает этот алгоритм в пространстве изображения.

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

Основной недостаток алгоритма — большой объем требуемой памяти. Если сцена подвергается видовому преобразованию и отсекается до фиксированного диапазона координат z значений, то можно использовать z-буфер с фиксированной точностью. Информацию о глубине нужно обрабатывать с большей точностью, чем координатную информацию на плоскости (x, y); обычно бывает достаточно 20 бит. Буфер кадра размером 512*512*24 бит в комбинации с z-буфером размером 512*512*20 бит требует почти 1.5 мегабайт памяти.

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

Формальное описание алгоритма z-буфера:

1) заполнить буфер кадра фоновым значением интенсивности или цвета;
2) заполнить z-буфер минимальным значением z;
3) преобразовать каждый многоугольник в растровую форму в произвольном порядке;
4) для каждого Пиксел(x, y) в многоугольнике вычислить его глубину z(x, y);
сравнить глубину z(x, y) со значением Zбуфер(x, y), хранящимся в z-буфере в этой же позиции;
5) если z(x, y) > Zбуфер(x, y), то записать атрибут этого многоугольника (интенсивность, цвет и т. п.) в буфер кадра и заменить Zбуфер(x, y) на z(x, y);
6) в противном случае никаких действий не производить.

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

Оптимизация алгоритма

Одним из наиболее распространённых модификаций алгоритма z-буфера является алгоритм иерархического z-буфера. Назовём грань скрытой (невидимой) по отношению к z-буферу, если для всех её точкек их глубины не меньше соответствующих значений в z-буфере (при попытке вывести такую грань в z-буфер ничего не изменится). Куб (прямоугольный параллелепипед) назовём скрытым по отношению к z-буферу, если все его лицевые грани являются скрытыми. Теперь объединим несколько граней сцены и обведём вокруг них куб. Для вывода этих граней в z-буфер проверяется, является ли этот куб скрытым по отношению к z-буферу. Если это так, что и все грани, объединённые кубом являются скрытыми по отношению к z-буферу и не выводятся в него, сокращая количество вычислений и ненужных операций сравнения. Если же это не так, то всё равно не все грани, объединённые кубом являются видимыми и этот куб разбивается на части, а затем для каждой части выполняются такие же операции сравнения. Таким образом реализация данного алгоритма может выглядеть следующим образом. Вокруг всей сцены описывается куб, он разбивается на 8 частей (тоже кубы), а каждая из частей, в свою очередь, разбивается ещё на 8 частей и так до тех пор, пока количество граней в кубе не станет меньше заданного числа, при котором уже нет смысла в дальнейшей разбивке на части. Далее для вывода очередного куба в z-буфер для него проверяется, является он скрытым или нет. Если да, то все грани, объединённые этим кубом игнорируются, а если нет, то соответствующая проверка выполняется для всех его частей и т.д

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *