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