Войти
UnityФорумПрограммирование

[РЕШЕНО] Затенение невидимых частей уровня на GPU

Advanced: Тема повышенной сложности или важная.

#0
(Правка: 3:05) 3:00, 17 окт. 2020

В игре вид сверху, примерно как в Diablo, но с чуть больее свободной камерой, что позволяет видеть далекие команаты.
Есть желание затемнить комнаты, которые в данный момент персонаж не должен видеть.

Есть разные способы это сделать, в данной теме хотел бы узнать, как это сделать с помощью механизма освещения, встроенного в движок.

Общая идея:
Допустим, у нас есть некий специальный источник освещения (глаза персонажа). Источник освещения бросает тени.

Пишем кастомный шейдер, который делает точку объекта видимой только если
1. до нее доходит свет от этого специального источника света
2. на нее не падает тень от этого источника

А сам свет источника к цвету пикселя традиционным образом не прибавляется.

У меня есть некоторый опыт написания шейдеров, но все они были сугубо утилитарные и не взаимодействовали явно с источниками света и тенями. Ткните меня носом, пожалуйста, с какого места начинать раскурку предмета, чтобы прийти к желаемому результату кратчайшим путем.

#1
11:28, 17 окт. 2020

По моему во всех играх использующих туман войны обычный постэффект.
Я бы сделал затенение по дистанции между персонажем и точкой в пространстве и накладывал постэффект с обычным блендингом.

#2
12:48, 17 окт. 2020

Kripto289
Обычный туман войны не подходит...

Видимость точки в подавляющем кол-ве случаев зависит от наличия препятствий между точкой и глазом, а не дистанции.

Вроде такого:
Изображение

#3
(Правка: 19:05) 18:50, 17 окт. 2020

Интересная кстати тема.

Один раз я хотел добиться эффекта в Unity как на скрине из Nox'а.
Для этого я для каждого объекта который мог бы перекрывать обзор делал его клон с белым материалом на другом слое, фигачил в то место где стоял персонаж с обзором единственный динамический источник света, отдельная камера хватала этот слой и рендерила в текстуру, которую я потом каким-то образом накладывал поверх картинки игры.

Какой я только бред не творил.
Впрочем, Imho, можно делать все, что угодно лишь бы оно:
1. давало нужный визуальный результат
2. не было видно игроку, как это работает.
3. не влияло особо на производительность.

Результат кстати был неудовлетворительным, не помню почему.
Да я без понятия что при этом грузится, CPU или GPU.

Я не против тоже узнать, как такие вещи делаются правильно.

#4
20:47, 17 окт. 2020

kkolyan
> Допустим, у нас есть некий специальный источник освещения (глаза персонажа).
> Источник освещения бросает тени.
>
> Пишем кастомный шейдер, который делает точку объекта видимой только если
> 1. до нее доходит свет от этого специального источника света
> 2. на нее не падает тень от этого источника
>
> А сам свет источника к цвету пикселя традиционным образом не прибавляется.

Shader "FogOfWar"
{
    Properties{
      _MainTex("Texture", 2D) = "white" {}
    }

    SubShader{
      Tags { "RenderType" = "Opaque" }
      CGPROGRAM
      #pragma surface surf Custom fullforwardshadows

      struct Input
      {
        float2 uv_MainTex;
      };

      sampler2D _MainTex;

      float4 LightingCustom(SurfaceOutput s, float3 lightDir, float atten)
      {
        float4 col = 1;

        half diffuse = dot(s.Normal, lightDir) * atten;

        if (_LightColor0.g > _LightColor0.r * 5)//is green light?
        {
          if (atten < 0.005)//is in shadow 
            col.rgb = float3(-10, -10, -10);//large negative color for shadow
          else
            col.rgb = 0;//zero light if not shadow
        }
        else
          //standard lightening
          col.rgb = s.Albedo * diffuse * _LightColor0.rgb / 2;

        return col;
      }

      void surf(Input IN, inout SurfaceOutput o)
      {
        o.Albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
      }

      ENDCG
    }
    Fallback "Diffuse"
}

Шейдер обрабатывает directional и точечные источники света(простое освещение Phong) и тени от них.
Но для особых источников света (строго зеленого цвета) - шейдер работает особым образом, делая нулевое освещение для тени этих источников.

"Зеленый" свет выключен:
Без затенения | [РЕШЕНО] Затенение невидимых частей уровня на GPU

"Зеленый" свет включен:
С затенением | [РЕШЕНО] Затенение невидимых частей уровня на GPU

#5
3:49, 18 окт. 2020

Zeus44
Огромное спасибо за столь развернутое объяснение!

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

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры

Другими словами, все работает, но с ньюансами. Тени движком рассматриваются лишь как декорация (и в этом есть здравое зерно) и он на свое усмотрение может их не рисовать, что для текущей задачи очень критично. Похоже, логику сокрытия частей уровня на этот механизм вешать не стоит.

#6
8:40, 18 окт. 2020

kkolyan
> Огромное спасибо за столь развернутое объяснение!
>
> На небольших тестовых локациях работает превосходно, но на тестовом уровне, вот
> лучшее чего удалось добиться:
Смени на deffered рендеринг, а не forward с лимитом на пиксельные источники света.

#7
13:54, 18 окт. 2020

kkolyan
1. Выставьте RenderMode = Important для зеленого источника света.
2. Увеличьте Range зеленого источника света.
3. В настройках качества увеличьте значение ShadowDistance

Пункты 2 и 3 - по необходимости.

#8
15:33, 18 окт. 2020

Zeus44

> 1. Выставьте RenderMode = Important для зеленого источника света.
на видео результат с уже выставленным Important :(

> 2. Увеличьте Range зеленого источника света.
Cтоит 40. это с запасом покрывает всю локацию:

+ Показать

Увеличение до 200 не возымело эффекта.

> 3. В настройках качества увеличьте значение ShadowDistance
на выбранном уровне качества стоит 150 (в том, что уровень качества действительно активный - убедился, временно уменьшив это значение до малой величины).

Выглядит так, будто движок просто целиком выбрасывает меши из отрисовки теней:

+ Показать
#9
(Правка: 16:00) 15:51, 18 окт. 2020

Kripto289
> Смени на deffered рендеринг, а не forward с лимитом на пиксельные источники света.

Просто смена Rendering Path камеры на deferred - не помогает. Из-за того, что Phong форсирует forward?

Пытаюсь перписать шейдер с Phong на Standard.
За точку отсчета взял вот такой код и пытаюсь по-разному врезаться в эти методы примерно как это сделал Zeus44 с LightingCustom:

+ Показать

Так нужный мне "atten", в такой схеме освещения спрятался в UnityGIInput. Но как-либо адекватно учесть его в LightingCustom_GI  пока не получается, а протащить в другие методы - тоже. Непонятно, как эти методы взаимодействуют между собой.

Судя по всему, сначала вызывается LightingCustom_GI, а затем в зависимости от rendering path - LightingCustom или LightingCustom_Deferred.

Но не ясно, на каком уровне они оперируют - вертексов или пикселей?

А еще - причем здесь GI? В Standard Lighting все считается за GI?

#10
1:12, 19 окт. 2020

Ага! У меня источник "зеленого света" стоял ровно на уровне пола, что добавляло учет фазы луны при определении - нужно ли затенять пол или нет. Поднял его выше и все заработало как надо. Ну как скзать, как надо... есть некоторые артефакты и PBR надо в новый шейдер еще занести. И тени на большом расстоянии дрожат, если их разрешение на медиуме. В общем, с тем как это пойдет на слабых тачка - надо смотреть.
Но, судя по всему, схема рабочая. И главное - можно опнять, насколько это юзабельно внешне.

Запустить видео по клику - Как делать игрыЗапустить видео по клику - Как делать игры
#11
(Правка: 16:38) 16:38, 19 окт. 2020

kkolyan
> Ага! У меня источник "зеленого света" стоял ровно на уровне пола, что добавляло
> учет фазы луны при определении - нужно ли затенять пол или нет. Поднял его выше
> и все заработало как надо. Ну как скзать, как надо... есть некоторые артефакты
> и PBR надо в новый шейдер еще занести. И тени на большом расстоянии дрожат,
> если их разрешение на медиуме. В общем, с тем как это пойдет на слабых тачка -
> надо смотреть.
> Но, судя по всему, схема рабочая. И главное - можно опнять, насколько это
> юзабельно внешне.
А в чём прикол этой "фичи"? Почему просто не прикрепить к персонажу фонарик, летающего светлячка, маслянную лампу на пояс, да или хотя бы просто невидимый источник света. На какой хер выставлять на уровне кучу источников света, а потом пытаться их скрыть?.... Так ещё и граница теней максимально убогая и резкая.
Я хз, бродилка по темнейшей перещере с факелом в руках выглядит в сотни раз лучше, и делает то же самое что и у тебя.

#12
(Правка: 17:25) 17:10, 19 окт. 2020

Спасибо что высказал свое мнение - это важно.

  • цель экспериментов - найти удачный способ сокрытия удаленных частей локации при условии что камера находится выше стен, но угол достаточно полог чтобы "смотреть вдаль".
  • есть и другие варианты, которые я не публиковал, т.к. они не вызвали проблем в реализации. и некоторые их них мне пока что нравятся больше чем рассмотренный в этом треде.
  • в игре не будет (или почти не будет) локаций с кромешной тьмой, так что решение с фонариком/светлячком/лампой/факелом не подходит.
  • резкость границы теней мне самому не нравится. но ведь это технический прототип - в чистовом варианте границу можно размыть, а черноту облагородить текстуркой.
  • PS: Погоняв прототип, понял что даже если сгладить границу теней, такое вот жесткое отсечение невидимой персонажу геометрии - выглядит спорно. Особенно в локациях под открытым небом.

    UnityФорумПрограммирование