Створення стерео картинки - уроки 3ds max

Майже всі бачили стерео картинки, я, наприклад, пам'ятаю в школі у нас були зошити, на обкладинці яких були надруковані незрозумілі карлючки. Але якщо подивитися на них «правильно» - ці каракулі знаходили обсяг. Не у всіх виходило побачити їх відразу, кому це було просто, а хто так і не зміг. Є кілька рекомендацій як досягти ілюзію обсягу: дивитися «крізь» картинку або прикласти її впритул до носа і повільно відводити.

Як людина сприймає обсяг? Чим ближче розташований даний об'єкт, тим більше спостерігач «зводить» очі. Т.ч. якщо даний об'єкт розташований на нескінченно віддаленій відстані, то очі будуть дивитися на нього «паралельно».

Створення стерео картинки - уроки 3ds max

Тепер розглянемо як досягти ілюзію обсягу на плоскій картинці. Отже, у нас є людина з двома очима (рис.1): ліве око O1 і правий O2, а так само площину проекції стерео зображення D1 D2. Нехай даний об'єкт B розташований на відстані h від спостерігача. Тоді для того щоб «побачити» його на зображенні необхідно збіг малюнка в точках B1 і B2. Виходить ми тримаємо перед собою плоский малюнок і дивимося як би «крізь» нього. Ліве око бачить точку B1. правий B2. зображення для них на аркуші збігаються і створюється ілюзія того, що насправді ми бачимо об'єкт B. розташований за площиною листа. Т.ч. чим більше відстань між точками B1 і B2, в яких збігається зображення, тим «далі» сприймається об'єкт B.

Тепер виведемо пару розрахункових формул, які стануть в нагоді для прорахунку стерео зображення. Нехай a - відстань між очима, H - відстань до максимально віддаленого об'єкта, h - відстань до об'єкта h_max - відстань від максимально віддаленого об'єкта до площини проекції стерео зображення. З подоби трикутників BB1 B2 і BO1 O2 слід B1 B2 / BE = O1 O2 / BF. B1 B2 = d. O1 O2 = a. BF = h. BE = BF-EF. EF = AF-AE = H-h_max. BE = h- (H-h_max). Разом d / (h-H + h_max)) = a / h.

Мабуть теорії поки досить, можна перейти до практики. Для цього знадобиться Photoshop і 3dsMAX. У Photoshop'е створимо текстуру, з якої надалі буде побудовано стерео зображення. Кілька рекомендацій: чим більше на текстурі деталей, тим краще вийти остаточний варіант (з однотонними взагалі нічого не вийти); висота текстури повинна збігатися з висотою зображення (тому відразу треба визначити розмір фінального стерео зображення); а ширина залежить від того, де ви збираєтеся надалі використовувати отриману картинку. На рис.1 величина d_max якраз відповідає ширині текстури, тому в реальності (на екрані монітора або після роздруківки) вона не повинна перевищувати відстані між очима a. У той же час чим більше пікселів по ширині буде в вихідної текстурі, тим якіснішим вийти остаточний результат. У мене вийшла ось така текстура:

Ширина її 100 пікселів, а висота 600 (в результаті розраховую отримати стерео зображення розміром 800 на 600 пікселів). Хоча краще зробити її так, щоб вона Тайлі по горизонталі. Далі створюємо або відкриває будь-який 3D-об'єкт в MAX'е. Я для цього використовував модель оси:

Для результату не знадобляться ні матеріали, ні сторонні рендери, ні джерела світла. Тому якщо все це є в сцені, то призначаємо на все стандартний матеріал, видаляємо всі джерела світла і використовуємо Default Scanline Renderer. Тепер можна приступити до написання скрипта. Для цього в меню Max'а вибираємо MAXScript -> New Script і відкриється вікно редагування скриптів. Наш скрипт буде мати свій власний інтерфейс. Це можна реалізувати двома способами: на панелі Utilites або в окремому вікні. Виберемо перший варіант і створимо нову скрипт-утиліту:

utility stereoImg "Stereo Image"

-- утиліта створення стерео зображення

Отже, список необхідних елементів: дві групи Texture і Render. У групі Texture є мітка, в якій буде відображатися інформація про текстуру, і кнопка, яка запускає діалог відкриття файлу з текстурою. У групі Render - спиннер (визначає ширину рендеру), мітка (інформація про висоту рендеру), спиннер (якість стерео зображення), індикатор процесу, кнопка вибору об'єкта і «найголовніша» кнопка запускає скрипт на прорахунок. Для цього в тіло утиліти треба додати наступні рядки:

fn fltr_cam obj = superClassOf obj == camera - функція повертає істину якщо obj камера (для фільтра вибору)

label l_T_img "no texture" - інформація про розмір

button b_T_img "Load texture bitmap" - завантаження текстури

label l_R_h "height: 0" - висота

spinner s_R_prec "quality of stereo image" range: [1, 10, 4] type: #integer width: 80 align: #right - якість стерео зображення

progressBar prb_R_status value: 0 - прогрес рендеру

pickbutton pb_R_cam "Pick camera" filter: fltr_cam across: 2 - вибір камери

button b_R_stereo ""- запуск рендеру

На самому початку тіла утиліти (перед описом призначених для користувача елементів) визначимо локальні змінні, необхідні для створення стерео зображення:

-- визначення локальних змінних

local width, height, d_max - ширина і висота рендеру, ширина текстури

Залишилося прописати обробники подій від елементів призначеного для користувача інтерфейсу. Опис всіх подій має бути таким чином: on<имя элемента> <название события> <список аргументов>do (<обработка события>). Для початку опишемо поведінку Max'а на натискання кнопки завантаження текстури. Що повинно відбуватися: відкритися вікно діалогу вибору файлу, після вибору файлу текстури вона завантажується в MAX, виводитися на форму інформація про розмір текстури і її назва, далі ініціюються локальні змінні і налаштовуються глобальні параметри рендеру.

T_file_name = getOpenFileName types: "bitmap (*. Bmp) | * .bmp" - діалог відкриття файлу

b_T_img.caption = filenameFromPath T_file_name - на кнопку ім'я файлу картинки

d_max = T_img.width - оновлення всіх локальних змінних

width = 4 * height / 3 as Integer - визначення ширини рендеру так щоб в результаті вийшли пропорції 3: 4

s_R_w.value = width - відображення інформації про дозвіл рендера

renderWidth = width - настройка глобальних параметрів рендера (ширина, висота)

Визначимо вид, який ми бажаємо отримає на стерео зображенні. Для цього нам знадобитися камера. Якщо в сцені вже є камери з «потрібним» видом - відмінно, можна використовувати їх. Якщо немає, то необхідно створити камеру. Найпростіше перейти до виду Perspective, де вибрати необхідний ракурс. Після чого створити камеру з виду: в головному меню Create -> Cameras -> CreateCameraFromView або Crtl + C. Згадаймо теорію, там були такі параметри як H - відстань до максимально віддаленого об'єкта і h_max - відстань від площини проектування до максимально віддаленого об'єкта. Щоб не вводити їх вручну, будемо брати їх з властивості камери, а саме з Clipping Planes. Виберемо необхідну камеру, на панелі Modify розкриємо сувій Parameters. ставимо галочку біля Clip Manually. Залишилося тільки налаштувати Near Clip і Far Clip. Площина Near Clip відповідає площині проекції стерео зображення, а Far Clip - відстань до максимально віддаленого об'єкта. Рекомендації: об'єкти, по яким буде будуватися стерео зображення, повинна розташовуватися між Far Clip і Near Clip. Near Clip дорівнює приблизно половині Far Clip. об'єкти повинні бути розташовані подалі від Near Clip і впритул до Far Clip. Хоча можна цим нехтувати і надалі експериментувати з Clipping Planes. для отримання більш бажаного результату. Т.ч. вид налаштований, залишилося «пояснити» скрипту, що ми будемо працювати з цією камерою, для цього напишемо обробник натискання кнопки вибору камери:

pb_R_cam.caption = pb_R_cam.object.name - на кнопку ім'я камери

-- обробка зміни ширини рендеру

Залишилося обробити остання подія - натискання «найголовнішою» кнопки. Знову доведеться зайнятися теорією на основі отриманої раніше формули: d / (h-H + h_max)) = a / h. Тут a. H і h_max - Констан. Останні дві получаються з Clipping Planes камери. Визначимо a. для цього підставимо замість h величину H - крайній випадок, коли об'єкт знаходиться на максимальній відстані. Звідси a = d_max * H / h_max. І остання формула: d = a * (h-H + h_max) / h - по ній обчислюється відстань між точками d. зображення в яких збігаються, для того щоб створювалася ілюзія, що даний об'єкт розташований на відстані h від спостерігача.

-- прорахунок стерео зображення

DOF_img = render camera: pb_R_cam.object outputwidth: width outputheight: height channels: # (#zDepth)

-- створення бітмапи для майбутнього стерео зображення

-- заповнення першої смуги текстурою

display OUT_img - відкриваємо вікно з фінальним стерео зображенням

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

Нижче наведено повний лістинг скрипт-утиліти з деякими поліпшеннями:

utility stereoImg "Stereo Image"

-- утиліта створення стерео зображення

-- визначення локальних змінних

local width, height, d_max - ширина і висота рендеру, ширина текстури

fn fltr_cam obj = superClassOf obj == camera - функція повертає істину якщо obj камера (для фільтра вибору)

label l_T_img "no texture" - інформація про розмір

button b_T_img "Load texture bitmap" - завантаження текстури

label l_R_h "height: 0" - висота

spinner s_R_prec "quality of stereo image" range: [1, 10, 4] type: #integer width: 80 align: #right - якість стерео зображення

progressBar prb_R_status value: 0 - прогрес рендеру

pickbutton pb_R_cam "Pick camera" filter: fltr_cam across: 2 - вибір камери

button b_R_stereo ""- запуск рендеру

T_file_name = getOpenFileName types: "bitmap (*. Bmp) | * .bmp" - діалог відкриття файлу

b_T_img.caption = filenameFromPath T_file_name - на кнопку ім'я файлу картинки

d_max = T_img.width - оновлення всіх локальних змінних

width = 4 * height / 3 as Integer - визначення ширини рендеру так щоб в результаті вийшли пропорції 3: 4

s_R_w.value = width - відображення інформації про дозвіл рендера

renderWidth = width - настройка глобальних параметрів рендера (ширина, висота)

prb_R_status.value = (100 * x / d_max) as integer - оновлення прогресу поточної операції

-- отрисовка "розширеного" стерео зображення

H = pb_R_cam.object.farclip - відстань від очей до максимальної глибини

h_max = pb_R_cam.object.farclip - pb_R_cam.object.nearclip - відстань від площини проектування до макс. глибини

-- порядкове побудова стерео зображення ( "розширене")

-- беремо з рендера глибини відстань від камери до найближчої точки (тут теж йде лінійне згладжування)

-- отримання з "розширеного" "нормальне" стерео зображення

FIN_img = bitmap (width + d_max) height color: black - створення бітмапи для фінального стерео зображення

display FIN_img - відкриваємо вікно з фінальним стерео зображенням

Зміна тут тільки в алгоритмі прорахунку стерео зображення. Додалася перевірка «від дурня»: прорахунок не почнеться, якщо не була обрана текстура і камера. Так само тут був реалізований алгоритм «поліпшеного» розрахунку стерео зображення з використанням спиннер «якість». У двох словах в чому це полягає: зображення спочатку лінійно розтягується і прорахунок ведеться теж «розтягнуто», а потім отриманий результат стискається до початкового розміру. Різницю можна побачити на рис.7, рис.8, рис.9.

На рис.7 якість виставлено в одиницю, а на рисунку 8 і 9 - 4 і 8 відповідно. Звичайно чим вище якість, тим більше час прорахунку.

От і все. Тепер у нас є скрипт-утиліта, за допомогою якої можна з будь-якого 3D-об'єкта отримати стерео зображення. Дуже сподіваюся що урок був зрозумілий (для цього треба дружити з геометрією :) і корисний (принаймні тим, що можна дійсно подивитися свої моделі «в обсязі»).

Схожі статті