Як я можу зробити так, щоб вода здавалася темнішою від глибини, як у Minecraft?


24

У Minecraft, коли дивишся на воду, тим глибше ти бачиш, чим вона стає темнішою. Хтось знає, як кодувати щось подібне?

Minecraft з ефектом minecraft з ефектом

подібна гра без ефекту подібна гра без ефекту


18
Це не робиться автоматично, оскільки матеріал кубика води напівпрозорий?
пік

Я не думаю, що так. Я додав малюнок без ефекту для порівняння.
Ксав'є

2
Можливо, це ефект адитивної суміші, застосовуваний лише на кубиках води? Знову ж, це має бути легко, оскільки матеріал напівпрозорий.
пік

1
ви також можете змінювати колір коробок відповідно до глибини.
Ali1S232

Відповіді:


51

По суті, існує два різних підходи до освітлення води на основі глибини:

Воксель-освітлення

Minecraft використовує освітлення на основі вокселів, яке працює, поширюючи світло на сусідні кубики, знижуючи яскравість залежно від типу блоку. Темні океани є побічною дією цієї системи.

Вода блокує сонячне світло і зменшує світло на 3 рівні на блок (замість 1 рівня за замовчуванням), що означає яскравість в океані на кожну відстань від поверхні:

0 (surface): 15 (direct sunlight)
1:           12
2:            9
3:            6
4:            3
5 and below:  0 (darkness)

Джерело: Minecraft Wiki - Light

Затінення на основі відстані

В іграх із традиційною моделлю освітлення цей ефект можна створити, вимірюючи кількість води, яка знаходиться між джерелом світла та океанським дном. Потім світло згасає, виходячи з цієї відстані. Для цього є кілька методів:

Прямий розрахунок

Якщо у вас рівна поверхня, ви можете легко обчислити відстань, яку проходить світло у воді, якщо перенести поверхню нормально подалі від водоймища \ vec {n}та крапкового добутку цього нормального та положення поверхні св шейдер геометрії.

Ефективна водна відстань - це

\ max (\ ліворуч (s - \ vec {n} \ cdot \ vec {p} \ праворуч), 0) \ cdot \ ліворуч (1 + \ tan (\ alpha) \ праворуч)

де \ vec {p}розташування вершини і альфакут між напрямком світла під поверхнею та нормальною поверхнею води до водойми.

На заході сонця альфадосягає лише трохи менше 50 °, оскільки світло переломлюється при надходженні у воду.
Ось запис у блозі з хорошим поясненням: Цифрова камера: Повна внутрішня рефлексія
Ще одна публікація з більш детальною інформацією: Цифрова камера: Закон переломлення Снелла

Якщо ви використовуєте мапу висоти на поверхні, паралельній воді, \ ліворуч (s - \ vec {n} \ cdot \ vec {p} \ праворуч)стає \ ліворуч (s - h \ праворуч). Правильний коефіцієнт дорівнює 1, якщо сонце знаходиться безпосередньо над поверхнею води.
За допомогою точкового світла ви повинні розраховувати альфадля кожної вершини виходячи з відносного положення до джерела світла.

При фіксованому рівні води або фіксованому напрямку світла частини рівняння є постійними і їх не слід обчислювати в шейдері з міркувань продуктивності.

Плюси:

  • Швидкий і акуратний

Мінуси:

  • Працює лише для рівних водних поверхонь або лише для світла безпосередньо зверху, оскільки зазвичай враховується лише одна поверхня. (Поєднання шорсткої поверхні та нахиленого світла могло б деяким чином поширюватися при паралельному відображенні.)
  • Ніякої їдкої їзди

Тіньова карта

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

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

Плюси:

  • Працює з відносно складною геометрією води до тих пір, поки вона не закривається. *
  • Може бути використаний майже для будь-якого виду частково прозорого обсягу.

Мінуси:

  • Повільніше, ніж прямий розрахунок.
  • Потрібна додаткова VRAM для карти глибини.
  • Не на 100% точний.

* Ви можете визначити кількість води перед найближчою твердою поверхнею, порахувавши глибину від ПОВ світла таким чином:

  1. Наведіть суцільну геометрію на сцені як звичайну. Для кожного фрагмента ви додаєте значення глибини текстурі результату.
  2. Візуалізуйте лицьові сторони води, не оновлюючи буфер глибини, і відніміть глибини фрагментів від результату.
  3. Візуалізуйте зворотні грані таким же чином, але додайте до результату глибину фрагмента.

Тепер текстура результату містить кількість води перед світлом у просторі зору світла, тому значення потрібно перетворити назад, перш ніж використовувати його. Цей метод працює для обчислення спрямованого світла (мінус заломлення), але призведе до неправильного навколишнього світла, якщо поверхні дуже нерегулярні і між водоймами, що впливають на ті самі фрагменти, є велика кількість повітря.
Плюси і мінуси такі ж, як і для звичайного тіньового відображення, за винятком того, що вам потрібен ще один буфер під час обчислення глибини, а продуктивність гірша, тому що ви повинні намалювати більше геометрії.

Промінь-трасування

Рейкове трасування є на сьогодні найбільш точним, але також найдорожчим рішенням для надання прозорих томів. Це можна зробити двома способами: 1. Простеження від дна океану до поверхні та 2. Відстеження від джерела світла до води. Для розрахунку яскравості для кожної точки на підлозі потрібно кілька променів.

Плюси:

  • Правильно працює з кожною геометрією.
  • Правильна їдкість!

Мінуси:

  • Повільно!

Додаткові ефекти

Є кілька речей, які слід враховувати при подачі води:

Туман

Світло у воді знову розсіюється під час подорожі до спостерігача, тому вам слід змішати його із суцільним кольором.

Якщо спостерігач занурений , ви можете просто вивести туман на основі кінцевого результату глибини буфера. Колір туману, але не його густина, повинен змінюватися в залежності від відстані спостерігача від поверхні! (Minecraft використовує лише цю частину ефекту.)

Якщо спостерігач дивиться на воду зверху , потрібно обчислити туман, виходячи з різниці глибини між поверхнею та геометрією під водою. Колір туману повинен стати трохи темнішим при більших перепадах глибини, але він повинен змінюватися лише до тієї точки, де туман повністю непрозорий.

Колір туману також повинен залежати від напрямку перегляду для кожного пікселя, тому він виглядає трохи темніше при огляді вниз в обох випадках.

Підробка каустики

Якщо ви використовуєте безшовну плиткову 3D-текстуру замість наклейки для підробленої їдкої їжі, ви можете уникнути розтягування на вертикальні поверхні. Сила розсіяного світла біля поверхні змінюється в трьох вимірах, тому використання 2D-текстури зазвичай створює розтягнення десь на сцені. Можна моделювати зміни кутів світла, проектуючи вершинні положення підлоги в іншу систему координат.

Іншою можливістю є обчислення щільності світла на основі положення поверхні в системі координат світла, хоча це, швидше за все, коштуватиме певної продуктивності.

Каустики повинні згасати швидше, ніж розсіяне світло зі збільшенням глибини.

Кольоровий градієнт

Кольори розкидаються по-різному, тому світлий колір повинен змінюватися зі збільшенням глибини. Це також запобігає різким узліссям, де, наприклад, пляж перетинає водну поверхню.

Кут падіння

Через заломлення світло потрапляє на океанське дно набагато крутіше, ніж зазвичай. Стаття у Вікіпедії про закон Снелла має формули для кутів та векторів.


6

Я вважаю, що ефект освітлення неба в Minecraft прямо знижується - все затьмарюється тим, що знаходиться над ними, незалежно від того, де знаходиться сонце. Тоді місцеве освітлення від смолоскипів тощо застосовується з ефектом випадання - чим далі від джерела світла, тим менше світла отримує куб.

Якщо зробити це таким чином, кожен шар води сукупно затінить шар під ним, тож кожен стає прогресивно темнішим. Дерево листя надає подібний відтінок, однак воно не є кумулятивним. Ви отримуєте однаковий відтінок під деревом, будь то 1 або 100 кубів листя.

Одне поняття, що це метод, який застосовується, - це те, що вода не стає темнішою, якщо далі від глядача - лише під час спуску вниз. Так, ефект туману дає удар на відстань, але не ефект темної води.

Отже, основна формула для обчислення освітлення була б приблизно такою в псевдокоді ...

light_on_cube = 1.0
for each cube above target cube, from lowest to highest {
   if cube being examined is tree foliage
      light_on_cube = 0.5
   else if cube being examined is water
      light_on_cube = light_on_cube - 0.1
   else if cube being examined is solid 
      light_on_cube = 0
}

Це не ідеально підходить для розрахунку освітлення під навісами або в печерах, оскільки це було б темно під накидом за допомогою цього методу. Але можна додати як локальні джерела світла (смолоскипи, пожежі тощо), так і трактувати блоки, освітлені сонцем, як джерела світла. Щось подібне може це зробити ...

  1. Обчисліть світло від сонця безпосередньо зверху (через псевдокод) для кожного куба.
  2. Якщо у куба поруч є джерело світла, вважайте його повністю освітленим (1.0)
  3. Якщо куб не отримує сонця прямо зверху, дайте йому трохи світла залежно від того, наскільки далеко він знаходиться від повністю освітленого куба. Ближче означає більше світла, далі - означає менше (до нуля).

Ідея тут полягає в тому, що якщо куб освітлений сонцем або факелом, куб поруч також буде певним чином засвічений. І чим далі ви будете від того освітленого куба, тим менше світла буде. Це щось на зразок хитрості для оцінки дифузного освітлення, але я думаю (?), Що це спрацює.


1
Так, я майже впевнений, що це квиток. Я щось подібне зробив у своїй грі.
MichaelHouse

До речі, я щойно додав ваш блог до свого списку читачів google Byte56 - блоги розробників FTW!
Тім Холт

О, чому тобі дякую Все ще поза темою цього питання, але я щойно прочитав ваш блог про клас професора Бейлі. Я був у тому класі минулого року! Я впевнений, що ви також провели цю презентацію минулого року. Я думав, що твоє ім’я знайоме. Маленький світ :)
MichaelHouse

3

Можливо, я не розумію питання, але чому ви не можете просто змінити колір блоків залежно від їх глибини?

Якщо у вас глибина d (в блоках, починаючи з 0), то розумним рівнянням яскравості буде:

L = (1- m ) e - kd + m

Код: L = (1.0 - m) * exp(-k * d) + m;

k керує тим, наскільки швидко стає темніше (вище = швидше). Доцільне значення було б 0,5.
m - мінімальна яскравість, яку ви бажаєте.
L варіюється від 0 до 1.

Якщо ви не знаєте, як змінити колір блоків у будь-якому графічному API, який ви використовуєте, то запитайте це як окреме запитання (вказуючи, який API ви використовуєте та чи використовуєте ви шейдери чи ні).


Я просто не думав цього робити. Тільки з цікавості, звідки ви взяли це рівняння?
Ксав'є

1
@Xavier: Я щойно придумав. e^-kdБіт просто експонентний спад, який є стандартною функцією для речей , які поступово наближаються до нуля в протягом деякого значення (глибини). Множення на (1-m)додавання та додавання mпросто розширюють та компенсують занепад так, що він закінчується як мінімум, mале все ще починається з 1. en.wikipedia.org/wiki/Exponential_decay
Петро Олександр

Справа в тому, що блоки більш глибокого відтінку будуть видно лише в тому випадку, якщо блоки мають альфа-кольори; в такому випадку немає необхідності змінювати колір блоку, альфа створить ефект автоматично.
Джонатан Коннелл

@Jonathan: Ви не візуалізуєте водяні блоки, ви виводите блоки на морському дні з темнішим кольором, а потім просто маєте один альфа-шар на поверхні води.
Петро Олександр

@Peter Alexander Ok, я припускав, що в цих іграх типу блоків навіть вода була з блоків.
Джонатан Коннелл
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.