Конференция "Media" » Delphi XE2 - проблемы вывода OpenGL на Bitmap
 
  • Prok186 © (20.11.11 17:54) [0]
    Давно и довольно успешно (начиная с  Delphi7) работал фрагмент кода для вывода OpenGL графики на BitMap (этот фрагмент вызывается в ходе выполнения программы многократно, в цикле: из Bitmap строится AVI-файл-мультик):

    procedure TFormOpenGL.RenderToBitmap(const ABitmap: TBitmap);
    var FormatIndex: Integer;    PFD: TPixelFormatDescriptor;  lFDC: HDC;
    begin
    lFDC := ABitmap.Canvas.Handle;
    FillChar(PFD, SizeOf(PFD), 0);
      With PFD do begin
      nSize := SizeOf(PFD);   nVersion := 1;
      dwFlags := pfd_Draw_to_Bitmap or pfd_Support_OpenGL;
      iPixelType := pfd_Type_RGBA;
      cColorBits := 24;    cDepthBits := 32;
      iLayerType := pfd_Main_Plane;
      end;
    FormatIndex := ChoosePixelFormat(lFDC, @PFD);
    SetPixelFormat(lFDC, FormatIndex, @PFD);
    FGLContext := wglCreateContext(lFDC);
    wglMakeCurrent(lFDC, FGLContext);
    MyPaint(True);
    .............
    end;


    Недавно перешёл на Delphi XE2. При компиляции 32bit - по-прежнему всё ОК. А вот при создании приложения 64bit -при выводе на Bitmap иногда (!!), причём ни сразу вылезают ошибки с сообщением "External error 80000003". Вопросы:
    1) Это моя ошибка, или ошибка нового компилятора?
    2) Почему при замене строки
    lFDC := ABitmap.Canvas.Handle;

    на строку
    lFDC := ABitmap.Handle;

    пустой лист (и под Delphi7 тоже)?
    3) Можно ли использовать при выводе на Bitmap флаг pfd_DoubleBuffer и затем делать как обычно SwapBuffers(...)?
    4) Не ли примера вывода на Bitmap функциями OpenGL сохранения текстуры или чем то ещё более правильным и не таким глючным?
  • MBo © (20.11.11 18:03) [1]
    >Почему при замене строки\
    Потому что контекст и битмап - совсем не одно и то же.
  • Prok186 © (20.11.11 19:27) [2]

    > Потому что контекст и битмап - совсем не одно и то же

    И что вы хотели этим сказать? Я не понял...В коде выше должен получаться контекст для битмапа. Что не так? В большинстве примеров рендеринга в битмап пишут именно
    lFDC := ABitmap.Handle

  • MBo © (21.11.11 08:53) [3]
    HDC и HBITMAP - дескрипторы разных графических объектов Windows
    Дельфийский TBitmap - сложный объект, в котором есть и битмап, и свой ручной контекст.
  • Prok186 © (23.11.11 10:22) [4]
    Сформулирую вопрос по-другому. При компиляции под 32бита приложение всегда работает устойчиво, генерит неограниченное количество битмапов безо всяких ошибок. А при компиляции под 64бита оно тоже работает, но через 10-100генераций битмапов (в зависимости от их сложности - количества графических примитивов) вылезает пресловутая ошибка "External exception 80000003", причём после неё приложение полностью не закрыть [x]: останки его болтаются где-то в памяти, и жрут ресурсы (даже немного процессорного времени). Добивается только через TaskMngr.
    Доходило до того, что гас экран, и появлялось сообщение типа "Драйвер видео был повреждён и успешно восстановлен системой Windows" - работа продолжается.
    Вопрос 1: о чём это говорит? О том что неправильно работает менеджер памяти на 64бита - грузит что-то в память, отведённую под OpenGL? Может ли это быть следствие моей программной ошибки, или ошибки в самом дельфовом программном коде в принципе не должны приводить к подобному краху? Можно ли отловить подобные ошибки, скажем, Application.OnException (Try-Except-finally - не помогает)?
    Вопрос 2: можно ли рисовать ф-ми OpenGL не на капризном битмапе, а на невидимом окне, а потом импортировать его попиксельный образ в битмап, или - раз уж окно невидимо - на нём ничего не нарисуется?
  • CrytoGen (23.11.11 16:02) [5]
    можно рендерить в текстуру, потом её читать и переписывать в битмап. так даже быстрее должно быть.
  • Prok186 © (23.11.11 17:47) [6]

    > можно рендерить в текстуру, потом её читать и переписывать
    > в битмап. так даже быстрее должно быть.

    Понимаю, что так. Но как рендерить в текстуру? И как её потом прочитать в битмап? Хорошо бы примерчик кода, если есть
  • Prok186 © (23.11.11 17:53) [7]
    Чтобы рендерить даже в текстуру, надо (ИМХО) всё равно иметь какой-то контекст, на котором будет рисовать ОГЛ. Как этот контекст для невидимого объекта типа текстуры задать - не понимаю....
  • CrytoGen (23.11.11 18:13) [8]
    я уже раз приводил здесь этот код
    контекст безусловно нужен, но на нём ничего рисоваться не будет. если у вас нет окна - создайте невидимое.
    вообще похоже есть варианты контекста без окна, но могут быть проблемы.
    unit FrameBufferUnit;

    interface

    uses
     Windows, SysUtils, Graphics, GL, GLu, GLext;

    type
     TFrameBuffer = class
     private
       width   : Integer;
       height  : Integer;
       frameb  : GLuint;
       depthb  : GLuint;
       texture : GLuint;
       ftex    : boolean;
     public
       constructor Create(const nWidth,nHeight:Integer;ctexture:Integer = 0 );
       destructor Destroy;override;
       procedure BindFrameBuffer;
       procedure UnbindFrameBuffer;
       function GetBitmap:TBitmap;
       procedure AttachTexture;
       procedure DettachTexture;    
     end;

    implementation

    { TFrameBuffer }

    type
     TRGBLine = array [0..1023] of TRGBTriple;
     PRGBLine = ^TRGBLine;

    procedure TFrameBuffer.AttachTexture;
    begin
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE _2D,texture,0);
    end;

    procedure TFrameBuffer.BindFrameBuffer;
    begin
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,frameb);
    end;

    constructor TFrameBuffer.Create(const nWidth, nHeight: Integer;ctexture:Integer);
    var
     res   : Integer;
     ww,hh : Integer;
    begin
    Width:=nWidth;
    Height:=nHeight;
    { ww:=1;
    while ww<Width do
     ww:=ww*2;
    hh:=1;
    while hh<Height do
     hh:=hh*2;}

    ww:=Width;
    hh:=Height;
    ftex:=ctexture<>0;
    if ftex then
     texture:=ctexture
    else
     glGenTextures(1,@texture);
    glBindTexture(GL_TEXTURE_2D,texture);
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D,GL_GENERATE_MIPMAP,0);
    glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA8,ww,hh,0,GL_RGBA,GL_UNSIGNED_BYTE,nil);
    glBindTexture(GL_Texture_2D,0);
    glGenFramebuffersEXT(1,@frameb);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,frameb);
    glGenRenderbuffersEXT(1,@depthb);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,depthb);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT,GL_DEPTH_COMPONENT,width,height);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT,0);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE _2D,texture,0);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,GL_DEPTH_ATTACHMENT_EXT,GL_RENDE RBUFFER_EXT,depthb);
    res:=glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    if res<>GL_FRAMEBUFFER_COMPLETE_EXT then
     raise Exception.Create('frame buffer error');
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
    end;

    destructor TFrameBuffer.Destroy;
    begin
    DettachTexture;
    glDeleteFramebuffersEXT(1,@frameb);
    glDeleteRenderbuffersEXT(1,@depthb);
    if not ftex then
     glDeleteTextures(1,@texture);
    inherited;
    end;

    procedure TFrameBuffer.DettachTexture;
    begin
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,GL_COLOR_ATTACHMENT0_EXT,GL_TEXTURE _2D,0,0);
    end;

    function TFrameBuffer.GetBitmap: TBitmap;
    var
     buf : Pointer;
     l   : PRGBLine;
     b   : ^TRGBTriple;
     i,j : Integer;
     h,w : Integer;
    function PtrAllign(APointer:Pointer; bytes: cardinal):Pointer;
    begin
     Result := Ptr(cardinal(APointer) + (bytes-cardinal(APointer) mod bytes) mod bytes);
    end;
    begin
    w:=width;
    h:=height;
    result:=TBitmap.Create;
    result.PixelFormat:=pf24bit;
    result.Width:=w;
    result.Height:=h;
    GetMem(buf,w*h*3);
    glBindTexture(GL_TEXTURE_2D,texture);
    ZeroMemory(buf,w*h*3);
    glGetTexImage(GL_TEXTURE_2D,0,GL_RGB,GL_UNSIGNED_BYTE,buf);
    glBindTexture(GL_TEXTURE_2D,0);
    b:=buf;
    for i:=h-1 downto 0 do
    begin
      l:=result.ScanLine[i];
      for j:=0 to w-1 do
      begin
        l[j].rgbtBlue:=b.rgbtRed;
        l[j].rgbtGreen:=b.rgbtGreen;
        l[j].rgbtRed:=b.rgbtBlue;
        Inc(b);
      end;
      b:=PtrAllign(b,4);
    end;
    FreeMemory(buf);
    end;

    procedure TFrameBuffer.UnbindFrameBuffer;
    begin
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
    end;

    end.

  • Prok12 (23.11.11 22:12) [9]

    > я уже раз приводил здесь этот код

    Да, спасибо: я этот кусок кода уже поиском находил. Не смог применить: это ведь некий вспомогательный (универсальный) модуль создания текстуры, и потом копирования её в обычный битмап. Так? Но как вызывать процедуры этого модуля? Я спрашивал скорее про пример его применения -  в виде кода полной тестовой программы, которая рисует на текстуре заданных размеров, скажем, сферу, а затем запихивает рисунок с текстуры в битмап.
  • CrytoGen (24.11.11 10:31) [10]
    Вначале подключаете GL_EXT_FrameBufferObject. Затем примерно так
    function TScene.DrawToBitmap(ww,hh:Integer): TBitmap;
    var
     w,h : Integer;
     fb  : TFrameBuffer;
    begin
    w:=Width;
    h:=Height;
    Width:=ww;
    Height:=hh;
    UseContext;
    fb:=TFrameBuffer.Create(ww,hh);
    fb.BindFrameBuffer;
    Draw(False);
    Result:=fb.GetBitmap;
    fb.UnbindFrameBuffer;
    fb.Free;
    Width:=w;
    Height:=h;
    end;

  • Prok186 © (24.11.11 12:13) [11]
    Спасибо огромное! Буду пробовать. Я так понимаю, надо скачать библиотеки GL, GLu, GLext - в модуле OpenGL некоторых использованных процедур нет? Это не сложно. И....начинаю тупить:  
    fb:=TFrameBuffer.Create(ww,hh);


    ...не маловато параметров? Там же ещё
    ...;ctexture: Integer);



    > Вначале подключаете GL_EXT_FrameBufferObject
    этого вообще не понял: где это подключение?
  • Prok186 © (24.11.11 12:21) [12]
    ...и ещё глупый вопрос в догонку: не нашёл, где вызывается proc.AttachTexture и что такое UseContext - библиотечная функция?
  • han_malign (24.11.11 15:54) [13]
    возможно поможет вот это:
    ABitmap.Canvas.Lock;
    lFDC := ABitmap.Canvas.Handle;
     ...
    wglMakeCurrent(lFDC, FGLContext);
    //не обращаться ни к каким методам и свойствам ABitmap(которые явно или неявно вызывают FreeContext)
     ...
    wglMakeCurrent(0, 0);
    ABitmap.Canvas.Unlock;

    - либо ручками сделать "некапризный битмап"
    CreateCompatibleDC + CreateCompatibleBitmap/CreateDIBSection + SelectObject
  • CrytoGen (24.11.11 17:11) [14]
    я использовал библиотеку dot, её сейчас уже не найти.
    UseContext это просто wglMakeCurrent(DC, hrc);

    ctexture по умолчанию равен 0 смотрите в интерфейсной части модуля.

    AttachTexture наверное не пригодился.
    GL_EXT_FrameBufferObject обычно есть в модулях типа GLExt. В случае использования dot его подключение выглядело так
    Load_GL_EXT_framebuffer_object;
    В принципе могу dot выложить.
  • Prok186 © (24.11.11 21:09) [15]

    > - либо ручками сделать "некапризный битмап"
    > CreateCompatibleDC + CreateCompatibleBitmap/CreateDIBSection
    > + SelectObject

    С Lock-UnLock не получилось: всё равно программа терпит крах (не с первого битмапа, а с 10-го - 100-го) с ошибкой "External Exception 8000003". Но! Теперь после краха её хоть можно закрыть нормально [x], а без Lock в памяти болтались её "останки", которые добивались только через TaskMngr. Уже неплохо!
    Вариант с DIB наверно, самый правильный и простой, но у меня с ним не вышло. Код использовал такой:
    ........................................
    var DC: HDC;   Binf: TBitMapInfo;    Dib: HBitmap;   Bits: Pointer;
    begin
      With Binf.bmiHeader do begin
      biSize := SizeOf(TBitmapInfoHeader);
      biWidth := ABitmap.Width;   biHeight := ABitmap.Height;
      biPlanes := 1;    biBitCount := 24;
      biCompression := bi_RGB;
      biSizeImage := biWidth * biHeight * 3;
      end;
    DC := GetDC(ABitmap.Canvas.Handle);
    Dib := CreateDIBSection(DC, Binf, DIB_RGB_Colors, Bits, 0, 0);
    ReleaseDC(ABitmap.Canvas.Handle, DC);
    lFDC := CreateCompatibleDC(0);
    SelectObject(lFDC, Dib);
    {lFDC := ABitmap.Canvas.Handle;}    { Так было раньше }
    FillChar(PFD, SizeOf(PFD), 0);
      With PFD do begin
      nSize := SizeOf(PFD);    nVersion := 1;
      dwFlags := pfd_Draw_to_Bitmap or pfd_Generic_Accelerated or
       pfd_Support_OpenGL;
      iPixelType:= pfd_Type_RGBA;
      cColorBits:= 24;    cDepthBits:= 16;
      iLayerType:= pfd_Main_Plane;
      end;
    FormatIndex := ChoosePixelFormat(lFDC, @PFD);
    SetPixelFormat(lFDC, FormatIndex, @PFD);
    FGLContext := wglCreateContext(lFDC);
    wglMakeCurrent(lFDC, FGLContext);
    TestPaint;
    glFinish;
    ..................


    Получаю в итоге белый пустой Битмап. Что не так в этом коде? Нет ли правильного примера с DIB?
  • CrytoGen (24.11.11 22:44) [16]
    может wglDeleteContext?
  • Prok12 (25.11.11 01:01) [17]
    Вот такой код с DIB у меня заработал в маленькой тестовой программе:

    procedure TFormMain.RenderDIB_BtnClick(Sender: TObject);
    var hMemRC, hOldRC: HGLRC;       FormatIndex, W1, H1: Integer;
     pfd: TPixelFormatDescriptor;   poBits: Pointer;
     bmpAux, BBitmap: TBitmap;      bmiAux: BitmapInfo;
     hhDc, hDIB, hMemDC, hOldDc: HDC;
    const BmpFileName = 'D:\FileOpenGL_DIB.bmp';
    begin
    { 1. Determine the DIB size }
    W1 := SpinEditR2BWidth.Value;    H1 := SpinEditR2BHeight.Value;
    { 2. Create the DIB section }
      With bmiAux.bmiHeader do begin
      biSize := SizeOf(BitmapInfoHeader);
      biWidth := W1;     biHeight := H1;
      biPlanes := 1;     biBitCount := 24;
      biCompression := bi_RGB;
      biSizeImage := 3 * W1 * H1;
      end;
    hhDC := GetDC(Panel1.Handle);
    hDIB := CreateDIBSection(hhDc, bmiAux, DIB_RGB_Colors, poBits, 0, 0);
    ReleaseDC(Panel1.Handle, hhDc);
    { 3. Create memory DC and associate it with the DIB }
    hMemDC := CreateCompatibleDC(0);
      If hMemDC = 0 then begin
      DeleteObject(hMemDC);   ShowMessage('DIB: Err1');
      Exit
      end;
    SelectObject(hMemDC, hDIB);
    { 4. Setup memory DC's pixel }
    FillChar(pfd, SizeOf(pfd), 0);
      With pfd do begin
      nSize := SizeOf(pfd);   nVersion := 1;
      dwFlags := pfd_Draw_To_Bitmap or pfd_Support_OpenGL
        or pfd_Generic_Accelerated;
      iPixelType := pfd_Type_RGBA;
      cColorBits := bmiAux.bmiHeader.biBitCount;
      cDepthBits := 32;
      iLayerType := pfd_Main_Plane;
      end;
    FormatIndex := ChoosePixelFormat(hMemDC, @pfd);
      If not SetPixelFormat(hMemDC, FormatIndex, @pfd) then begin
      DeleteObject(hDIB);     DeleteDC(hMemDC);
      ShowMessage('
    DIB: Err2');
      Exit
      end;
    { 5. Create memory RC }
    hMemRC := wglCreateContext(hMemDC);
      If hMemRC = 0 then begin
      DeleteObject(hDIB);    DeleteDC(hMemDC);
      ShowMessage('
    DIB: Err3');
      Exit
      end;
    { 6. Store old DC and RC }
    hOldDc := wglGetCurrentDC;     hOldRC := wglGetCurrentContext;
    { 7. Make the memory RC current }
    wglMakeCurrent(hMemDC, hMemRC);
    { 8. Draw the scene }
    TestPaint;
    glFinish;   { Дадим закончиться фун-ям OpenGL- иначе Access Violation }
    { 9. Release memory RC and restore the old DC and RC }
    wglMakeCurrent(0, 0);
    wglDeleteContext(hMemRC);
    wglMakeCurrent(hOldDc, hOldRC);
    { 10. Copy and save the image }
    bmpAux := TBitmap.Create;
    bmpAux.Width := W1;   bmpAux.Height := H1;
    StretchDIBits(bmpAux.Canvas.Handle, 0, 0, W1, H1,
    0, 0, W1, H1, poBits, bmiAux, DIB_RGB_Colors, SRCCopy);
    DeleteFile(BmpFileName);    { Удалим старый файл }
    bmpAux.SaveToFile(BmpFileName);
    { 11. Release memory }
    DeleteObject(hDIB);    DeleteDC(hMemDC);
    {... Прочитаем картинку из записанного файла, и выведем на форму .....}
    BBitmap := TBitmap.Create;
      With BBitmap do begin
      PixelFormat := pf24bit;
      Width := W1;    Height := H1;
      LoadFromFile(BmpFileName);
      end;
    With ImageGL.Canvas do StretchDraw(ClipRect, BBitmap);
    BBitmap.Free;
    ShowMessage('
    OK 2 !');
    end;


    Завтра попробую подключить его - т.е. вариант с DIB - к "большой" программе.
  • han_malign (25.11.11 09:51) [18]

    > biSizeImage := biWidth * biHeight * 3;

      //"скан-линия" выравнивается на размер DWORD
      cbPitch:= ((biWidth * biBitCount + 31) and not $1F{31}) div 8;
      biSizeImage := cbPitch * biHeight;
      //!!!biClrUsed и biClrImportant - и в полноцветном варианте имеют смысл(предпочтительная палитра)
      biClrUsed:= 0;
      biClrImportant:= 0;


    - а лучше не мелочиться и использовать глубину цвета 32-бита(в отличие от 24- bpp - поддерживаемую аппаратно)...
    - более предпочтителен Compatible Bitmap, но GetDIBBits - может затратным оказаться...
    (Вечная дилемма - с одной стороны рендеринг быстрее на видеопамяти, с другой - чтение видеопамяти - очень медленное(т.к. все оптимизировано только на вывод) )

    DC := GetDC(ABitmap.Canvas.Handle);
    - вообще бред - у GetDC - на входе HWND, а не HDC(в отличие от CreateCompatibleDC) и даже не HBITMAP...
    CreateDBISection - прекрасно на hMemDC(:= CreateCompatibleDC(0)) отрабатывает... Но если уж очень хочется - то GetDC(0) (весь экран)...

    З.Ы. Если тебе лень напрямую данные(TBitmapFileHeader + bmiAux + poBits) в файл писать, то StretchDIBits - лучше заменить на BitBlt/StretchBlt, т.к. DC->DC - операции обычно быстрее...
  • Prok186 © (25.11.11 13:10) [19]
    Увы... В "большой" программе, где отрисовка на невидимый Bitmap или даже на DIB происходит в цикле многократно, рано или поздно ошибка External Exception 800003 всё равно выскакивает. Причём только при компиляции Delphi XE2 под 64бит. В то же время, на видимую форму точно такая же картинка выводится прекрасно хоть тысячу раз в процессе счёта. Видимо, это всё-таки баг нового 64-битного компилятора (или менеджера памяти?) - потому как при компиляции под 32бит всегда всё ОК. Нет ли примера, как выводить без флага Draw_To_Bitmap например, на панель на невидимой форме, а потом уже считывать биты цвета с этой панели (невидимой формы) в необходимый битмап? С текстурами уж больно мудрёно...
  • Prok186 © (25.11.11 16:29) [20]

    > AttachTexture наверное не пригодился.
    > GL_EXT_FrameBufferObject обычно есть в модулях типа GLExt.
    >  В случае использования dot его подключение выглядело так
    > Load_GL_EXT_framebuffer_object;
    > В принципе могу dot выложить.

    С рисованием на невидимой текстуре - а"ля подложке на невидимой форме для накапливания рисунка - разобрался: в тестовой программе под 32бита идёт нормально! Обошёлся без внешних библиотек (только дельфовая OpenGL - GLu, GLut, GLext не понадобились): все необходимые константы и gl-функции работы с текстурами подгружаю сам (код новой FrameBufferUnit выложу, когда доделаю). Вызывать AttachTexture всё-таки надо (!): сразу после создания FrameBuffer (иначе на выходе битмам - чёрный квадрат Малевича). Одно НО. Завершающая процедура копирования битов с текстуры в битмап написана неудачно: не учтено, что Delphi для 64бит запрещает записывать указатели в целые переменные, а там похоже это есть - чувствую, но найти не могу (в итоге под 64бит - AccessViolation):
    function TFrameBuffer.GetBitmap: TBitmap;
    var buf: Pointer;   l: PRGBLine;   b: ^TRGBTriple;   i,j, h,w: Integer;
      function PtrAllign(APointer: Pointer; bytes: cardinal): Pointer;
      begin
      Result := Ptr(cardinal(APointer) +
       (bytes - cardinal(APointer) mod bytes) mod bytes);
      end;
    begin
    w := width;   h := height;
    result := TBitmap.Create;
    result.PixelFormat := pf24bit;
    result.Width := w;    result.Height := h;
    GetMem(buf, w*h*3);
    glBindTexture(GL_TEXTURE_2D, texture);
    ZeroMemory(buf, w*h*3);
    glGetTexImage(GL_TEXTURE_2D,0, GL_RGB,GL_UNSIGNED_BYTE, buf);
    glBindTexture(GL_TEXTURE_2D,0);
    b := buf;
      for i := h-1 downto 0 do begin
      l := result.ScanLine[i];
         for j:=0 to w-1 do begin
         l[j].rgbtBlue := b.rgbtRed;
         l[j].rgbtGreen := b.rgbtGreen;
         l[j].rgbtRed := b.rgbtBlue;
         Inc(b);
         end;
      b := PtrAllign(b,4);
      end;
    FreeMemory(buf);
    end;


    Думаю, ошибка во встроенной ф-ии PtrAllign. Поможете найти?
  • CrytoGen (25.11.11 17:22) [21]
  • Prok186 © (25.11.11 17:43) [22]

    > Замените Cardinal на NativeInt

    Не помогает! Это первое, что я сделал. В этом коде вообще довольно вольно обходятся с указателями - я стараюсь избегать такого заумного и глючного кода. Нельзя ли как-то по-простому текстуру скопировать в битмап: осталось то только это для 64бит? Быстродействие для меня ни малейшей роли не играет: просто в двойном цикле. Как? Там же, насколько помню, и цвета и строки перевёрнуты как-то?
  • Prok186 © (26.11.11 12:16) [23]
    С этим уже тоже разобрался. Вот работающий код под 32 и 64 бита.
    function TFrameBuffer.GetBitmap: TBitmap;
    var tbuf,buf,Pline: Pointer;   i,h,w: Integer;    waserror: boolean;
    begin
    waserror := TRUE;    buf := nil;
      Try
      w := width;   h := height;
      Result := TBitmap.Create;
      Result.PixelFormat := pf24bit;
      Result.Width := width;    Result.Height := height;
      GetMem(buf, w*h*3);
      glPixelStorei(GL_PACK_ALIGNMENT, 1);
      glBindTexture(GL_TEXTURE_2D, texture);
      glGetTexImage(GL_TEXTURE_2D, 0, GL_BGR, GL_UNSIGNED_BYTE, buf);
      tbuf := buf;
         For i := h-1 downto 0 do begin
         Pline := Result.ScanLine[i];
         Move(tbuf^, Pline^, w*3);
         Inc(NativeInt(tbuf), w*3);
         end;
      waserror := FALSE;
      finally
      If waserror then FreeAndNil(Result);
      FreeMemory(buf);
      end;
    end;


    Осталась одна (?) проблема. Рендеринг в текстуру работает у меня только на одном компе (карта NVIDIA GTS450). На пяти (!!) других не выходит подгрузить вспомогательные процедуры работы с текстурой и FrameBuffer. Пытаюсь подгружать функцией:
    function dglGetProcAddress(ProcName: PAnsiChar;
    LibHandle: Pointer = nil): Pointer;
    begin
    If LibHandle = nil then LibHandle := GL_LibHandle;
    Result := GetProcAddress(HMODULE(LibHandle), ProcName);
    If result <> nil then exit;
    If Addr(wglGetProcAddress) <> nil then
     Result := wglGetProcAddress(ProcName);
    If Result = nil then ShowMessage('No Proc.ADDRES ' + ProcName);
    end;


    У меня задана LibHandle=nil (возможно, это не верно, но на одном компе всё-таки работает). Задаю, скажем, второй параметр ProcName='glGenTextures' ---> получаю фиг (точнее nil). Как правильно подгружать процедуры, связанные с видеокартой?
  • Prok186 © (26.11.11 14:13) [24]
    Похоже, понятно... Функции типа glFramebufferTexture2DEXT и др. для работы с FrameBufferObject (FBO) большинством современных видеокарт не поддерживаются. Мёртвые они... Скачал пример-EXE для работы с FBO - идёт опять же только на одном компе. Так что, рендеринг в текстуру - не выходит.
  • Prok186 © (26.11.11 16:02) [25]
    В догонку (общаюсь похоже сам с собой)... Вот теперь к своему изумлению выяснил следующее. Не всякая поверхность строится нормально - некоторые сразу дают ошибку External Exception 8000003.
    Итак, тестовая поверхность, заданная просто на сетке.
    Индекс i = 0.....1000 Индекс j = 0.....1200
    Координата X = i*0.02 Координата Y = j*0.02
    Т.е, просто декартова сетка (ради теста).
    Сама поверхность:
    Z = 0.2 + 0.2*sin((i+j) / 30) - слабо волнистая - строится нормально
    Z = 0.2 + 0.2*sin((i+j) / 5) - сильно волнистая - не строится вообще
    Нормали к поверхности задавать или нет - без разницы.
    Пробовал в каждой ячейке сетки строить пару треугольников (понятно как) - режим gl_Triangles
    Пробовал так же полосами - режим gl_Quad_strip. Тоже без разницы.
    Что это за бред такой? Не хватает какого-то буфера глубины? Или это глюк нового компилятора??
  • CrytoGen (26.11.11 21:15) [26]
    Они не мёртвые, скорее всего у вас драйвера от Микрософта - в них нет поддержки OpenGL.
  • Prok12 (26.11.11 21:21) [27]

    > Они не мёртвые, скорее всего у вас драйвера от Микрософта
    > - в них нет поддержки OpenGL.

    А вообще - это "продвинутая" технология - Frame Buffer Object, или она в дальнейшем сойдёт на нет и поддерживаться не будет, как считаете?
  • Prok12 (27.11.11 09:10) [28]
    Вот что с текстурами на сег. день вышло:
    1) Полностью рендеринг в них работает только на одном из 7 компов (не густо!)
    2) На одних компах подгружаемые функции-расширения OpenGL не находятся функцией типа ....:=wglGetProcAddress('glFramebufferTexture2DEXT') - это стандартная функция поиска в библиотеках dll .  Драйверы, скачанные с офиц. сайтов видеокарт, не всегда помогают...
    3) На другом компе (ноутбук, карта NVIDIA 9600M GT) функции подгрузились успешно, но в самом начале инициализации работы с FrameBuffer стоят 2 строки:
    glGenRenderbuffersEXT(1, @depthb);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthb);


    Затыкается уже на второй (первая проходит)... Ошибку не выдаёт, но функция проверки завершения:
    Res := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);


    не даёт желаемое
    Res = GL_FRAMEBUFFER_COMPLETE_EXT

    (только если вторую строку закомментировать - ОК, все операции инициализации буферов идут до конца)
    У меня задано:
    GL_RENDERBUFFER_EXT = $8D41;
    ......
    type TglBindRenderbufferEXT = procedure(target: GLenum;
     renderbuffer: GLuint); stdcall;;
    ...................
    var glBindRenderbufferEXT: TglBindRenderbufferEXT;

  • CrytoGen (27.11.11 20:44) [29]
    У меня на всех компьютерах работает.
    Из последних карт: GT 9600M, GTX460, GTX550, всякие GTS 250 и так далее, из AMD Radeon 2600 XT
  • Prok12 (28.11.11 00:59) [30]
    Может я как-то не так подгружаю библиотеки? Вот начало Вашей слегка переделанной Unit:
    unit FrameBufferUnit;

    interface

    uses Windows, SysUtils, Graphics, OpenGL, Dialogs;

    type TFrameBuffer = class
    private
      width, height: Integer;
      frameb, depthb, texture: GLuint;
      ftex: boolean;
    public
      constructor Create(const nWidth, nHeight: Integer;
       ctexture: Integer = 0);
      destructor Destroy;override;
      procedure BindFrameBuffer;
      procedure UnbindFrameBuffer;
      function GetBitmap:TBitmap;
      procedure AttachTexture;
      procedure DettachTexture;
    end;
    {---------------------------------------------------------------------}
    procedure glGenTextures(n: GLsizei; textures: PGLuint);
    stdcall;   external 'opengl32.dll';
    procedure glBindTexture(target: GLenum; texture: GLuint);
    stdcall;   external 'opengl32.dll';
    procedure glDeleteTextures(n: GLsizei;
     const textures: PGLuint); stdcall;  external 'opengl32.dll';
    {---------------------------------------------------------------------}
    implementation  { TFrameBuffer }

    type TglFramebufferTexture2DEXT = procedure(target: GLenum;
    attachment: GLenum; textarget: GLenum;
     texture: GLuint; level: GLint);  stdcall;
    TglBindFramebufferEXT = procedure(target: GLenum;
     framebuffer: GLuint); stdcall;
    TglGenFramebuffersEXT = procedure(n: GLsizei;
     framebuffers: PGLuint); stdcall;
    TglGenRenderbuffersEXT = procedure(n: GLsizei;
     renderbuffers: PGLuint); stdcall;
    TglBindRenderbufferEXT = procedure(target: GLenum;
     renderbuffer: GLuint); stdcall;
    TglRenderbufferStorageEXT = procedure(target: GLenum;
     internalformat: GLenum; width: GLsizei; height: GLsizei); stdcall;
    TglFramebufferRenderbufferEXT = procedure(target: GLenum; attachment:
     GLenum; renderbuffertarget: GLenum; renderbuffer: GLuint); stdcall;
    TglCheckFramebufferStatusEXT = function(target:GLenum):GLenum; stdcall;
    TglDeleteFramebuffersEXT = procedure(n: GLsizei;
     const framebuffers: PGLuint); stdcall;
    TglDeleteRenderbuffersEXT = procedure(n: GLsizei;
     const renderbuffers: PGLuint); stdcall;

    var glFramebufferTexture2DEXT: TglFramebufferTexture2DEXT;
       glBindFramebufferEXT: TglBindFramebufferEXT;
       glGenFramebuffersEXT: TglGenFramebuffersEXT;
       glGenRenderbuffersEXT: TglGenRenderbuffersEXT;
       glBindRenderbufferEXT: TglBindRenderbufferEXT;
       glRenderbufferStorageEXT: TglRenderbufferStorageEXT;
       glFramebufferRenderbufferEXT: TglFramebufferRenderbufferEXT;
       glCheckFramebufferStatusEXT: TglCheckFramebufferStatusEXT;
       glDeleteFramebuffersEXT: TglDeleteFramebuffersEXT;
       glDeleteRenderbuffersEXT: TglDeleteRenderbuffersEXT;

    const
     GL_FRAMEBUFFER_EXT = $8D40;
     GL_COLOR_ATTACHMENT0_EXT = $8CE0;
     GL_GENERATE_MIPMAP = $8191;
     GL_RGBA8 = $8058;
     GL_RGB8 = $8051;
     GL_RENDERBUFFER_EXT = $8D41;
     GL_DEPTH_ATTACHMENT_EXT = $8D00;
     GL_FRAMEBUFFER_COMPLETE_EXT = $8CD5;
     GL_BGR = $80E0;
     GL_STENCIL_ATTACHMENT_EXT = $8D20;
     GL_DEPTH_COMPONENT24 = $81A6;

    var GL_LibHandle: Pointer = nil;

    {=====================================================================}
    function dglGetProcAddress(ProcName: PAnsiChar;
    LibHandle: Pointer = nil): Pointer;
    begin
    If LibHandle = nil then LibHandle := GL_LibHandle;
    Result := GetProcAddress(HMODULE(LibHandle), ProcName);
    If result <> nil then exit;
    If Addr(wglGetProcAddress) <> nil then
     Result := wglGetProcAddress(ProcName);
    If Result = nil then ShowMessage('No Proc.ADDRES ' + ProcName);
    end;

    {=====================================================================}
    procedure TFrameBuffer.AttachTexture;
    begin
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
    GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);
    end;

    {=====================================================================}
    procedure TFrameBuffer.BindFrameBuffer;
    begin
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameb);
    end;

    {=====================================================================}
    constructor TFrameBuffer.Create(const nWidth, nHeight: Integer;
    ctexture: Integer);
    var Res: Integer;
    begin
    glFramebufferTexture2DEXT :=
     dglGetProcAddress(PAnsiChar('glFramebufferTexture2DEXT'));
    glBindFramebufferEXT := dglGetProcAddress('glBindFramebufferEXT');
    glGenFramebuffersEXT := dglGetProcAddress('glGenFramebuffersEXT');
    glGenRenderbuffersEXT := dglGetProcAddress('glGenRenderbuffersEXT');
    glBindRenderbufferEXT := dglGetProcAddress('glBindRenderbufferEXT');
    glRenderbufferStorageEXT :=
    dglGetProcAddress('glRenderbufferStorageEXT');
    glFramebufferRenderbufferEXT :=
    dglGetProcAddress('glFramebufferRenderbufferEXT');
    glCheckFramebufferStatusEXT :=
    dglGetProcAddress('glCheckFramebufferStatusEXT');
    glDeleteFramebuffersEXT:= dglGetProcAddress('glDeleteFramebuffersEXT');
    glDeleteRenderbuffersEXT :=
    dglGetProcAddress('glDeleteRenderbuffersEXT');
    {.....................................................................}
    Width := nWidth;   Height:=nHeight;
    ftex := ctexture <> 0;
    If ftex then texture := ctexture else glGenTextures(1, @texture);
    glBindTexture(GL_TEXTURE_2D,texture);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
    GL_LINEAR_MIPMAP_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_GENERATE_MIPMAP,0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height,
    0, GL_RGBA, GL_UNSIGNED_BYTE, nil);
    glBindTexture(GL_Texture_2D, 0);
    {.....................................................................}
    glGenRenderbuffersEXT(1, @depthb);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthb);
    glGenFramebuffersEXT(1, @frameb);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, frameb);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT,
    width, height);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT,
    GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, texture, 0);
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT,
    GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthb);
    {.....................................................................}
    Res := glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
    If Res <> GL_FRAMEBUFFER_COMPLETE_EXT then
    raise Exception.Create('Frame Buffer error');
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    { Присоединяем текстуру }
    AttachTexture;
    end;

    Спотыкается почти а всех компах именно на glBindRenderbufferEXT уже в Constructor, даже когда находит все необходимые функции-расширения (на карте 9600M GT)
  • CrytoGen (28.11.11 07:33) [31]
    контекст выбран?
  • Prok12 (28.11.11 08:35) [32]

    > контекст выбран?

    Конечно! Без его выбора программа тоже не найдёт функции-расширения OpenGL. Да и, как говорил, на одном компе всё идёт...

    {= Получение контекста для вывода ф-ми OpenGL на экран ========}
    function TFormMain.GetScreenContext(const CanHandle: THandle): HGLRC;
    var nPixelFormat: Integer;   PFD: TPixelFormatDescriptor;
    begin          { См. Краснов, Ex25 - MDI, Глава 1 }
    {................. Устанавливаем формат пикселей .....................}
    FillChar(PFD, SizeOf(PFD), 0);
      With PFD do begin
      nSize := SizeOf(PFD);   nVersion := 1;
      dwFlags := pfd_Draw_to_Window or pfd_Generic_Accelerated or
        pfd_Support_OpenGL or pfd_DoubleBuffer;
      iPixelType := pfd_Type_RGBA;
      cColorBits := 24;      cDepthBits := 32;
      iLayerType := pfd_Main_Plane;
      end;
    nPixelFormat := ChoosePixelFormat(CanHandle, @PFD);
    SetPixelFormat(CanHandle, nPixelFormat, @PFD);
    Result := wglCreateContext(CanHandle);   { Rendering Context }
    end;

    {=============================================}
    function TFormMain.DrawToText(ww,hh: Integer): TBitmap;
    var FrameBuffer: TFrameBuffer;
    var MyDC: HDC;      MainGLContext: HGLRC;
    begin
    Hide;       { Скрываем форму - чтобы рисовать на невидимом объекте }
    MyDC := GetDC(Panel1.Handle);
    MainGLContext := GetScreenContext(MyDC);
    wglMakeCurrent(MyDC, MainGLContext);
    FrameBuffer := TFrameBuffer.Create(ww, hh);
    FrameBuffer.BindFrameBuffer;
    TestPaint;   SwapBuffers(MyDC);
    glFinish;
    Result := FrameBuffer.GetBitmap;
    FrameBuffer.UnbindFrameBuffer;
    FrameBuffer.Free;
    Sleep(300);  Show;   { Снова показываем форму }
    end;

  • Prok186 © (28.11.11 13:34) [33]
    Дополнение к сообщению от 26.11.11 16:02
    Говорил там о совершенно бредовой ошибке: успех вывода на битмап (и DIB тоже) зависит от того, насколько сложная поверхность рисуется. Ну не бред ли? Этот баг проявляется только при компиляции под 64бит.
    Всё-таки сделал отдельный тестовый проект и выложил на файлообменник.
    1) http://exfile.ru/232178   - полная версия, где для картинки с синусами есть нормали, блеск, можно повращать и помасштабировать её обычными средствами (Ctrl-Shifr-Alt-mouse). Там в основной форме 2 кнопки. Первая просто рисует графику OpenGL на ещё одну форму. Вторая (справа) рисует, а потом рендерит в битмап (DIB) и сохраняет в файл. Успех работы второй зависит от значения DENOMINATOR, выбираемого на форме - это знаменатель в формуле с синусом, определяющей поверхность. Под 32бит идёт всё. Под 64бит идёт только при Denominator >100: когда "волны" синуса не сильно затеняют друг-друга.
    2) http://exfile.ru/232180   - усечённая версия: кода меньше, с ней проще работать, анализировать - картинки не столь красивы, неподвижны, но баги те же (на любом компе).
    Привожу там полные тексты PAS и все EXE (64bit и 32bit). Всё делалось под Delphi XE2 Update2 (сборка 4316).
    Похоже, у Embarcadero возникла проблема с буфером глубины именно для DIB или Bitmap под 64бит.
  • CrytoGen (29.11.11 21:26) [34]
    Начал переписывать под xe2 64 bit. Сам пока заткнулся на d3d, но нашёл одну возможную проблему.
    Покажите ваш прототип  wglGetProcAddress
  • Prok12 (30.11.11 00:01) [35]

    > Покажите ваш прототип  wglGetProcAddress

    Не совсем понимаю, о чём речь?  У меня с текстурами сейчас по-прежнему - на одном компе идёт выложенный выше код. На другом - никак не проходит пресловутая ф-я
    glGenRenderbuffersEXT(1, @depthb);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthb);


    Причём все остальные функции - расширения OpenGL идут нормально...
    Не могли бы Вы выложить на какой-то файлообменник полностью код примера с FBO и необходимыми библиотеками?
    Кстати, вот здесь выложил совсем укороченный пример кода с OpenGL и Bitmap, который идёт под 32бита, но не идёт под 64  http://exfile.ru/232453   Там опять же - нажать правую кнопку - откроется окно с "синусоидой". При закрытии его - должен пойти рендеринг в битмап (DIB)...   Но на 64битах он пойдёт только если периодов синусоиды немного: Denominator=100. При 20 - крах. Сам код там уже довольно простой, без арифметики: только OpenGL .
 
Конференция "Media" » Delphi XE2 - проблемы вывода OpenGL на Bitmap
Есть новые Нет новых   [120135   +120][b:0][p:0.007]