GPU имеют несколько основных мест, где они расходуют вычислительную мощность. Это должно быть довольно очевидно. Один запускает вершинный шейдер один раз на вершину. Другой запускает фрагментный шейдер один раз на пиксель / фрагмент.
Почти всегда на тонну больше пикселей, чем вершин. Один экран 1920x1080 имеет почти 2 миллиона пикселей, но может быть покрыт 3-х вершинным треугольником или 4 или 6-ю вершинным квадратом (2 треугольника). Это означает, что на весь экран вершинный шейдер работал от 3 до 6 раз, а фрагментный шейдер - 2 миллиона раз !!!
Отправка слишком большого объема работы фрагментному шейдеру называется "заполненной границей". Вы максимально увеличили скорость заполнения (заполняя треугольники пикселями), и это то, что вы видите. В худшем случае на моем MacBook Pro 2014 года я мог бы рисовать только с 6 или около того экранами с количеством пикселей, прежде чем достигну предела скорости заполнения для обновления экрана со скоростью 60 кадров в секунду.
Существуют различные решения для этого.
Первый - это z-буфер. Графический процессор сначала проверит буфер глубины, чтобы увидеть, нужно ли вообще запускать фрагментный шейдер. Если проверка глубины не пройдена, графическому процессору не нужно запускать фрагментный шейдер. Таким образом, если вы сортируете и рисуете непрозрачные объекты, самые близкие объекты - сначала самые дальние, а потом - самые последние, то большинство этих объектов на расстоянии не пройдут тест глубины при рендеринге пикселей их треугольников. Обратите внимание, что это возможно только в том случае, если ваш фрагментный шейдер не записывает в gl_FragDepth
и не использует ключевое слово discard
.
Это метод «избегания оверрейда». Overdraw - это любой пиксель, который рисуется более одного раза. Если вы нарисуете куб на расстоянии, а затем нарисуете сферу так близко, чтобы она покрывала куб, то для каждого пикселя, отрисованного для куба, он был «перезаписан» пикселями сферы. Это была пустая трата времени.
Если ваши фрагментные шейдеры действительно сложны и поэтому работают медленно, некоторые 3D-движки нарисуют «предварительный проход Z-буфера». Они нарисуют всю непрозрачную геометрию с помощью простейшего вершинного и фрагментного шейдера. Вершинный шейдер нуждается только в положении. Фрагмент шейдера просто излучает постоянное значение. Они даже отключат рисование в цветовом буфере gl.colorMask(false, false, false, false)
или, возможно, создадут кадровый буфер только глубины, если это поддерживается аппаратным обеспечением. Затем они используют это, чтобы заполнить буфер глубины. По окончании они рендерит все заново с помощью дорогого шейдера и теста глубины, установленного на LEQUAL
(или что-то еще, что работает для их двигателя) Таким образом, каждый пиксель будет отображаться только один раз. Конечно, это не бесплатно, графическому процессору все еще требуется время, чтобы попытаться растеризовать треугольники и протестировать каждый пиксель, но это может быть быстрее, чем перерисовка, если шейдеры дороги.
Другой способ - попытаться выяснить, какие объекты будут закрыты более близкими объектами, и даже не отправлять их в графический процессор. Существует множество способов сделать это , обычно с использованием ограничивающих сфер и / или ограничивающих рамок. Некоторые потенциально видимые наборы техники также могут помочь при отбраковке окклюзии. Вы даже можете попросить графический процессор вычислить часть этого, используя запросы окклюзии , хотя это доступно только в WebGL2
Самый простой способ проверить, ограничены ли вы заливкой, - сделать ваш холст крошечным, например, 2x1 пикселя (или просто по-настоящему маленьким размером окна браузера). Если ваше приложение начинает работать быстро, скорее всего, оно заполнено. Если он все еще работает медленно, он может быть привязан либо к геометрии (вершинный шейдер выполняет слишком много работы), либо к процессору (любая работа, которую вы выполняете на процессоре, занимает слишком много времени, будь то просто вызов команд WebGL, вычислительная анимация или коллизии или физика или что-то еще).
В вашем случае вы, скорее всего, ограничены заливкой, так как видите, что когда все треугольники маленькие, он работает быстро (потому что рисуется очень мало пикселей) по сравнению с увеличением, когда множество треугольников покрывает экран, а затем медленно (потому что рисуется слишком много пикселей).
Не существует действительно «простых» решений. Я действительно просто зависит от того, что ты пытаешься сделать. Видимо, вы используете three.js, я знаю, что он может сортировать прозрачные объекты. Я понятия не имею, сортирует ли он непрозрачные объекты. Другие методы, перечисленные, я полагаю, находятся за пределами сферы действия Three.js и более, и ваше приложение может принимать и выводить объекты со сцены или устанавливать их видимость на false и т. Д. *
Примечание: Вот простая демонстрация, показывающая, как мало оверлей может обрабатывать ваш графический процессор . Это просто рисует кучу полноэкранных четырехугольников. По умолчанию он, скорее всего, не может нарисовать так много, особенно в полноэкранном режиме, до тех пор, пока он больше не сможет достигать 60 кадров в секунду. Включите сортировку спереди назад, и она сможет рисовать больше, и все равно ударит 60 кадров в секунду.
Также обратите внимание, что включение смешивания происходит медленнее, чем при отключенном смешивании. Это должно быть понятно, потому что без наложения GPU просто пишет пиксель. При смешивании графический процессор должен сначала прочитать целевой пиксель, чтобы он мог выполнять смешивание, поэтому он медленнее.