Конференция "Игры" » Вопрос по теням (из Example DirectX8/StencilBuffer/ShadowVolume) [Delphi, DirectX 9]
 
  • ElectriC © (11.05.07 17:35) [0]
    Как сделать, чтобы тени не рисовались на камере?
  • XProger © (11.05.07 18:14) [1]
    Не рисовать их на камере...
  • ElectriC © (11.05.07 21:17) [2]
    Так как эт сделать?
  • ElectriC © (12.05.07 02:56) [3]
    Ещё, возникает такой 'побочный эффект' -> http://narod.yandex.ru/filemanager/sp.xhtml?f=Shadow.jpg. Как его можно устранить?
    P.S. Плиз, подскажите, хотя бы, как исправить этот 'побочный эффект'!!!
  • ElectriC © (12.05.07 03:03) [4]
    Вернее вот -> http://zmiy-data.narod.ru/Incorrect_shadow.jpg - такой же глюк!
  • XProger © (12.05.07 06:26) [5]
    ElectriC, сделать так чтобы и куб отбрасывал тень? )
  • ElectriC © (12.05.07 12:38) [6]

    > ElectriC, сделать так чтобы и куб отбрасывал тень? )

    )) Так он отбрасывает тень)) просто тень для куба была выключена
    А можно как нить исправить этот "глюк"? Или нет?
  • XProger © (12.05.07 16:08) [7]
    ElectriC, можно конечно! Либо делать правильные тени от всего, либо не рисовать стороны куба )
  • ElectriC © (12.05.07 17:54) [8]
    Так тени рисуются правильно, просто нужно убрать этот глюк!
    А как, я не знаю((
  • Sapersky (13.05.07 12:21) [9]
    Что-то знакомое... а, вот:
    http://pda.delphimaster.net/?id=1165158185&n=9
  • ElectriC © (13.05.07 16:44) [10]

    > Sapersky

    Та вы писали что-то про маску.

    > Напрашивается некая заранее просчитанная "маска", что ли, которая
    > будет отсекать задние грани и перекрытые другими поверхности.

    Как её реализовать?
  • Sapersky (14.05.07 14:06) [11]
    Для начала рекомендую ещё раз перечитать
    http://en.wikipedia.org/wiki/Source_engine#Future_technologies
    (то, что слева)
    и осознать ограничения, которые даёт данный метод.

    Реализуется, думаю, примерно так же, как и статичное освещение лайтмэпами (lightmap). Т.е. на этапе создания уровня для каждого сектора  определяется источник света, от которого будут отбрасываться тени, зачем проходим по всем полигонам с определённым шагом и генерируем для них набор текстур, где точка, на которую может падать тень - белая, на которую не может (полигон развёрнут к источнику света задней стороной или точку заслоняют другие полигоны) - чёрная. Или, возможно, удобнее будет наоборот, поскольку тени чёрного цвета. При наложении тени комбинировать её с текстурой маски (умножать или ещё как-нибудь).
    Примеры с лайтмэпами должны быть здесь:
    http://dxlab.host.sk/index_en.html
  • ElectriC © (14.05.07 16:28) [12]
    Спасибо за информацию.
  • ElectriC © (14.05.07 17:20) [13]
    А можно, как-нить, убрать рисование тени на камере?
    P.S. Из-за этого появляются некоторые 'побочные эффекты' - экран перекрашивается в цвет тени, а тени - в белый цвет! Очень раздражает!
  • Sapersky (14.05.07 18:29) [14]
    Можно, и здесь даже написано, как:
    http://www.devmaster.net/articles/shadow_techniques/
    http://www.rasterise.com/CarmackOnShadowVolumes.txt
    Но готового кода на Delphi, разумеется, нет.
  • ElectriC © (14.05.07 19:16) [15]
    Спасибо.
  • ElectriC © (20.05.07 15:43) [16]
    Можно как-нить оптимизировать теневые объёмы (двухсторонние трафареты, не предлагать - уже реализованы), а то при отбрасывании теней от сложных объектов + перемещение источника света -> наблюдается слишком большое падение кадров, при отрисовке теней. Если да, то как?
    P.S. Если не трудно, ввиде исходного текста.
  • ElectriC © (09.06.07 00:08) [17]
    Может кто-нить помочь с написание кода на Delphi, который предотвращает рисование теней на камере?
    P.S. Сам читал Help [14], но толком не понял, как это сделать?
  • ElectriC © (09.06.07 00:08) [18]
    Может кто-нить помочь с написание кода на Delphi, который предотвращает рисование теней на камере?
    P.S. Сам читал Help [14], но толком не понял, как это сделать?
  • ElectriC © (09.06.07 00:09) [19]
    Блин, лаг что-ли с нетом;))
  • @!!ex_ (09.06.07 08:07) [20]
    Я так и не въехал что за тень на камере??
    На скрин щоте только тенб на кубе, но исправляется соответственно просто... Либо отбрасыванием тени кубом, либо отрисовку куба после отрисовки SV.
  • Sapersky (09.06.07 13:22) [21]
    Скриншот, ИМХО, здесь вообще не в тему. Телепатор подсказывает, что имеется в виду эта проблема (из 2-й ссылки в [14]):

    I first implemented stencil shadow volumes over two years ago in the
    post-Q2 research period. They looked great until you flew the viewpoint
    into one of the volumes, and depending on the exact test you used, either
    most of the screen went into negative shadow, or most of the shadows
    disappeared.

    Разглагольствования Кармака - это, конечно, интересно, но по 1-й ссылке решение проблемы описано более коротко и внятно:

    First off, the algorithm only works if the viewpoint is outside of shadow, otherwise the stencil counting is inverted. This can be remedied by testing this special case, inverting the shadow test, and also initializing the stencil buffer to 2N−1 (where N is the stencil buffer precision), as the stencil buffer holds only unsigned values and decrementing from a zero value would again result in incorrect shadows. A more elegant solution is proposed in [Everitt and Kilgard 2002] , which was partially inspired by an insight of John Carmack, lead programmer of id software [Carmack 2000]. First off, instead of computing the stencil values by incrementing front facing shadow quads and decrementing back facing shadow quads on Z-buffer pass, the entire process is modified to count from infinity instead of from the viewpoint, the so called Z-fail version:

    procedure COMPUTEFRAGMENTSINSHADOW(Z-fail)
    for all shadow casting objects do
       compute potential silhouette edges (PSE) of the polygonal model
       compute the shadow volume polygons (shadow quads) from the light source(s) and the PSE
    end for

    for all front facing shadow quads from viewpoint do
       if Z-buffer test fails then
           decrement stencil buffer value
       end if
    end for

    for all back facing shadow quads from viewpoint do
       if Z-buffer test fails then
           increment stencil buffer value
       end if
    end for

    The two representations (Z-pass and Z-fail) are completely equivalent and compute the same value, yet they do not suffer from the problems mentioned above: the viewer being in shadow is no longer a special case.
  • ElectriC © (09.06.07 16:46) [22]
    Так я не понимаю, как реализовать это на дельфи?
  • ElectriC © (09.06.07 16:47) [23]

    > Я так и не въехал что за тень на камере??

    Поставь камеру именно (внутрь) в тень!!!
  • Sapersky (09.06.07 17:52) [24]
    В примере из SDK8 (CMyD3DApplication.RenderShadow) реализован метод Z-pass:

    for all front facing shadow quads from viewpoint do
       if Z-buffer test passes then
           increment stencil buffer value
       end if
    end for

    for all back facing shadow quads from viewpoint do
       if Z-buffer test passes then
           decrement stencil buffer value
       end if
    end for

    Нужно переделать его на Z-fail (см. ранее). Буквально несколько констант поменять. Может, сам сообразишь, какие? А то я всё равно проверить не смогу, нельзя в этом примере влезть внутрь тени.
  • ElectriC © (10.06.07 16:35) [25]

    > Sapersky

    Лана, попробую!
  • ElectriC © (10.06.07 17:25) [26]
    function CMyD3DApplication.RenderShadow: HResult;
    begin
     // Disable z-buffer writes (note: z-testing still occurs), and enable the
     // stencil-buffer
     m_pd3dDevice.SetRenderState(D3DRS_ZWRITEENABLE,  iFALSE);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE, iTRUE);

     // Dont bother with interpolating color
     m_pd3dDevice.SetRenderState(D3DRS_SHADEMODE,     D3DSHADE_FLAT);

     // Set up stencil compare fuction, reference value, and masks.
     // Stencil test passes if ((ref  and mask) cmpfn (stencil  and mask)) is true.
     // Note: since we set up the stencil-test to always pass, the STENCILFAIL
     // renderstate is really not needed.
     m_pd3dDevice.SetRenderState(D3DRS_STENCILFUNC,  D3DCMP_ALWAYS);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILZFAIL, D3DSTENCILOP_KEEP);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILFAIL,  D3DSTENCILOP_KEEP);

     // If ztest passes, inc/decrement stencil buffer value
     m_pd3dDevice.SetRenderState(D3DRS_STENCILREF,       $1);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILMASK,      $ffffffff);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILWRITEMASK, $ffffffff);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILPASS,      D3DSTENCILOP_INCR);

     // Make sure that no pixels get drawn to the frame buffer
     m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, iTRUE);
     m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_ZERO);
     m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

     // Draw front-side of shadow volume in stencil/z only
     m_pd3dDevice.SetTransform(D3DTS_WORLD, m_matObjectMatrix);
     m_pShadowVolume.Render(m_pd3dDevice);

     // Now reverse cull order so back sides of shadow volume are written.
     m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,   D3DCULL_CW);

     // Decrement stencil buffer value
     m_pd3dDevice.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR);

     // Draw back-side of shadow volume in stencil/z only
     m_pd3dDevice.SetTransform(D3DTS_WORLD, m_matObjectMatrix);
     m_pShadowVolume.Render(m_pd3dDevice);

     // Restore render states
     m_pd3dDevice.SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
     m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,  D3DCULL_CCW);
     m_pd3dDevice.SetRenderState(D3DRS_ZWRITEENABLE,     iTRUE);
     m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE,    iFALSE);
     m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, iFALSE);

     Result := S_OK;
    end;

    Можешь подсказать, что, примерно, нужно изменять в этой функции?
  • Sapersky (11.06.07 10:34) [27]
    Похоже, что не всё так просто и методы эти не вполне эквивалентны.
    Если рисовать по z-fail, рисуются части теневых объёмов ЗА отбрасывающим тень объектом (что логично, z-fail фактически означает "рисовать невидимое"). При отрисовке тени они вылезают наверх, и появляются лишние тени на объекте. То ли метод Кармака рассчитан на то, чтобы рисовать объект после теневых объёмов (но тогда никакого самозатенения), то ли я чего-то недопонял.
    В общем, надо копать глубже, смотреть [Everitt and Kilgard 2002], или some good code and comprehensible presentations from http://developer.nvidia.com
  • ElectriC © (11.06.07 18:42) [28]
    Да, видимо, всё сложнее, чем думалось))
  • ElectriC © (11.06.07 19:14) [29]
    Кста:

    >Исправляем ситуацию, когда камера находится внутри объёма.
    >1. Отключим запись в буфер глубины и цвета
    >2. Включим отсечение передних граней. Установим операция в >инкрементирование на единицу при провале теста глубины. Нарисуем >объём.
    >3. Включим отсечение задних граней. Установим операцию трафарета в >уменьшение на единицу при провале теста глубины. Нарисуем объём.
    >Все освещённые точки будут иметь значение 0 в буфере трафарета.
    >Для нормальной работы метода может потребоваться отрисовка всех >задних граней модели, вытянутых в бесконечность, но в большинстве >случаев этого не требуется, так как эти грани в любом случае были бы >закрыты геометрией.

    Как реализовать этот метод - может поможет???
  • Sapersky (12.06.07 13:14) [30]
    Это тот же z-fail.
    Как выяснилось отсюда:
    http://developer.nvidia.com/attach/6831
    отличие его в том, что нужно "заткнуть" теневой объём с двух сторон полигонами модели. Спереди (или сверху, в общем, с той стороны, которая ближе к источнику света) - лицевыми по отношению к источнику света, сзади - соответственно, задними, перемещёнными к заднему срезу теневого объёма и отмасштабированными под этот срез. Ну вот эти самые "задние грани модели, вытянутые в бесконечность".

    Догадываюсь, какой будет следующий пост...
    "Так я не понимаю, как реализовать это на дельфи?"
  • ElectriC © (12.06.07 15:25) [31]
    Конечно ;))))))))))
  • ElectriC © (12.06.07 15:27) [32]
    Так как же??? ;)))
  • Sapersky (12.06.07 17:40) [33]
    А я вот, в свою очередь, не понимаю, что мешает попытаться понять самостоятельно.
    Ну, кроме лени, конечно. А мне, что ли, не лень писать за тебя?

    Векторную алгебру, насколько помню, в школе изучают. Даже если ещё не - тем лучше, сейчас выучишь, потом не надо будет.
    Фактически здесь нужно использовать:
    1) для определения, какой стороной полигон повёрнут к ист. света - скалярное произведение (dot product) даёт косинус угла между векторами. Синусы/косинусы в школе изучают однозначно...
    2) угол нужно искать между нормалью полигона и вектором от ист. света. Нормаль (перпендикуляр) получается векторным произведением (cross product) двух граней полигона.
    3) для получения заднего среза теневого объёма - масштабирование (scale), сложение/вычитание (add/subtract) векторов.

    Причём с нуля писать ничего не надо, всё это уже есть в TShadowVolume.BuildFromMesh (построение стандартного теневого объёма), но используется несколько по-другому. Т.е. нужно понять, что именно там происходит, и дописать на основе это кода "затычки" к теневому объёму.

    Что касается переделки CMyD3DApplication.RenderShadow на z-fail - там чуть ли не каждая строчка откомментирована, даже не зная английского можно уловить знакомые словосочетания "z-pass" и "z-fail" и сделать выводы. Щёлкнув с Ctrl на любую константу вроде D3DRS_STENCILPASS можно найти в Direct3D.pas список других подходящих для этой функции констант, часто с поясняющими комментариями.

    Могу даже сделать перевод (пока это всё ещё z-pass, я только выкинул кое-что лишнее):

    function CMyD3DApplication.RenderShadow: HResult;
    begin
    m_pd3dDevice.SetTransform(D3DTS_WORLD, m_matObjectMatrix);

    // Выключаем запись в z-буфер (но z-тест работает)
    m_pd3dDevice.SetRenderState(D3DRS_ZWRITEENABLE,  iFALSE);
    // включаем stencil-буфер
    m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE, iTRUE);

    // Выключить цветовую интерполяцию (для оптимизации, не обязательно)
    m_pd3dDevice.SetRenderState(D3DRS_SHADEMODE,     D3DSHADE_FLAT);

    // Выключаем вывод на экран теневых объёмов.
    // Чтобы они рисовались - закомментировать
    m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, iTRUE);
    m_pd3dDevice.SetRenderState(D3DRS_SRCBLEND,  D3DBLEND_ZERO);
    m_pd3dDevice.SetRenderState(D3DRS_DESTBLEND, D3DBLEND_ONE);

    // для большей наглядности при рисовании теневых объёмов можно ещё включить wireframe-режим
    //  m_pd3dDevice.SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);

    // Устанавливаем stencil-функцию сравнения, в данном случае stencil-тест
    // всегда проходит (D3DCMP_ALWAYS). Другие константы в Direct3D8.pas.
    m_pd3dDevice.SetRenderState(D3DRS_STENCILFUNC,  D3DCMP_ALWAYS);

    // Если stencil и Z-тесты проходят, увеличиваем значения в буфере.
    // поскольку stencil-тест проходит всегда, фактически получается "если Z-тест проходит"
    m_pd3dDevice.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_INCR);
    // Рисуем только передние грани
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,   D3DCULL_CСW);
    // выводим объём
    m_pShadowVolume.Render(m_pd3dDevice);

    // Если stencil и Z-тесты проходят, уменьшаем значения в буфере
    m_pd3dDevice.SetRenderState(D3DRS_STENCILPASS, D3DSTENCILOP_DECR);
    // Рисуем только задние грани
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,   D3DCULL_CW);
    // выводим объём
    m_pShadowVolume.Render(m_pd3dDevice);

    // Восстанавливаем стейты
    m_pd3dDevice.SetRenderState(D3DRS_SHADEMODE, D3DSHADE_GOURAUD);
    m_pd3dDevice.SetRenderState(D3DRS_CULLMODE,  D3DCULL_CCW);
    m_pd3dDevice.SetRenderState(D3DRS_ZWRITEENABLE,     iTRUE);
    m_pd3dDevice.SetRenderState(D3DRS_STENCILENABLE,    iFALSE);
    m_pd3dDevice.SetRenderState(D3DRS_ALPHABLENDENABLE, iFALSE);
    m_pd3dDevice.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);

    Result := S_OK;
    end;

    Будут КОНКРЕТНЫЕ вопросы по деталям - задавай.
  • ElectriC © (12.06.07 18:19) [34]
    За перевод спасибо (хотя и не надо было - перевёл давно уже сам).
    Мне бы разобраться, как не рисовать тень на камере.
  • ElectriC © (12.06.07 18:56) [35]
    Кстати, если не лень, можешь посмотреть, почему на тенях пояляются
    артефакты ввиде белых точек?
    P.S. Если не лень скачай моё первое творение ;))) -> http://slil.ru/24404040/874845188/School_54.rar
  • ElectriC © (13.06.07 15:04) [36]
    .
  • Sapersky (13.06.07 17:10) [37]
    почему на тенях пояляются  артефакты ввиде белых точек?

    Похоже, из-за кривых мешей, в которых имеются избыточные вершины.
    Удалить такие вершины - D3DXWeldVertices.

    Творение посмотрел... ну что сказать... неплохо для начала.
    С тенями на встроенной видео страшно тормозит - 2-3 fps, без - около 30.
    Подозреваю, что теневые объёмы пересчитываются на каждом кадре для всех объектов. Так вот, для статичных объектов (школы) можно сделать это один раз в начале, ещё хорошо бы загнать их в VB вместо рисования через DrawPrimitiveUP.

    "Отражения" в окнах можно сделать динамическими, см. пример SphereMap из SDK. В SDK8.1 его зачем-то переделали на шейдер, но только Delphi-вариант, С++ остался (видимо, Cooltie забыл обновить) на автоматической генерации текстурных координат, что ИМХО проще:

           // Generate spheremap texture coords, and shift them over
           D3DXMATRIX mat;
           mat._11 = 0.5f; mat._12 = 0.0f; mat._13 = 0.0f; mat._14 = 0.0f;
           mat._21 = 0.0f; mat._22 =-0.5f; mat._23 = 0.0f; mat._24 = 0.0f;
           mat._31 = 0.0f; mat._32 = 0.0f; mat._33 = 1.0f; mat._34 = 0.0f;
           mat._41 = 0.5f; mat._42 = 0.5f; mat._43 = 0.0f; mat._44 = 1.0f;
           m_pd3dDevice->SetTransform( D3DTS_TEXTURE0, &mat );
           m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );
           m_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL );

    Само собой, окна тогда нужно рендерить отдельно от всего.
    Некоторые пояснения есть в readme к примеру.
  • ElectriC © (13.06.07 17:14) [38]

    > Подозреваю, что теневые объёмы пересчитываются на каждом
    > кадре для всех объектов.

    Кстати пересчитываются только один раз - при первом запуске программы!!!
  • ElectriC © (13.06.07 18:06) [39]
    Может объяснить, как работать с D3DXWeldVertices?
    P.S. Если можно, то на примере.
  • Sapersky (13.06.07 18:38) [40]
    В ProgressiveMesh (SDK) есть пример.
  • ElectriC © (13.06.07 21:19) [41]
    Так белые точки как были, так и остались.
    Может я что-то не так делаю или ещё что:
    ...
    var
     AdjBuffer : ID3DXBuffer;
     Epsilons  : TD3DXWeldEpsilons;
    ...

    ZeroMemory(@Epsilons, SizeOf(TD3DXWeldEpsilons));
    D3DXWeldVertices(SLMeshObj, D3DXWELDEPSILONS_WELDPARTIALMATCHES, @Epsilons,
                   AdjBuffer.GetBufferPointer,
                   AdjBuffer.GetBufferPointer, nil, nil);
    D3DXValidMesh(SLMeshObj, AdjBuffer.GetBufferPointer, nil);
    ...
  • ElectriC © (14.06.07 16:09) [42]

    > Похоже, из-за кривых мешей, в которых имеются избыточные
    > вершины.

    Тогда получатся, что самолёт (из SDK) тоже кривая мешь, т.к. на тени тоже
    появляются точки,а в SDK их нет?((((
    Что делать?
  • Sapersky (15.06.07 14:47) [43]
    Я для проверки воткнул в CD3DMesh.Create_ после D3DXLoadMeshFromX:

    FillChar(eps, SizeOf(eps), 0);
    eps.Flags := D3DXWELDEPSILONS_WELDALL;
    Result := D3DXWeldVertices(m_pSysMemMesh, @eps,
               pAdjacencyBuffer.GetBufferPointer,
               pAdjacencyBuffer.GetBufferPointer, nil, nil);

    Самолёт - тоже кривой, да.
  • ElectriC © (15.06.07 15:18) [44]
    Так почему в SDK нет "белых точек"?
  • Sapersky (15.06.07 15:36) [45]
    В некоторых ракурсах есть.
    Эффект от WeldVertices виден на самолёте и без тени, освещение  становится более гладким.
  • ElectriC © (15.06.07 18:50) [46]
    Так что посоветуете делать?
  • ElectriC © (23.06.07 15:18) [47]
    .
 
Конференция "Игры" » Вопрос по теням (из Example DirectX8/StencilBuffer/ShadowVolume) [Delphi, DirectX 9]
Есть новые Нет новых   [120291   +66][b:0][p:0.001]