HTML и CSS безумие [перевод]

    или Создаем 3D миры при помощи HTML, CSS и JS


    image
    В прошлом году, я сделал демо, которое показывает как можно использовать CSS 3D transforms для создания 3D пространства. Демо было технической демонстрацией того, чего можно достичь при помощи CSS на то время, но я хотел увидеть насколько далеко я могу зайти, поэтому последние несколько месяцев я работал над новой версией с еще более сложными моделями, реалистичным освещением, тенями и определением столкновений. Этот пост документирует то, как я это делал и какие техники применял.

    Демо Демо2

    Создаем 3D объекты


    В современных 3D движках объекты хранятся в виде набора точек (или векторов), каждая имеет X, Y и Z значение для декларации своей позиции в 3D пространстве. Квадрат, к примеру, будет определен 4-мя векторами, по одному для каждого угла. Каждым из векоторов можно манипулировать в индивидуальном порядке, перемещая его по X, Y и Z осям, позволяя тем самым квадрату вытягиваться в различные фигуры. Визуализатор 3D движка будет использовать эти векторы и множество умной математики чтобы нарисовать 3D объект на Вашем 2D экране.
    С CSS трансформациями все наоборот. Мы не можем задавать произвольные фигуры набором точек, наши руки связаны HTML элементами которые постоянно квадратные и имеют двухмерные свойства, такие как top, left, width и height для обозначения их позиций и размеров. Но во многом, это делает работу с 3D намного проще, так как в таком случае нет сложной математики — просто нужно применить CSS трансформацию для поворота элемента вокруг осей и готово!
    Создавать объекты из квадратов может сначала показаться ограниченным методом, но Вы можете создать удивительное их количество, особенно когда начнете играть с PNG альфа каналами. На рисунке снизу Вы можете наблюдать, как верхняя часть бочки и колесо кажутся круглыми, не смотря на то что созданы из квадратов.

    image

    пример 3D объектов, созданных полностью из квадратных <div> элементов


    Все объекты созданы при помощи JavaScript, используя набор методов для создания примитивной геометрии. Самый простой объект, который может быть создан, — это плоскость, которая по существу обычный <div> элемент. Плоскости могут быть добавлены в наборы, обертка <div> над ними позволяет всему объекту вращаться и перемещаться как одной сущности. Труба это набор плоскостей, повернутых вокруг осей и бочка это труба с плоскостью на верху и плоскостью внизу.

    Данный пример показывает сказанное на практике, взгляните на вкладку JS.

    Свет


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

    image
    скриншот комнаты без освещения


    Как я уже говорил, объект в обычном 3D движке задается сериями векторов. Чтобы высчитать свет, эти векторы используются для подсчета «нормали» которая может быть использована для определения количества света, которое отражается от центральной точки поверхности объекта.
    Это формулирует проблему, когда создаешь 3D объекты при помощи HTML элементов, потому что этих векторов не существуют. Итак первое препятствие — написание набора методов для вычисления четырех векторов (по одному на каждый угол) для элемента который был трансформирован при помощи CSS, по которым может быть рассчитан свет. Как только я это определил, сразу начал экспериментировать с различными способами освещать объекты.

    В моем первом эксперименте я использовал несколько фоновых изображений для имитации света падающего на поверхность, путем объединения linear-gradient с картинкой. Эффект использует градиент, который начинается и заканчивается с тем же rgba значением, производя сплошной цветовой блок. Измение значения альфа канала позволяет лежащему ниже изображению просвечиваясь через цветовой блок, создавать иллюзию затенения.

    image
    пример использования градиента для затенения текстуры


    Чтобы достичь максимально темного эффекта на изображении сверху я применил слудующие стили:
    element {
        background: linear-gradient(rgba(0,0,0,.8), rgba(0,0,0,.8)), url("texture.png");
    } 
    

    Практически, эти стили не задекларированы заранее, они высчитываются динамически и применяются прямо к аттрибуту style элемента, используя JavaScript.
    Эта технику называют плоским затенением (flat shading). Это эффективный метод затенения, но его результат — вся поверхность имеет одинаковую детализацию. Например, если я создаю 3D стену которая отдаляется на определенное расстояние, она будет затенена одинаково на протяжении всей своей длины. Мне хотелось сделать что-нибудь более реалистичное.

    Второй штурм освещения


    Чтобы имитировать реальное освещение, поверхности необходимо затемняться при отдалении от источника света, и если мы имеем несколько таких источников, падающих на поверхность, она должна быть затенена соответственно.
    Для плоской затененной поверхности, мне нужно было только рассчитать свет, падающий на центральную точку, но теперь мне нужно замерять свет в различных точках поверхности, чтобы определить, насколько освещенной или затененной должна быть каждая из точек. Математика требовала создания этой световой информации так же, как и в случае плоского затенения. Я пытался создавать radial-gradient из световой информации, чтобы использовать его на месте linear-gradient моей предидущей попытки. Результаты были более реалистичны, но множественные источники света были до сих пор проблемой, так как наслаивание нескольких градиентов один на другой постепенно затемняет низлежащие текстуры. Если бы CSS поддерживало совмещение изображений и режимы смешивания(blending они на подходе), имелась бы возможность заставить радиальные градиенты работать.
    Решением было использование <canvas> элемента, для программной генерации новой текстуры, которая может быть использована как световая карта. При помощи рассчитанной световой информации я смог нарисовать наборы черных пикселей, у каждого изменяя альфа канал, исходя из количества света, которое должно падать на поверхность в данной точке.
    В конце я использовал canvas.toDataURL() метод для закодирования изображения, которое использовал вместо linear-gradient моего первого эксперимента. Повторяя этот процесс, для каждой поверхности, я воспроизвел эффект реалистичного освещения для всего пространства эксперимента.
    Высчитывание и рисование таких тесктур — напряженная работа. Потолок и пол подвала, оба имеют размер в 1000х2000 пикселей, создаая текстуру для покрытия всей этой площади не очень практично, поэтому я замеряю свет только каждые 12 пикселей, что производит световую карту в 12 раз меньшую, чем поверхность, которую она покроет.
    Установка background-size: 100% заставляет браузер масштабировать текстуру, используя билинейную (или похожую) фильтрацию, поэтому световая карта покрывает всю нужную нам поверхность. Эффект масштабирования создает результат который практически полностью идентичен к световой карте, сгенерированной для каждого пикселя.

    Пример правила стиля для фона, которое применяется для задания световой карты и поверхности, выглядит примерно так:

    element {
        background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAyCAYAAAAqRkmtAAACiUlEQVRoQ9VZa0vEQAy8+/8/yPcbFVFEEREREcS/4eNSmpKmmU12t/XYD6W3beGmk8kk2a5Xq9XR5vjeHD+B47d/xjrTtdSxud3dl+d+OTmt+yvDmX4c9n8eAbtVoAeCyRRYBknMb4XRfRVyC6wEqYFK0Cj0HG4OfSr8HG56ZhR6AhoJO2u4JPxInxK4BDYCyYu9YOglk/xbsymvy8RhpjlrNEArqWRCrWlBQEsYRWC98EfAmlm/W8FoBKzWp2aT11KbZugJ6NKMzpJMFlANPJX1Wpdyjdj0NKozv9PoTjD0pRYVDb2b9VGgVtZHNKrLpuelEzb5DRhoRKel1alGox1wGfpmgFIYPbCIUa+MRiuTpdMJox7QrSdTM/bUlOFzZ7SERqM+6pbQZpqSZtq8ZhrnZkaRZoa7ZsblZjYgjgM1vmYClYOd1+LBnpRM9iQItLTFQ/0o6vLhXH9aCTTaOWnAehp1K9NZP4rUbufo2UnP9ajV82b6Tg70FudiZtKtHmrtoiOIpU9PpzD0Fwoo2n6sbZo9gJJZcwq9DGi0JpFyhjuoU7pxtSCjtXP9aDfv2gFaOoJofaKst5JJ+ukwM91kMirtyMp0a5MsOtyZicSobxMaLd3K0dZksZlt+HcZoUdsImZTY0g20PtA6OeypohFQR99MCqTDnnqA0NKp7My+ggYjerz34A+LQRUsolCrnWaNPznSqCoeyo1e/bV0T4+LV4KgCJwJAPNZI41WfV+MPzXAFDLluY2e83kqDoR2jcD6ByJhJoRrUtea31OgL4roCU9aORDWMRDIav0Fh890JR3lnz/1GVU1nsGlJX1n4UaRQkV6ZpQ+Uwm05ej0TkSycp8i2Hoo3+utUtvDhk9pwAAAABJRU5ErkJggg==") 0 0 / 100% 100%, url("texture.png") 0 0 / auto auto;
    }
    


    Что создает освещенную поверхность:

    image
    изображение масштабированной и наложенной на текстуру световой карты маленького разрешения


    Наложение теней


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

    image
    скриншот результирующей комнаты с освещением и тенями


    Определение столкновений


    Для определения столкновений используется карта высоты — изображение ниже использует цвет для отображения высоты объектов на нем. Белый цвет отображает наиболее глубокую, а черный наиболее высокую из возможных позиций, которую игрок может достичь. По мере того как играющий двигается по уровню, я конвертирую его позицию в 2D координаты и использую их для проверки цвета на карте высоты. Если цвет светлее чем последняя позиция игрока, он падает, если цвет немного темнее, игрок может подняться или подпрыгнуть на объект. Если цвет значительно темнее, игрок останавливается, я пользуюсь этим для задания стен и препятствий. Это изображение нарисованно от руки, но оно смотрится так же, как и созданное динамически.

    image
    изображение карты высоты и ее отношение к уровням


    Что дальше?


    Ну, полноценная игра будет логичным следующим шагом — будет интересно посмотреть, насколько масштабируемы эти техники. Пока что я начал работать над прототипом CSS3 renderer  для восхитительной Three.js  которая использует те же техники для рендеринга геометрии и света, созданных настоящим 3D движком.

    первоисточник
    Метки:
    Поделиться публикацией
    Похожие публикации
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 71
    • +14
      Впечатляет. Голь на выдумки хитра.
      • +33
        Вот только не забудьте отметить, что это перевод (на хабре есть отдельный тип топика для этого). Помимо этого, там не только HTML и CSS, но и много JS, я бы об этом тоже не забыл упомянуть в начале статьи, а то сбивает с толку.
        • +3
          Хм… думал что достаточно публикации в хаб «переводы». Пост перенести не успел, но добавил в заголовок [перевод]? В следующий раз обязательно! Спасибо!
          • +1
            По качеству текста понятно, что это перевод. И понятно, чем он был сделан.
          • +47
            У меня безбожно глючит демка, все текстуры мигают, а управлениее вообще слабо поддается контролю, один раз W нажал, и он так и будет пару минут вперед бежать. Mac OS, Chrome 24
            • +5
              Mac, Safari 6.0.2, все безупречно и сверхгладко.

              Keith Clark — монстр, я даже не думал, что такое возможно.
              • 0
                У меня на маке в Chrome глючит, в Safari работает без нареканий. Интересно.
              • +2
                Теже самые проблемы на том же самом железе, софте. Даже поворот мышки заставляет все мигать. В первой демке что была в кубической комнате с Маком (старой версии) такого не было. А так очень интересно, надо будет на досуге попробовать.
              • +3
                Все мигает, но при этом управление персонажем и движение идеально плавное.
                Chrome 24.0.1312.57, Windows.
                • +2
                  Windows:

                  Chrome: анимация плавная, но текстуры периодически пропадают
                  Firefox: безбожно тормозит и глючит, постоянные пересечения и дырки в стенах
                  Safari: анимация плавная, текстуры не пропадают, но при движении мыши картинка не обновляется (если постоянно ходить, не так заметно)
                  Opera: ничего
                  IE: ничего

                  P.S. Приехали. Теперь Опера в списке рядом с IE… Причём с IE неоднозначно, потому что CSS 3D transforms он теоретически поддерживает, а вот демка использует только webkit префиксы (Chrome, Safari, Opera) и безпрефиксную запись (Firefox).
                  • 0
                    Opera уже на самом деле давно рядом с IE, я уже даже не рискнул эту демку запускать в ней. 99% эксперементальных проектов вроде этого в Опере не работают, что крайне печально.
                    • 0
                      Opera на webkit поправит дело. Хоть и есть поговорка как исправить горбатого, но вектор развития крайне верный.
                  • 0
                    А у меня все гладко и быстро работает под 8й вендой на хроме, но тоже все моргает во время движения, насколько я понял это глюк буфера глубины, каждый раз отдельные полигоны оказываются то ближе других, то дальше… И еще мышь при переключении вкладок продолжает отлавливаться, не знаю баг это или фича…

                    Но в целом я впечатлен!
                    • 0
                      Win7х64+последний Chrome, мигание и невозможность перемещаться кроме как вперед, машина зависла в адских мучениях, даже курсор мыши ощутил на себе частичный паралич и сублимацию.
                      А так, я впечатлен полетом мысли. Полагаю, это движок JS+CSS+HTML5 пока еще не вырос.
                      • 0
                        так-же. Но мне показалось что это от железа зависит. Проц интел, видяха встроенная. Win 7 chrome
                      • +6
                        Жуткие тормоза и глюки. Стены просвечивают, текстуры теряются. А потом вообще комп повис.
                        До игры тут очень далеко.
                        • +13
                          Это ведь демка. И очень крута.
                          • 0
                            Согласен. Не ожидал такого увидеть.
                            Но в текущем состоянии её сложно применять. Разве что для создания объемных логотипов.
                            • НЛО прилетело и опубликовало эту надпись здесь
                              • +1
                                кто как хочет, тот так и проводит свое свободное время.

                                Именно.
                                А работа интересная. Тем и крута.
                          • 0
                            Очень сильно. Мой рабочий комп это всё дело не тянет, но моё впечатление не передать словами ))
                            • +20
                              Как же круто осознавать как всё повторяется и повторяется. Сначала был 2d в дос потом появился глючный 3d. Потом тоже самое с directX. Потом в вебе на флеше. Сейчас в вебе на css3. Причём это всё с кучей восторженных и проклинающих комментариев и постов. Сначала в фидо теперь на хабре. Это если очень кратко. Причём тенденция сохраняется, как только это всё «прокачается» на предыдущем уровне, появляется новый, где опять глючат коллизии, видны стыки и прочее прочее…
                              =)
                              • +2
                                Это прогресс, %username%!
                              • +1
                                Ubuntu 13.04
                                Opera Next — не работает
                                Chrome Unstable — аналогично
                                Firefox — дикие глюки и тормоза

                                Очень хотел посмотреть, но не получилось. И да, комп достаточно мощный.
                                • 0
                                  Автор утверждает что лучше всего смотреть в Safari
                                  • +12
                                    Чего же это у вас такое нестабильное ПО?
                                    При чем начиная с ОС
                                    • +2
                                      Привык уже, интересно новшества пробовать, составлять баг репорты.
                                      Текущая Ubuntu намного стабильнее того, что было в 12.10 — там было очень весело. 13.04 уже можно использовать.

                                      К стати, у меня ещё ядра из GitHub, но на фоне остального «unstable» это уже не так критично.
                                  • +15
                                    А у меня в хроме вообще все плоское.
                                    Скрин
                                    Free Image Hosting at www.ImageShack.us
                                    • +1
                                      Было аналогично. а когда полез смотреть webgl демки выяснилось что у меня еще и webgl отключен. В общем пошаманил с about:flags, включил webgl, а заодно и демка заработала…
                                      • 0
                                        Действительно, по умолчанию WebGL отключен.
                                    • +4
                                      В Chrome все отлично, FF глюки. Судя по всему оптимизировано под webkit
                                      • +1
                                        Ждем CSS-фреймворк по типу бутстрапа для 3d-моделинга =)
                                        • 0
                                          Тогда уж по типу, например, Unreal Engine… а что — bomberman в 3D…
                                        • 0
                                          Старенький Мак, Хром 24.0.1312.57 — все плавно и гладко.
                                          Восхищен.
                                          • 0
                                            Старенький lenovo t61 + хром 24.0.1312.57 — все немного дергается…
                                            • 0
                                              MBP late 2008 — гладко, вообще без рывков…
                                              • –3
                                                Браузер самим выдумать?
                                          • +10
                                            Мигает в хроме, как новогодняя ёлка.
                                            • +3
                                              Даешь CS 1.6 для браузера!
                                              • 0
                                                Даешь Steam для браузера!
                                                  • 0
                                                    Первая ссылка просто шедевральна!
                                                    Я в восторге =)
                                                    • 0
                                                      Тогда вам должно понравиться и www.quakelive.com/
                                                      • +3
                                                        Дык это же плагин, так не прикольно.
                                                        • +1
                                                          +1
                                                          Круто, это когда не нужно инсталить!
                                                          Зашел и играешь )
                                                      • +3
                                                        Ещё более круто — BananaBread
                                                        В него реально уже можно. Я поганял ботов с удовольствием)
                                                        • 0
                                                          Вот это вещь. Получается, уже вовсю можно создавать браузерки на WebGL и пересаживать геймеров с осла на правильные браузеры) Главное иметь прямые руки, потому что, как я понял, кода в BananaBread много, и вообще он преобразован из C++ в JS, т.е. с оптимизацией будет куда как быстрее.

                                                          Из всех перечисленных в посте демок эта является самой перспективной.
                                                      • 0
                                                        Вот WebGL это другое дело.
                                                      • +6
                                                        Не, даешь CS:S на CSS.
                                                      • +3
                                                        Кто-нибудь уже нашёл патроны? Видимо пистолет разряжен…
                                                        • +3
                                                          Вы не сняли с предохранителя
                                                        • –3
                                                          Троллейбус_из_буханки_хлеба.jpg
                                                          • +3
                                                            Не сразу понял, в чём дело :)
                                                            image
                                                            • 0
                                                              Что мешало запечь лайтмапу в стороннем редакторе и не мучать голову математикой света?
                                                              • 0
                                                                Не буду врать, перевод почти сломал меня.
                                                                • 0
                                                                  … но я справился и дочитал до самого конца.
                                                                • 0
                                                                  Ха, валидатор-то ругается
                                                                  • +3
                                                                    Как теперь жить?
                                                                    • +2
                                                                      Да, страница не может правильно отобразиться, если на ней такая критическая ошибка:

                                                                      Bad value 75% for attribute height on element img: Expected a digit but saw % instead.

                                                                      <img src="mouse.png" height="75%" alt="mouse">
                                                                    • 0
                                                                      как стрелять то?))
                                                                      • +2
                                                                        По div'у на каждый квад? Seriously?
                                                                        Демка крута, но… это как писать что-то сложнее хелло ворлда на брейнфаке. Вроде как и круто, но совершенно непонятно, зачем нужно было ТАК извращаться с неподходящими для этого инструментами :). Ну кроме фана, конечно.
                                                                        • 0
                                                                          Ну, если не «каждый див руками», а был написан какой-то конвертер моделей в дивы — почему бы и нет. Задача хоть и сомнительной применимости, но совершенным извращением не пахнет.
                                                                          • 0
                                                                            Это не то чтобы пахнет, это и есть извращение. Проводя аналогию, если сделать транслятор, скажем, JS->Brainfuck — разве в итоге не будет бред? Тут-то модели были примитивнейшие (с виду не более 100 полигонов). А что, когда в модели будет 1000 полигонов? 10000? А когда таких объектов будет много?..
                                                                            Для 3D, более серьезного чем трансформация пары элементов на веб-странице, существует WebGL, который хоть и тоже не идеален, но лишен большинства проблем. А эта демка никакого практического интереса не представляет. Хоть и весьма занятна как демонстрация возможностей CSS3
                                                                        • 0
                                                                          Мегакруто!
                                                                          • 0
                                                                            26.0.1403.0 dev-m на Win 7 x64 (ICi5-2410M, 4Gb, NV GF555) полёт шустрый, но текстуры мигают иногда.
                                                                            • +1
                                                                              HTML5 Game Development от Google
                                                                              Достаточно интересный, судя по всему, курс по разработке игр на HTML5
                                                                              • +2
                                                                                То-то я думаю бочки знакомые…
                                                                                Half-Life 2
                                                                              • +1
                                                                                Я нажал пробел и ме-е-едленно взлетел. Вспомнил школу, самопальную сигарету за углом…
                                                                                • 0
                                                                                  Вам везёт.
                                                                                  Вот лучшее, что мне выдал Chrome 24.0.1312.57 m в первом демо:

                                                                                  То есть, двухмерный фон вращается при движении мыши.
                                                                                  В последней версии Opera (12.14) просто статичная картинка, где ничего нельзя сделать:
                                                                                  .
                                                                                  В Firefox — лаконичный loading и никакой реакции.
                                                                                  • 0
                                                                                    Прыгают (мигают) текстуры на ящиках.
                                                                                    Движение чувака как под водой — очень медленное, при это повороты сверх-быстрые.
                                                                                    Также мыша работает как тач-пад — при достижении края экрана поворот прекращается.

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