Ответы пользователя по тегу OpenGL
  • Как влияет перекрыетие объектов на скорость отрисовки?

    @MarkusD
    все время мелю чепуху :)
    Абсолютно точным ответом на твой вопрос является: смотря как настроишь контекст.

    Здесь стоит припомнить некоторые базовые этапы графического конвейера.

    Первым этапом является обработка вершинных данных. Она выполняется для каждой вершины в примитиве. Отказаться от обработки какой-либо вершины пользователь не в праве. С другой стороны, обработка вершинных данных, обычно, выполняется быстро и вершин, обычно, куда меньше чем фрагментов.
    Поэтому, чтобы загружать GPU только правильными вершинам, эти вершины стоит правильно выбрать для передачи ему. За это должна отвечать стадия отсечения примитивов при подготовке кадра к презентации.

    Вторым важным сейчас этапом является обработка фрагментных данных. Растеризация, если проще.
    Для растеризации каждый примитив бьется на фрагменты, обработка которых происходит в индивидуальном порядке. На этапе растеризации от обработки фрагмента уже можно отказаться прямо в шейдере. Это может немного ускорить презентацию кадра.
    Обработка фрагментов разной геометрии никак не регламентирована и может происходить в произвольном порядке.
    При этом, если один фрагмент имеет отношение к нескольким участкам геометрии, он будет вычислен из каждого такого участка. Это называется Overdraw (Перекрытие).
    Перекрытие не только ведет к обработке ненужных, часто не видимых, фрагментов, но и приводит к графическим артефактам, вроде z-fighting. Разработчики стараются не допускать перекрытия геометрии через более агрессивное отсечение полигонов в модели и сортировку геометрии, а не моделей, в пространстве. Так же есть ряд эффективных техник работы с буфером глубины (например), которые позволяют практически полностью справиться перекрытием.

    Поэтому, при правильной настройке контекста, при выборе только важной для кадра геометрии, при работе с буфером глубины, при использовании Adaptive Shading и вовремя отбрасывая фрагменты из стадии растеризации скорость презентации будет выше чем без всех этих настроек.
    Если ты сделаешь у себя z-prepass, то сортировка геометрии по расстоянию до камеры для тебя станет избыточной.
    Ответ написан
    Комментировать
  • Как возможно задать параметры, а именно текстуру отдельно для каждого треугольника?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Коротко, тебе нужно усвоить вот этот урок.

    Развернуто:

    У тебя из атрибутов вершины сейчас есть только позиция. Уникальных вершин для твоей плоскости в таком случае будет всего 4. Под уникальностью у вершины понимается уникальность комбинации всех ее атрибутов.

    Сейчас тебе надо добавить атрибуты текстурных координат. Так как смешивание тебя не интересует, тебе необходим всего один набор текстурных координат на вершину. В таком случае уникальность твоей вершины будет обусловлена уже не просто ее позицией, но еще и уникальностью набора текстурных координат. Одновременно с этим новым правилом уникальности для вершины вводится и еще одно правило уникальности: уникальность по материалу.
    Когда ты добавляешь одну лишь текстуру, чтобы твоя отрисовка не встала на костыли тебе нужно поднять целую инфраструктуру обеспечения правильности ее работы.

    Под материалом понимается комплекс из программы и ресурсов, которые отображаются на геометрии.
    Материал отображает на геометрию заданный своими свойствами набор текстур и параметров. С точки зрения геометрии, материал - это DIP (Draw Indexed Primitive - твой glDrawElements). С точки зрения шейдера, материал - это комплекс из программы шейдера и набора входных/выходных параметров этой программы.
    Один материал одной геометрии - это один DIP с установкой набора текстур и параметров материала, а так же - с установкой нужного буфера индексов, вершин и инстансов.

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

    Таким образом ты реализуешь очень простую систему поддержки материалов и выполнишь свою задачу.
    Ответ написан
    Комментировать
  • Как нарисовать такую звезду openGL?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Фигура состоит из 10 вершин, соединенных 10 ребрами.
    На первом шаге тебе надо равномерно распределить по единичной окружности 10 вершин. Сделать это нужно в специально отведенном массиве вершин, т.к. индексированные примитивы в этом старом режиме вывести нельзя.
    На втором этапе переключаем контекст на вывод замкнутой линии (GL_LINE_LOOP) и выводим все 10 точек из массива вершин, но со смещением на 3 точки и по модулю 10.

    Псевдокод:
    void display()
    {
       glClear( GL_COLOR_BUFFER_BIT );
       glBegin( GL_LINE_LOOP );
    
       for( size_t index = 0; index < vertices.size(); ++index )
       {
          const Vertex& vertex = vertices[ ( index * 3 ) % vertices.size() ];
          glVertex2i( vertex.x, vertex.y );
       }
    
       glEnd();
       glFlush(); 
    }
    Ответ написан
    1 комментарий
  • OpenGL и C++. Как лучше отсортировать полигоны?

    @MarkusD
    все время мелю чепуху :)
    Честно говоря, странно видеть в заголовке и тегах вопрос об OpenGL, а в теле вопроса - про коллизии и, возможно, физику.

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

    Тебе надо иметь два набора вершин. Один - набор вершин для визуализации объекта. Второй - набор вершин для симуляции его физики, не только коллизий. И таких наборов может быть много. Для симуляции повреждений и деформаций, для скиннинга, для хитбоксов, для постройки маршрутов перемещений, для звуков...
    BSP подходят больше для отсечения видимой геометрии. В наши дни этот подход не оправдывает своих затрат, его не используют. Для эффективной симуляции коллизий лучше подходят QTree, OTree или KD-списки.

    Почитать начать можно отсюда: Study path for game programmer. Твой раздел: 9. Game Physics and Animation.
    Далее, тебе сюда: https://gamedev.ru/code/articles/?physics
    Да, геймдев все еще жив, хоть уже и не принадлежит Сереге Ваткину.
    Следом лучше пойти на хабр: [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11].
    У Фабьена Сангларда есть набор статей о внутреннем устройстве DOOM 3 и Quake 3, там есть материал и про столкновения.

    На этом этапе у тебя уже должно сформироваться представление о предметной области симуляции коллизий.
    Ответ написан
    Комментировать
  • Как ограничить FPS в OpenGL и glut?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Само ограничение частоты кадров делается очень просто. Снаружи idle-функции нужно содержать переменную с временем последнего вызова idle-функции, а в самой функции нужно просто накопить дельту частоты кадров и вызвать glutPostRedisplay.
    double GetCurrentTime()
    {
    	using Duration = std::chrono::duration<double>;
    	return std::chrono::duration_cast<Duration>( 
    		std::chrono::high_resolution_clock::now().time_since_epoch() 
    	).count();
    }
    
    const double frame_delay = 1.0 / 60.0; // 60 FPS
    double last_render = 0;
    void OnIdle()
    {
    	const double current_time = GetCurrentTime();
    	if( ( current_time - last_render ) > frame_delay )
    	{
    		last_render = current_time;
    		glutPostRedisplay();
    	}
    }


    Данный код является сильно упрощенным механизмом, лишь показывающим общий принцип ограничения частоты кадров. Его можно применять там, где не требуется точный контроль дельты между кадрами. К слову, представление времени в типе double помогает легче находить дельту между кадрами и контролировать ошибку дельты.
    Для более точного контроля частоты кадров стоит обратиться, например, к такому материалу.
    Ответ написан
    1 комментарий
  • OpenGL не сразу отображает данные из кадрового буфера?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Я не вижу в твоем коде вызова glutIdleFunc. В документации написано что GLUT будет постоянно вызывать переданную функцию в свободное от обработки системных сообщений время. А если в glutIdleFunc передан nullptr (или idle-функция не была задана), то GLUT будет строго ждать следующего системного сообщения.

    Если я все правильно помню, то передача настоящей функции, в которой будет вызываться функция glutPostRedisplay, в glutIdleFunc приведет и к регулярной перерисовке экрана.
    Как правило, в качестве idle-функции в GLUT передают update-функцию, в которой обновляется состояние объектов перед рендерингом.
    Ответ написан
    5 комментариев
  • Что такое анимация и с чем ее кушать если OpenGL?

    @MarkusD Куратор тега C++
    все время мелю чепуху :)
    Spine C runtime.
    https://github.com/EsotericSoftware/spine-runtimes...
    Это не серебряная пуля и не идеальные практики. Пожалуй, в плане кода это пример наоборот, как код писать точно не надо.
    Но вот в плане реализации анимаций эта репка тебе очень поможет.

    Суть такова. Есть модель, она статична. Есть отрезок времени (таймлайн), на этом отрезке есть точки - ключевые кадры. Ключевой кадр несет в себе информацию о том, какую часть модели и как сместить. Чем проще инструкция в ключевом кадре, тем удобнее. Масштаб, поворот и смещение многие любят разделять по разным таймлайнам.
    OGL в этом деле не нужен, до поры.

    С помощью SRT таймлайнов можно анимировать объекты в пространстве целиком, но если тебе захочется точно так же анимировать части меша модели, то впереди тебя будут ждать трудности.
    Анимацию меша лучше реализовать на основе скелета. Это дело в двух словах уже не описать, тут лучше читать статьи.
    www.gamedev.ru/code/terms/SkeletalAnim
    www.gamedev.ru/code/articles/skeletal_animation
    https://habrahabr.ru/post/219509/
    https://habrahabr.ru/post/304042/
    www.gamedev.ru/code/articles/?id=4182

    Самое зерно скелетной анимации в том, что модель остается моделью, анимируются только кости скелета. И именно анимация кости приводит к перемещению фрагмента меша.

    Только на одной скелетной анимации далеко все равно не уедешь. Когда требуется на одной модели одновременно задействовать сразу несколько скелетных анимаций, если сделать в лоб, то меш поплывет во все стороны.
    Для смешивания различных скелетных анимаций применяют так называемые Blend Trees (ссылок под рукой нету, так что сорри).

    В общем, как то так анимация выглядит. Анимируем в цикле обновления, вершины трансформируем в момент формирования кадра. OGL нам нужен, собственно, чтобы нарисовать раскоряченый анимацией меш.
    OGL для непосредственного анимирования мешей тебе понадобится только тогда, когда ты достаточно глубоко нырнешь в анимации, в тот же момент ты уже полностью поймешь что от OGL понадобится для этого.
    Ответ написан
    2 комментария
  • Как упаковать текстуры в libGDX?

    @MarkusD
    все время мелю чепуху :)
    Андрей, у тебя вопрос, как видно по тексту, немного о другом. По описанию видно что ты перед собой стену видишь, а про дверь в стене даже не подозреваешь. Итак, вот дверь, даже несколько. :)

    Первое - надо понять, насколько много графики у тебя в проекте. Для современного mid-end устройства на андроиде 5.0 совсем не проблема держать 2-4 RGBA текстуры с разрешением 4096 по ребру. Это не говоря о сопутствующих шейдерах, буферах растра/глубины/вершин/индексов. Минимализм в ресурсах приветствуется, но и зажимать себя в тески не стоит.

    Второе - давай взглянем на параграф "Open GL Version" странички https://developer.android.com/about/dashboards/ind...
    OpenGL 3.x занимает большую долю всех устройств. Эта версия OpenGL умеет работать с форматом ETC2 (благодаря заявленной поддержке от GPU), в котором уже можно сохранять альфу. А в вики ( https://en.wikipedia.org/wiki/Ericsson_Texture_Com... ) еще и написано про обратную совместимость.
    Тут в моем опыте пробел, с ETC2 я еще не работал и наглядно про обратную совместимость с ETC1 ничего не знаю. Поэтому предлагаю устроить обмен знаниями в этой теме. :)

    Третье - Аппаратно-поддерживаемых форматов сжатия текстур на самом деле много: S3TC, ATC, PVR-TC. ETC - не единственный. Каждый из форматов поддерживается своим производителем. ATC - Quallcomm; PVR-TC - Imagination Tec. Но вот S3TC (более известный как DXT3/DXT5) поддерживается обоими QC и ImgTec, но тайно. И только ARM Mali поддерживает один лишь формат ETC.
    К чему я это. Тройка профильных форматов и альфу поддерживает, и обрабатывается быстрее того же ETC (и уж тем боле пары ETC1).
    Поэтому может тебе удобнее будет использовать конвертацию в эту тройку форматов?
    У меня на гитхабе есть очень быстрая библиотека для программного чтения PVR-TC.v2 в RGBA буфер, что позволит загрузить текстуры для Mali GPU.
    Ответ написан
    3 комментария