Моласар
Вот этот твой материал, если я правильно понял:
Некорректна тут запись в буфер глубины. Если после этого материала рисовать ещё что-то, потенциально находящееся сзади, будет фигня. Объект нарисуется за решёткой, но будет закрывать электрический разряд. Если же ничего позади этого материала ничего не рисовать, то и запись в буфер глубины не нужна.
Но всё-же, лучше пусть объект перекроет электроразряд, но не перекроет решётку, чем перекроет всё, вот эту проблему и решил Кармак, добавляя флаг depthWrite в шейдеры. Хотя опять - же, это очень редкий случай - смотреть сквозь такую решётку на динамический объект с прозрачностью.
Если хочешь парится с исправлениями таких артефактов, делай 2 прохода для таких шейдеров. Для остальных же 99% случаев одного прохода хватит.
Panzerschrek[CN]
Ты просил пример - я привёл пример. В строгом соответствии со спецификациями ку3шных шейдеров. Иными словами, я ответил за слова, а ты - нет, т.к. код GLSL-шейдера в ответ не привёл. Вместо признания неправоты ты начинаешь изобретать отмазки, да ещё и предлагаешь мне "если хочешь - париться с исправлениями". Не вижу смысла продолжать разговор с человеком, который не отвечает за базар.
Моласар
> код GLSL-шейдера в ответ не привёл
Могу привести код, дающий в Quake III ту-же самую картинку. Но содержимое буфера глубины финального кадра будет отличаться. Так что считай, что всё можно сделать за 1 проход.
#version 330
uniform Sampler2D tex[2];
in vec2 tc[2];
out vec4 color;
void main()
{
color= vec4(texture(tex[0], tc[0]).xyz, 1.0) * texture(tex[1], tc[1]);
}Panzerschrek[CN]
> Могу привести код, дающий в Quake III ту-же самую картинку
Мимо. В первом слое ку3-шейдера glBlendFunc( 1, 1 ), во втором glBlendFunc( 1, 0 ) + альфатест. Не вижу, чтобы твой шейдер это учитывал.
Моласар
> В общем случае это утверждение неверно. Оптимизация делается не с целью
> ускорить все функции, а с целью ускорить самые медленные. А их надо искать в
> релизе, а не дебаге.
> Приведу всего один пример: оптимизация с использованием SSE-интринсиков в
> дебаге не даёт вообще никакого прироста, а иногда может привести и к падению
> производительности. Но стоит собрать проект в релизе - картина меняется
> кардинально. И то, что раньше было критическим местом, перестаёт им быть.
>
>
SSE по-мойму вообще в дебаге не работает. У меня по-крайне мере критические ошибки вываливаеся. Я имел ввиду обычные алгоритмы, без спицифической оптимизации. Такие вещи, как SSE оптимизации, конечно, надо учитывать и не пытаться делать в дебаге.
Моласар
> glBindBuffer или glBindVertexArray? Первое быстрое, второе медленное, почему - я написал постом выше твоего.
SDL_GL_SwapBuffers()
Моласар
> Да, прекрасно. А потом кто-нибудь засунет в движок вот эту ку3-карту:
> http://www.youtube.com/watch?v=QrRwmvLNFGw#t=54
> И всё подохнет даже не на геометрии и не на конском overdraw, а на переключениях текстур, шейдеров и стейтов.
Ну вот, что-то похожее что я пытаюсь получить :) Т.е. нужен вывод карт больше, чем 1-2 комнаты.
Maris
> SDL_GL_SwapBuffers()
Ну так в это время все рисование фрейма и происходит. Ты разве не знал что видеокарта асинхронно работает?
Maris
> SSE по-мойму вообще в дебаге не работает
Ну отчего же? Работает. Ассемблерные вставки дают прирост и в дебаге. А вот с интринсиками другая ситуация.
Он отлаживает код, переполненный шаблонами и функциями-аксессорами. Ничего, да?
Ну я его кода не видел, чтобы про шаблоны утверждать.
Не исключено, что драйвер буферизует draw calls.
конечно не исключено.
Нарисовать что-нибудь прозрачненькое, а поверх накрыть решёткой.
А ничего, что он с обоих сторон выглядит одинаково? Там же нет сортировки.
g-cont
> Ну я его кода не видел, чтобы про шаблоны утверждать.
Тогда ты в заведомо проигрышной позиции, и тебе не следовало мне возражать. :)
> А ничего, что он с обоих сторон выглядит одинаково?
По правде говоря, с другой стороны там уже другой полигон.
> Там же нет сортировки.
По расстоянию от камеры - нет, а так есть.
Тогда ты в заведомо проигрышной позиции, и тебе не следовало мне возражать. :)
Но ты его кода не видел тем более, чтобы предполагать. И про SSE тоже не знаешь есть он там или нет.
По правде говоря, с другой стороны там уже другой полигон.
Это смотря на какой карте.
g-cont
> Но ты его кода не видел тем более, чтобы предполагать
Почему же? Видел. Я, в отличие от тебя, читаю тему, прежде чем в неё отвечать. :)
Моласар
ты про STL что ли? Я думал у него свои какие-то шаблоны.
g-cont
> ты про STL что ли? Я думал у него свои какие-то шаблоны
Шаблоны есть, но довольно мало. Кстати, скомпилил в релизе, разницы по-сравнению с дебаг версией не получил... Т.е. как прыгает фпс около 500 в дебаге, столько же и в релизе..
Посмотрел исходники q3. На первый взгляд разница только:
1. В ку3 список видимых листьев не пересчитывается, если текущий кластер не меняется. Я в конечном итоге я тоже до этого дошел, и не пересчитываю его.
2. Список видымых поверхностей (индексов) пересчитывается каждый раз. За счет этого можно использовать frustrum culling. У меня же геометрия пересчитывается, только при изменении кластера. Что самое удивительное, как раз это подход ку3 работает судя по-всему шутро, а у меня нет.
В исходниках ioq3 переделали и как-то очень хитро состыковывают эти поверхности, видимо что бы избежать отрисовки некскольких треугольников за один draw call....
/* ================ R_RecursiveWorldNode ================ */ static void R_RecursiveWorldNode(mnode_t *node, int planeBits, int dlightBits ) { do { int newDlights[2]; // if the node wasn't marked as potentially visible, exit if ( node->visframe != tr.visCount) { return; } // if the bounding volume is outside the frustum, nothing // inside can be visible OPTIMIZE: don't do this all the way to leafs? if ( !r_nocull->integer ) { int r; if ( planeBits & 1 ) { r = BoxOnPlaneSide( node->mins, node->maxs, &tr.viewParms.frustum[0]); if ( r == 2) { return; // culled } if ( r == 1 ) { planeBits &= ~1; // all descendants will also be in front } } if ( planeBits & 2 ) { r = BoxOnPlaneSide( node->mins, node->maxs, &tr.viewParms.frustum[1]); if ( r == 2) { return; // culled } if ( r == 1 ) { planeBits &= ~2; // all descendants will also be in front } } if ( planeBits & 4 ) { r = BoxOnPlaneSide( node->mins, node->maxs, &tr.viewParms.frustum[2]); if ( r == 2) { return; // culled } if ( r == 1 ) { planeBits &= ~4; // all descendants will also be in front } } if ( planeBits & 8 ) { r = BoxOnPlaneSide( node->mins, node->maxs, &tr.viewParms.frustum[3]); if ( r == 2) { return; // culled } if ( r == 1 ) { planeBits &= ~8; // all descendants will also be in front } } } if ( node->contents != -1 ) { break; } // node is just a decision point, so go down both sides // since we don't care about sort orders, just go positive to negative // determine which dlights are needed newDlights[0] = 0; newDlights[1] = 0; if ( dlightBits ) { int i; for ( i = 0 ; i < tr.refdef.num_dlights ; i++ ) { dlight_t *dl; float dist; if ( dlightBits & ( 1 << i ) ) { dl = &tr.refdef.dlights[i]; dist = DotProduct( dl->origin, node->plane->normal ) - node->plane->dist; if ( dist > -dl->radius ) { newDlights[0] |= ( 1 << i ); } if ( dist < dl->radius ) { newDlights[1] |= ( 1 << i ); } } } } // recurse down the children, front side first R_RecursiveWorldNode ( node->children[0], planeBits, newDlights[0] ); // tail recurse node = node->children[1]; dlightBits = newDlights[1]; } while ( 1 ); { // leaf node, so add mark surfaces int c; msurface_t *surf, **mark; tr.pc.c_leafs++; // add to z buffer bounds if ( node->mins[0] < tr.viewParms.visBounds[0][0] ) { tr.viewParms.visBounds[0][0] = node->mins[0]; } if ( node->mins[1] < tr.viewParms.visBounds[0][1] ) { tr.viewParms.visBounds[0][1] = node->mins[1]; } if ( node->mins[2] < tr.viewParms.visBounds[0][2] ) { tr.viewParms.visBounds[0][2] = node->mins[2]; } if ( node->maxs[0] > tr.viewParms.visBounds[1][0] ) { tr.viewParms.visBounds[1][0] = node->maxs[0]; } if ( node->maxs[1] > tr.viewParms.visBounds[1][1] ) { tr.viewParms.visBounds[1][1] = node->maxs[1]; } if ( node->maxs[2] > tr.viewParms.visBounds[1][2] ) { tr.viewParms.visBounds[1][2] = node->maxs[2]; } // add the individual surfaces mark = node->firstmarksurface; c = node->nummarksurfaces; while ( c--) { // the surface may have already been added if it // spans multiple leafs surf = *mark; R_AddWorldSurface( surf, dlightBits ); mark++; } } }
Maris
> видимо что бы избежать отрисовки некскольких треугольников за один draw call
Я думал, в этом-то и состоит главная задача - рисовать максимум треугольников за один draw call.
Моласар
> Я думал, в этом-то и состоит главная задача - рисовать максимум треугольников
> за один draw call.
Да. Я к тому, что в оригинале Кармак не парился над этим и не пытался треугольники склееть, а у него все довольно шустро работало.
Тема в архиве.