Опубликован: 28.04.2009 | Доступ: свободный | Студентов: 1840 / 107 | Оценка: 4.36 / 4.40 | Длительность: 16:40:00
Специальности: Программист
Лекция 3:

Усложненные технологии визуализации

3.2.1. Выбор оптимального видеорежима

В примере Ch03\Ex03 (листинг 3.4) мы неявно предполагаем, что абсолютно все современные и будущие видеоподсистемы будут поддерживать видеорежим 640Ч480Ч32 bpp6BPP – Bit Per Pixel (Бит на пиксель) . Если это вдруг окажется не так, приложение аварийно завершит работу с исключением System.InvalidOperationException. Поэтому было бы логичным на всякий случай предусмотреть поведение приложение при отсутствии поддержки требуемого видеорежима. Например, при неудачной попытке создания полноэкранного графического устройства приложение могло бы попытаться создать графическое устройство, осуществляющее визуализацию в окне:

presentParams = new PresentationParameters();
presentParams.BackBufferCount = 1;
presentParams.SwapEffect = SwapEffect.Discard;
presentParams.IsFullScreen = true;
// Используем полноэкранный режим 640Ч480, 32 бита на пиксель, 60 герц
presentParams.BackBufferWidth = 640;
presentParams.BackBufferHeight = 480;
presentParams.BackBufferFormat = SurfaceFormat.Bgr32;
presentParams.FullScreenRefreshRateInHz = 60;
try {
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware, this.Handle,
options, presentParams);
 }
catch(InvalidOperationException) 
{
// Если попытка использования полноэкранного режима 
потерпела фиаско, используем 
// визуализацию в окне
presentParams.IsFullScreen = false;
presentParams.BackBufferWidth = 0;
presentParams.BackBufferHeight = 0;
presentParams.BackBufferFormat = SurfaceFormat.Unknown;
presentParams.FullScreenRefreshRateInHz = 0;

// После выполнения этой команды интерфейс формы будет 
заблокирован: форму нельзя 
// будет перемещать по экрану, изменять ее размер, 
минимизировать и т.п.
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware,
this.Handle, options, presentParams); 
}

Хотя этот не содержит ничего противоестественного, на практике он не будет нормально функционировать: из-за ошибки в XNA Framework после неудачной попытки перехода в полноэкранный режим приложение уже не может использовать оконный режим. В следующих версиях XNA Framework эта проблема по видимости будет исправлена, ну а пока нам придется, как и всем настоящим героям, идти в обход7По-видимости, виновником данной проблемы является не сам XNA Framework, а текущая версия DirectX, используемая XNA Framework .

И так, при создании полноэкранного графического устройства необходимо подобрать видеорежим, гарантированно поддерживаемый данной видеокартой и монитором. А почему бы нам вместо гадания на кофейной гуще не использовать текущий видеорежим рабочего стола, по определению поддерживаемый видеоподсистемой компьютера? Ведь, как известно, в качестве видеорежима рабочего стола обычно используется видеорежим, наиболее оптимальный для текущего монитора компьютера. Это особенно актуально для LCD -мониторов, оптимизированных для работы каком-то одном разрешении экрана (как правило, 1024x768 или 1280x1024). Информация о текущем видеорежиме доступна посредством свойства CurrentDisplayMode класса GraphicsAdapter:

public DisplayMode CurrentDisplayMode { get; }

Вся информация о видеорежиме хранится в структуре DisplayMode:

public struct DisplayMode
{
// Количество пикселей по горизонтали
public int Width { get; } 
// Количество пикселей по вертикали
public int Height { get; } 
// Формат пикселей
public SurfaceFormat Format { get; } 
// Частота обновления экрана
public int RefreshRate { get; }
}

Соответственно, код создания графического устройства примет следующий вид:

// Получаем описание текущего видеорежима
DisplayMode displayMode = GraphicsAdapter.DefaultAdapter.
CurrentDisplayMode;
presentParams = new PresentationParameters();
presentParams.IsFullScreen = true;
presentParams.BackBufferCount = 1;
presentParams.SwapEffect = SwapEffect.Discard;
// Использует такие же параметры визуализации, 
как у текущего видеорежима
presentParams.BackBufferWidth = displayMode.Width;
presentParams.BackBufferHeight = displayMode.Height;
presentParams.BackBufferFormat = displayMode.Format;
presentParams.FullScreenRefreshRateInHz = displayMode.RefreshRate;
GraphicsDeviceCapabilities caps =
GraphicsAdapter.DefaultAdapter.GetCapabilities(DeviceType.Hardware);
CreateOptions options = CreateOptions.SingleThreaded;
if (caps.DeviceCapabilities.SupportsHardwareTransformAndLight)
options |= CreateOptions.HardwareVertexProcessing; else
options |= CreateOptions.SoftwareVertexProcessing;
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware, 
this.Handle, options, presentParams);
Листинг 3.8.
3.2.2. Получение списка доступных видеорежимов

Хотя использование видеорежима рабочего стола дает вполне нормальные результаты, в ряде случаев подобный подход оказывается неприемлемым. В основном это касается дешевых видеокарт, демонстрирующих довольно низкую производительность в современных трехмерных приложениях, в результате чего приемлемая частота смены кадров достигается лишь в самых низких разрешениях вроде 640Ч480 или 800Ч600. Поэтому было логичным предоставить пользователю возможность самостоятельно выбирать параметры видеорежима в зависимости от своих потребностей.

Наше следующее приложение будет отображать на экране перечень всех видеорежимов, поддерживаемых видеокартой и затем переключаться в видеорежим, выбранный пользователем, и визуализировать изображение диска (рисунок 3.7). Эту функциональность достаточно легко реализовать, так как разработчики XNA Framework снабдили класс GraphicsAdapter коллекцией SupportedDisplayModes, содержащей набор структур DisplayMode с информацией обо всех видеорежимах, поддерживаемых указанной связкой видеокарта – монитор.

 Диалоговое окно выбора видеорежима

Рис. 3.7. Диалоговое окно выбора видеорежима

Итак, создайте новое приложение Windows Forms и добавьте в него новую форму. Присвойте свойствам формы значения согласно таблице 3.2. Поместите на форму компонент ListBox и назовите его displayModeListBox. В заключение, поместите на форму кнопку Ok и присвойте ее свойству DialogResult значение OK.

Таблица 3.2. Свойства формы выбора видеорежима
Свойство Значение
Name DisplayModeForm
Text Выберите видеорежим
FormBorderStyle FixedDialog
MaximizeBox False
MinimizeBox False

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

public partial class DisplayModeForm : Form 
{
// Конструктор формы. В качестве параметра 
принимает объект графического адаптера, для 
// которого необходимо выбрать графический режим. Public
DisplayModeForm(GraphicsAdapter adapter) 
{
InitializeComponent(); 
// Перебираем все графические режимы, поддерживаемые 
графическим адаптером и добавляем их на
// панель displayModeListBox
foreach (DisplayMode diplayMode in adapter.SupportedDisplayModes)
displayModeListBox.Items.Add(diplayMode);
// Выбираем самый первый элемент списка видеорежимов
displayModeListBox.SelectedIndex = 0; 
}
// Возвращает информацию о графическом режиме, выбранном пользователем
public DisplayMode SelectedDisplayMode 
{
get 
{
return (DisplayMode)displayModeListBox.SelectedItem; 
} 
  } 
}
Листинг 3.9.

И наконец, в обработчик события Load главной формы необходимо вставить код взаимодействия с диалоговым окном выбора видеорежима:

private void MainFormLoad(object sender, EventArgs e) 
{
SetStyle(ControlStyles.Opaque | ControlStyles.ResizeRedraw, true);
MinimumSize = SizeFromClientSize(new Size(1, 1));
presentParams = new PresentationParameters();
presentParams.IsFullScreen = true;
presentParams.BackBufferCount = 1;
presentParams.SwapEffect = SwapEffect.Discard;
// Создаем диалоговое окно выбора видеорежима
using (DisplayModeForm displayModeForm = new
DisplayModeForm(GraphicsAdapter.DefaultAdapter))
{ 
// Отображаем диалоговое окно
displayModeForm.ShowDialog();
// Получаем видеорежим, выбранный пользователем
DisplayMode mode = displayModeForm.SelectedDisplayMode;
// Задаем требуемый видеорежим
presentParams.BackBufferWidth = mode.Width;
presentParams.BackBufferHeight = mode.Height;
presentParams.BackBufferFormat = mode.Format;
presentParams.FullScreenRefreshRateInHz = mode.RefreshRate; 
}
// Создаем графическое устройство
device = new GraphicsDevice(GraphicsAdapter.DefaultAdapter, 
DeviceType.Hardware, 
4> this.Handle, options, presentParams);
}
Листинг 3.10.

Оставшийся код приложения не содержит чего-либо интересного, поэтому мы не будем на нем останавливаться. Готовое приложение находится в каталоге Examples\Ch03\Ex05.

Запустите полученное приложение и попробуйте поэкспериментировать с различными разрешениями экрана. Очень скоро вы заметите одну важную особенность: в некоторых видеорежимах диск вместо круглой формы растягивается/сплющивается в эллипсовидную фигуру. После внимательного изучения соотношения сторон разрешений экрана мы прейдем к выводу, что диск корректно отображается лишь при условии совпадения отношения сторон монитора с отношением соответствующих размерностей разрешения экрана. Например, отношение сторон моего монитора NEC MultiSync FE991SB равно 4:3 = 1.33, поэтому в разрешениях 320Ч240, 640Ч480, 800Ч600, 1024Ч768, 1280Ч960, 1600Ч1200, 1792Ч1344 изображение отображается без искажений. В разрешении 1280Ч1024 (соотношение сторон 5:4=1.25) круг едва заметно сплющивается вдоль оси Y, а вот в разрешении 1360Ч768 (соотношение сторон 16:9=1.77) сплющивание круга вдоль оси X становится более чем заметным. С другой стороны, на широкоэкранных мониторах с соотношением сторон 16:9 именно разрешения 1088Ч612, 1280Ч768, 1360Ч768 и 1600Ч900 (т.е. с соотношением сторон 16:9) будут давать изображение без искажений.

 Искажение формы круглого диска, визуализированного в разрешении 1360Ч768, при отображение на мониторе с соотношение сторон 4:3

увеличить изображение
Рис. 3.8. Искажение формы круглого диска, визуализированного в разрешении 1360Ч768, при отображение на мониторе с соотношение сторон 4:3

Чем обусловлено данное явление? Для борьбы с искажениями наше приложение использует область визуализации квадратной формы. Однако если хорошо подумать, равенство ширины и высоты области визуализации еще не гарантирует квадратной области визуализации, так как необходимо еще одно условие: соотношение сторон разрешение экрана должно совпадать с соотношением сторон экрана монитора. Если эта не так, квадратная область визуализации в действительности окажется неквадратной и форма изображения будет искажена.

Примечание

Обратите внимание на отсутствие в списке видеорежимов с форматами пикселей с поддержкой альфа канала. Дело в том, что коллекция SupportedDisplayModes содержит только видеорежимы экранного буфера, а экранный буфер не поддерживает альфа-канал по причине банальной ненужности (см. таблицу 3.1). Так что отсутствие поддержки альфа-канала экранным буфером вовсе не означает отсутствие поддержки форматов с альфа-каналом задним буфером. Более того, если современная видеокарта поддерживает формат SurfaceFormat.Bgr32, то она с вероятность 99.9% поддерживает и формат SurfaceFormat.Color. Впрочем, эти нюансы для нас пока не актуальны, так как наши текущие приложения все равно не используют альфа-канал.

Андрей Леонов
Андрей Леонов

Reference = add reference, в висуал студия 2010 не могу найти в вкладке Solution Explorer, Microsoft.Xna.Framework. Его нету.