Форум программистов, компьютерный форум, киберфорум
Наши страницы
OpenGL
Войти
Регистрация
Восстановить пароль
 
Рейтинг: Рейтинг темы: голосов - 157, средняя оценка - 4.97
Vladiator
1569 / 639 / 79
Регистрация: 24.02.2009
Сообщений: 9,269
#1

Создание простой фигуры в 3D-формате, OpenGL и DirectX - OpenGL

07.01.2010, 19:22. Просмотров 21226. Ответов 8

Не по теме:

Решил написать небольшой FAQ по этой теме


Создание простой фигуры в 3D-формате, OpenGL, Delphi (вообще, процедуры OpenGL везде почти одинаковы).
В Delphi должен быть заранее модуль OpenGL.pas. Его надо включить в раздел uses:

http://www.cyberforum.ru/opengl/thread164687.html
Delphi
1
2
uses 
...,OpenGL,...
Далее надо будет создать процедуру:

Delphi
1
2
3
4
5
6
7
8
9
10
procedure SetDCPixelFormat;
var 
 pfd:TPixelFormatDescriptor;
 nPixelFormat:Integer;
begin
 FillChar(pfd,SizeOf(pfd),0);
 pfd.dwFlags:=PFD_DRAW_TO_WINDOW or PFD_DOUBLEBUFFER or PFD_SUPPORT_OPENGL;
 nPixelFormat:=ChoosePixelFormat(DC,@pfd);
 SetPixelFormat(DC,nPixelFormat,@pfd);
end;
В раздел var надо добавить:

Delphi
1
2
3
var 
 DC:HDC;
 HRC:HGLRC;
Обработать события формы OnCreate, OnDestroy и OnResize так:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
procedure TForm1.FormCreate(Sender: TObject);
begin 
 DC:=GetDC(Handle); //получить дескриптор Form1
 SetDCPixelFormat; HRC:=wglCreateContext(DC);
 wglMakeCurrent(DC,HRC);
 Form1.WindowState:=wsMaximized; //на весь экран;
 ShowCursor(False); //невидимый курсор
 glClearColor(0.0,0.0,0.0,1.0); //цвет фона
 glEnable(GL_DEPTH_TEST);
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  wglMakeCurrent(0,0);
  wglDeleteContext(HRC); 
  ReleaseDC(Handle,DC); 
  DeleteDC(DC); 
end;
 
procedure TForm1.FormResize(Sender: TObject);
begin
  glViewport(0, 0, ClientWidth, ClientHeight); //размер видимой зоны
  glMatrixMode(GL_PROJECTION); 
  glLoadIdentity; 
  gluPerspective(30.0, ClientWidth / ClientHeight, 0.1, 1000.0); 
  glMatrixMode(GL_MODELVIEW); 
  glLoadIdentity; 
end;
Итак, мы закончили ту часть, которая обязательна (будет ещё немного, но это после того, как Вы научитесь создавать объекты). Теперь пора задуматься о самих 3D объектах. В OpenGL есть так называемые листы . Они служат для облегчения создания 3D графики - распределения её на части. Чтобы открыть слои, есть процедура glNewList(ListIndex, mode:cardinal) - чтобы открыть и glEndList - чтобы закрыть создание листа. После glEndList он нарисуется. ListIndex - номер листа. Если указать уже использовавшийся номер листа, то этот номер будет перерисован .Mode - желательно установить GL_COMPILE. Для рисования также надо ставить ограничители рисования - glBegin(mode:cardinal) - начало рисования и glEnd - конец рисования. Mode - режим рисования. Их существует несколько, но мы сначала разберём, как это всё рисовать. 2D графику не буду задевать, сразу про 3D. Есть такая процедура glVertex3F(x,y,z:single), которая рисует точки. Обратите внимание: эти точки задаются в вещественном типе данных single, потому что, допустим, если x=1, то это не значит, что x=1 пиклель. Это больше, чем 1 пиксель, а чтобы сделать меньше, надо использовать дробную часть - x=0,001, например. То же самое с y и z.
  • GL_POINTS - рисует точки (один вызов glVertex3F - одна точка)
  • GL_LINES - рисует линии (два вызова glVertex3F - одна линия)
  • GL_LINE_STRIP - рисует ломанную (каждый вызов glVertex3F даёт новую точку, куда будет доведена линия. Это всё будет продолжаться, пока не поставить glEnd).
  • GL_LINE_LOOP - рисует ломанную (использование glVertex3F такое же, как и в предыдущий раз, после glEnd последняя точка соединяется с первой).
  • GL_TRIANGLES - рисует треугольники (три вызова glVertex3F - один треугольник)
  • GL_TRIANGLE_STRIP - рисует треугольники с общей стороной
  • GL_TRIANGLE_FAN - рисует треугольники с общей стороной, но по другому (редко пригождается)
  • GL_QUADS - рисует четырёхугольник (четыре вызова glVertex3F - один четырёхугольник)
  • GL_QUAD_STRIP - рисует четырёхугольники с общей стороной
  • GL_POLYGON - рисует многоугольник (каждый вызов glVertex3F даёт новую точку, куда будет доведена линия. Это всё будет продолжаться, пока не поставить glEnd, после этого фигура закрашивается).
Насчёт закрашивания. Цвет задаётся процедурой glColor3F(r,g,b:single). R - красный, G - зелёный, B - синий. Параметры задаются в пределах от 0 до 1. Например: glVertex3f(0,0,0) - чёрный, glVertex3f(1,1,1) - белый, glVertex3f(1,0,0) - красный, glVertex3f(0.5,0.5,0.5) - серый и т. д. Цвет можно задавать и так - glVertex4f(r,g,b,a:single). Здесь "a" - параметр Alpha (прозрачность).

Вот пример одного квадрата в высоту.

Delphi
1
2
3
4
5
6
7
8
9
glNewList(1,GL_COMPILE); //1-й лист
glbegin(gl_quads); //начало блока рисования, режим - четырёхугольники
glColor3f(0,0,1); //синий цвет
glVertex3f(10,0,0); //1-я координата четырёхугольника
glVertex3f(10,10,0); //2-я координата четырёхугольника
glVertex3f(-10,10,0); //3-я координата четырёхугольника
glVertex3f(-10,0,0); //4-я координата четырёхугольника
glEnd; //конец блока рисования
glEndList; //закрытие 1-го слоя
Это всё желательно прописать в Form1.OnCreate. Если вы создаёте какой-нибудь большой проект (где множество объектов), желательно в Form1.OnResize в

Delphi
1
gluPerspective(30.0, ClientWidth / ClientHeight, 0.1, 1000.0);
последний параметр сделать больше.
Если glColor3F ставить перед каждым glVertex3F с разными параметрами, то получится что-то вроде градиента.

Delphi
1
2
3
4
5
6
7
8
9
10
glNewList(1,GL_COMPILE); //1-й лист
glbegin(gl_quads); //начало блока рисования, режим - четырёхугольники
glColor3f(0,0,1); //синий цвет
glVertex3f(10,0,0); //1-я координата четырёхугольника
glVertex3f(10,10,0); //2-я координата четырёхугольника
glColor3f(0,0,1); //красный цвет
glVertex3f(-10,10,0); //3-я координата четырёхугольника
glVertex3f(-10,0,0); //4-я координата четырёхугольника
glEnd; //конец блока рисования
glEndList; //закрытие 1-го слоя
Теперь поработаем над перемещением в 3d пространстве. Создаём новый тип:

Delphi
1
2
3
4
5
6
7
8
9
10
11
type 
...
 User=record
  Position:record 
   x,y,z:Single;
  end ;
  Rotation:record 
   y,zx:Single;
  end ;
 end ;
...
В раздел var и const добавляем:

Delphi
1
2
3
4
5
6
7
8
9
const 
... 
 z=0; 
... 
var 
... 
 Human:User;
 mt:gluInt;
...
В FormCreate добавляем:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
with Human do
  begin 
    with Position do 
    begin 
      x:=0; 
      y:=-2.5; 
      z:=-8; 
    end ; 
    with Rotation do 
    begin 
      y:=0; 
      zx:=45; 
    end ; 
  end 
  SetCursorPos(Round(Form1.ClientWidth/2), 
               Round(Form1.ClientHeight/2));
Добавляем на форму TTimer, Interval=50. Обрабатываем его событие OnTimer:

Delphi
1
2
3
4
5
6
procedure TForm1.Timer1Timer(Sender: TObject);
begin 
  SetCursorPos(Round(Form1.ClientWidth/2), 
               Round(Form1.ClientHeight/2)); 
  InvalidateRect(Handle,nil,false); 
end ;
Form1.OnMouseMove, OnPaint и onKeyDown:

Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
procedure TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X, 
  Y: Integer); 
const 
  Divider=8; 
begin 
   Human.Rotation.y:=Human.Rotation.y+
      Round((Mouse.CursorPos.X-Round(Form1.ClientWidth/2))/Divider); //поворот
  if Human.Rotation.y>=360 then Human.Rotation.y:=0;
  if Human.Rotation.y<0 then 
 Human.Rotation.zx:=Human.Rotation.zx+ 
      Round((Mouse.CursorPos.Y-Round(Form1.ClientHeight/2))/Divider); //угол (куда смотрит)
  if Human.Rotation.zx>90 then Human.Rotation.zx:=90; 
  if Human.Rotation.zx<-90 then Human.Rotation.zx:=-90; 
end ;
 
procedure TForm1.FormPaint(Sender: TObject);
var 
  ps:TPaintStruct; 
  i,t:integer; 
begin 
  BeginPaint(Handle,ps); //начало рисования
  glClear(GL_COLOR_BUFFER_BIT or 
          GL_DEPTH_BUFFER_BIT); 
  glLoadIdentity; 
  glRotatef(Human.Rotation.zx,Abs(cos(DegToRad(Human.Rotation.y))),0,0); //поворот
  glRotatef(Human.Rotation.y,0,1,0); //поворот
  glTranslatef(Human.Position.x, 
               Human.Position.y, 
               Human.Position.z); 
  glCallList(1); //нарисовать 1-й слой
  EndPaint(Handle,ps); //конец рисования
  SwapBuffers(DC); //наложить буфера друг на друга
end ;
 
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word;
  Shift: TShiftState); 
const 
  SPEED=0.2; //размер шага
begin 
  case key of 
    27: Form1.Close; //escape
    37: begin //влево
          Human.Position.z:=Human.Position.z+ 
          sin(DegToRad(Human.Rotation.y))*SPEED; 
          Human.Position.x:=Human.Position.x+ 
          cos(DegToRad(Human.Rotation.y))*SPEED; 
        end ; 
    38: begin //вверх
          Human.Position.z:=Human.Position.z+ 
          cos(DegToRad(Human.Rotation.y))*SPEED; 
          Human.Position.x:=Human.Position.x- 
          sin(DegToRad(Human.Rotation.y))*SPEED; 
        end ; 
    39: begin //вправо
          Human.Position.z:=Human.Position.z- 
          sin(DegToRad(Human.Rotation.y))*SPEED; 
          Human.Position.x:=Human.Position.x- 
          cos(DegToRad(Human.Rotation.y))*SPEED; 
        end ; 
    40: begin  //вниз
          Human.Position.z:=Human.Position.z- 
          cos(DegToRad(Human.Rotation.y))*SPEED; 
          Human.Position.x:=Human.Position.x+ 
          sin(DegToRad(Human.Rotation.y))*SPEED; 
        end ; 
  end ; 
end ;
Теперь перемещение в 3D мире осуществляется с помощью мыши и клавиатуры.
Скачать этот проект (написан на Delphi 2010)
источник (мой сайт, т. к. FAQ создавал я)
14
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
07.01.2010, 19:22
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Создание простой фигуры в 3D-формате, OpenGL и DirectX (OpenGL):

Directx or OpenGL
Что лучше учить Directx или OpenGL? OpenGL кроссплатформенный и ничем не...

Borland C++ DirectX OpenGL
Кто владеет тайнами 3D-графики помогите. срочно нужен примитивный исходник на...

C# OpenGl DirectX. Игры
Здравствуйте, очень надеюсь на вашу помощь. Есть череда вопросов на которые...

OpenGL 4.x & DirectX 11.2
Вобшем такой вопрос. Читал что в новом директе всяких фич крутих понапихали,...

DirectX или OpenGL
Как вы думаете что лучше изучать в графике DirectX или OpenGL?

8
insideone
Модератор
Автор FAQ
3657 / 937 / 112
Регистрация: 10.01.2010
Сообщений: 2,512
06.06.2010, 16:54 #2
Рассмотрим создание 3D геометрии в DirectX 9 на C++. Т.к. в реальности никаких кубиков вы сами создавать не будет - есть 2 варианта:
1. Загрузка геометрии из 3D моделей
2. Генерация геометрии

Попробуем сгенерировать 3D поверхность

Все что нам понадобится это включить данные файлы:
C++
1
2
3
4
5
6
7
8
#include <windows.h>
#include <tchar.h>
#include "math.h"
 
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "d3d9.lib")
#include <d3dx9core.h>
#include <d3d9.h>
Идем дальше... для того чтобы описывать геметрию в 3D пространстве используются массивы точек. Из называют вершинами. Естественно что вершина должна содержать как минимум 3 координаты (местоположение) но в нашем случае добавим ещё и цвет (чтобы поверхность была раскрашена)

C++
1
2
3
4
5
6
7
8
9
// Какие у нас вершины ландшафта
struct Vertex
{
    D3DXVECTOR3 Pos;    // Имеют позицию
    DWORD Color;        // И цвет
    static const DWORD FVF =
            D3DFVF_XYZ      // Говорит о том что есть позиция в структуре
        |   D3DFVF_DIFFUSE; // Говорит о том что есть цвет в структуре
};
Обратите внимание - FVF статический член и в принципе внутри объектов не присутствует - это важно. FVF - сумма флагов которая дает DirectX понять какую структуру вершины мы создали.

Добрались до программы:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int APIENTRY _tWinMain(
    HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine, int nCmdShow)
{
    // Размер экрана
    int Width = 1024, Height = 768;
    // Размер ландшафта ( LandscapeSize х LandscapeSize )
    size_t LandscapeSize = 256;
 
    // ******** Начало инициализации окна ******** //
    HWND hWnd; WNDCLASSEX wcex;
    
    wcex.cbSize = sizeof(WNDCLASSEX);   wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = DefWindowProc;    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDC_WAIT));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WAIT);
    wcex.lpszClassName  = "wcex";
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDC_WAIT));
    RegisterClassEx(&wcex);
    hWnd = CreateWindow(wcex.lpszClassName, lpCmdLine, WS_VISIBLE,
    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    // ******** Конец инициализации окна ******** //
    ShowWindow(hWnd, nCmdShow); // Показываем что создали
В принципе - без комментариев, стандартные функции создания Windows окна, а в начале идут наши переменные параметры, название коотрых говорит за себя (кроме того они прокомментированы)

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    // ******** Начало инициализации DirectX ******** //
    IDirect3D9* D3D; IDirect3DDevice9* Device;  // указатели которые будут проициализированы функциями ниже
    D3D = Direct3DCreate9(D3D_SDK_VERSION); // создаем экземпляр IDirect3D9
 
    D3DPRESENT_PARAMETERS d3pp;
    memset(&d3pp, 0, sizeof d3pp); // d3pp станет чист как стеклышко
    d3pp.Windowed = true; // программа будет выводиться в окне, так удобнее
    d3pp.EnableAutoDepthStencil = TRUE; 
    d3pp.AutoDepthStencilFormat = D3DFMT_D24X8;
 
    D3DDISPLAYMODE d3ddm;
    memset(&d3ddm, 0, sizeof(d3ddm));
    // какие сейчас используются настройки в системе для отображения. мы должны им следовать как и остальные окна
    D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm); 
    d3pp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3pp.BackBufferFormat = d3ddm.Format;
    d3pp.BackBufferWidth = Width;
    d3pp.BackBufferHeight = Height;
 
    // создаем устройство
    D3D->CreateDevice(  D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3pp, &Device);
    // ******** Конец инициализации DirectX ******** //
Внимание - проверка ошибок опущена Итак после этого всего мы имеет проинициализированный DirectX и готовы к основному коду приложения.

Выше мы говорили о вершинах. Теперь нужно завести место где мы будем их хранить - вершинный буфер.
C++
1
2
3
4
5
6
7
8
9
    // Создание буфера вершин
    IDirect3DVertexBuffer9* VBuf;
    Device->CreateVertexBuffer( 
            sizeof(Vertex) * LandscapeSize * LandscapeSize, // размер буфера в байтах
            0,
            Vertex::FVF, // формат вершин описываемый DWORD переменной
            D3DPOOL_DEFAULT,
            &VBuf, // куда поместить указатель на созданный буфер
            NULL);
Прокомментированы только значимые параметры, остальные "по умолчанию"
Итак мы создали буфер вершин который может хранить LandscapeSize * LandscapeSize точек, т.е. получаем такую квадратную сетку. Чтобы у нас получилась поверхность, надо для каждой точки задать координаты, при этом x, z координаты мы условимся брать в соотвествии с "сеткой" (представьте тетрадный лист) а высоту точек в этой сетке будем записывать в координату y. Так откуда же брать координату y? Можно из файла - изображения, а можно вычислять по определенной формуле, для упрощения возьмём вариант с формулой

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    // Заполнение буфера вершин
    Vertex* VData = NULL;
    VBuf->Lock(0, 0, (void**)&VData, 0); // Перед испльзованием буфера его нужно заблокировать
        for (size_t i = 0; i < LandscapeSize; ++i)
            for (size_t j = 0; j < LandscapeSize; ++j)
            {
                // Высота обратно пропорциональна расстоянию от центра
                // ((i - 128)^2 + (j - 128)^2)^0.5
                float dist = pow(
                                pow((i - (LandscapeSize / 2.0f)), 2.0f) + 
                                pow((j - (LandscapeSize / 2.0f)), 2.0f),
                        0.5f);
                VData->Pos.y = abs(sin( dist / 3.14f ) - log( dist * 1.0f ));
                VData->Pos.x = (float)i;
                VData->Pos.z = (float)j;
                VData->Color = D3DCOLOR_XRGB(80, 80, (DWORD)(VData->Pos.y * 40.0f));
 
                VData++; // переход к следующей вершине
            }
    VBuf->Unlock(); // и не забываем разблокировать после блокировки
float dist - переменная расчитывающая расстояние от центра поверхности до края
VData->Pos.y - расчитывается как мы и договорились по какой то формуле ) можете тут поэкспериментировать сами
VData->Pos.x, VData->Pos.z - заполняются по "сетке"
VData->Color - синий цвет вершины пропорционален высоте, вообще говоря если вы измените формулу просчета y то расчет цвета нужно будет тоже поменять

А теперь большая проблема - мы отлично описали "поверхность" по точкам и высотам, но DirectX такое вывести не сможет. Ему подавай полигоны - треугольники. Поэтому нам нужен ещё один буфер - на этот раз индексный.
Индексный он потому что содержит "индексы" т.е. номера вершин, номера откуда? Из вершинного буфера, т.е. они будут работать в паре. Причем отрисовка полигонов будет в принципе задаваться из индексного буфера, если у нас будет вершинный буфер с кучей вершин но пустой индексный буфер то ничего не отрисуется
Или ещё так можно объяснить - вершинный буфер выступает в роли базы данных где указаны разные точки, и индексный буфер указывает в каком порядке использовать эти данные чтобы вывести на экран то что нужно (в нашем случае много полигонов которые образуют поверхность)
Создадим индексный буфер
C++
1
2
3
4
5
6
7
8
9
    // Создание индексного буфера
    IDirect3DIndexBuffer9*  IBuf;
    Device->CreateIndexBuffer( 
            sizeof(short) * 2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ) * 3, 
            0, 
            D3DFMT_INDEX16,
            D3DPOOL_DEFAULT,
            &IBuf,
            NULL);
sizeof(short) * 2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ) * 3 - вес всех индексов. Т.е. вес однго индеса умножить на их количество. Количество индексов - давайте подумаем. Нам нужно отобразить полигоны. Полигон это 3 вершины. В одном квадрате (смотря по сетке) 2 полигона. А самих квадратов в сетке NxN будет (N-1)x(N-1). Вот и получилась такая формула.

Осталось заполнить индексный буфер:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
    // Расчет индексов
    unsigned short* index = NULL; size_t idx = 0;
    IBuf->Lock(0, 0, (void**)&index, 0);
        for (size_t x = 0; x < LandscapeSize - 1; x++)
            for (size_t y = 0; y < LandscapeSize - 1; y++)
            {      
                index[idx++] = y * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x + 1;
                index[idx++] = y * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x + 1;
                index[idx++] = y * LandscapeSize + x + 1;
            }
    IBuf->Unlock();
Закрываем - открываем как и в прошлом случае. То что в { } позволяет заполнить по 6 индексов, т.е. мы описываем 2 треугольника. А 2 треугольника будут формировать изогнутый квадрать по диагонали, множество таких квадратов и сформируют поверхность
По форумулам - y это "строка" в сетке. y * LandscapeSize - текущая строка. (y+1) * LandscapeSize - та что ниже. x - текущая точка, x+1 следующая.
Название: Lpoint.png
Просмотров: 9084

Размер: 4.7 Кб

Осталось немного - показать DirectX где мы находимся в этом мире (установить камеру просмотра) и матрицу проекции:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
    // Создаем и устанавливаем матрицу вида, представляющую нашу точку
    // и направление просмотра в виртуальном 3D мире
    D3DXMATRIX mView;
    D3DXVECTOR3 LookAt  = D3DXVECTOR3(128.0f, 20.0f, 128.0f);
    D3DXVECTOR3 Eye     = D3DXVECTOR3(-50.0f, 500.0f, -0.0f);
    D3DXVECTOR3 Up      = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    D3DXMatrixLookAtLH(&mView,      &Eye,       &LookAt,        &Up);
    Device->SetTransform(D3DTS_VIEW, &mView);
 
    // Создаем и устанавливаем матрицу проекции
    D3DXMATRIX mProjection; 
    D3DXMatrixPerspectiveFovLH(
        &mProjection,       // полученная итоговая матрица проекции
        D3DX_PI / 4,        // поле зрения в направлении оси Y в радианах
        (Width / Height),   // соотношения сторон экрана
        1.0f,               // передний план отсечения сцены
        1000.0f             // задний план отсечения сцены
    );
    Device->SetTransform(D3DTS_PROJECTION, &mProjection);
А теперь некоторые настройки отображения
C++
1
2
3
4
5
    Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
    Device->SetRenderState(D3DRS_LIGHTING, FALSE);
    Device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    Device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
    Device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
D3DRS_CULLMODE - D3DCULL_CW - показываем треугольники только одной стороной (поверхность мы будем смотреть только с одной стороны, так что тратить время на визуализацию обеих видеокарте не нужно)
D3DRS_LIGHTING - FALSE - у нас нет источников света, так что если не отключить освещение то все будет в темноте и мы ничего не увидим
Остальное - связано с Z буфером, для правильности порядка отображения поверхности

Теперь скажем видеокарте что будем выводить графику из наших буферов
C++
1
2
3
4
    // Сообщаем видеокарте о том что будем рисовать вершины из VBuf
    Device->SetStreamSource(0, VBuf, 0, sizeof Vertex); 
    Device->SetIndices(IBuf);       // Аналогично с индексным буфером
    Device->SetFVF(Vertex::FVF);    // И какой тип вершин будет использоваться
Некоторые приготовления:
C++
1
2
3
    ShowCursor(FALSE);
    DWORD Time = GetTickCount();
    float fi = 0.0f;    // Переменная для изменения позиции камеры
А теперь основное - цикл приложения:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    while (! GetAsyncKeyState(VK_ESCAPE))   // Пока не нажат Escape
    {
        fi += 0.001f * (GetTickCount() - Time); // Изменяем угол
        Time = GetTickCount();                  // Новое время
        Eye.x = 100.0f * sin(fi);               // Меняются позиции камеры
        Eye.y = 300.0f + 300.0f * sin(fi);
        D3DXMatrixLookAtLH(&mView, &Eye, &LookAt, &Up); // Изменяется матрица просмотра
        Device->SetTransform(D3DTS_VIEW, &mView);       // Устанавливается
 
        // Очищаем экран
        Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0f, 0);
 
        Device->BeginScene();
            Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, LandscapeSize * LandscapeSize, 0, 2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ));
        Device->EndScene();
 
        Device->Present(NULL, NULL, NULL, NULL);
    }
Изменением точки Eye мы меняем положение камеры, чтобы она летала вокруг нашей поверхности (в статике смотреть скучно ). Потом применяем это, чтобы DirectX знал что мы создали новую точку просмотра
Device->DrawIndexedPrimitive отрисовываем вершинный буфер (установленный выше) по индексному буферу (аналогично установленный выше)
D3DPT_TRIANGLELIST параметр означает что мы выводим список треугольников, собственно мы только и занимались тем что его составляли так что думаю это понятно
LandscapeSize * LandscapeSize количество вершин
2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ) количество треугольников

Выход из приложения - по кнопке ESC
и вот что нужно сделать под конец - освободить ресурсы DirectX
C++
1
2
3
4
5
6
7
    // Освобождаем выделенные ресурсы
    IBuf->Release();
    VBuf->Release();
    Device->Release();
    D3D->Release();
return 0;
}
Весь код для копипасты
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
#include <windows.h>
#include <tchar.h>
#include "math.h"
 
#pragma comment (lib, "d3dx9.lib")
#pragma comment (lib, "d3d9.lib")
#include <d3dx9core.h>
#include <d3d9.h>
 
// Какие у нас вершины ландшафта
struct Vertex
{
    D3DXVECTOR3 Pos;    // Имеют позицию
    DWORD Color;        // И цвет
    static const DWORD FVF =
            D3DFVF_XYZ      // Говорит о том что есть позиция в структуре
        |   D3DFVF_DIFFUSE; // Говорит о том что есть цвет в структуре
};
 
int APIENTRY _tWinMain(
    HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPTSTR lpCmdLine, int nCmdShow)
{
    // Размер экрана
    int Width = 1024, Height = 768;
    // Размер ландшафта ( LandscapeSize х LandscapeSize )
    size_t LandscapeSize = 256;
 
    // ******** Начало инициализации окна ******** //
    HWND hWnd; WNDCLASSEX wcex;
    
    wcex.cbSize = sizeof(WNDCLASSEX);   wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = DefWindowProc;    wcex.cbClsExtra     = 0;
    wcex.cbWndExtra     = 0;    wcex.hInstance      = hInstance;
    wcex.hIcon          = LoadIcon(hInstance, MAKEINTRESOURCE(IDC_WAIT));
    wcex.hCursor        = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_WAIT);
    wcex.lpszClassName  = "wcex";
    wcex.hIconSm        = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDC_WAIT));
    RegisterClassEx(&wcex);
    hWnd = CreateWindow(wcex.lpszClassName, lpCmdLine, WS_VISIBLE,
    CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);
    // ******** Конец инициализации окна ******** //
    ShowWindow(hWnd, nCmdShow); // Показываем что создали
 
    // ******** Начало инициализации DirectX ******** //
    IDirect3D9* D3D; IDirect3DDevice9* Device;
    D3D = Direct3DCreate9(D3D_SDK_VERSION);
 
    D3DPRESENT_PARAMETERS d3pp;
    memset(&d3pp, 0, sizeof d3pp);
    d3pp.Windowed = true;
    d3pp.EnableAutoDepthStencil = TRUE;
    d3pp.AutoDepthStencilFormat = D3DFMT_D24X8;
 
    D3DDISPLAYMODE d3ddm; memset(&d3ddm, 0, sizeof(d3ddm));
    D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &d3ddm);
    d3pp.SwapEffect = D3DSWAPEFFECT_DISCARD;    d3pp.BackBufferFormat = d3ddm.Format;
    d3pp.BackBufferWidth = Width;               d3pp.BackBufferHeight = Height;
 
 
    D3D->CreateDevice(  D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
                        D3DCREATE_SOFTWARE_VERTEXPROCESSING, &d3pp, &Device);
    // ******** Конец инициализации DirectX ******** //
 
 
 
    // Создание буфера вершин
    IDirect3DVertexBuffer9* VBuf;
    Device->CreateVertexBuffer( 
            sizeof(Vertex) * LandscapeSize * LandscapeSize,
            0,
            Vertex::FVF,
            D3DPOOL_DEFAULT,
            &VBuf, NULL);
 
    // Заполнение буфера вершин
    Vertex* VData = NULL;
    VBuf->Lock(0, 0, (void**)&VData, 0);
        for (size_t i = 0; i < LandscapeSize; ++i)
            for (size_t j = 0; j < LandscapeSize; ++j)
            {
                // Высота обратно пропорциональна расстоянию от центра
                // ((i - 128)^2 + (j - 128)^2)^0.5
                float dist = pow(
                                pow((i - (LandscapeSize / 2.0f)), 2.0f) + 
                                pow((j - (LandscapeSize / 2.0f)), 2.0f),
                        0.5f);
                VData->Pos.y = abs(sin( dist / 3.14f ) - log( dist * 1.0f ));
                VData->Pos.x = (float)i;
                VData->Pos.z = (float)j;
                VData->Color = D3DCOLOR_XRGB(80, 80, (DWORD)(VData->Pos.y * 40.0f));
 
                VData++;
            }
    VBuf->Unlock();
 
    // Создание индексного буфера
    IDirect3DIndexBuffer9*  IBuf;
    Device->CreateIndexBuffer( 
            sizeof(short) * 2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ) * 3, 
            0, 
            D3DFMT_INDEX16,
            D3DPOOL_DEFAULT,
            &IBuf,
            NULL);
 
    // Расчет индексов
    unsigned short* index = NULL; size_t idx = 0;
    IBuf->Lock(0, 0, (void**)&index, 0);
        for (size_t x = 0; x < LandscapeSize - 1; x++)
            for (size_t y = 0; y < LandscapeSize - 1; y++)
            {      
                index[idx++] = y * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x + 1;
                index[idx++] = y * LandscapeSize + x;
                index[idx++] = (y + 1) * LandscapeSize + x + 1;
                index[idx++] = y * LandscapeSize + x + 1;
            }
    IBuf->Unlock();
 
    // Создаем и устанавливаем матрицу вида, представляющую нашу точку
    // и направление просмотра в виртуальном 3D мире
    D3DXMATRIX mView;
    D3DXVECTOR3 LookAt  = D3DXVECTOR3(128.0f, 20.0f, 128.0f);
    D3DXVECTOR3 Eye     = D3DXVECTOR3(-50.0f, 500.0f, -0.0f);
    D3DXVECTOR3 Up      = D3DXVECTOR3(0.0f, 1.0f, 0.0f);
    D3DXMatrixLookAtLH(&mView,      &Eye,       &LookAt,        &Up);
    Device->SetTransform(D3DTS_VIEW, &mView);
 
    // Создаем и устанавливаем матрицу проекции
    D3DXMATRIX mProjection; 
    D3DXMatrixPerspectiveFovLH(
        &mProjection,       // полученная итоговая матрица проекции
        D3DX_PI / 4,        // поле зрения в направлении оси Y в радианах
        (Width / Height),   // соотношения сторон экрана
        1.0f,               // передний план отсечения сцены
        1000.0f             // задний план отсечения сцены
    );
    Device->SetTransform(D3DTS_PROJECTION, &mProjection);
 
    Device->SetRenderState(D3DRS_CULLMODE, D3DCULL_CW);
    Device->SetRenderState(D3DRS_LIGHTING, FALSE);
    Device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
    Device->SetRenderState(D3DRS_ZWRITEENABLE, TRUE);
    Device->SetRenderState(D3DRS_ZFUNC, D3DCMP_LESSEQUAL);
 
 
    // Сообщаем видеокарте о том что будем рисовать вершины из VBuf
    Device->SetStreamSource(0, VBuf, 0, sizeof Vertex); 
    Device->SetIndices(IBuf);       // Аналогично с индексным буфером
    Device->SetFVF(Vertex::FVF);    // И какой тип вершин будет использоваться
 
 
    ShowCursor(FALSE);
    DWORD Time = GetTickCount();
    float fi = 0.0f;    // Переменная для изменения позиции камеры
    while (! GetAsyncKeyState(VK_ESCAPE))   // Пока не нажат Escape
    {
        fi += 0.001f * (GetTickCount() - Time); // Изменяем угол
        Time = GetTickCount();                  // Новое время
        Eye.x = 100.0f * sin(fi);               // Меняются позиции камеры
        Eye.y = 300.0f + 300.0f * sin(fi);
        D3DXMatrixLookAtLH(&mView, &Eye, &LookAt, &Up); // Изменяется матрица просмотра
        Device->SetTransform(D3DTS_VIEW, &mView);       // Устанавливается
 
        // Очищаем экран
        Device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(255,255,255), 1.0f, 0);
 
        Device->BeginScene();
            Device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, LandscapeSize * LandscapeSize, 0, 2 * ( LandscapeSize - 1 ) * ( LandscapeSize - 1 ));
        Device->EndScene();
 
        Device->Present(NULL, NULL, NULL, NULL);
    }
 
    // Освобождаем выделенные ресурсы
    IBuf->Release();
    VBuf->Release();
    Device->Release();
    D3D->Release();
return 0;
}


Результат: Создание простой фигуры в 3D-формате, OpenGL и DirectX

11
insideone
Модератор
Автор FAQ
3657 / 937 / 112
Регистрация: 10.01.2010
Сообщений: 2,512
06.06.2010, 21:02 #3
Открыто для предложений и исправлений, после исправления пост будет удален, а в руководстве делается пометка о вкладе этого участника. Любые другие обсуждения удаляются или переносятся в отдельные темы.
3
ncuX1
brony
72 / 52 / 3
Регистрация: 02.01.2012
Сообщений: 521
16.02.2012, 23:02 #4
файла opengl.pas нету по линку.
0
ncuX1
brony
72 / 52 / 3
Регистрация: 02.01.2012
Сообщений: 521
05.03.2012, 20:30 #5
ps
без модуля math перемещение не работает...
0
Leptis
0 / 0 / 0
Регистрация: 30.03.2012
Сообщений: 6
30.03.2012, 22:50 #6
подскажите а как сделать тоже на PascalABC.net
0
Vladiator
1569 / 639 / 79
Регистрация: 24.02.2009
Сообщений: 9,269
30.03.2012, 22:59  [ТС] #7
Недостающие файлы. Извините за неудобства
1
Вложения
Тип файла: rar OpenGL.rar (17.7 Кб, 128 просмотров)
Тип файла: rar OpenGLP.rar (302.1 Кб, 114 просмотров)
Leptis
0 / 0 / 0
Регистрация: 30.03.2012
Сообщений: 6
01.04.2012, 19:32 #8
и ещё пожалуйста обьясните что это за

Delphi
1
2
3
4
5
  Form1: TForm1;
  DC:HDC;
  HRC:HGLRC;
  Human:User;
  mt:gluInt;
в openGL и зачем оно нужно

а и чуть не забыл для чего вот это
Delphi
1
2
3
4
5
6
7
8
  DC:=GetDC(Handle);
  SetDCPixelFormat;
  HRC:=wglCreateContext(DC);
  wglMakeCurrent(DC,HRC);
  Form1.WindowState:=wsMaximized;
  ShowCursor(False);
  glClearColor(0.0,0.0,0.0,1.0);
  glEnable(GL_DEPTH_TEST);
Добавлено через 3 минуты
в ссылке openGLP.rar вариант проги для паскаля абц не робит - требует модули разные.. а можете дать ссылку на то-же но с модулями и пояснениями. плиз
0
Vladiator
1569 / 639 / 79
Регистрация: 24.02.2009
Сообщений: 9,269
01.04.2012, 19:51  [ТС] #9
Цитата Сообщение от Leptis Посмотреть сообщение
DC:HDC;
HRC:HGLRC;
Это, вроде бы, что-то с дескрипторами. Очень давно не работал с виндой, так что, не знаю...
Цитата Сообщение от Leptis Посмотреть сообщение
Human:User;
mt:gluInt;
Местонахождение человека, что такое mt - уже не знаю. Так как оно больше не появляется, можно удалить.
Цитата Сообщение от Leptis Посмотреть сообщение
DC:=GetDC(Handle);
SetDCPixelFormat;
HRC:=wglCreateContext(DC);
wglMakeCurrent(DC,HRC);
Это всё что-то с дескрипторами. Пусть помогут более знающие в этом деле...
Цитата Сообщение от Leptis Посмотреть сообщение
Form1.WindowState:=wsMaximized;
ShowCursor(False);
glClearColor(0.0,0.0,0.0,1.0);
glEnable(GL_DEPTH_TEST);
Разворачиваем окно, скрываем курсор, устанавливаем цвет фона.
Цитата Сообщение от Leptis Посмотреть сообщение
а можете дать ссылку на то-же но с модулями и пояснениями. плиз
К сожалению, нет, слишком давно это было... сейчас могу немного помочь с Glut/С++.
0
01.04.2012, 19:51
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
01.04.2012, 19:51
Привет! Вот еще темы с решениями:

OpenGL или DirectX?
Добрый день. Является ли opengl и directx полноценными графическими движками?...

DirectX или OpenGL. 2D
Здравствуйте дорогие форумчане. В общем хотелось бы изучить программирование 2D...

Литературы по opengl или directx
Доброго времени суток. Кто нибудь знает литературы по opengl,directx. Я сам...

Основные отличия OpenGL от DirectX?
Всем привет! Меня мучает такой вопрос: что перспективнее на данный момент...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
9
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru