Virtual & Really.Ru - Реально о Виртуальном

Виртуальная реальность и 3D Стерео Технологии

Friday, Nov 24th

Last update03:29:08 PM GMT

Вы здесь: Статьи VR и 3D Софт Программирование под ЖК стерео очки

Программирование под ЖК стерео очки

Печать PDF

Проблемы, при разработке ПО, связаны с программной синхронизацией частоты обновления кадров Электронной Лучевой трубки (ЭЛТ) с частотой срабатывания затворов ЖК( LCD ) стерео очков. Программное обеспечение для предъявления стереокадра условно делится  на две большие группы...

14/05/2005. Программирование под ЖК(LCD) стерео очки
(Автор кандидат технических наук Всеволод Ляховецкий. Статья опубликована в авторской редакции)
14/05/2005. Часть 1.
15/06/2005. Часть 2.

Программирование под ЖК(LCD) стерео очки
(часть 1)


LCD Stereo Glasses KitОсновные проблемы, при разработке программного обеспечения, связаны с программной синхронизацией частоты обновления кадров Электронной Лучевой трубки (ЭЛТ) с частотой срабатывания затворов ЖК (LCD) стерео очков. Все программное обеспечение для предъявления стереокадра можно условно разделить на две большие группы:

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

Основное преимущество специального программного обеспечения – надежность и стабильность работы. Основной недостаток специального программного обеспечения – плохая документированность возможностей функций пользовательских библиотек. При этом производителями зачастую накладываются произвольные ограничения на использование некоторых форматов стереокадра. Рассмотрим 2 варианта специального программного обеспечения, предназначенного для видеокарт ASUS V3800 Deluxe и ASUS V6600 Deluxe.

В SDK (Software Development Kit) для видеоадаптера ASUS V3800 Deluxe, входит библиотека AsusVR.dll, предоставляющая сервисные функции для работы со стереограммами. Из этих функций наибольший интерес представляют две.

Функция

VRBitBlt2(int nDestX, int nDestY, HANDLE hLSrc, HANDLE hRSrc, LPRECT lpSrcRect)

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

nDestX, nDestY – координаты левого нижнего угла изображения,

HLSrc и hRSrc – хэндлеры, указывающие на изображения левой и правой части стереограммы,

lpSrcRect – указатель на структуру, содержащую информацию о высоте и ширине изображения.

Функция

VREnable(HWND hWnd, DWORD dwWidth, DWORD dwHeight, DWORD dwBitsPerPixel, DWORD dwRefreshRate, DWORD dwStereoMode)

используется на старте приложения для того, чтобы установить формат стереокадра для “окна” с хэндлером hWnd с требуемыми шириной dwWidth и высотой dwHeight, количеством бит цветности на пиксель dwBitsPerPixel, частотой обновления кадров dwRefreshRate и форматом стереокадра dwStereoMode.

Переменная, определяющая формат стереокадра, принимает значение VR_INTERLACEMODE (чередование строк с чересстрочной разверткой) и VR_FLIPINGMODE (постраничный).

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

Разработчиками SDK видеоадаптеров ASUS значение частоты обновления кадров dwRefreshRate ограничено величиной 75 Гц. Это означает, что реальная частота обновления кадров для наблюдателя в ЖК очках составит не более 37 Гц. Однако для предъявления статического изображения для большинства испытуемых такой низкой частоты обновления кадров, по-видимому, достаточно.

Интересно отметить, что мне не удалось корректно вызвать эти функции из-под Delphi (подвисала намертво очередь сообщений). Поэтому программу и примеры пришлось писать на C++ Builder.

#define VR_FLIPINGMODE 2 //режим чередования страниц

typedef BOOL (*tVREnable)(HWND hWnd, DWORD dwWidth, DWORD dwHeight, DWORD dwBitsPerPixel, DWORD dwRefreshRate, DWORD dwStereoMode);
typedef BOOL (*tVRBitBlt2)(int nDestX,int nDestY,HANDLE hLSrc,HANDLE hRSrc,LPRECT lpSrcRect);

HINSTANCE hDLL;
HBITMAP BitmapL, BitmapR;
tVREnable VREnable;
tVRBitBlt2 VRBitBlt2;

//инициализация
hDLL = LoadLibrary("AsusVr.dll");
(tVREnable)VREnable = (tVREnable)GetProcAddress(hDLL,"VREnable");
(tVRBitBlt2)VRBitBlt2 = (tVRBitBlt2)GetProcAddress(hDLL,"VRBitBlt2");
//переход в стереорежим , Form1 – главная форма приложения
VREnable(Form1->Handle,800, 600, 32, 75, VR_FLIPINGMODE);
//вывод стереограммы
VRBitBlt2(0, 100, BitmapR, BitmapL, NULL);

В SDK видеоадаптеров ASUS V6600 Deluxe и выше входит библиотека VRnn.dll,
где nn – две первые цифры номера серии видеокарты.

Из ее функций выделим функцию

SetModeInfo(DWORD dwWidth, DWORD dwHeight, DWORD dwBitsPerPixel, DWORD dwRefreshRate, DWORD dwStereoMode),

которая устанавливает формат стереокадра. Ее отличие от функции VREnable заключается в том, что она позволяет использовать только формат чередования строк (в постраничном формате доступна лишь верхняя половина экрана). Значение параметра dwRefreshRate также, как и для видеоадаптера ASUS V3800, ограничено величиной 75 Гц. Это означает, что в АПК предпочтительно использовать видеоадаптер ASUS V3800 Deluxe, позволяющий использовать постраничный формат стереокадра.

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

#define VR_INTERLACEMODE 1

typedef BOOL (*tSetModeInfo)(DWORD dwWidth, DWORD dwHeight, DWORD dwBitsPerPixel, DWORD dwRefreshRate, DWORD dwStereoMode);

tSetModeInfo VRSetModeInfo;

HINSTANCE hDLL;
HDC dcMem, dc;
DWORD dwWidth, dwHeight, dwBitsPerPixel, dwRefreshRate, dwStereoMode;
Int maxX, maxY;
HBITMAP MyBitmap;

//инициализация

HDLL = LoadLibrary("Vr66.dll");
(tSetModeInfo)VRSetModeInfo = (tSetModeInfo)GetProcAddress(hDLL,"SetModeInfo");
dc = GetDC(Form1->Handle); // Form1 – главная форма приложения
dcMem = CreateCompatibleDC(dc);
MyBitmap = CreateCompatibleBitmap(dc, maxX, 2*maxY);
VRGetModeInfo(&dwWidth, &dwHeight, &dwBitsPerPixel, &dwRefreshRate, &dwStereoMode);//определяем текущий режим работы видеокарты
VRSetModeInfo(dwWidth, dwHeight, dwBitsPerPixel, dwRefreshRate, VR_INTERLACEMODE);//переходим в чересстрочный стереорежим

SelectObject(dcMem, MyBitmap);
// Image1 – экземпляр типа Timage, расположенный на Form1
// maxX, maxY – размер стереограммы, maxY умножаем на 2 из-за того, что режим
// вывода – чересстрочный, в нечетных строках MyBitmap строки левой части
//стереограммы, а в четных – правой части.
BitBlt(Image1->Canvas->Handle, 0, 0, maxX, 2*maxY, dcMem, 0, 0, SRCCOPY);

Основное преимущество универсальных программ – возможность их использования для управления ЖК( LCD ) стерео очками различных производителей. Однако применение универсальных программ таит в себе некоторые проблемы. Первоначально большинство ЖК ( LCD ) стерео очков было рассчитано на работу в среде DOS, в которой легко получить доступ к аппаратным ресурсам компьютера. В настоящее время широкое распространение получила операционная система Windows, и большинство экспериментаторов предпочитают использовать программное обеспечение под Windows. Однако реализация поддержки ЖК ( LCD ) стерео очков в операционной системе Windows, гораздо сложнее, чем в DOS.

Во-первых, “легальный” способ управления видеосистемой в операционной системе Windows – использование функций API ( Appliance Program Interface - интерфейса прикладного программирования), которые выполняются медленнее, чем при низкоуровневом программировании (в среде DOS) контроллера соответствующего устройства, например, видеоадаптера.

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

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

DirectX – набор интерфейсов разработчика приложений (API) для создания высокопроизводительных мультимедийных приложений. Визуализация стереограмм происходит с помощью библиотеки DirectDraw - части DirectX, основное назначение которой заключается в предоставлении прямого доступа к видеопамяти. Детальное описание функций DirectDraw приведено в SDK DirectX. Опишем здесь лишь необходимую последовательность вызова функций, обращая внимание на значения некоторых их параметров.

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

DirectDrawCreateEx(NULL, &LPVOID(lpDD), IID_IDirectDraw7, NULL);
lpDD->SetCooperativeLevel(Handle, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN);
lpDD->SetDisplayMode (dwWidth, dwHeight, dwBitsPerPixel, dwRefreshRate, 0);
ddsd.dwSize = sizeof(ddsd);
ddsd.dwBackBufferCount = 1;
ddsd.dwFlags = DDSD_CAPS | DDSD_BACKBUFFERCOUNT;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX;
lpDD->CreateSurface(&ddsd, &lpDDSPrimary, NULL);
ddscaps.dwCaps = DDSCAPS_BACKBUFFER;
lpDDSPrimary->GetAttachedSurface(&ddscaps, &lpDDSBack);

Здесь lpDD – указатель на объект DirectDraw7, IID_IDirectDraw7 – номер (GUID) интерфейса DirectDraw7, Handle – хэндлер главного окна приложения, ddsd – описание комплексной поверхности (видеобуфера, содержащего первичную “экранную” поверхность lpDDSPrimary и вторичную “заэкранную” поверхность lpDDSBack).

Допустимые комбинации параметров dwWidth, dwHeight, dwBitsPerPixel, dwRefreshRate зависят от конкретного сочетания драйверов монитора и видеоадаптера.

Для работы с ЖК(LCD) очками необходимо вызывать функцию SetCooperativeLevel, задающую уровень доступа к видеопамяти, с флагами DDSCL_FULLSCREEN и DDSCL_EXCLUSIVE. Такой формат вызова дает полный доступ к видеопамяти и управлению видеорежимами.

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

LpDDSBack->BltFast(nDestX, nDestY, lpDDS, NULL, DDBLTFAST_WAIT);

lpDDSPrimary->Flip(NULL, DDFLIP_WAIT);

Здесь nDestX, nDestY – координаты левого нижнего угла изображения, lpDDS – хэндлер поверхности, содержащей изображение левой или правой части стереограммы. Функция BltFast копирует часть стереограммы в заэкранную поверхность видеобуфера. Функция Flip меняет местами экранную и заэкранную поверхности видеобуфера. Флаги DDBLTFAST_WAIT и DDFLIP_WAIT необходимы - без их использования функции BltFast и Flip возвращают управление сразу после запуска, а при их указании - только после окончания процесса копирования.

Достоинство этого метода заключается в возможности предъявления стереокадра в режиме разбиения на страницы независимо от видеоадаптера

Недостаток этого метода визуализации стереограмм заключается в том, что до старта приложения невозможно определить, в каком кадре (предъявляемом левому или правому глазу) будет присутствовать левая, а в каком – правая часть стереограммы.

Программирование под ЖК(LCD) стерео очки
(часть 2).

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

В данной части коротко рассмотрим программную реализацию несколько иной задачи – как предъявить наблюдателю, надевшему стереоочки, одно изображение трехмерной сцены таким образом, чтобы возник стереоэффект. Такая задача возникает, например, при разработке игр, призванных поддерживать стереорежимы. Поэтому для синтеза изображений необходимо использовать программные средства, позволяющие в зависимости от видеокарты и настроек ее драйвера успешно воспроизводить одну и ту же сцену как в стерео-, так и в обычном видеорежиме. Одним из таких программных средств, доступных разработчику, являются библиотеки DirectX и, в частности, модуль DirectXGraphics. Подробные сведения об использовании функций DirectX содержатся в многочисленных статьях, однако вопрос поддержки стереорежима в них не рассматривается. При написании данного примера были использованы главы книги, выложенные по адресу http://www.softera.ru/literature/directx/1/Nn.htm, где Nn – номер главы.

Важные моменты синтеза трехмерной объемной сцены с помощью DirectX сводятся к следующему:

  • При описании вершин графических примитивов необходимо задействовать третью координату объектов, что сводится к отказу от системы координат, связанной с окном приложения (определяемой константой DSDFVF_XYZRHW).
  • При создании окна приложения необходимо использовать полноэкранный режим.
  • В настройках видеодрайвера на вкладке, посвященной стереорежиму, необходимо установить поддержку DirectX. Например, для видеокарты ASUS V7700 Deluxe на вкладке ”Direct3d VR” на панели “эффект виртуальной реальности” следует установить галочку “включить стереорежим”.

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

unit SimpleStereo;

interface

uses

Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, DirectXGraphics , DxgUtils , StdCtrls;

type

TCUSTOMVERTEX = packed record

X, Y, Z: Single;//!! не используем RHW
Color: DWord;

end ;

TForm1 = class(TForm)

procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormPaint(Sender: TObject);
procedure InitPoint;
procedure FormKeyPress(Sender: TObject; var Key: Char);

public

Angle: Real;
Vertices: array [0..6] of TCUSTOMVERTEX;
g_pD3D: IDirect3D8;
g_device: IDirect3DDevice8;
FD3DVB: IDIRECT3DVERTEXBUFFER8;

end ;

const

.//!! Не используем константу DSDFVF_XYZRHW
D3DFVF_COSTOMVERTEX = D3DFVF_XYZ or D3DFVF_DIFFUSE;
fmtFullscreenArray : a rray [0..4] of DWORD =
(D3DFMT_R5G6B5, D3DFMT_X1R5G5B5, D3DFMT_A1R5G5B5, D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8);

var

Form1: TForm1;// Form1 – “пустая” форма, поэтому dfm-файл приводить нецелесообразно

implementation

{$R *.DFM}

procedure TForm1.FormCreate(Sender: TObject);

var

d3dpp: TD3DPresent_Parameters;
iFmt: Integer;
FDSDfmtFullscreen: DWord;

begin

g_pD3D := Direct3DCreate8(D3D_SDK_VERSION);
if g_pD3D = nil then

exit;

for iFmt := 0 to High(fmtFullscreenArray) do

begin

if SUCCEEDED(g_pD3D.CheckDeviceType(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL,fmtFullscreenArray[iFmt], fmtFullscreenArray[iFmt], False)) then

begin

FDSDfmtFullscreen := fmtFullscreenArray[iFmt];
Break; // Найден подходящий

end ;

end;

ZeroMemory(@d3dpp, sizeof(d3dpp));
d3dpp.Windowed := False; //!!полноэкранный режим
d3dpp.BackBufferFormat := FDSDfmtFullscreen;
d3dpp.BackBufferWidth := 800;
d3dpp.BackBufferHeight := 600;
d3dpp.SwapEffect := D3DSWAPEFFECT_DISCARD;
if FAILED(g_pD3D.CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Handle,D3DCREATE_HARDWARE_VERTEXPROCESSING, d3dpp, g_device)) then

exit ;

InitPoint;

end ;

 

procedure TForm1.FormDestroy(Sender: TObject);

begin

g_device := nil;
g_pD3D := nil;

end ;

 

procedure TForm1.FormPaint(Sender: TObject);

var

matRotate, matTranslate , matView, matProj : TD3DMatrix; // Матрицы 4x4

begin

g_device.Clear(0, nil, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0, 0);
g_device.BeginScene();
g_device.SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
g_device.SetRenderState(D3DRS_LIGHTING, DWORD(False));
SetRotateXMatrix(matRotate, Angle);
SetTranslateMatrix(matTranslate, -1.0, 2.0, 0.0);// Треугольник сдвигается на единицу влево и на 2 ед . вверх
g_device.SetTransform(D3DTS_WORLD, MatrixMul(matRotate, matTranslate));// Устанавливаем мировую матрицу трансформаций
g_device.DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1);
SetRotateYMatrix(matRotate, 2 * Angle);
SetTranslateMatrix(matTranslate, 1.0, 2.0, 0.0);// Квадрат сдвигается на единицу вправо и на 2 ед . вверх
g_device.SetTransform(D3DTS_WORLD, MatrixMul(matTranslate, matRotate)); // Матрица трансформаций для квадрата
g_device.DrawPrimitive(D3DPT_TRIANGLESTRIP, 3, 2);
SetViewMatrix(matView, D3DVector(0, 0, 5), D3DVector(0, 0, 0), D3DVector(0, 1, 0)); // Устанавливаем видовую матрицу
g_device.SetTransform(D3DTS_VIEW, matView);
SetProjectionMatrix(matProj, 1, 1, 1, 10); // Задаем матрицу проекций
g_device.SetTransform(D3DTS_PROJECTION, matProj);// Устанавливаем матрицу проекций
g_device.EndScene();
g_device.Present(nil, nil, 0, nil);

end ;

 

procedure TForm1.InitPoint;

var

pVertices : PByte;

begin

Angle := 0;
Vertices[0].X := 0.0; // Первая вершина треугольника
Vertices[0].Y := 1.0;
Vertices[0].Z := 0.0;
Vertices[0].Color := $00FF0000;
Vertices[1].X := 1.0; // Вторая вершина треугольника
Vertices[1].Y := -1.0;
Vertices[1].Z := 0.0;
Vertices[1].Color := $0000FF00;
Vertices[2].X := -1.0; // Третья вершина треугольника
Vertices[2].Y := -1.0;
Vertices[2].Z := 0.0;
Vertices[2].Color := $000000FF;
Vertices[3].X := -1.0; // Первая вершина квадрата
Vertices[3].Y := -1.0;
Vertices[3].Z := 0.0;
Vertices[3].Color := $00FFFF00;
Vertices[4].X := -1.0; // Вторая вершина квадрата
Vertices[4].Y := 1.0;
Vertices[4].Z := 0.0;
Vertices[4].Color := $00FFFF00;
Vertices[5].X := 1.0; // Третья вершина квадрата
Vertices[5].Y := -1.0;
Vertices[5].Z := 0.0;
Vertices[5].Color := $00FFFF00;
Vertices[6].X := 1.0; // Четвертая вершина квадрата
Vertices[6].Y := 1.0;
Vertices[6].Z := 0.0;
Vertices[6].Color := $00FFFF00;
g_device.CreateVertexBuffer(SizeOf(Vertices), D3DUSAGE_WRITEONLY, D3DFVF_COSTOMVERTEX, D3DPOOL_DEFAULT, FD3DVB);
FD3DVB.Lock(0, SizeOf(Vertices), pVertices, 0);
Move(Vertices, pVertices^, SizeOf(Vertices));
FD3DVB.Unlock();
g_Device.SetStreamSource(0, FD3DVB, SizeOf(TCUSTOMVERTEX));
g_device.SetVertexShader(D3DFVF_COSTOMVERTEX);

end ;

procedure TForm1.FormKeyPress(Sender: TObject; var Key: Char);

begin

Angle := Angle + 0.5;
SendMessage(Self.HANDLE, WM_PAINT, 0, 0);

end ;

end .

Полезные ссылки:

  • Домашняя страница автора - http://sevolod.narod.ru/

  • Публикации в сети посвящены биологическим аспектам стерео зрения.

    1) A Role of Tuned-Inhibitory Cells in Depth Selection //Perception Vol.30 Supplement. 2001. pp.77-78

    Авторы - Lyakhovetskii V.A., Ilyushov G.S., Alexeenko S.V. http://www.perceptionweb.com/ecvp01/0182.html

    2) Stereovision by visual stimulus of different quality // Proceedings of the IV seminar "Ocular biomechanics" Moscow, 2004. pp.82-89.

    Авторы - Lyakhovetskii V.A. Krumina G., Ozolinsh M.

    http://www.cfi.lu.lv/optometry/gunta/GK_MO_VL_english.pdf


 

Author of review Lyakhovetskii V.A.

Автор:
кандидат
технических наук
Всеволод Ляховецкий



P.S. Выражаю огромную благодарность неизвестному мне автору,
создавшему ныне исчезнувшую страницу http://www.northstar.nm.ru/comp/vr100.htm.
AddThis Social Bookmark Button