Доброго времени!
Учитывая что материал может быть абсолютно разным и содержать разное кол-во параметров, разные типы параметров и кол-во текстур. Вопрос сортировки таких материалов существенно усложняется.
Но если отсортировать ещё более-менее реально, то генерация необходимых стейтов, точнее очереди из них совсем тяжко мне даётся. Точнее даже не знаю с чего начать.
Существует ли статьи, книги, рекомендаций Как правильно организовать всё это используя ООП?
ЗЫ: Сейчас у меня отрисовка каждого объекта влечёт установку всех(!) юниформов шейдера независимо от уже установленных и в большинстве случаев точно таких же в предыдущих объектах.
ЗЗЫ: Отметил Тема повышенной сложности хотя для вас возможно это и не так ;)
ЗЫ
Какой хоть графический АЙПИ ?
ЗЫ :)
ЗЗЫ :)
> Какой хоть графический АЙПИ ?
Ой. Забыл сказать. OpenGL 3.3 core
snake32
Ой. Забыл сказать. Я же не знаю OpenGL 3.3 core :)
ronniko
так распиши для мантла, яка разница =)
snake32
А зачем сортировать то?
Я избавлялся от лишних биндов так:
1. Где-то прячем список (1) _всех_ материалов на сцене. Добавление/удаление из списка производится автоматически при присвоении материала объекту(чтобы неиспользуемые материалы в списке не были).
2. Каждый элемент списка (1) указывает на материал и список моделей в видеопамяти (2), использующих этот материал
3. Каждые элемент списка (2) указывает на модель в видеопамяти(VBO) и список объектов (3), которые используют модель.
При рендере:
1. Идешь по списку (1), биндишь материал
2. Открываешь список (2), для текущего материала, биндишь модель(VBO)
3. Открываешь список (3), передаешь в шейдер актуальные только для конкретного объекта данные
4. Рисуешь
5. ???
6. Профит
В пункте 3. можно использовать инстансинг, а если совсем уж часто данные повторяются, то можно проверять различаются ли новые данные от данных с предыдущего прохода.
Да, кстати, тип GAPI для меня сейчас не особо важен. Подойдёт любой, где есть шейдеры и юниформы(dx9,dx10,dx11). Хотел понять общий принцип алгоритма выуживания изменений, дабы лишний раз их не устанавливать и разгрузить CPU.
The Player
>А зачем сортировать то?
Может я не правильно понимаю абстракцию Материал?
В моём понимании Материал это - один слинкованный шейдер с конкретными значениями его юниформов. Изменился шейдер или хотябы одно значение его юниформа, то это уже другой материал.
Таким образом, отсортированный по материалам массив объектов при последовательном просмотре гарантирует что изменений будет минимум(как по шейдерам так и по юнифрмам).
Следующий шаг - извлечение разницы материалов и формировании из них нового массива для последующего многократного "рендера" этого массива.
Вот здесь у меня проблемы. По мимо "разниц материалов" в этот же массив необходимо вставлять "геометрические данные"(набор VBO(VAO) и glDrawElements ) каждого объекта. Очевидно, класс "разница материалов" и "геометрические данные" объекта должны иметь общего предка, иначе полиморфизм не возможен. В итоге стремлюсь к такому результату:
[pas]for obj in DiffMaterialAndGeometryData_List do
obj.Apply;[/pas]
переключение шейдера во много раз дольше чем смена юниформы, так что как сортировать есть смысл так чтобы шейдеры лишний раз не переключать. А юниформы - без разницы. Тем более если ты буферы юниформ используешь, то вообще нет смысла ловить "разницу материалов" - изменился и изменился, ставим другой буфер.
kipar
>Тем более если ты буферы юниформ используешь, то вообще нет смысла ловить "разницу материалов" - изменился и изменился, ставим другой буфер.
нет. UBO я ещё не использовал. У меня пока тупо идёт цикл по всем активным юниформам шейдера для каждого объекта.
procedure TGLProgramObject.SetUniforms(const Uniforms:TUniforms ); var i:integer; begin for i:=0 to Length( FActiveUniforms )-1 do Uniforms[ FActiveUniforms[i].enum ].Apply( FActiveUniforms[i].location ); end;
Где Uniforms - глобальный массив всех uniform'ов используемых во всех шейдерах
TUniformEnum = (uProjection, uView, uModel, uModelView, uModelViewProjection, uDeltaTime, uSampler0, uSampler1, uOffsetX, uColor, uRange, uSelect, uViewport, uBillboard ); TUniformParam = class public procedure Apply( const location:GLint ); virtual; abstract; end; TUniforms = array[TUniformEnum] of TUniformParam; TUniformType<T:record> = class( TUniformParam ) public Data:T; end; TUniformMat4 = class( TUniformType<TMat4> ) procedure Apply( const location:GLint ); override; end; TUniformVec4 = class( TUniformType<TVec4> ) procedure Apply( const location:GLint ); override; end;
snake32
ну, можно закешировать последнее значение, в Apply что-то типа (if lastValue <> Value then begin glUniform...(Value); LastValue := Value end;).
Хотя в драйвере оно, возможно, и так кешируется, но так будет меньше вызовов API. Ну и при смене шейдера все равно все юниформы надо перезагружать.
Суть материалов в том чтобы одним drawcallом рисовать все объекты с одним материалом, а экономия на установке юниформ (все или только половину выставлять) между материалами по сравнению с этим - мизер.
kipar
>if lastValue <> Value then begin glUniform...(Value); LastValue := Value end;
У меня сейчас стейты так тестятся
procedure TPassMain.Draw(state:TGLState ); begin if state.BindFrameBuffer <> 0 then begin glBindFrameBuffer( GL_FRAMEBUFFER, 0 ); state.BindFrameBuffer := 0; end; if state.ViewPort <> ViewPort then begin glViewport( ViewPort.x, ViewPort.y, ViewPort.z, ViewPort.w ); state.ViewPort := ViewPort; end; if state.ClearColor <> FClearColor then begin glClearColor( FClearColor.x, FClearColor.y, FClearColor.z, FClearColor.w ); state.ClearColor := FClearColor; end; if state.EnableBlend <> true then begin glEnable( GL_BLEND ); state.EnableBlend := true; end; if( state.BlendFuncFactorS <> GL_SRC_ALPHA )or( state.BlendFuncFactorD <> GL_ONE_MINUS_SRC_ALPHA )then begin glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); state.BlendFuncFactorS := GL_SRC_ALPHA; state.BlendFuncFactorD := GL_ONE_MINUS_SRC_ALPHA; end; glClear( GL_COLOR_BUFFER_BIT ); state.Uniforms[uProjection] := proj; state.Uniforms[uView] := view; state.Uniforms[uViewport] := vp; inherited; end;
>Суть материалов в том чтобы одним drawcallом рисовать все объекты с одним материалом
Дык у всех одинаковоматериальных объектов могут быть разные юниформы и почти всегда разные VBO. Как это можно отрендерить одним drawcallом?
snake32
Вот этот подход работает шикарно - http://realtimecollisiondetection.net/blog/?p=86
snake32
Ну сортировать по материалам, расстоянию до камеры(для близких к камере объектов) и прозрачности.
Причем если есть прозрачные объекты то от ближним к дальним. с дальнего расстояния лучше сортировать по материалам, так-же с учетом прозрачности. Ведь в далеке затраты на Owerdaw будут минимально, ибо объекты буду иметь размеры в несколько пикселей, и для них переключение материалов будет намного тяжелее.
Cортировка по расстоянию имеет нюансы на некоторых платформах. К примеру на IOS Apple это не рекомендует делать, там используется Tile Based Rendering (TBR).
И наконец для облегчения реализации сортировки по этим условиям все это, пакуется в ключ, как описано в ссылке приведенной StiX
Пока не было шейдеров самым дорогим было переключение текстур и как следствие - завязанных на них стейтов. Теперь, соответственно, шейдеры самое дорогое переключение. Но там замкнутый круг - сделаешь один универсальный шейдер с условиями - будет тормозить обработка этих условий. Сделаешь убер-шейдер и рантайм-компиляцию - надо будет сортировать их, чтобы избежать слишком частых переключений. А сортировка тоже небесплатная, особенно когда много полигонов в кадре. У меня были случаи когда на сортировку уходило столько же времени, сколько на переключение шейдеров. Их немного таких случаев, процентов 5 от общего кол-ва, но всё-таки были.
Тема в архиве.