Статьи

Путешествие в следующее измерение с Papervision3D: Часть 2

Продолжая с первой части, где мы познакомились с Papervision3D, вторая часть продвигает нашу сцену немного дальше. В этой заключительной части вы узнаете, как заставить свои вращающиеся кубы выпрыгивать на зрителя в 3D-очках.




Я знаю, там написано, что этот урок о Papervision3D во Flash, но на самом деле о том, как обмануть свой мозг. Мы заставим ваш мозг думать, что он смотрит на настоящий трехмерный объект, тогда как на самом деле он просто видит двухмерное изображение.

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

Конечный результат

Направьте палец в воздух и держите его перед носом.

Смотрите на это только правым глазом, а затем левым. Видите, как кажется, что он прыгает слева направо, а то, что находится на заднем плане, почти не движется?

Ваш мозг замечает эти различия между тем, что видит каждый ваш глаз, и использует их для создания полной трехмерной симуляции окружающего вас мира. В твоей голове. Мгновенно. Очень впечатляет!

Если мы собираемся обмануть ваш мозг, нам нужно, чтобы каждый ваш глаз видел различное изображение. Итак, для начала давайте добавим еще один «глаз» (еще одну камеру) к сцене. Это будет использоваться для создания изображения для вашего правого глаза, в то время как изображение с существующей камеры будет подаваться на ваш левый глаз.

Создайте новую публичную переменную с именем camera2

1
public var camera2:Camera3D;

… и инициализировать его в функции конструктора Main ():

1
camera = new Camera3D();

Нам понадобится еще один видовой экран для вывода с камеры, поэтому добавьте следующее:

1
public var viewport2:Viewport3D;
1
2
3
viewport2 = new Viewport3D();
viewport2.autoScaleToStage = true;
addChild( viewport2 );

(Я тороплюсь через это, так как мы покрывали все это в прошлый раз .)

Помните, viewport2 будет пустым, пока мы не отобразим что-либо для него. Нам не нужно создавать новый рендер для этого; просто используйте существующий дважды. Итак, в Main (), возьмите этот код:

1
2
renderer = new BasicRenderEngine();
renderer.renderScene( scene, camera, viewport );

… и добавьте новую строку для рендеринга второй камеры во второй видовой экран, вот так:

1
2
3
renderer = new BasicRenderEngine();
renderer.renderScene( scene, camera, viewport );
renderer.renderScene( scene, camera2, viewport2 );

Сделайте то же самое в функции onEnterFrame ():

1
2
renderer.renderScene( scene, camera, viewport );
renderer.renderScene( scene, camera2, viewport2 );

Если вы протестируете это сейчас, это будет выглядеть так же, как и раньше:

Но это не удивительно: во-первых, две камеры находятся в одном месте!

По умолчанию камеры расположены в (x = 0, y = 0, z = -1000) — вы можете проверить, выполнив трассировку (camera.x, camera.y, camera.z) .

Итак, если смотреть сверху, сцена выглядит так:

Сверху

При взгляде со стороны это выглядит так:

Со стороны

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

Сверху, бок о бок

Давайте попробуем переместить его на 50 пикселей вправо, и посмотрим, что произойдет. Поместите это в Main (), где-нибудь после создания второй камеры:

1
camera2.x = camera.x + 50;

Теперь проверьте это и посмотрите, что вы получите:

За тобой!

Это выглядит странно, но это явно работает! Область вокруг кубов прозрачна в каждом окне просмотра, поэтому первый виден за вторым.

Будет полезно иметь возможность посмотреть на то, что видит каждая камера в отдельности, поэтому давайте продолжим аналогию с глазами, заставляя их «подмигивать», когда мы нажимаем. Сначала добавьте прослушиватель событий в каждое окно просмотра, чтобы обнаружить щелчки мыши:

1
2
viewport.addEventListener( MouseEvent.CLICK, onClickViewport );
viewport2.addEventListener( MouseEvent.CLICK, onClickViewport2 );

Поместите это в конец функции Main (). Не забудьте импортировать MouseEvent:

1
import flash.events.MouseEvent;

Добавьте эти новые функции в свой код вне функции Main (), но внутри класса Main:

1
2
3
4
5
6
7
8
9
public function onClickViewport( mouseEvt:MouseEvent ):void
{
     
}
 
public function onClickViewport2( mouseEvt:MouseEvent ):void
{
     
}

Когда мы щелкаем первый видовой экран, нам нужно, чтобы он стал невидимым, а второй стал видимым (и наоборот, когда мы щелкаем по второму). Поэтому измените ваши обработчики событий следующим образом:

01
02
03
04
05
06
07
08
09
10
11
public function onClickViewport( mouseEvt:MouseEvent ):void
{
    viewport.visible = false;
    viewport2.visible = true;
}
 
public function onClickViewport2( mouseEvt:MouseEvent ):void
{
    viewport.visible = true;
    viewport2.visible = false;
}

Попробуйте это:

Обратите внимание, что вы должны нажать на один из кубов; прозрачные области не запускаются щелчком мыши.

Вам понадобится пара 3D-очков «анаглиф»; вид, где каждый объектив другого цвета. У пары, которую я использую, желтая линза в левом глазу и синяя линза в правом глазу (так называемая система ColorCode3D ), но также популярны другие цветовые комбинации, такие как красный и голубой.

Вы можете найти эти очки на eBay за несколько долларов: просто найдите 3d-очки

Кроме того, вы могли бы даже сделать свой собственный! Андре разместил комментарий к первой части этого урока, объяснив, что он использует коробки Tic-Tac разных цветов, чтобы сделать его. Genius!

Хотя мои примеры будут сделаны для желтых и синих очков, я постараюсь объяснить принципы, чтобы вы могли заставить свои работать с любыми цветами, которые у вас есть. Это немного сложно, поэтому, пожалуйста, оставьте комментарий ниже, если это сбивает с толку!

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

Регулируя яркость каждой «лампочки», мы можем изменить цвет этого пикселя:

  • В белом пикселе все три лампы включены полностью
  • Черный пиксель отключил все три
  • Зеленый пиксель включает зеленую лампочку, а два других выключены

Линзы в 3D-очках не дают свету некоторых из этих ламп пройти. Например, синяя линза в моих очках блокирует попадание красного и зеленого света в мои глаза.

Поэтому, если я посмотрю на это изображение через свой синий объектив, я не смогу прочитать его:

Шрифт от urbanpixel.ca

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

Хорошо, если вы попробуете это сами, вы можете обнаружить, что это не совсем так. Ваш объектив, вероятно, не на 100% чистый синий, поэтому он пропускает немного зеленого и красного света. Или, возможно, цвета вашего монитора откалиброваны не так, как у меня, поэтому зеленый текст содержит небольшое количество красного и синего. Если честно, то же самое верно и для меня, но это не имеет значения, если текст трудно читать.

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

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

На этом шаге давайте сделаем так, чтобы изображение левого видового окна излучало только красный и зеленый свет; нет синего света вообще. Мы можем сделать это с помощью ColorTransform . Это легко настроить; во-первых, импортируйте класс:

1
import flash.geom.ColorTransform;

Затем создайте новую открытую переменную для хранения экземпляра класса:

1
public var leftViewportColorTransform:ColorTransform;

Теперь фактически создайте этот новый экземпляр внутри Main ():

1
leftViewportColorTransform = new ColorTransform();

Наконец, скажите Flash, чтобы применить это ColorTransform к области просмотра левой камеры:

1
viewport.transform.colorTransform = leftViewportColorTransform;

(Эта последняя строка должна быть после того, как вы создали видовой экран и преобразование цвета. Вероятно, проще всего просто поместить его прямо в конец Main ().)

SWF пока не будет выглядеть по-другому, потому что ColorTransform по умолчанию не меняет изображение.

Мы можем выбрать, какую долю света каждой лампы мы хотим пропустить, изменив свойства нашего ColorTransform blueMultiplier , greenMultiplier и redMultiplier .

Установка любого из них в 1 указывает Flash оставить его в покое, в то время как установка 0 указывает Flash полностью отключить его. И, естественно, 0,5 заставит его выдавать половину нормального значения, 2,0 — удвоить его, и так далее.

Таким образом, чтобы удалить все синее из левого окна просмотра, мы можем сделать это:

1
leftViewportColorTransform.blueMultiplier = 0;

Вы должны поместить это перед строкой, которая применяет преобразование к области просмотра, как это:

1
2
leftViewportColorTransform.blueMultiplier = 0;
viewport.transform.colorTransform = leftViewportColorTransform;

Если у ваших очков красная или зеленая линза над правым глазом, вам следует установить для redMultiplier или greenMultiplier значение 0 , а не синий. Для любых других цветов вам нужно использовать смесь красного, зеленого и синего цветов — подробнее об этом в Шаге 15.

Проверьте SWF:

Посмотри как они сияют для тебя

Трудно разглядеть «а» логотипа через синюю линзу, но совсем не сложно разглядеть форму кубиков на этом белом фоне!

Если вы измените фон вашего SWF, вы увидите проблему. Поскольку «белая» область вокруг кубов на самом деле не белая, а скорее прозрачная и пропускает белый фон SWF, на нее не влияет ColorTransform.

Чтобы это исправить, нам нужно дать вьюпорту белый фон, поверх которого будут отображаться кубы. Это достаточно просто; просто вернитесь туда, где вы создали видовой экран:

1
2
viewport = new Viewport3D();
viewport.autoScaleToStage = true;

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

1
2
3
4
viewport = new Viewport3D();
viewport.autoScaleToStage = true;
viewport.graphics.beginFill( 0xffffff );
viewport.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );

Проверьте это снова:

И это было все желтое

Изображение должно быть действительно тусклым, тусклым и плохо видимым через правую линзу, но таким же простым, как обычное изображение, видимым через левую линзу.

Нам нужно сделать то же самое для правильного окна просмотра сейчас. Итак, создайте ваш новый ColorTransform:

1
public var rightViewportColorTransform:ColorTransform;
1
rightViewportColorTransform = new ColorTransform();

Теперь примените это новое преобразование к правому окну просмотра:

1
viewport2.transform.colorTransform = rightViewportColorTransform;

Так как моя левая линза желтая, мне нужно слить все желтое из этой области просмотра.

(Если левый объектив красного цвета, этот шаг прост — просто установите красный множитель вашего нового ColorTransform на 0.)

Но как мы можем получить желтый из красного, зеленого и синего?

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

Изображение из Wikimedia Commons - спасибо, Майк Хорват и якоболус!

Изображение из Wikimedia Commons. Спасибо, Майк Хорват и Якоболус!

Как вы можете видеть, красный и зеленый смешиваются друг с другом, чтобы сделать желтый (что объясняет, почему фон левого видового экрана стал желтым, когда мы удалили весь синий). Таким образом, чтобы удалить весь желтый цвет с нашего изображения, нам нужно удалить весь красный и весь зеленый!

Мы можем сделать это так:

1
2
3
4
rightViewportColorTransform = new ColorTransform();
rightViewportColorTransform.redMultiplier = 0;
rightViewportColorTransform.greenMultiplier = 0;
viewport2.transform.colorTransform = rightViewportColorTransform;

Да, мы тоже должны заполнить фон этого окна.

Сделайте это так же, как и раньше:

1
2
3
4
viewport2 = new Viewport3D();
viewport2.autoScaleToStage = true;
viewport2.graphics.beginFill( 0xffffff );
viewport2.graphics.drawRect( 0, 0, stage.stageWidth, stage.stageHeight );

Проверьте это:

да ба де да ба ди

Желтая линза на моих очках фактически пропускает достаточное количество синего света, но при просмотре через эту линзу она намного темнее, чем при просмотре через синюю, и это главное.

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

Самый очевидный способ, позволяющий нам увидеть оба этих элемента одновременно, — это уменьшить альфа (прозрачность) области просмотра спереди, примерно так:

1
viewport2.alpha = 0.5;

Проверьте это:

мутный

Ах. ОК, не воодушевляющий успех. Альфа явно не подходящий инструмент для работы; что еще у нас есть?

Flash дает нам выбор из нескольких различных режимов наложения, методов наложения двух экранных объектов вместе. Здесь их полный список, но я просто сосредоточусь на том, который называется add .

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

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

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

Получить картину? Отлично, давайте использовать это. Удалите только что написанную строку, которая корректировала альфа, и замените ее следующим

1
viewport2.blendMode = «add»;

(Нам не нужно применять режим наложения для обоих видов, только тот, который находится вверху списка отображения .)

Проверьте это:

Великий Скотт!

Это больше походит на это!

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

Тем не менее, изображение еще не совсем выглядит 3D. Давайте разберемся с этим.

Даже если ваши глаза видят разные изображения, этого недостаточно, чтобы обмануть ваш мозг. Понимаешь, твой мозг довольно умен; некоторые даже сказали бы, что это самый умный орган в вашем теле. Он знает, что он должен использовать другие ключи, чтобы понять, является ли объект трехмерным или нет. Перспектива, например: как маленький объект должен выглядеть в зависимости от того, как далеко он находится.

Основная проблема здесь заключается в том, что наши две камеры расположены намного дальше друг от друга, чем наши собственные глаза. Это не удивительно, так как я выбрал число 50 наугад.

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

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

Добавьте эту строку в функцию onEnterFrame ():

1
camera2.x = camera.x + ( mouseX * 0.1 );

Теперь измените ваши функции onClickViewport и onClickViewport2 следующим образом:

1
2
3
4
5
6
7
8
9
public function onClickViewport( mouseEvt:MouseEvent ):void
{
    trace( camera2.x — camera.x );
}
 
public function onClickViewport2( mouseEvt:MouseEvent ):void
{
    trace( camera2.x — camera.x );
}

Запустите SWF и переместите мышь влево и вправо. Это будет выглядеть так:

Наденьте очки, выключите свет и продолжайте двигать мышь, пока не найдете «сладкое пятно», где изображение кажется сплошным. Щёлкните мышью в этой точке, и она найдет номер. Запишите это!

Как только вы нашли оптимальное разделение, удалите строку, которую мы только что добавили в функцию onEnterFrame ():

1
camera2.x = camera.x + ( mouseX * 0.1 );

Теперь найдите строку в Main (), где вы устанавливаете расстояние между камерами:

1
camera2.x = camera.x + 50;

… и замените 50 на любое число, которое вы записали. Я обнаружил, что 18 был хорошим номером для моей установки.

Наконец, проверьте SWF:

ThreeeeeDeeeee

Та-да!

Отлично, вы создали 3D-сцену во Flash и использовали некоторые хитрости, чтобы перенести ее в 3D-сцену внутри вашей головы;)

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

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

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