Skip to content

GL: fix dynamic sun shadows on macOS (Apple GL) - #2196

Open
Stepan-sovok1917 wants to merge 1 commit into
OpenXRay:devfrom
Stepan-sovok1917:gl-fix-dynamic-sun-shadows
Open

Stepan-sovok1917 wants to merge 1 commit into
OpenXRay:devfrom
Stepan-sovok1917:gl-fix-dynamic-sun-shadows

Conversation

@Stepan-sovok1917

@Stepan-sovok1917 Stepan-sovok1917 commented Oct 17, 2026 •

Copy link
Copy Markdown

Проблема

На macOS (OpenGL 4.1 core profile от Apple поверх Metal) динамические тени от солнца не
работали:

  • интерьеры освещены, как на улице — «крыша пропускает весь свет»;
  • тени отбрасывала только листва (alpha-test), у дерева не было тени от ствола;
  • крыши и ворота не давали тень вообще.

Причина

Три независимых дефекта GL-порта, каждый вносил свою часть в одну и ту же картину:

  1. Непрозрачная геометрия не попадала в shadow map. Проходы SE_R2_SHADOW блендеров
    отложенного рендера собирались с пиксельным шейдером "null". В OpenGL core profile
    draw без валидного фрагментного шейдера не выполняется (Apple'овский GL молча
    игнорирует его), поэтому стволы/крыши/стены никогда не записывались в карту теней.
    В DX-ветках тех же блендеров уже стоит тривиальный "dumb" — "null" был ошибкой
    именно GL-порта.
  2. s_smap не был comparison-сэмплером. GLSL-шейдеры объявляют sampler2DShadow, но
    сэмплер-объект, привязанный к этой текстурной единице, не имел режима сравнения.
    textureProj() на такой паре даёт неопределённое поведение; Apple'овский
    Metal-бэкенд молча возвращает сырую глубину (~0.9–1.0), что отключает динамические
    тени. DX11 для того же прохода использует comparison-сэмплер (smp_smap).
  3. Теневая камера была настроена неверно. glm::lookAt(eye, center, up) принимает
    целевую точку, а не направление: передача L_dir направляла теневую камеру в
    начало координат вместо хода по направлению солнца (DX-аналог использует
    build_camera_dir). Кроме того, glm::ortho() (GL-конвенция) ожидает положительные
    near/far, а z ограничивающего бокса приходит из right-handed view, где геометрия
    перед источником света имеет отрицательный z — старый вызов передавал около
    −530..−480 и выводил всю геометрию за пределы диапазона клипа [−1,1], из-за чего
    ближняя shadow map оставалась пустой.

Фикс

  • src/Layers/xrRender/blenders/{Blender_BmmD_deferred,Blender_Model_EbB_deferred,
    blender_deffer_flat,blender_deffer_model}.cpp: "null"→"dumb"вSE_R2_SHADOW
    (эти ветки лежат внутри RENDER == R_GL; DX-ветки не тронуты).
  • src/Layers/xrRender/blenders/Blender_tree_deferred.cpp: случай теневого прохода
    дерева лежит в ветке, общей с R_R3/R_R4, поэтому GL-изменение обёрнуто в
    #if RENDER == R_GL.
  • src/Layers/xrRender/blenders/{blender_light_direct,blender_light_direct_cascade}.cpp:
    C.r_Sampler_cmp("s_smap", r2_RT_smap_depth) под #if defined(USE_OGL) (настройка
    DX-сэмплеров не менялась).
  • src/Layers/xrRenderPC_GL/r2_R_sun.cpp: glm::lookAt(L_pos, L_pos + L_dir, L_up) и
    -bb.vMax.z … -bb.vMin.z для near/far в render_sun() и render_sun_near().

Проверка

macOS 27.0.1, Apple M4, OpenGL 4.1 core, S.T.A.L.K.E.R. CoP, сборка Release:

  • яркость кадра с включённым солнцем: 64.4 % до фикса (свет затекал в интерьеры);
    после: 57.6 % с солнцем против 57.3 % с выключенным солнцем — интерьеры
    совпадают с базой «только запечённый свет»;
  • тени от стволов, крыш и ворот присутствуют в игре;
  • ветки DX9/10/11 проверены на неизменность (все изменённые строки лежат внутри
    RENDER == R_GL / USE_OGL).

Влияние на платформы

Только OpenGL-рендер (ветки RENDER == R_GL / USE_OGL и GL-единственный
r2_R_sun.cpp). Рендеры D3D9/10/11 не затронуты.

Примечания

Первый вклад в проект; дефекты локализованы и проверены на реальном железе совместно с
ИИ-ассистентом.

Three defects in the GL port produced the same symptom set - sunlight
leaking through roofs/walls into interiors, no shadows from opaque
geometry, only alpha-tested foliage casting shadows:

- SE_R2_SHADOW passes were compiled with the 'null' pixel shader. In an
  OpenGL core profile a draw without a valid fragment shader is not
  executed, so opaque casters never got written into the sun shadow
  map. The DX branches of the same blenders already use the trivial
  'dumb' PS; switch the GL branches to it as well. The tree caster case
  sits in a branch shared with R_R3/R_R4, so it is wrapped in
  '#if RENDER == R_GL'.

- s_smap is declared as sampler2DShadow in the GLSL shaders, but the
  sampler object bound to that unit had no comparison mode. textureProj()
  on such a pair is undefined; Apple's Metal-backed GL silently returns
  raw depth (~0.9-1.0) which disables dynamic shadows. Bind a comparison
  sampler (r_Sampler_cmp) under USE_OGL, matching what DX11 does with
  smp_smap.

- The sun shadow camera was wrong: glm::lookAt takes a target point, not
  a direction, so passing L_dir aimed the camera at the world origin
  instead of along the sun direction (the DX counterpart uses
  build_camera_dir). The orthographic projection also passed raw view-
  space z (negative in front of a right-handed lookAt) as near/far,
  which mapped the geometry outside the [-1,1] clip range and left the
  near shadow map empty. Aim the camera along L_dir and negate the z
  bounds, as the GL convention requires.

Verified on macOS (Apple M4, OpenGL 4.1 core, CoP):
without the fix a frame with the sun on rendered at 64.4% of full
brightness (light leaking into interiors); after the fix 57.6% with the
sun on vs 57.3% with it off, shadows from trunks/roofs/gates are
present.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

1 participant