#6 Подборка: игровые механики и техдемки

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

#5 Подборка: медузы, сферы и орбитальная станция

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

🪼 Aurelia — медузы в глубине

Как анимации и WebGL делают бизнес успешнее: взгляд изнутри

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

Я помню, как впер…

2

Дизайн, интерактив и будущее веба

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

2

#3 Подборка: Интерактивы и эффекты

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

1

#2 Подборка: Веб-интерактив и геймификация

Привет держи интересные находки для вдохновения и экспериментов с анимацией и взаимодействием.

7

#1 Подборка: Анимация и интерактив

Привет! Подобрал несколько сайтов для вдохновения:

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

2

Демонстрация продукта в игровом формате

Демонстрация продукта в игровом формате

Наше агентство EASY DIGITAL в партнерстве с агентством PRT запустили уникальный проект с игровой механикой для бренда Xiaomi, который демонстрирует, из чего состоит продукт, какие детали используются в его создании и в чем их отличие друг от друга.

3

Промо-сайт нашего стартапа на Vue.js и WebGL — как он создавался и зачем нужен

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

1

Настраиваем полноценную сборку

Перед тем как начать

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

То, как мы с вам…

Трансформация объектов (часть 1)

Перед тем как начать

В предыдущем уроке мы настроили полноценную сборку проекта с three.js при помощи webpack, если вы это пропустили, то советую вернуться!

Трансформация объектов (часть 1)

Создание простой сцены с three.js

Перед тем как начать

В предыдущем уроке мы с вами разобрали как самым простым способом подключить three.js к нашему проекту, если вы пропустили, то обязательно прочитайте!

Создание простой сцены с three.js
\n","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Файл src/script.js выглядит примерно вот так:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"import * as THREE from 'three';\n\n// Подключим файл со стилями\nimport './style.css';\n\n// Сцена\nconst scene = new THREE.Scene();\n\n// Объект\nconst geometry = new THREE.BoxGeometry(1, 1, 1);\nconst material = new THREE.MeshBasicMaterial({ color: 'purple', wireframe: true });\nconst mesh = new THREE.Mesh(geometry, material);\nscene.add(mesh);\n\n// Объект с размерами\nconst sizes = {\n width: 600,\n height: 600\n};\n\n// Камера\nconst camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);\ncamera.position.z = 3;\nscene.add(camera);\n\n// Отрисовщик\nconst renderer = new THREE.WebGLRenderer({\n canvas: document.querySelector('canvas.canvas-js'),\n alpha: true,\n});\nrenderer.setSize(sizes.width, sizes.height);\nrenderer.render(scene, camera);","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Так же давайте создадим пустой файл со стилями:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"touch src/style.css","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

И сейчас, запустив в консоли скрипт npm run dev у вас откроется браузер с нашим кубиком, поздравляю!

"}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

Я стараюсь создать комьюнити людей, которым интересна веб-разработка, 3d и путешествия, поэтому зову вас присоединиться к моему блогу в telegram!

"}},{"type":"delimiter","cover":false,"hidden":false,"anchor":"","data":{"type":"default"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Предыдущий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Следующий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

#threejs #javascript #3d

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":1,"reposts":0,"views":41,"hits":529,"reads":null,"online":0},"dateFavorite":0,"hitsCount":529,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1270438/509202-nastraivaem-polnocennuyu-sborku","author":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":509224,"customUri":null,"subsiteId":1270438,"title":"Трансформация объектов (часть 1)","date":1664269557,"dateModified":1664269557,"blocks":[{"type":"quote","cover":false,"hidden":false,"anchor":"","data":{"text":"

Перед тем как начать

В предыдущем уроке мы настроили полноценную сборку проекта с three.js при помощи webpack, если вы это пропустили, то советую вернуться!

","subline1":""}},{"type":"media","cover":true,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"6dc2e964-2dd6-5a85-9e94-3e5f97cd52de","width":1280,"height":720,"size":85085,"type":"jpg","color":"171e27","hash":"","external_service":[]}}}]}},{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Теперь, когда у нас все готово, мы можем изучить функциональные возможности three.js. Сегодня мы рассмотрим как перемещать и масштабировать объекты...

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Перед анимацией нашей сцены нам необходимо знать, как трансформировать объекты в нашей сцене. Мы уже сделали это с камерой, переместив ее назад с помощью строчки camera.position.z = 3.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Существует 4 свойства для преобразования объектов в нашей сцене:

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["position (для перемещения)","scale (для изменения размера)
","rotation (для вращения)
","quaternion (тоже для вращения, но об этом позже 😏)
"],"type":"UL"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Все классы, которые наследуются от класса Object3D, обладают такими свойствами, как PerspectiveCamera или Mesh, а также классы, которые мы еще не рассматривали.

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Подготовка"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Перемещение объектов"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

position обладает тремя основными свойствами- x, y и z. Это свойства необходимы для позиционирования чего-либо в трехмерном пространстве.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Направление каждой оси является произвольным, и может меняться в зависимости от окружающей среды. В three.js обычно считают, что ось y направлена вверх, ось z - назад, а ось x - вправо.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Например, если вы собираетесь создать дом, то, вероятно, вам следует считать, что 1 единица - это 1 метр.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Давайте переместим наш куб:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"mesh.position.x = -1;\nmesh.position.y = -0.8;\nmesh.position.z = 0.4;","lang":""}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"e2e2aff6-9dc5-5906-8558-19b1b24b1f61","width":1716,"height":1070,"size":9974,"type":"png","color":"d5bad7","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"Vector3_methods","data":{"text":"

Свойство position - это экземпляр класса Vector3. Этот класс имеет не только свойства x, y и z, но еще и множество полезных методов.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вы можете получить длину вектора:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"console.log(mesh.position.length());","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вы можете получить расстояние от другого Vector3 (убедитесь, что используете этот код после создания камеры):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"console.log(mesh.position.distanceTo(camera.position));","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Можно нормализовать значение вектора (это означает, что вы уменьшите длину вектора до 1 единицы, но сохраните его направление):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"console.log(mesh.position.normalize());","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для перемещения объекта, вместо того чтобы изменять x, y и z по отдельности, можно также использовать метод set(...):

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"mesh.position.set(-1, -0.8, 0.4);","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Добавляем визуализацию осей"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Одним из хороших решений является использование three.js AxesHelper.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

AxesHelper отобразит 3 линии, соответствующие осям x, y и z, каждая из которых начинается в центре сцены и идет в соответствующем направлении.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Чтобы создать AxesHelper, создайте объект и добавьте на сцену. В качестве единственного параметра можно указать длину, с которой нужно отрисовать оси. Мы будем использовать 3:

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Отрисовка осей координат\nconst axesHelper = new THREE.AxesHelper(3);\nscene.add(axesHelper);","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"8d4e8bda-5160-5844-9366-8f698a906880","width":1720,"height":1072,"size":11355,"type":"png","color":"ddc8df","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вы должны увидеть зеленую и красную линии.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Зеленая линия соответствует оси y. Красная линия соответствует оси x, а синяя линия соответствует оси z, но мы ее не видим, потому что она идеально выровнена относительно камеры.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Масштабирование объектов"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

scale также является объектом класса Vector3. По умолчанию x, y и z равны 1, что означает, что к объекту не применяется масштабирование. Если вы поставите значение 0,5, объект будет иметь половину своего размера по этой оси, а если вы поставите значение 2, он будет вдвое больше своего первоначального размера по этой оси.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"mesh.scale.x = 0.5;\nmesh.scale.y = 2;\nmesh.scale.z = 0.7;","lang":""}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"1a7eae10-3e1e-5c5c-bc19-509be5ec02ee","width":1706,"height":1058,"size":8055,"type":"png","color":"e4d6df","hash":"","external_service":[]}}}]}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Поскольку scale это Vector3, мы можем использовать все ранее упомянутые методы этого класса.

"}},{"type":"delimiter","cover":false,"hidden":false,"anchor":"","data":{"type":"default"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Предыдущий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Следующий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

#threejs #javascript #3d

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":57,"hits":626,"reads":null,"online":0},"dateFavorite":0,"hitsCount":626,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1270438/509224-transformaciya-obektov-chast-1","author":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}},{"type":"entry","data":{"id":509162,"customUri":null,"subsiteId":1270438,"title":"Создание простой сцены с three.js","date":1664264979,"dateModified":1664264979,"blocks":[{"type":"quote","cover":false,"hidden":false,"anchor":"","data":{"text":"

Перед тем как начать

В предыдущем уроке мы с вами разобрали как самым простым способом подключить three.js к нашему проекту, если вы пропустили, то обязательно прочитайте!

","subline1":""}},{"type":"media","cover":true,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"c9d1b212-fdb8-5935-a8e2-34ebdbe9a1c0","width":1280,"height":720,"size":156580,"type":"png","color":"161e27","hash":"","external_service":[]}}}]}},{"type":"text","cover":true,"hidden":false,"anchor":"","data":{"text":"

Сейчас пришло время создать нашу первую сцену и вывести что-то на экран.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того чтобы это сделать нам нужно 4 элемента:

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Сцена","Камера","Отрисовщик","Объекты"],"type":"UL"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Сцена"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Сцена - что-то вроде контейнера. Мы будем складывать в этот контейнер объекты, модели, свет и т.д. После чего мы можем отрендерить сцену и вывести ее содержимое на экран.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Сцена\nconst scene =newTHREE.Scene();","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Камера"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Камеру не видно на итоговом изображении. Это скорее что-то вроде абстрактного взгляда на сцену. В момент, когда мы будем рендерить сцену все будет видно от лица камеры.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Чаще всего мы будем использовать одну камеру, но возможна работа и с несколькими камерами (в таком случае можно делать переключения между ними).

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Существуют различные типы камер, но подробнее о каждом типе мы поговорим в дальнейших уроках. Сейчас мы воспользуемся перспективной камерой (она воспринимает сцену так же, как и человеческий глаз. Чем дальше объект - тем он меньше, чем ближе - тем больше).

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того, чтобы создать камеру воспользуемся классом PerspectiveCamera.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Чтобы создать объект данного класса нам нужно задать два параметра.

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["Поле зрения","Соотношение сторон"],"type":"UL"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Поле зрения"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"f3dd0926-c799-5705-9056-3c8cc9f0e787","width":1306,"height":834,"size":33573,"type":"png","color":"c8c7c7","hash":"","external_service":[]}}}]}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Соотношение сторон"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Так же важно не забыть поместить камеру на созданную нами сцену.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Размеры\nconst sizes = {\n width: 600,\n height: 600\n};\n\n// Камера\nconst camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);\n\n// Добавление камеры на сцену\nscene.add(camera);","lang":""}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Отрисовщик"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Задача отрисовщика - отрендерить созданную сцену на экране.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Говоря просто - мы просим отрисовщика отобразить на canvas созданную нами сцену, на которую вы смотрим со стороны созданной камеры.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Можно создать canvas вручную или попросить отрисовщика создать его и поместить на страницу.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Пока мы сами руками создадим canvas на странице и передадим его отрисовщику.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Давайте создадим canvas в нашем index.html (поместить его нужно до загрузки скриптов) и добавим ему class.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того, чтобы создать отрисовщика нам нужно воспользоваться классом WebGLRenderer. В качестве параметров этому классу передается объект, который может содержать разные настройки. Сейчас нам надо передать ему свойство canvas, значением которого будет <canvas>, который мы только что добавили на страницу.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Создадим переменную, которая будет хранить элемент из DOM дерева\nconst canvas = document.querySelector('canvas.canvas-js');\n\n// Создадим объект отрисовщика\nconst renderer = new THREE.WebGLRenderer({\n canvas: canvas,\n alpha: true,\n});\n\n// Изменим размер нашего отрисовщика (а с ним и размер canvas)\nrenderer.setSize(sizes.width, sizes.height);","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Если вы сейчас перезагрузите страницу, то должны получить черный квадрат со стороной 600px.

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Объекты"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Объектами, которые можно отрисовать, могут быть множество вещей. Например, это может быть какая-то примитивная геометрия, импортированные 3D модели, свет и т.д.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В данном уроке мы с вами создадим простой куб.

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того, чтобы создать куб нам нужно создать объект классы Mesh.

"}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

Mesh - это комбинация геометрии (формы объекта) и материала (то как выглядит).

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того чтобы создать геометрию мы используем класс BoxGeometry и передаем в качестве первых трех параметров размер параллелепипеда, который хотим создать.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Геометрия\nconst geometry = new THREE.BoxGeometry(1, 1, 1);","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Есть разные способы чтобы задать цвет.

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["С помощью шестнацетеричного числа (0xff0000)","При помощи строки, которая хранит это число ('0xff0000')","С использованием названия цвета ('red')","Используя класс цвета Color (подробнее об этом позже)"],"type":"UL"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Материал\nconst material = new THREE.MeshBasicMaterial({ color: 'purple', wireframe: true });","lang":""}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для создания итогового объекта нужно воспользоваться классом Mesh и передать геометрию и материал в качестве параметров. А потом уже можно добавить его на сцену.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Объект\nconst mesh = new THREE.Mesh(geometry, material);\n\n// Добавим на сцену\nscene.add(mesh);","lang":""}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

Если вы не добавите объект на сцену, то и увидеть его вы не сможете!

"}},{"type":"header","cover":false,"hidden":false,"anchor":"","data":{"style":"h2","text":"Первый рендер"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"renderer.render(scene, camera);","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В браузере и получаем:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"578065cd-5cc2-5e71-b15b-66a504f7da0a","width":1212,"height":1216,"size":2927,"type":"png","color":"e4d4e4","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Так погодите... Снова что-то не то?

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Для того чтобы что-то увидеть нам нужно переместить объекты. Для этого у нас есть доступ к таким свойствам объекта как:

"}},{"type":"list","cover":false,"hidden":false,"anchor":"","data":{"items":["position","rotation","scale","quaternion"],"type":"UL"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

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

"}},{"type":"incut","cover":false,"hidden":false,"anchor":"","data":{"text":"

🤖 Свойство position - это объект, у которого есть три поля x, y и z. По умолчанию в three.js за приближение / отдаление отвечает ось z.

"}},{"type":"code","cover":false,"hidden":false,"anchor":"","data":{"text":"// Созданный нами объект камеры\nconst camera = new THREE.PerspectiveCamera(75, sizes.width / sizes.height);\n\n// Перемещение камеры\ncamera.position.z = 3;\n\n// Добавление камеры на сцену\nscene.add(camera);","lang":""}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В браузере и получаем:

"}},{"type":"media","cover":false,"hidden":false,"anchor":"","data":{"items":[{"title":"","image":{"type":"image","data":{"uuid":"3874443a-3bb1-5171-ac07-1a0ae7f0d633","width":1212,"height":844,"size":4586,"type":"png","color":"dec8de","hash":"","external_service":[]}}}]}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Вот и все, сейчас вы можете поздравить себя с первой созданной сценой!

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

В продолжении статей с курсом по three.js мы с вами детально разберем свойства position, rotation и scale, научимся их изменять и анимировать.

"}},{"type":"delimiter","cover":false,"hidden":false,"anchor":"","data":{"type":"default"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Предыдущий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

Следующий урок

"}},{"type":"text","cover":false,"hidden":false,"anchor":"","data":{"text":"

#threejs #javascript #3d

"}}],"summaryContent":null,"isExistSummaryContent":false,"warningFromEditor":null,"warningFromEditorTitle":null,"counters":{"comments":0,"favorites":0,"reposts":0,"views":40,"hits":1804,"reads":null,"online":0},"dateFavorite":0,"hitsCount":1804,"isCommentsEnabled":true,"isLikesEnabled":true,"isRemovedByUserRequest":false,"isFavorited":false,"isPinned":false,"repostId":null,"repostData":null,"subscribedToTreads":false,"isEditorial":false,"isAudioAvailable":false,"audioUrl":null,"isAudioAvailableToGenerate":false,"commentEditor":{"enabled":true,"who":null,"text":"","until":null,"reason":null,"type":"everybody"},"isBlur":false,"isPublished":true,"isDisabledAd":false,"withheld":[],"ogTitle":null,"ogDescription":null,"url":"https://vc.ru/id1270438/509162-sozdanie-prostoi-sceny-s-threejs","author":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"subsite":{"id":1270438,"name":"Traveler Logs","nickname":null,"description":"Веб-разработка, 3d моделирование и путешествия. По всем вопросам и предложениям - жду в директ тг @guvictory ✌","uri":"","avatar":{"type":"image","data":{"uuid":"0ca6b7e6-8c48-59d2-9fe1-c76a7229c5f1","width":640,"height":640,"size":61268,"type":"jpg","color":"080509","hash":"","external_service":[]}},"cover":null,"achievements":[{"title":"3 года на vc.ru","code":"registration_3_years","description":"Провёл 3 года вместе с vc.ru. Получена 13 августа 2025.","previewUuid":"d9d72ac5-bcb5-55e0-8c72-b99251e5cdd9","formats":{"glb":"https://static.vc.ru/achievements/shark.glb","usdz":"https://static.vc.ru/achievements/shark.usdz"},"viewData":{"contentColor":"#8E6F09","textMaxWidth":0.66796875,"textX":0.5205078125,"textY":0.341796875,"logoX":0.5205078125,"logoY":0.4609375,"logoXNoText":0.5,"logoYNoText":0.3662109375},"id":5532011,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/5532011"},{"title":"Год на vc.ru","code":"registration_1_year","description":"Первый год с vc.ru. Получена 24 июля 2025.","previewUuid":"0d11c244-49de-50e7-894e-b9b27945d42b","formats":{"glb":"https://static.vc.ru/achievements/fish.glb","usdz":"https://static.vc.ru/achievements/fish.usdz"},"viewData":{"contentColor":"#C67AA3","textMaxWidth":0.634765625,"textX":0.5888671875,"textY":0.54296875,"logoX":0.5859375,"logoY":0.6669921875,"logoXNoText":0.6044921875,"logoYNoText":0.5439453125},"id":4177708,"userId":1270438,"count":0,"shareImage":"https://api.vc.ru/achievements/share/4177708"}],"lastModificationDate":1764925093,"isSubscribed":false,"isSubscribedToNewPosts":false,"isMuted":false,"isAvailableForMessenger":true,"badgeId":null,"isDonationsEnabled":false,"isPlusGiftEnabled":true,"isUnverifiedBlogForCompanyWithoutPro":false,"isRemovedByUserRequest":false,"isFrozen":false,"isDisabledAd":false,"isPlus":false,"isVerified":false,"isPro":false,"yandexMetricaId":null,"badge":null,"isOnline":false,"tgChannelShortname":null,"isUnsubscribable":true,"type":1,"subtype":"personal_blog"},"reactions":{"counters":[],"reactionId":0},"isNews":false,"source":null,"clusters":[],"donations":{"amount":0,"isDonated":false},"commentsSeenCount":null}}],"ogTitle":null,"ogDescription":null,"isAnonymized":true}};