На главную
ПРАВИЛА FAQ Помощь Участники Календарь Избранное DigiMania RSS
msm.ru
! Правила раздела "Программирование графики"
1) Данный раздел предназначен для обсуждения проблем, возникающих при программировании задач, связанных с чтением, сохранением, обработкой, созданием, отрисовкой графической информации (в том числе - 3D [OpenGL, Direct3D] и анимации [в т.ч. VFW, DirectShow, OpenDML]).
Флэш обсуждают здесь!.

2) Если вы хотите получить совет для конкретной платформы/языка программирования, обязательно укажите их в вопросе.

3) Уважаемые новички! Мы приветствуем Ваше желание научить всех посетителей раздела правильному программированию. Но огромная просьба, перед тем, как писать поучения в старых (последний ответ - "старее" месяца, а особенно, если вопрошавший не появляется на форуме уже не первый месяц, в чем можно убедиться в его профиле) темах, хорошо подумать, будет ли кому-нибудь, кроме Вас cамих, это интересно.



Ваше мнение о модераторах: user posted imageBarazuk, user posted imageOpenGL, user posted imageMikle
Модераторы: OpenGL, barazuk, Mikle
  
> Перерисовка окна, C & OpenGL
    Когда я писал простые графические вещи - такие как рисование в окне, то функции рисования я размещал в обработчике сообщения WM_PAINT. Все прекрасно работало. Однако, возникла необходимость написания 3D графики. Выбрал OpenGL.

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

    Теперь в чем проблема : в описанных примерах запускается бесконечный цикл while, который шурует постоянное рисование и тем самым перегревая процессор.
    Так как графика у меня статическая и должна изменяться первое - по действию пользователя (например нажал на кнопку графический объет повернулся на угол и т.д) и второе - при перекрытии окон и изменении их размеров. Поэтому крутить бесконечный цикл - как-то не рационально.
    ExpandedWrap disabled
      void ThreedviewCreateDlg(HWND hwnd, HINSTANCE hinstance)
      {
         // ...
         // здесь создается диалоговое дочернее окно, в котором будет выводиться 3D графика
         //...
       
          //while(!e::threedviewdone) // Loop That Runs While threedviewdone=FALSE
          //{
          DrawGLScene();
          SwapBuffers(e::threedviewhdc);
          //}
      }
       
      BOOL DrawGLScene()
      {
          glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
          glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
          glLoadIdentity();
          glTranslatef(-1.5f,0.0f,-6.0f);
          glRotatef(e::threedviewrtri, 0.0f, 1.0f, 0.0f);
          GLUquadricObj *q;
          q = gluNewQuadric();
          glColor3f(0.0f, 0.0f, 1.0f);    
          gluCylinder(q, 1.0f, 1.0f, 3.0f, 32, 32);
          return TRUE;
      }
       
      LRESULT CALLBACK WndProcThreedview(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam)
      {
         // ...
          switch (message)
          {
       
          //case WM_PAINT:
          //    DrawGLScene();
          //    SwapBuffers(e::threedviewhdc);
          //    return 0;
       
          return DefWindowProc(hwnd, message, wparam, lparam);
      }


    Вопрос : Как определить, когда нужно перерисовать окно, какие обрабатывать сообщения ?
    Сообщение отредактировано: Mr.Brooks -
      Перерисовка по событию таймера решает проблему лишней перегрузки процессора.
      Вопрос решен.
        Цитата Mr.Brooks @
        Как определить, когда нужно перерисовать окно, какие обрабатывать сообщения ?

        Цитата Mr.Brooks @
        например нажал на кнопку графический объет повернулся на угол и т.д

        Ты сам же ответил на свой вопрос. И таймер здесь не нужен
        char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
        for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;


        user posted image Чат Исходников в СкайпеЧат Исходников в ДискордеНе проходим, а заходим!
          Цитата
          Ты сам же ответил на свой вопрос. И таймер здесь не нужен

          Когда мое приложение имеет фокус то таймер действительно не за чем, обрабатываются:
          1. WM_SIZE
          2. WM_MOVE - потому как не изменяя размеры можно вынести окно за границы desktop и вернуть затертое окно
          3. WM_KEYDOWN - это действия пользователя клавиатурой (типа поворот, приближение)
          4. WM_LBUTTONDOWN - это действия пользователя мышью (типа поворот, приближение)
          но при потере фокуса если переключиться на другое приложение и носить его над моим окном произойдет затирание, вот здесь я и включаю таймер
          ExpandedWrap disabled
                case WM_ACTIVATEAPP:
                    if (wparam)
                    {
                        ThreedviewRefresh();
                        KillTimer(hwnd, 1);
                    }
                    else
                    {
                        SetTimer(hwnd, 1, 10, 0);
                    }
                    return 0;
                case WM_TIMER:
                    if (wparam == 1)
                    {
                        ThreedviewRefresh();
                    }
                    return 0;

          Использование таймера для перерисовки позволяет отказаться от цикла while (картинка статическая fps-ы не нужны) и заметно уменьшить загрузку процессора.
          Сообщение отредактировано: Mr.Brooks -
            А это нормально вообще - рисовать (переделывать=пересчитывать=...) сцену много раз в секунду, ежели на ней ничего не меняется? Ненужный же напряг граф. проц-а! Или как?..
              Цитата
              А это нормально вообще - рисовать (переделывать=пересчитывать=...) сцену много раз в секунду, ежели на ней ничего не меняется

              Мне тоже это не нравится. Хорошо, тогда вопрос, а как запомнить то, что было нарисовано в окне и как это потом отрисовать, если окно было перекрыто другим?
              Сообщение отредактировано: Mr.Brooks -
                В OpenGL всё (по умолчанию) рисуется в буфер, кой потом быстро кидается граф. проц-ром в память/на экран. Если вы озаботитесь, чтобы у вас в обоих буферах нарисована была одна и та же сцена, то нужно лишь будет "менять" активность буферов (wglSwapBuffers и т.п.), что сделает почти мгновенным переброс=перерисовку картинки на экране. Для эксперимента, попробуйте:
                1.Нарисовать сцену.
                2.Поменять буферы.
                3.Нарисовать её же.
                4.При всяком переносе/перекрытии окна просто меняйте буферы.

                П.С. в идеале бы, конечно, как-то сказать на шаге №3, чтобы перекинуть готовое в текущий буфер, но как сделать сие (и можно ли?) не знаю.
                  Попробовал

                  вместо
                  ExpandedWrap disabled
                       case WM_ACTIVATEAPP:
                           if (wparam)
                           {
                               ThreedviewRefresh();
                               KillTimer(hwnd, 1);
                           }
                           else
                           {
                               SetTimer(hwnd, 1, 10, 0);
                           }
                           return 0;
                       case WM_TIMER:
                           if (wparam == 1)
                           {
                               ThreedviewRefresh(); // где рисование + копирование с буфера
                           }
                           return 0;


                  сделал
                  ExpandedWrap disabled
                       case WM_ACTIVATEAPP:
                           if (wparam)
                           {
                               ThreedviewRefresh();
                               KillTimer(hwnd, 1);
                           }
                           else
                           {
                               SetTimer(hwnd, 1, 10, 0);
                           }
                           return 0;
                       case WM_TIMER:
                           if (wparam == 1)
                           {
                               SwapBuffers(e::threedviewhdc); // где только копирование с буфера
                           }
                           return 0;


                  разницы не заметил

                  Возможно, потому что у меня нарисован только один цилиндр и разница наверное будет видна когда на сцене будет пару сотен фигур.

                  Идею с таймером оставлю, она мне нравится, во-первых не надо заморочиваться что делает пользователь, (если бы было такое сообщение WM_ОКНО_ПЕРЕКРЫТО_ДРУГИМ_ОКНОМ то обработал бы его, а так как узнать что окно перекрыто, хуки ставить - закопаюсь...),
                  во-вторых ситуация когда пользователь работает с одним приложением и временно переключается на другое - ни есть основной способ работы.
                  Это так сказать временно - или музыку включил или калькулятором воспользовался и потом вернулся.

                  Но за совет спасибо.
                    1.Наверное заметнее=правильнее будет, если не просто пара сотен, а пара сотен тысяч! :rolleyes:
                    2.Все перекрытия (освобождения открытых кусков областей) обработаются WM_PAINT'ом. Я давно хотел написать прожку, чтобы она в фоновом режиме поверх всех окон таскала (аки шарик пинг-понга) своё маленькое окошко (для отладки отрисовки/перерисовки других окон=приложений), но так руки и не дошли (да и чего-то ГОСТ'овского хотелось... :yes-sad: ).
                      Цитата Mr.Brooks @
                      Использование таймера для перерисовки позволяет отказаться от цикла while (картинка статическая fps-ы не нужны) и заметно уменьшить загрузку процессора.

                      Тем более неправильный подход. Я бы даже сказал -- костыль. Потому что правильно это делается двойной буферизацией. То, что озвучил выше Славян. Только он не довёл идею до самого главного, отрисовку со вторичного кадрового буфера следует производить в обработчике WM_PAINT.
                      char *out = "|*0>78-,+<|"; size_t cc = char_traits<char>::length(out);
                      for (size_t i=0;i<cc;i++){cout<<static_cast<char>((out[i]^89));}cout<<endl;


                      user posted image Чат Исходников в СкайпеЧат Исходников в ДискордеНе проходим, а заходим!
                        Да, B.V., у меня как-то так и было:
                        ExpandedWrap disabled
                          void __fastcall WMMyPaint(Messages::TWMPaint &message)
                              {
                                  if( !message.DC ) // HDC==0 ?
                                  {
                                  PAINTSTRUCT PS; // фикция какая-то
                                  HWND hwnd;
                                  ::BeginPaint( hwnd=Handle, &PS);
                                  ::EndPaint( hwnd, &PS);
                                  }
                                  message.Result = 0;     // мы - самые быстрые
                              }
                          virtual void __fastcall Dispatch(void *message) // 2009-01-23
                              {
                                  if( ((PMessage)message)->Msg==WM_ERASEBKGND && traek.v )
                                  WMEraseBkgnd( (Messages::TWMEraseBkgnd *)message );
                                  else
                                  if( ((PMessage)message)->Msg==WM_PAINT )
                                  {
                                      WMMyPaint( *((Messages::TWMPaint *)message) );
                                      if( ... )
                                      if( ghDC && ghRC ) wglSwapBuffers( ghDC );
                                  }else TComponent::Dispatch( message );
                              }
                          А почему нельзя просто использовать обсервер? Подписываешься на значение рендером, значение изменяется - картинка перерисовывается. А ещё лучше обсервером с троттлингом в несколько мс, таким образом ререндер будет происходить не чаще чем раз в X мс. Т.е. кумулятивные изменения не будут заставлять несколько раз перерисовывать всё.

                          Добавлено
                          По этим приниципам живёт фронтенд. А некоторые либы просто делают дифф состояний и на основе этого строят виртуальное дерево перерисовки, понимая что и где надо перерисовать.
                          user posted image
                            Цитата
                            А почему нельзя просто использовать обсервер

                            Что это и как это сделать? Где можно посмотреть примеры?
                              user posted image
                              1 пользователей читают эту тему (1 гостей и 0 скрытых пользователей)
                              0 пользователей:


                              Рейтинг@Mail.ru
                              [ Script Execution time: 0,1197 ]   [ 14 queries used ]   [ Generated: 22.07.18, 01:18 GMT ]