Интересные статьи

Создается небольшой поект. Одной из задач проекта является морфинг поверхности, состоящей из полигонов. В связи с большим количеством точек поверхности и медленным выполнением кода было решено искать пути ускорения процесса. Переход на OpenMP дал прирост производительности порядка 2-х раз на 4-х ядерном процессоре Intel Q6800. В настоящее время эксперементирую с CUDA. Для сравнения скорости привожу кусок кода проверки попадания точек полигонов в усеченый конус.  

 

Данные о процессоре:

 

Number of CPU(s) One Physical Processor / 2 Cores / 2 Logical Processors / 64 bits

CPU Full Name Intel(R) Pentium(R) D CPU 3.20GHz

CPU Name Intel Pentium D 940

CPU Code Name Presler

Platform Name Socket 775 LGA

Revision B1

Technology 65 nm

Instructions MMX, SSE, SSE2, SSE3, ET64, VMX

Original Clock 3200 MHz

Original System Clock 200 MHz

Original Multiplier 16.0

CPU Clock 3201 MHz

System Clock 200.0 MHz

FSB 800.1 MHz

Core 0 Speed 3200.6 MHz

Core 0 Multiplier 16.0

Core 1 Speed 3200.6 MHz

Core 1 Multiplier 16.0

L1 Data Cache 2 x 16 KBytes

L1 Trace Cache 2 x 12 Kuops

L2 Cache 2 x 2048 Kbytes

 

Данные о видео:

 

Number of GPUs 2

Display Adapter NVIDIA GeForce 9400 GT

Video Processor GeForce 9400 GT

Video memory size 1024 MBytes

Adapter DAC Type Integrated RAMDAC

BIOS String Version 62.94.72.00.C1

BIOS Date 08/28/09

Display Drivers nv4_disp.dll

Driver Version 6.14.12.5721

Driver Date 2010-06-07 23:57:00

Inf File Name oem104.inf

Inf Section Section005

Display Adapter BB Capture Driver

Driver Version 1.00

Driver Date 2008-05-19 04:30:53

Inf File Name oem42.inf

Inf Section bbcap

 

В таблице приведены результаты.

Первый столбик: 0 без CUDA/1 с CUDA.

Второй столбик: количество полигонов.

Третий столбик: общее количество точек.

Четвертый столбик: затраченое время в тиках процессора.

Как видно из таблицы, при изменении количества точек в полигоне, среднее время, затраченное на одну точку остается постоянным (примерно 3100-3200 на точку), а с использованием CUDA при увеличении количества точек, имеется прирост производительности до 10 раз(около 400 на точку).

 

0   16   18512   59129664

0   16   18512   58459152

0   16   18512   58712096

0   16   18512   60967168

0   16   18512   57244720

 

1   16   18512   502950240 (первый длительный цикл с CUDA связан с инициализацией GPU)

1   16   18512   52466864

1   16   18512   53369744

1   16   18512   50947872

1   16   18512   49795088

 

0   16   69872   224359936

0   16   69872   216826112

0   16   69872   218405712

0   16   69872   228633536

0   16   69872   225921376

 

1   16   69872   510279616

1   16   69872   62507984

1   16   69872   59920208

1   16   69872   58379904

1   16   69872   72676896

 

0   16   270992   865252016

0   16   270992   871903296

0   16   270992   875956464

0   16   270992   866460912

0   16   270992   871480384

 

1   16   270992   546036912

1   16   270992   102836928

1   16   270992   95773008

1   16   270992   101953184

1   16   270992   96566416

 

 

 

 

Код Microsoft Visual C++ 2008:

 

__int64 t1,t2,t3;

t2=0;

t3=0;

for (int i=0; ipMesh.size(); i++){

Model->pMesh[i]->GetVertexBuffer(&mVB);

mVB->Lock( 0, 0, ( void** )&m, D3DLOCK_READONLY );

DWORD i2=Model->pMesh[i]->GetNumVertices();

for (DWORD i1=0; i1

{

p[i1].x=m[i1].p.x;

p[i1].y=m[i1].p.y;

p[i1].z=m[i1].p.z;

t3++;

}

 

t1=__rdtsc();

if (Model->CUDA==TRUE)

{

// cuda

if (Cuda_PoligonInstr(i2, p, _matr1, _a, _d)==1)

_Count.push_back(i);

}

else

{

for (int i1=0; i1

D3DXVECTOR3 _vecSource;

D3DXVec3TransformCoord(&_vecSource,&m[i1].p,&Matr1);

if (_vecSource.z  > 0 ){

D3DXVECTOR2 _l=D3DXVECTOR2(_vecSource.x, _vecSource.y);

float l=D3DXVec2Length(&_l);

float l1=_a*_vecSource.z+_d;

if (l

_Count.push_back(i);

i1=i2;

}

}

}

}

t2+=__rdtsc()-t1;

 

mVB->Unlock();

mVB->Release();

m=NULL;

}

delete(p);

 

//////////////////////

std::ofstream out;

 

out.open("prim.txt",std::ios.app);

out

 

out.close();

//////////////////////

 

 

Код CUDA:

__global__ void PoligonInstrKernel(float3* point, float* matr, float a, float r, int* len)

{

unsigned int x1 = threadIdx.x+blockIdx.x*blockDim.x;

float3 p;

p.x = matr[0]*point[x1].x+matr[1]*point[x1].y+matr[2]*point[x1].z+matr[12];

p.y = matr[4]*point[x1].x+matr[5]*point[x1].y+matr[6]*point[x1].z+matr[13];

p.z = matr[8]*point[x1].x+matr[9]*point[x1].y+matr[10]*point[x1].z+matr[14];

point[x1]=p;

if (point[x1].z>0)

if (x1

{

float l=sqrt(point[x1].x*point[x1].x+point[x1].y*point[x1].y);

float l1=a*point[x1].z+r;

if (l

}

}

 

 

_declspec(dllexport) int Cuda_PoligonInstr(int len, float3* point, float* matr, float a, float r)

{

float3* point1 = new float3[len];

float3* devpoint;

cutilSafeCall(cudaMalloc((void**)&devpoint, sizeof(float3) * len));

cutilSafeCall( cudaMemset(devpoint, 0, sizeof(float3) * len) );

cutilSafeCall(cudaMemcpy(devpoint, point, sizeof(float3) * len, cudaMemcpyHostToDevice));

 

float* devmatr;

cutilSafeCall(cudaMalloc((void**)&devmatr, sizeof(float) * 16));

cutilSafeCall(cudaMemcpy(devmatr, matr, sizeof(float) * 16, cudaMemcpyHostToDevice));

int* l= new int[2];

l[0]=0;

l[1]=len;

int* devlen;

cutilSafeCall(cudaMalloc((void**)&devlen, sizeof(int)*2));

cutilSafeCall(cudaMemcpy(devlen, l, sizeof(int)*2, cudaMemcpyHostToDevice));

 

dim3 gridSize = dim3(1, 1, 1);

dim3 blockSize  = dim3(len, 1, 1);

 

PoligonInstrKernel(devpoint, devmatr, a, r, devlen);

 

cudaEvent_t syncEvent;

cudaEventCreate(&syncEvent);

cudaEventRecord(syncEvent, 0);

cudaEventSynchronize(syncEvent);

cudaMemcpy(point1, devpoint, sizeof(float3) * len, cudaMemcpyDeviceToHost);

 

cutilSafeCall(cudaMemcpy(l, devlen, sizeof(float)*2, cudaMemcpyDeviceToHost));

cudaFree(devpoint);

cudaFree(devmatr);

delete point1;

 

int _a;

if (l[0]>0) _a=1; else _a=0;

delete l;

return _a;

}

G-code

Материал из Википедии — свободной энциклопедии

G-код — условное именование языка программирования устройств с ЧПУ (Числовое программное управление). Был создан компанией Electronic Industries Alliance в начале 1960-х. Финальная доработка была одобрена в феврале 1980 года как стандарт RS274D. Комитет ИСО утвердил G-код, как стандарт ISO 6983-1:1982, Госкомитет по стандартам СССР — как ГОСТ 20999-83. В советской технической литературе G-код обозначается, как код ИСО 7-бит (ISO 7-bit).

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

Содержание

      

Структура программы

Программа, написанная с использованием G-кода, имеет жесткую структуру. Все команды управления объединяются в кадры — группы, состоящие из одной или более команд. Кадр завершается символом перевода строки (ПС/LF) и имеет номер, за исключением первого кадра программы и комментариев. Первый кадр содержит только один символ «%». Завершается программа командой M02 или M30. Комментарии к программе размещаются в круглых скобках, занимая отдельный кадр.

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

Подпрограммы должны быть описаны после команды M02, но до M30. Начинается подпрограмма с кадра вида Lxx, где xx — номер подпрограммы, заканчивается командой M17.

Сводная таблица кодов

Основные (называемые в стандарте подготовительными) команды языка начинаются с буквы G:

  • Перемещение рабочих органов оборудования с заданой скоростью (линейное и круговое)
  • Выполнение типовых последовательностей (таких, как обработка отверстий и резьб)
  • Управление параметрами инструмента, системами координат, и рабочих плоскостей
Подготовительные (основные) команды
КодыОписание
G00-G04Позиционирование инструмента
G17-G19Переключение рабочих плоскостей (XY, XZ, YZ)
G20-G21Не стандартизовано
G40-G44Компенсация размера различных частей инструмента (длина, диаметр)
G53-G59Переключение систем координат
G80-G84Циклы сверления, нарезания резьбы
G90-G92Переключение систем координат (абсолютная, относительная)

Таблица основных команд

КодОписаниеПример
G00Ускоренное перемещение инструмента (холостой ход)G0 X0 Y0 Z100
G01Линейная интерполяцияG01 X0 Y0 Z100 F200
G02Круговая интерполяция по часовой стрелкеG02 X15 Y15 R5 F200
G03Круговая интерполяция против часовой стрелкиG03 X15 Y15 R5 F200
G04Задержка на E миллисекундG04 E500
G40Отмена компенсации размера инструментаG1 G40 X0 Y0 F200
G41Компенсировать радиус инструмента слева от траекторииG41 X15 Y15 D1 F100
G42Компенсировать радиус инструмента справа от траекторииG42 X15 Y15 D1 F100
G43Компенсировать длину инструмента положительноG43 X15 Y15 Z100 H1 S1000 M3
G44Компенсировать длину инструмента отрицательноG44 X15 Y15 Z4 H1 S1000 M3
G53Отключить смещение начала системы координат станкаG53 G0 X0 Y0 Z0
G54-G59Переключиться на заданную оператором систему координатG54 G0 X0 Y0 Z100
G80Отмена циклов сверления (G81-G84)G80
G81Цикл сверленияG81 X0 Y0 Z-10 R3 F100
G82Цикл сверления с задержкойG82 X0 Y0 Z-10 R3 P100 F100
G83Цикл сверления с отходомG83 X0 Y0 Z-10 R3 Q8 F100
G84Цикл нарезания резьбыG95 G84 X0 Y0 Z-10 R3 F1.411
G90Задание абсолютных координат опорных точек траекторииG90 G1 X0.5 Y0.5 F10
G91Задание координат относительно последней введённой опорной точкиG91 G1 X4 Y5 F100
G94F (подача) — в формате мм/мин.G94 G80 Z100
G95F (подача) — в формате мм/об.G95 G84 X0 Y0 Z-10 R3 F1.411

Таблица технологических кодов

Технологические команды языка начинаются с буквы М. Включают такие действия, как:

  • Сменить инструмент
  • Включить/выключить шпиндель
  • Включить/выключить охлаждение
Вспомогательные (технологические) команды
КодОписаниеПример
M00Приостановить работу станка до нажатия кнопки «старт» на пульте управления, так называемый "технологический останов"G0 X0 Y0 Z100 M0
M01Приостановить работу станка до нажатия кнопки «старт», если включён режим подтверждения остановаG0 X0 Y0 Z100 M1
M02Конец программыM02
M03Начать вращение шпинделя по часовой стрелкеM3 S2000
M04Начать вращение шпинделя против часовой стрелкиM4 S2000
M05Остановить вращение шпинделяM5
M06Сменить инструментM6 T15
M07Включить дополнительное охлаждениеM3 S2000 M7
M08Включить основное охлаждениеM3 S2000 M8
M09Выключить охлаждениеG0 X0 Y0 Z100 M5 M9
M17Конец подпрограммыM17
M30Конец информацииM30

Параметры команд

Параметры команд задаются буквами латинского алфавита

КодОписаниеПример
XКоордината точки траектории по оси XG0 X100 Y0 Z0
YКоордината точки траектории по оси YG0 X0 Y100 Z0
ZКоордината точки траектории по оси ZG0 X0 Y0 Z100
EВеличина задержки в микросекундахG04 E101
FСкорость рабочей подачиG1 G91 X10 F100
SСкорость вращения шпинделяS3000 M3
RПараметр стандартного цикла или радиус дуги (расширение стандарта)G81 R1 0 R2 -10 F50 или G1 G91 X12.5 R12.5
DПараметр коррекции выбранного инструментаM06 T1 D1
PЧисло вызовов подпрограммыL82 P10
I,J,KПараметры дуги при круговой интерполяцииG03 X10 Y10 I0 J0 F10
LВызов подпрограммы с данной меткойL12

Пример

Обработка буквы W (вписанной в прямоугольник 34х27 мм, см рис.) на условном вертикально-фрезерном станке с ЧПУ, фрезой диаметром 4 мм, в заготовке из органического стекла:

 


КадрСодержаниеКомментарий
  %Начало программы
N1G90 G40 G17Система координат абсолютная, компенсация на инструмент выключена, плоскость интерполяции XOY
N2G00 X2.54 Y26.15Переход в точку начала обработки
N3S500 F0.5 M3Выбрать режимы резания и включить привод главного движения
N4G01 Z-1.0Врезание в заготовку
N5G01 X5.19 Y 2.0Первый штрих буквы W
N6G01 X7.76Продолжение движения
N7G01 X16.93 Y26.15Второй штрих буквы W
N8G01 X18.06Продолжение движения
N9G01 X25.4 Y2.0Третий штрих буквы W
N10G01 X25.96Продолжение движения
N11G01 X32.17 Y 26.15Четвертый штрих буквы W
N12G00 Z12Отвод инструмента от заготовки
N13M5Выключить привод главного движения
N14M02Конец программы

Дополнительная информация

Источник: http://www.compdoc.ru/prog/builder/opengl/ 

 

На кого рассчитана статья

Я рассчитываю на то, что вы знакомы с азами создания приложений в С++Builder или Delphi и совсем не знаете OpenGL.

Введение

OpenGL (Open Graphics Library) - популярная библиотека для работы с 3D графикой. Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает годы своего самого бурного развития.

Чуть-чуть побольше узнать об OpenGL и о том, как с ним работать в VC, можно, почитав wat'а: http://www.gamedev.ru/coding/11203.shtml

Я хочу показать, как работать с этой библиотекой в таких популярных и, на мой взгляд, очень удобных средах разработки, как Delphi и С++Builder.

Эта - первая - статья посвящена в основном инициализации OpenGL.

Инициализация

Первым делом нужно подключить заголовочные файлы:

С++

#include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h>

Delphi

uses OpenGL;

Если вы используете Delphi, то всё необходимое для работы с OpenGL находится в модуле OpenGL.dcu. А если вы используете С++Builder, то подключать придётся несколько файлов:

  • gl.h и glu.h содержат прототипы основных функций OpenGL определённых в opengl32.dll и glu32.dll.
  • glaux.h содержит вспомогательные (auxiliary) функции (glaux.dll). В этой статье я не буду использовать glaux.h, т.к. его функции не доступны в Delphi, да и не люблю я эту библиотеку. Кроме того основные задачи glaux (как, в прочем, и аналогичной, но более качественной, библиотеки GLUT) - это создание окон, таймеров, обработка клавиатуры и мыши, всё это есть в ИСР (Интегральная Среда Разработки) Delphi или С++Builder.

После подключения заголовочных файлов нужно установить формат пикселей. Я для этой цели использую следующую функцию:

С++

      BOOL bSetupPixelFormat(HDC hdc)
       {
           PIXELFORMATDESCRIPTOR pfd, *ppfd;
           int pixelformat;
           ppfd = &pfd;
           ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR);
           ppfd->nVersion = 1;
           ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
           ppfd->dwLayerMask = PFD_MAIN_PLANE;
           ppfd->iPixelType = PFD_TYPE_RGBA;
           ppfd->cColorBits = 16;
           ppfd->cDepthBits = 16;
           ppfd->cAccumBits = 0;
           ppfd->cStencilBits = 0;
           if ((pixelformat = ChoosePixelFormat(hdc, ppfd)) == 0)
           { 
              MessageBox(NULL, "ChoosePixelFormat failed", "Error",          MB_OK);
               return FALSE;
           }
           if (SetPixelFormat(hdc, pixelformat, ppfd) == FALSE)
           {
               MessageBox(NULL, "SetPixelFormat failed", "Error",          MB_OK);
               return FALSE;
           } 
          return TRUE;
       }

Delphi

      function bSetupPixelFormat(DC:HDC):boolean;
       var
           pfd:PIXELFORMATDESCRIPTOR;
           ppfd:PPIXELFORMATDESCRIPTOR;
           pixelformat:integer;
       begin
           ppfd := @pfd;
           ppfd.nSize := sizeof(PIXELFORMATDESCRIPTOR);
           ppfd.nVersion := 1;
           ppfd.dwFlags :=  PFD_DRAW_TO_WINDOW xor
                            PFD_SUPPORT_OPENGL xor
                            PFD_DOUBLEBUFFER; 
          ppfd.dwLayerMask := PFD_MAIN_PLANE;
           ppfd.iPixelType := PFD_TYPE_RGBA;
           ppfd.cColorBits := 16;
          ppfd.cDepthBits := 16;
           ppfd.cAccumBits := 0;
           ppfd.cStencilBits := 0;
           pixelformat := ChoosePixelFormat(dc, ppfd);
           if pixelformat=0 then
           begin
               MessageBox(0, 'ChoosePixelFormat failed', 'Error', MB_OK);
               bSetupPixelFormat:=FALSE;
               exit;
           end;
           if SetPixelFormat(dc, pixelformat, ppfd)=false then
           begin
               MessageBox(0, 'SetPixelFormat failed', 'Error', MB_OK);
               bSetupPixelFormat:=FALSE;
               exit;
           end; 
          bSetupPixelFormat:=TRUE;
       end;

Вряд ли вам придётся менять что-нибудь в этой функции, но кое-что о структуре PIXELFORMATDESCRIPTOR сказать надо.

cColorBits - глубина цвета

cDepthBits - размер буфера глубины (Z-Buffer)

cStencilBits - размер буфера трафарета (мы его пока не используем)

iPixelType - формат указания цвета. Может принимать значения PFD_TYPE_RGBA (цвет указывается четырьмя параметрами RGBA - красный, зленный, синий и альфа) и PFD_TYPE_COLORINDEX (цвет указывается индексом в палитре). Как вы видите, я использую RGBA, и вам придётся поступить также, т.к. если вы захотите использовать COLORINDEX, то вам придётся изменить мою функцию: добавить пару флагов и дать начальные значения ещё нескольким переменным.

Более подробную информацию смотрите в справочнике или в MSDN.

Функция ChoosePixelFormat подбирает формат пикселей, максимально удовлетворяющий нашим требованиям, и возвращает его дескриптор, а SetPixelFormat устанавливает его в контексте устройства (dc).

После того как в контексте устройства установлен формат пикселей, нужно создать контекст воспроизведения (Rendering Context) для этого в OpenGL определены следующие функции:

С++

  HGLRC wglCreateContext(HDC hdc);   BOOL wglMakeCurrent(HDC hdc, HGLRC hglrc);

Delphi

  function wglCreateContext(dc: HDC): HGLRC;   function wglMakeCurrent(dc: HDC; glrc: HGLRC):Boolean;

Наверное, объяснять их значение не стоит

Теперь перейдём к форме. В объявлении класса формы в области private добавьте следующее:

С++

  HGLRC ghRC;   HDC   ghDC;   void Draw();

Delphi

  ghRC:HGLRC;   ghDC:HDC;   procedure Draw;

ghRC - указатель на контекст воспроизведения (Rendering Context)

ghDC - дескриптор устройства (для нас - просто указатель на окно)

Процедура Draw будет отвечать за рисование.

Далее заполняем FormCreate:

С++

      void __fastcall TForm1::FormCreate(TObject *Sender)
       {
          ghDC = GetDC(Handle);
          if (!bSetupPixelFormat(ghDC))
             Close();
          ghRC = wglCreateContext(ghDC);
          wglMakeCurrent(ghDC, ghRC);
          glClearColor(0.0, 0.0, 0.0, 0.0);
          FormResize(Sender);
          glEnable(GL_COLOR_MATERIAL);
          glEnable(GL_DEPTH_TEST);
          glEnable(GL_LIGHTING);
          glEnable(GL_LIGHT0);
            float p[4]={3,3,3,1},
                  d[3]={-1,-1,-3};
          glLightfv(GL_LIGHT0,GL_POSITION,p);
          glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,d);
       }

Delphi

      procedure TForm1.FormCreate(Sender: TObject);
       var
          p: TGLArrayf4;
          d: TGLArrayf3;
        begin
          ghDC := GetDC(Handle);
          if bSetupPixelFormat(ghDC)=false then            Close();
          ghRC := wglCreateContext(ghDC);
          wglMakeCurrent(ghDC, ghRC);
          glClearColor(0.0, 0.0, 0.0, 0.0);
          FormResize(Sender);
          glEnable(GL_COLOR_MATERIAL);
          glEnable(GL_DEPTH_TEST);
          glEnable(GL_LIGHTING);
          glEnable(GL_LIGHT0);
            p[0]:=3;
            p[1]:=3;
            p[2]:=3;
            p[3]:=1;
            d[0]:=-1;
            d[1]:=-1;
            d[2]:=-3;
          glLightfv(GL_LIGHT0,GL_POSITION,@p);
          glLightfv(GL_LIGHT0,GL_SPOT_DIRECTION,@d);
       end;

Вы видите, что тут вызывается FromResize, который мы ещё не описали. Надо это исправить. Поместите туда следующий код:

С++, Delphi

  glViewport( 0, 0, Width, Height );
   glMatrixMode( GL_PROJECTION );
   glLoadIdentity();
   glOrtho(-5,5, -5,5, 2,12);
   gluLookAt(0,0,5, 0,0,0, 0,1,0);
   glMatrixMode( GL_MODELVIEW );

Теперь, наверное, надо кое-что объяснить.

glClearColor устанавливает цвет (в нашем случае чёрный), которым будет заполняться экран при очищении. У этой процедуры - 4 параметра, что соответствует RGBA. Вместо нее можно написать glClearIndex(0.0). Эта процедура устанавливает индекс цвета в палитре.

glViewport устанавливает область вывода - область, в которую OpenGL будет выводить изображение. В нашем случае - вся форма.

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

glLoadIdentity заменяет текущую матрицу видового преобразования на единичную (матрицу идентичности), т.е. просто сбрасывает ваши изменения.

glOrtho устанавливает режим ортогонального (прямоугольного) проецирования. Это значит, что изображение будет рисоваться как в изометрии. 6 параметров типа GLdouble (или просто double): left, right, bottom, top, near, far определяют координаты соответственно левой, правой, нижней, верхней, ближней и дальней плоскостей отсечения, т.е. всё, что окажется за этими пределами, рисоваться не будет. На самом деле эта процедура просто устанавливает масштабы координатных осей. Для того чтобы установить перспективное проецирование, используются процедуры glFrustum и gluPerspective, но о них - потом.

gluLookAt устанавливает параметры камеры: первая тройка - её координаты, вторая - вектор направления, третья - направление оси Y.

В OpenGL всё включается и выключается (разрешается и запрещается) процедурами glEnable и glDisable. Таким образом, мы разрешили тест глубины (GL_DEPTH_TEST), чтобы изображение было объёмным, разрешили давать нашим объектам какой-то цвет (GL_COLOR_MATERIAL), разрешили освещение (GL_LIGHTING) и включили <лампочку №0> (GL_LIGHT0).

glLightfv устанавливает свойства <лампочек>: позицию и направление света.

После того, как вы завершили работу с OpenGL, нужно освободить занятые ресурсы: освободить контекст, вызвав wglMakeCurrent с параметром ноль для идентификатора контекста OpenGL и разрушить этот контекст функцией wglDeleteContext. Кроме того нужно удалить дескриптор ghDC. Так как обычно работу с OpenGL завершается при завершении работы приложения, то соответствующий код нужно поместить в FormClose:

С++

      void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction          &Action)
       {
         if(ghRC)
         {
           wglMakeCurrent(ghDC,0);
           wglDeleteContext(ghRC);
         }
         if(ghDC)           ReleaseDC(Handle, ghDC);
       }

Delphi

      procedure TForm1.FormClose(Sender: TObject; var Action:          TCloseAction);
       begin
         if ghRC<>0 then
         begin
           wglMakeCurrent(ghDC,0);
           wglDeleteContext(ghRC);
         end;
         if ghDC<>0 then
           ReleaseDC(Handle, ghDC);
       end;

А теперь, давайте уже что-нибудь нарисуем!

Пример

Давайте нарисуем сферу, а потом заставим её крутиться. Итак, всё, что нам понадобится - это форма и таймер.

Установите интервал таймера на 10 миллисекунд (нам этого будет вполне достаточно). Теперь скопируйте все представленные выше фрагменты кода в соответствующие места. В процедуре Timer1Timer напишите одну сточку: Draw(); (в Delphi без скобок).

Теперь нам осталось только что-нибудь нарисовать, т.е. отредактировать функцию Draw.

С++

      void TForm1.Draw()
       {
         glClear(GL_DEPTH_BUFFER_BIT xor GL_COLOR_BUFFER_BIT);
         GLUquadricObj *quadObj;
         quadObj:=gluNewQuadric();
         gluQuadricDrawStyle(quadObj, GLU_FILL);
         glColor3f(1,0,0);
         gluSphere(quadObj, 2,10,10);
           glRotatef(3, 0,1,0);
          gluDeleteQuadric(quadObj);
         SwapBuffers(ghDC);
       }

Delphi

      procedure TForm1.Draw;
       var 
        quadObj :GLUquadricObj;
       begin
         glClear(GL_DEPTH_BUFFER_BIT xor GL_COLOR_BUFFER_BIT);
         quadObj:=gluNewQuadric;
         gluQuadricDrawStyle(quadObj, GLU_FILL);
         glColor3f(1,0,0);
         gluSphere(quadObj, 2,10,10);
           glRotatef(3, 0,1,0);
         gluDeleteQuadric(quadObj);
          SwapBuffers(ghDC);
       end;

Всё, можно нажимать F9!!!

А теперь кое-что поясню (в процедуре Draw не встретилось ни одной знакомой строчки).

glClear сбрасывает значения всего перечисленного в качестве параметров (в нашем случае очищает буфер цвета и буфер глубины). Этой процедуре передавать много всяких буферов для очистки, но лично я использую только 3: GL_DEPTH_BUFFER_BIT, GL_COLOR_BUFFER_BIT, иногда GL_STENCIL_BUFFER_BIT (буфер трафарета).

glColor устанавливает цвет фигуры. Существует следующий синтаксис как для glColor, так и для других функций OpenGL:

gl<name>[n][type]

Поясняю, каждая функция OpenGL начинается с префикса . Далее следует название функции. После названия - количество параметров (если функция определена для разного кол-ва параметров). И, наконец, переменными какого типа являются параметры:

  • b - GLbyte байт
  • s - GLshort короткое целое
  • i - GLint целое
  • f - GLfloat дробное
  • d - GLdouble дробное с двойной точностью
  • ub - GLubyte беззнаковый байт
  • us - GLushort беззнаковое короткое целое
  • ui - GLuint беззнаковое целое
  • v - вектор - массив из n элементов указанного типа

Итак, glColor3f означает, что цвет задаётся тремя компонентами типа GLfloat.

Для рисования сферы мы используем механизм из glu32.dll. Создаём объект типа GLUquadricObj и инициализируем его функцией gluNewQuadric. Далее устанавливаем стиль фигуры функцией gluQuadricDrawStyle (quadObj, GLU_FILL). Стиль может быть GLU_FILL, GLU_LINE   или GLU_POINT. Что каждый из них значит, проверьте сами.

gluSphere - делает из quadObj сферу. Три последних параметра - это радиус и количество разбиений поперёк и вдоль оси Z соответственно. Я взял маленькое число разбиений, чтобы было видно, что сфера крутится.

И не забудем освободить память, занимаемую под quadObj - gluDeleteQuadric(quadObj).

glRotatef - заставляет нашу сферу крутиться. О том, как это делается - в следующей статье.

И, наконец, SwapBuffers (ghDC) выводит всё на экран.

Архив с проектом примера на Delphi

Пока всё.

Луковкин Сергей,
Адрес электронной почты защищен от спам-ботов. Для просмотра адреса в вашем браузере должен быть включен Javascript.

Советую почитать:

  1. Тарасов И.А. <Основы программирования OpenGL: учебный курс>.
    Скачать её можно здесь: http://itsoft.miem.edu.ru/
  2. Тихомиров Ю. <Программирование трёхмерной графики>.
    Пишет мудрёно, но очень много полезной информации. Кстати, у него вышла вторая книга, может она ещё толковее и понятней?
  3. wat OpenGL: Основы http://www.gamedev.ru/coding/11203.shtml Просто, доступно, но для другой ИСР.