{"id":346705,"date":"2023-03-14T09:03:01","date_gmt":"2023-03-14T09:03:01","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=346705"},"modified":"-0001-11-30T00:00:00","modified_gmt":"2023-03-14T09:03:01","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=346705","title":{"rendered":"<span>\u0412\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u043d\u0438\u0435 WebGL \u0432 HTML-\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e Three.JS<\/span>"},"content":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><a href=\"https:\/\/articles-simbirsoft.github.io\/demo\/\"><strong>demo<\/strong><\/a><strong>, <\/strong><a href=\"https:\/\/github.com\/articles-simbirsoft\/demo\"><strong>github<\/strong>\u00a0<\/a><\/p>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u042f \u0410\u043b\u0435\u043a\u0441\u0435\u0439, frontend\u2011\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0SimbirSoft. \u0412\u044b, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u0438\u0434\u0435\u043b\u0438 \u043f\u043e\u0442\u0440\u044f\u0441\u0430\u044e\u0449\u0438\u0435 \u0432\u0435\u0431\u2011\u0441\u0430\u0439\u0442\u044b, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 <a href=\"https:\/\/www.awwwards.com\">www.awwwards.com<\/a>. \u042d\u0442\u043e \u043e\u043d\u043b\u0430\u0439\u043d\u2011\u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043b\u0443\u0447\u0448\u0438\u0445 \u0432\u0435\u0431\u2011\u0441\u0430\u0439\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0435\u0442 \u0438 \u043d\u0430\u0433\u0440\u0430\u0436\u0434\u0430\u0435\u0442 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u0438\u0435 \u0438 \u0438\u043d\u043d\u043e\u0432\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u0435\u0431\u2011\u0434\u0438\u0437\u0430\u0439\u043d\u0435\u0440\u043e\u0432 \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432. \u041d\u0430\u00a0\u044d\u0442\u043e\u043c \u0441\u0430\u0439\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043a\u0440\u0435\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0432\u0435\u0431\u2011\u0434\u0438\u0437\u0430\u0439\u043d\u0430, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432. \u0422\u0430\u043a\u0438\u0435 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c WebGL. \u042d\u0442\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0438 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u044f\u044e\u0449\u0438\u0445 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u0431\u0435\u0437\u00a0\u0443\u0449\u0435\u0440\u0431\u0430 \u0434\u043b\u044f\u00a0\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0414\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0WebGL \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u0430\u043a\u00a0Three.js, PIXIJS \u0438\u043b\u0438\u00a0BABYLON, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b \u043f\u0440\u0438\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0438\u0433\u0440.<\/p>\n<p>\u0412\u00a0\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u043e\u0432\u043c\u0435\u0449\u0435\u043d\u0438\u0435 WebGL\u2011\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0441\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b HTML, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 Three.js. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441\u00a0\u043d\u0435\u0439 \u0432\u043e\u00a0\u043c\u043d\u043e\u0433\u043e\u043c \u0441\u0445\u043e\u0436\u0430 \u0441\u00a0\u0440\u0430\u0431\u043e\u0442\u043e\u0439 3D\u2011\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 (3ds Max, Maya, Blender \u0438\u00a0\u0442.\u00a0\u0434.). \u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432\u00a0\u0432\u0438\u0434\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438\u043b\u0438\u00a0\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443, \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u00a0\u043d\u0435\u0435 \u043a\u0430\u043c\u0435\u0440\u0443, \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 (\u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044e \u0438\u043b\u0438 3D\u2011\u043c\u043e\u0434\u0435\u043b\u044c), \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430. <\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 middle \u0438 senior \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u2011\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u044f\u0442 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441\u00a0Three. \u0412\u00a0\u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u043e \u0442\u0435\u043e\u0440\u0438\u0438 \u0438 \u0432\u0432\u043e\u0434\u043d\u044b\u0445 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u0432, \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0430\u00a0\u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u00a0\u0437\u043d\u0430\u0435\u0442\u0435, \u043a\u0430\u043a\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 <a href=\"https:\/\/threejs.org\/\"><u>Three.js<\/u><\/a> \u0438 \u0448\u0435\u0439\u0434\u0435\u0440\u044b, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441\u00a0\u044d\u0442\u043e\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u0430\u00a0\u043f\u043e\u0441\u043b\u0435 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a\u00a0\u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/d7c\/980\/221\/d7c9802219f73ec39bf823cc88a609ef.png\" width=\"780\" height=\"440\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d7c\/980\/221\/d7c9802219f73ec39bf823cc88a609ef.png\"\/><\/figure>\n<h2>\u041a\u0430\u043a \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435  <\/h2>\n<p>\u0423\u00a0\u043d\u0430\u0441 \u0435\u0441\u0442\u044c HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u043e \u0441\u043a\u0440\u044b\u0442\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438. \u0411\u043b\u043e\u043a canvas \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e <strong>body <\/strong>\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0437\u0430\u00a0HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435\u0439. \u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0446\u0435\u043d\u044b \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438, \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 (\u0442\u0435\u00a0\u0436\u0435 \u0441\u0430\u043c\u044b\u0435, \u0447\u0442\u043e\u00a0\u0438 \u043d\u0430\u00a0HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435). \u0422\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0445 \u0438 \u043f\u0438\u043a\u0441\u0435\u043b\u044c\u043d\u044b\u0445 \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u043f\u0440\u0438\u00a0\u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043c\u044b\u0448\u0438. \u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043c\u044b \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <strong>getBoundingClientRect <\/strong>\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u00a0\u043d\u0430\u0448\u0438\u043c \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044f\u043c \u0432\u00a0\u0441\u0446\u0435\u043d\u0435, \u0447\u0442\u043e\u00a0\u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0438\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0445 \u0442\u0430\u043a\u043e\u0433\u043e\u00a0\u0436\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u0430, \u043a\u0430\u043a\u00a0\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u041f\u0440\u0438\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u0432\u0435\u0440\u0445.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/1ca\/fdf\/4b1\/1cafdf4b16636d49d15f14a2e75a6796.png\" width=\"806\" height=\"619\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/1ca\/fdf\/4b1\/1cafdf4b16636d49d15f14a2e75a6796.png\"\/><\/figure>\n<h2>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c vite. \u041a\u0430\u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c vite, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/vitejs.dev\/guide\/\"><u>\u0442\u0443\u0442. <\/u><\/a>\u041a\u043e\u043c\u0430\u043d\u0434\u0430\u00a0<\/p>\n<p><code>npm create vite@latest app --template vanilla<\/code><\/p>\n<p>\u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 JavaScript. \u0421\u0440\u0430\u0437\u0443 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c Three.js, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<p><code>npm install three<\/code><\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0448\u0435\u0439\u0434\u0435\u0440\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/www.npmjs.com\/package\/vite-plugin-glsl\"><u>vite-plugin-glsl<\/u><\/a>, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p><code>npm i vite-plugin-glsl --save-dev<\/code><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b vite.config.js \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0442\u0430\u043c \u043a\u043e\u043d\u0444\u0438\u0433:<\/p>\n<p><code>\/\/ vite.config.js<\/code><\/p>\n<pre><code class=\"javascript\">import glsl from 'vite-plugin-glsl'; import { defineConfig } from 'vite';  export default defineConfig({   plugins: [glsl()] });<\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/15a\/910\/4b6\/15a9104b60a6e8c8f29d453fff07f249.jpg\" alt=\"\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\" title=\"\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430\" width=\"901\" height=\"446\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/15a\/910\/4b6\/15a9104b60a6e8c8f29d453fff07f249.jpg\" data-blurred=\"true\"\/><\/p>\n<div><figcaption>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0443\u0442\u044c \u0434\u043e \u0444\u0430\u0439\u043b\u0430 main.js \u0432 \u0444\u0430\u0439\u043b\u0435 index.html, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 src.<\/p>\n<h2>\u0411\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0446\u0435\u043d\u044b<\/h2>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u0441\u0446\u0435\u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 <a href=\"https:\/\/threejs.org\/docs\/#api\/en\/cameras\/PerspectiveCamera\"><u>\u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043a\u0430\u043c\u0435\u0440\u0430<\/u><\/a>. \u0427\u0442\u043e\u0431\u044b \u043a\u0430\u043c\u0435\u0440\u0430 \u0437\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u0430 \u0432\u0441\u044e \u0448\u0438\u0440\u0438\u043d\u0443 \u0438 \u0432\u044b\u0441\u043e\u0442\u0443 \u044d\u043a\u0440\u0430\u043d\u0430, \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u0443\u0433\u043e\u043b \u043e\u0431\u0437\u043e\u0440\u0430 <strong>fov<\/strong>:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/dde\/02b\/afb\/dde02bafb7eca3067c15bfa58f342f29.jpg\" width=\"748\" height=\"601\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/dde\/02b\/afb\/dde02bafb7eca3067c15bfa58f342f29.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0443\u0440\u043e\u043a \u0448\u043a\u043e\u043b\u044c\u043d\u043e\u0439 \u0442\u0440\u0438\u0433\u043e\u043d\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \ud83d\ude42 \u041a\u0430\u043c\u0435\u0440\u0430 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0430 \u043d\u0430 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0438 1000 px \u043e\u0442 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 (\u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043a\u0430\u043a canvas), \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0410\u0440\u043a\u0442\u0430\u043d\u0433\u0435\u043d\u0441 fov\/2 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u0435\u043d (\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043b\u0435\u0436\u0430\u0449\u0435\u0433\u043e \u043a\u0430\u0442\u0435\u0442\u0430 \u043a \u043f\u0440\u0438\u043b\u0435\u0436\u0430\u0449\u0435\u043c\u0443) window.innerHeight \/ 2 \/ perspective. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0443\u0433\u043e\u043b \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445, \u0443\u043c\u043d\u043e\u0436\u0438\u043c \u0443\u0434\u0432\u043e\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0430 180 \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 PI:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/6c6\/0b7\/641\/6c60b7641d1e27cfb61cb000a73a80ff.jpg\" width=\"465\" height=\"216\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6c6\/0b7\/641\/6c60b7641d1e27cfb61cb000a73a80ff.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0438\u0437 <a href=\"https:\/\/github.com\/articles-simbirsoft\/demo\"><u>\u043c\u043e\u0435\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u043d\u0430 github<\/u><\/a>, \u0442\u043e \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 main \u0432 css, \u0447\u0442\u043e\u0431\u044b \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b\u043e.<\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u043a\u0440\u043e\u0435\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 \u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u0438 (opacity: 0). \u0414\u043b\u044f \u0442\u0435\u0433\u0430 canvas \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0438\u043b\u0438:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/133\/799\/3d4\/1337993d4101697e3d299e8e2d525d9c.JPG\" width=\"301\" height=\"169\"\/><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 Sketch \u0438 \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430:\u00a0<\/p>\n<p>\u0444\u0430\u0439\u043b:<code>src\/main.js<\/code>  <\/p>\n<pre><code class=\"javascript\">import \"\/src\/assets\/style.scss\"; import * as THREE from 'three';   class Sketch {     constructor() {         this.body = document.querySelector('body');         this.createScene();         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();     }       get viewport() {         const width = window.innerWidth;         const height = window.innerHeight;         const aspectRatio = width \/ height;         return { width, height, aspectRatio };     }       createScene() {         this.scene = new THREE.Scene();     }       createCamera() {         const perspective = 1000;         const fov = (180 * (2 * Math.atan(window.innerHeight \/ 2 \/ perspective))) \/ Math.PI;         this.camera = new THREE.PerspectiveCamera(fov, this.viewport.aspectRatio, 1, 1000)         this.camera.position.set(0, 0, perspective);     }       createMesh() {         const geometry = new THREE.PlaneGeometry( 250, 250, 10, 10 );         const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         const mesh = new THREE.Mesh(geometry, material);         this.scene.add(mesh);     }       onWindowResize() {                 this.camera.aspect = this.viewport.aspectRatio;         this.createCamera();         this.camera.updateProjectionMatrix();         this.renderer.setSize(this.viewport.width, this.viewport.height);     }       initRenderer() {         this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });         this.renderer.setSize(this.viewport.width, this.viewport.height);         this.renderer.setPixelRatio(window.devicePixelRatio);         this.body.appendChild(this.renderer.domElement);     }       render() {         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }   new Sketch();<\/code><\/pre>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043a\u043b\u0430\u0441\u0441\u0430 Sketch:<\/p>\n<p><strong>viewport<\/strong>\u00a0\u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u044b \u0438 \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0440\u043e\u043d \u044d\u043a\u0440\u0430\u043d\u0430;<\/p>\n<p><strong>createScene<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0446\u0435\u043d\u0443;<\/p>\n<p><strong>createCamera<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u0430\u043c\u0435\u0440\u0443 \u0441\u00a0\u0440\u0430\u0441\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438;<\/p>\n<p><strong>createMesh<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b, \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0435 \u0432\u00a0\u043d\u0430\u0448\u0443 \u0441\u0446\u0435\u043d\u0443;<\/p>\n<p><strong>THREE.PlaneGeometry (250, 250, 10, 10)<\/strong>\u00a0\u2014 \u0437\u0430\u0434\u0430\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 250\u00d7250\u00a0\u0438 10\u00d710\u00a0\u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u043b\u0438\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u043a\u0438 \u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u043f\u043e\u00a0\u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438 \u0438 \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438;<\/p>\n<p><strong>THREE.MeshBasicMaterial({ color: 0\u00d700ff00, wireframe: true })<\/strong>\u00a0\u2014 \u0437\u0430\u0434\u0430\u0435\u043c \u0446\u0432\u0435\u0442 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u00a0\u0444\u043e\u0440\u043c\u0430\u0442\u0435 Hex Color. wireframe: true\u00a0\u2014 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u00a0\u0432\u0438\u0434\u0435 \u043f\u043e\u043b\u0438\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u043a\u0438 (\u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c);<\/p>\n<p><strong>onWindowResize<\/strong>\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f\u00a0\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u043f\u0440\u0438\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u044d\u043a\u0440\u0430\u043d\u0430;<\/p>\n<p><strong>initRenderer<\/strong>\u00a0\u2014 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430, \u043f\u0440\u0438\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u044d\u043a\u0440\u0430\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e onWindowResize;<\/p>\n<p><a href=\"https:\/\/threejs.org\/docs\/#api\/en\/renderers\/WebGLRenderer.setPixelRatio\"><strong><u>setPixelRatio<\/u><\/strong><\/a>\u00a0\u2014 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u0435 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0445\u043e\u043b\u0441\u0442\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u00a0\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0445 mac retina \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044f \u043d\u0435\u00a0\u0442\u043e\u0440\u043c\u043e\u0437\u0438\u043b\u0430.<\/p>\n<p><strong>render <\/strong>\u2014 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0430\u043c\u0443 \u0441\u0435\u0431\u044f \u0434\u043b\u044f\u00a0\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u0441\u0446\u0435\u043d\u044b.<\/p>\n<p>\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/0c8\/82f\/094\/0c882f094859a654e04ce7596e9438d3.jpg\" width=\"1870\" height=\"900\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0c8\/82f\/094\/0c882f094859a654e04ce7596e9438d3.jpg\" data-blurred=\"true\"\/><\/figure>\n<h2>\u0417\u0430\u043c\u0435\u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043d\u0430 Three.js \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448 \u043a\u043b\u0430\u0441\u0441 Sketch.<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">\u0441lass Sketch{     constructor(){         this.body = document.querySelector('body');          this.images = [...document.querySelectorAll('img')];         this.meshItems = [];          this.createScene();         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();      }      initRenderer() {         window.addEventListener('resize', this.onWindowResize.bind(this), false);         this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });         this.renderer.setSize(this.viewport.width, this.viewport.height);         this.renderer.setPixelRatio(window.devicePixelRatio);         this.body.appendChild(this.renderer.domElement);     } ...     createMesh() {         \/\/ const geometry = new THREE.PlaneGeometry( 250, 250, 10, 10 );         \/\/ const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         \/\/ const mesh = new THREE.Mesh(geometry, material);         \/\/ this.scene.add(mesh);         this.images.map((image) => {             const meshItem = new MeshItem(image, this.scene);             this.meshItems.push(meshItem);         })     }      ...     render(){         for(let i = 0; i &lt; this.meshItems.length; i++){             this.meshItems[i].render();         }         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }<\/code><\/pre>\n<p><strong>this.images<\/strong>\u00a0\u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u0430\u0441\u0441\u0438\u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a;<\/p>\n<p><strong>this.meshItems<\/strong>\u00a0\u2014 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0432\u0435\u043d \u043f\u0443\u0441\u0442\u043e\u043c\u0443 \u043c\u0430\u0441\u0441\u0438\u0432\u0443;<\/p>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 createMesh \u0443\u0434\u0430\u043b\u0438\u043c \u0440\u0430\u043d\u0435\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u0432\u0448\u0438\u0439 \u043a\u043e\u0434 \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0446\u0438\u043a\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 MeshItem \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0438 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0435\u0433\u043e \u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0435 this.meshItems. \u041f\u0440\u0438\u0441\u0432\u043e\u0438\u043c \u043a\u0430\u0436\u0434\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 id, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0435\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0435. \u0414\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 render \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0446\u0438\u043a\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0445 \u043f\u0435\u0440\u0435\u0440\u0435\u043d\u0434\u0435\u0440 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u0442\u0430\u043f\u0435 requestAnimationFrame;<\/p>\n<p>\u0412\u0441\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 Sketch \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 MeshItem:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">class MeshItem{     constructor(element, scene){         this.element = element;         this.scene = scene;         this.offset = new THREE.Vector2(0,0);         this.sizes = new THREE.Vector2(0,0);         this.createMesh();     }      getDimensions(){         const {width, height, top, left} = this.element.getBoundingClientRect();         this.sizes.set(width, height);         this.offset.set(left - window.innerWidth \/ 2 + width \/ 2, -top + window.innerHeight \/ 2 - height \/ 2);     }      createMesh(){         const geometry = new THREE.PlaneGeometry( 1, 1, 10, 10 );         const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         this.mesh = new THREE.Mesh(geometry, material);         this.scene.add(this.mesh);     }      render(){         this.getDimensions();         this.mesh.position.set(this.offset.x, this.offset.y, 0);         this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);     } }<\/code><\/pre>\n<p><a href=\"https:\/\/threejs.org\/docs\/#api\/en\/math\/Vector2\"><strong><u>THREE.Vector2<\/u><\/strong><\/a>\u00a0\u2014 \u043a\u043b\u0430\u0441\u0441, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u044f\u044e\u0449\u0438\u0439 \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u0432\u0435\u043a\u0442\u043e\u0440. \u0422\u043e\u0447\u043a\u0430 \u0432 2D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435 (\u0442\u043e \u0435\u0441\u0442\u044c \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435 \u043d\u0430\u00a0\u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438) \u0438\u043b\u0438\u00a0\u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438, \u043a\u0430\u043a\u00a0\u0443\u00a0\u043d\u0430\u0441. \u042d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 Vector2\u00a0\u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u00a0\u043f\u043e\u043b\u044f\u043c\u0438 (x, y), \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0438\u043c \u043d\u0430\u0447\u0430\u043b\u044c\u043d\u044b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f (0,0);<\/p>\n<p><strong>this.offset<\/strong>\u00a0\u2014 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438;<\/p>\n<p><strong>this.sizes<\/strong>\u00a0\u2014 \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438;<\/p>\n<p><strong>getDimensions<\/strong>\u00a0\u2014 \u0437\u0430\u0434\u0430\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u044b \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044f\u043c \u0432\u00a0\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0438\u0438 \u0441\u00a0\u0440\u0430\u0437\u043c\u0435\u0440\u0430\u043c\u0438 \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a. \u0412\u00a0THREE JS \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0437\u0430\u0434\u0430\u0435\u0442\u0441\u044f \u043d\u0435\u00a0\u043a\u0430\u043a\u00a0\u0432\u00a0\u0431\u0440\u0430\u0443\u0437\u0435\u0440\u0435 \u043e\u0442\u00a0\u0432\u0435\u0440\u0445\u043d\u0435\u0433\u043e \u043b\u0435\u0432\u043e\u0433\u043e \u0443\u0433\u043b\u0430, \u0430\u00a0\u043e\u0442\u00a0\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 x=0, y=0, z=0\u00a0(\u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0445 \u0446\u0435\u043d\u0442\u0440\u0443 \u044d\u043a\u0440\u0430\u043d\u0430 \u0432\u00a0\u043d\u0430\u0448\u0435\u043c \u0441\u043b\u0443\u0447\u0430\u0435) \u0434\u043e\u00a0\u0446\u0435\u043d\u0442\u0440\u0430\u043b\u044c\u043d\u043e\u0439 \u0442\u043e\u0447\u043a\u0438 \u043e\u0431\u044a\u0435\u043a\u0442\u0430.<\/p>\n<p><strong>render <\/strong>\u2014 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0438 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u044b \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438.<\/p>\n<p>\u0414\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0fc\/ad1\/3d2\/0fcad13d2dcb41e4067e0a2a51343b57.gif\" width=\"650\" height=\"308\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0fc\/ad1\/3d2\/0fcad13d2dcb41e4067e0a2a51343b57.gif\"\/><\/figure>\n<p>\u041f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b \u043c\u043e\u0436\u043d\u043e \u0437\u0430\u043c\u0435\u0442\u0438\u0442\u044c, \u0447\u0442\u043e \u043d\u0430\u0448\u0438 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u043d\u0435\u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0430\u0437\u0434\u044b\u0432\u0430\u044e\u0442 \u0437\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438 (\u043f\u0440\u0438 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0437\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438 \u0432\u0438\u0434\u043d\u044b \u043d\u0430\u0448\u0438 \u0437\u0435\u043b\u0435\u043d\u044b\u0435 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438). \u041d\u0430 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0432\u0438\u0434\u0438\u043c\u044b\u043c\u0438 (opacity: 1), \u0447\u0442\u043e\u0431\u044b \u044d\u0444\u0444\u0435\u043a\u0442 \u0431\u044b\u043b \u043b\u0443\u0447\u0448\u0435 \u0432\u0438\u0434\u0435\u043d. \u041d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043b\u0430\u0432\u043d\u0443\u044e \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435, \u0447\u0442\u043e\u0431\u044b \u0438\u0437\u0431\u0430\u0432\u0438\u0442\u044c\u0441\u044f \u043e\u0442 \u044d\u0442\u043e\u0433\u043e.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/624\/a81\/ecb\/624a81ecbf7a20637f4871fd6329b66c.gif\" width=\"650\" height=\"308\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/624\/a81\/ecb\/624a81ecbf7a20637f4871fd6329b66c.gif\"\/><\/figure>\n<h2>\u0414\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u043f\u043b\u0430\u0432\u043d\u0443\u044e \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0443 \u043d\u0430 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443<\/h2>\n<p>\u041f\u0440\u0438 \u0441\u043a\u0440\u043e\u043b\u043b\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u044e\u0442\u0441\u044f \u043d\u0435 \u043f\u043e\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u0442\u0435\u043b\u044c\u043d\u043e 1,2,3,4,5, \u0430 \u0441 \u043f\u0440\u043e\u043f\u0443\u0441\u043a\u043e\u043c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0442\u0430\u043a\u0442\u043e\u0432 \u0434\u043b\u044f \u0443\u0432\u0435\u043b\u0438\u0447\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0418\u0437-\u0437\u0430 \u044d\u0442\u043e\u0433\u043e \u0432 \u043d\u0430\u0448\u0435\u0439 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u043d\u0435 \u0432\u0441\u0435\u0433\u0434\u0430 \u0443\u0441\u043f\u0435\u0432\u0430\u044e\u0442 \u0437\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438.\u00a0<\/p>\n<p>\u041f\u043b\u0430\u0432\u043d\u044b\u0439 \u0441\u043a\u0440\u043e\u043b\u043b \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043f\u043e-\u0440\u0430\u0437\u043d\u043e\u043c\u0443, \u044f \u0441\u0434\u0435\u043b\u0430\u044e \u0447\u0435\u0440\u0435\u0437 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e transform: translate3d.<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">import \"\/src\/assets\/style.scss\"; import * as THREE from 'three';   const lerp = (start, end, t) => {     return start * (1 - t) + end * t; };  class Sketch {     constructor() {         this.body = document.querySelector('body');           this.images = [...document.querySelectorAll('img')];         this.meshItems = [];           this.scrollable = document.querySelector(\".smooth-scroll\");         this.current = 0;         this.target = 0;         this.ease = 0.065;           this.createScene();         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();     }       get viewport() {         const width = window.innerWidth;         const height = window.innerHeight;         const aspectRatio = width \/ height;         return { width, height, aspectRatio };     }       smoothScroll = () => {         this.target = window.scrollY;         this.current = lerp(this.current, this.target, this.ease);         this.scrollable.style.transform = `translate3d(0,${-this.current}px, 0)`;     };       createScene() {         this.scene = new THREE.Scene();     }       createCamera() {         const perspective = 1000;         const fov = (180 * (2 * Math.atan(window.innerHeight \/ 2 \/ perspective))) \/ Math.PI;         this.camera = new THREE.PerspectiveCamera(fov, this.viewport.aspectRatio, 1, 1000)         this.camera.position.set(0, 0, perspective);     }       createMesh() {         this.images.map((image) => {             const meshItem = new MeshItem(image, this.scene);             this.meshItems.push(meshItem);         })         document.body.style.height = `${this.scrollable.getBoundingClientRect().height}px`;     }       onWindowResize() {         document.body.style.height = `${this.scrollable.getBoundingClientRect().height}px`;         this.camera.aspect = this.viewport.aspectRatio;         this.createCamera();         this.camera.updateProjectionMatrix();         this.renderer.setSize(this.viewport.width, this.viewport.height);     }       initRenderer() {         window.addEventListener('resize', this.onWindowResize.bind(this), false);         this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });         this.renderer.setSize(this.viewport.width, this.viewport.height);         this.renderer.setPixelRatio(window.devicePixelRatio);         this.body.appendChild(this.renderer.domElement);     }       render() {         this.smoothScroll();         for(let i = 0; i &lt; this.meshItems.length; i++){             this.meshItems[i].render();         }         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }     new Sketch();<\/code><\/pre>\n<p>\u0421\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e <strong>translate3d <\/strong>\u0431\u0443\u0434\u0435\u043c \u0441\u0434\u0432\u0438\u0433\u0430\u0442\u044c \u0431\u043b\u043e\u043a \u0441\u00a0\u043a\u043b\u0430\u0441\u0441\u043e\u043c <strong>smooth\u2011scroll <\/strong>\u0432\u0432\u0435\u0440\u0445. <\/p>\n<p><strong>this.target<\/strong>\u00a0\u2014 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0432\u00a0\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435\u00a0\u0431\u044b\u043b\u0438 \u043f\u0440\u043e\u043b\u0438\u0441\u0442\u0430\u043d\u044b \u043d\u0430\u00a0\u0434\u0430\u043d\u043d\u044b\u0439 \u043c\u043e\u043c\u0435\u043d\u0442 \u043e\u0442\u00a0\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0439 \u043f\u043e\u0437\u0438\u0446\u0438\u0438;<\/p>\n<p><strong>lerp <\/strong>\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f\u00a0\u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438;<\/p>\n<p><strong>this.current<\/strong>\u00a0\u2014 \u0442\u0435\u043a\u0443\u0449\u0435\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438;<\/p>\n<p><strong>this.ease<\/strong>\u00a0\u2014 \u043a\u043e\u044d\u0444\u0444\u0438\u0446\u0438\u0435\u043d\u0442\u00a0\u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438 \u0434\u043b\u044f\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438;<\/p>\n<p><strong>this.scrollable.getBoundingClientRect().height <\/strong>\u2014 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u0434\u0430\u0432\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b \u0442\u0435\u0433\u0443 body \u0440\u0430\u0432\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044e \u0432\u044b\u0441\u043e\u0442\u044b \u043a\u043e\u043d\u0442\u0435\u043d\u0442\u0430.<\/p>\n<p>CSS \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0440\u043e\u043f\u0438\u0441\u0430\u043d\u044b \u0434\u043b\u044f \u0431\u043b\u043e\u043a\u043e\u0432 html, body, main, scrollable \u0442\u043e\u0436\u0435 \u0432\u043b\u0438\u044f\u044e\u0442 \u043d\u0430 \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u043e \u0438 \u043f\u043b\u0430\u0432\u043d\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438. \u0412 HTML-\u0434\u043e\u043a\u0443\u043c\u0435\u043d\u0442\u0435 \u044d\u0442\u043e \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0442\u0430\u043a:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>index.html<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/f1d\/11f\/f9f\/f1d11ff9f55013831e466b82f043c2c3.jpg\" width=\"1066\" height=\"719\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/f1d\/11f\/f9f\/f1d11ff9f55013831e466b82f043c2c3.jpg\" data-blurred=\"true\"\/><\/figure>\n<p>\u0444\u0430\u0439\u043b:<code>src\/assets\/style.scss<\/code><\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/a4a\/f9b\/646\/a4af9b64698fdaa750864f6506c8d1ee.jpg\" alt=\"\" title=\"\" width=\"808\" height=\"724\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/a4a\/f9b\/646\/a4af9b64698fdaa750864f6506c8d1ee.jpg\" data-blurred=\"true\"\/><\/figure>\n<h2>\u041e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432<\/h2>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u044b \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u0432\u0435\u0447\u0430\u0442\u044c \u043d\u0430\u0448\u0443 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044e. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b vertex.glsl \u0432 \u043f\u0430\u043f\u043a\u0435 glsl:<\/p>\n<p>\u0444\u0430\u0439\u043b:<code>src\/glsl\/vertex.glsl<\/code><\/p>\n<pre><code class=\"javascript\">uniform sampler2D uTexture; uniform vec2 uOffset; uniform vec2 u_mouse; uniform float u_time; varying vec2 vUv;  #define PI 3.14  vec3 deformationCurve(vec3 position, vec2 uv, vec2 offset) {    position.y = position.y + (sin(uv.x * PI) * offset.y);    return position; }  void main() {    vUv = uv;    float noise = 1. - sin(4. * uv.x + u_mouse.x * 90.) \/ 30.;    vec3 newPosition = deformationCurve(position, uv , uOffset + noise * 0.02 * sin(u_time));    gl_Position = projectionMatrix * modelViewMatrix * vec4( newPosition, 1.0 ); }<\/code><\/pre>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u0444\u0430\u0439\u043b fragment.gls l\u0432 \u043f\u0430\u043f\u043a\u0435 glsl:<\/p>\n<p>\u0444\u0430\u0439\u043b:<code>src\/glsl\/fragment.glsl<\/code><\/p>\n<pre><code class=\"javascript\">uniform sampler2D uTexture; uniform float uAlpha; uniform vec2 uOffset; varying vec2 vUv; uniform vec2 u_mouse; uniform float u_time;  vec3 rgbShift(sampler2D textureImage, vec2 uv, vec2 offset) {     float noise = 1. - sin(2. * sin(uv.x * u_mouse.x) * 20. + u_time * 0.6) \/ 30.;    vec3 rgb = texture2D(textureImage, uv * noise).rgb;    rgb.b = texture2D(textureImage, uv * noise + sin(u_mouse.x * u_time - u_time * 0.3)*0.015).b;    rgb.r = texture2D(textureImage, uv * noise - sin(u_mouse.x * u_time + u_time * 0.3)*-0.02).r;    return vec3(rgb); }  void main() {    vec3 color = rgbShift(uTexture, vUv, uOffset);    gl_FragColor = vec4(color, uAlpha); }<\/code><\/pre>\n<p>\u0418\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u0443\u0435\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u044b:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">import \"\/src\/assets\/style.scss\";   import * as THREE from 'three'; import vertex from '.\/glsl\/vertex.glsl'; import fragment from '.\/glsl\/fragment.glsl';<\/code><\/pre>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u043e\u043f\u0438\u0441\u0430\u043d\u0438\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0448\u0435\u0439\u0434\u0435\u0440\u0430\u043c\u0438. \u0422\u0435\u043c\u0430 \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u043e\u0431\u044a\u0435\u043c\u043d\u0430\u044f \u0438 \u0432 \u0440\u0430\u043c\u043a\u0430\u0445 \u043e\u0434\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0438 \u043d\u0435 \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u0441\u044f \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u044d\u0442\u043e \u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435\u043d\u043d\u043e. \u0414\u043b\u044f \u0438\u0437\u0443\u0447\u0435\u043d\u0438\u044f \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <a href=\"https:\/\/thebookofshaders.com\/?lan=ru\"><u>The Book of Shaders<\/u><\/a> \u2013 \u044d\u0442\u043e \u043f\u043e\u0448\u0430\u0433\u043e\u0432\u043e\u0435 \u0440\u0443\u043a\u043e\u0432\u043e\u0434\u0441\u0442\u0432\u043e \u043f\u043e \u0430\u0431\u0441\u0442\u0440\u0430\u043a\u0442\u043d\u043e\u0439 \u0438 \u0441\u043b\u043e\u0436\u043d\u043e\u0439 \u0432\u0441\u0435\u043b\u0435\u043d\u043d\u043e\u0439 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u044b\u0445 \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432.\u00a0<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0434\u043b\u044f \u043d\u0430\u0448\u0435\u0439 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438:<\/p>\n<p>\u0444\u0430\u0439\u043b:<code>src\/main.js <\/code>\u043a\u043b\u0430\u0441\u0441:<code>MeshItem<\/code><\/p>\n<pre><code class=\"javascript\">    createMesh() {         const geometry = new THREE.PlaneGeometry(1, 1, 10, 10);         const imageTexture = new THREE.TextureLoader().load(this.element.src);         imageTexture.minFilter = THREE.LinearFilter;         this.uniforms = {             uTexture: { value: imageTexture },             uOffset: { value: new THREE.Vector2(0.0, 0.0) },             uAlpha: { value: 1.0 },             u_mouse: { type: \"v2\", value: new THREE.Vector2() },             u_time: { type: \"f\", value: 0.0 },         };           const material = new THREE.ShaderMaterial({             uniforms: this.uniforms,             vertexShader: vertex,             fragmentShader: fragment,             transparent: true,             \/\/wireframe: true,             side: THREE.DoubleSide         })           this.mesh = new THREE.Mesh(geometry, material);         this.scene.add(this.mesh);     }<\/code><\/pre>\n<p><strong>imageTexture <\/strong>\u2014 \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u0442 \u043d\u0430\u0448\u0443 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u043f\u043e\u00a0\u0443\u043a\u0430\u0437\u0430\u043d\u043d\u043e\u043c\u0443 \u043f\u0443\u0442\u0438;<\/p>\n<p><strong>THREE.LinearFilter<\/strong>\u00a0\u2014 \u043d\u0443\u0436\u0435\u043d \u0434\u043b\u044f\u00a0\u043f\u0440\u0430\u0432\u0438\u043b\u044c\u043d\u043e\u0433\u043e \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u043e \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0435\u043c\u044b\u0445 \u0442\u0435\u043a\u0441\u0442\u0443\u0440;<\/p>\n<p><strong>this.uniforms <\/strong>\u2014 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u043e Three.js, \u0447\u0435\u0440\u0435\u0437 \u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u043c\u043e\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u00a0\u0448\u0435\u0439\u0434\u0435\u0440\u044b;<\/p>\n<p><strong>uTexture <\/strong>\u2014 \u0432\u00a0\u044d\u0442\u043e \u043f\u043e\u043b\u0435 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430;<\/p>\n<p><strong>uOffset<\/strong>\u00a0\u2014 \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u0432\u0435\u043a\u0442\u043e\u0440, \u0432\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u043c \u043f\u0435\u0440\u0435\u0434\u0430\u0432\u0430\u0442\u044c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b;<\/p>\n<p><strong>uAlpha<\/strong>\u00a0\u2014 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0430\u043b\u044c\u0444\u0430 \u043a\u0430\u043d\u0430\u043b\u0430. 0\u00a0\u2014 \u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u0439, 1\u00a0\u2014 \u043d\u0435\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u044b\u0439;<\/p>\n<p><strong>u_mouse <\/strong>\u2014 \u0432\u00a0\u044d\u0442\u043e \u043f\u043e\u043b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b (x \u0438 y) \u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u043c\u044b\u0448\u0438;<\/p>\n<p><strong>u_time <\/strong>\u2014 \u0432\u00a0\u044d\u0442\u043e \u043f\u043e\u043b\u0435 \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u0447\u0435\u0442\u0447\u0438\u043a\u0430 \u0432\u0440\u0435\u043c\u0435\u043d\u0438.<\/p>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0438 \u043f\u0435\u0440\u0435\u0434\u0430\u0434\u0438\u043c \u0432 \u043d\u0435\u0433\u043e \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0445 \u0438 \u0432\u0435\u0440\u0442\u0435\u043a\u0441\u043d\u044b\u0445 \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432, \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u044e\u043d\u0438\u0444\u043e\u0440\u043c\u044b, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u0438\u00a0 \u0438 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b \u0434\u0432\u0443\u0445\u0441\u0442\u043e\u0440\u043e\u043d\u043d\u0438\u043c:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js <\/code>\u043a\u043b\u0430\u0441\u0441: <code>Sketch<\/code><\/p>\n<pre><code class=\"javascript\">class Sketch {      imageLoaded(url) {         return new Promise(function (resolve, reject) {             var img = new Image();             img.onload = function () {                 resolve(url);             };             img.onerror = function () {                 reject(url);             };             img.src = url;         });     }       createMesh() {         const imagesLoaded = this.images.map((image) => {             const meshItem = new MeshItem(image, this.scene);             this.meshItems.push(meshItem);             return this.imageLoaded(image.src);         })           Promise.all(imagesLoaded).then(() => {             document.body.style.height = ${this.scrollable.getBoundingClientRect().height}px`;         })     } }   new Sketch();<\/code><\/pre>\n<p><strong>imageLoaded <\/strong>\u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u0430\u00a0\u043b\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0438 \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u0442 \u043f\u0440\u043e\u043c\u0438\u0441;<\/p>\n<p><strong>Promise.all(imagesLoaded) <\/strong>\u2014 \u0435\u0441\u043b\u0438 \u0432\u0441\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0437\u0430\u0433\u0440\u0443\u0436\u0435\u043d\u044b, \u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b body (\u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0433\u043e \u043d\u0435\u00a0\u0441\u0434\u0435\u043b\u0430\u0442\u044c, \u0442\u043e \u0432\u044b\u0441\u043e\u0442\u0430 \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u0430\u0435\u0442\u0441\u044f \u0434\u043e\u00a0\u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a, \u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0432\u044b\u0441\u043e\u0442\u044b \u0431\u0443\u0434\u0435\u0442 \u043c\u0435\u043d\u044c\u0448\u0435).<\/p>\n<h2>\u0414\u0435\u043b\u0430\u0435\u043c Hover-\u044d\u0444\u0444\u0435\u043a\u0442 \u043f\u0440\u0438 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043a\u0443\u0440\u0441\u043e\u0440\u0430 \u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443\u00a0<\/h2>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code>\u00a0 \u043a\u043b\u0430\u0441\u0441: <code>MeshItem<\/code><\/p>\n<pre><code class=\"javascript\">class Sketch {     constructor() {         this.body = document.querySelector('body');           this.images = [...document.querySelectorAll('img')];                 this.scrollable = document.querySelector(\".smooth-scroll\");         this.current = 0;         this.target = 0;         this.ease = 0.065;           this.meshItems = [];         this.planeItems = [];           this.mouseCoordinates = new THREE.Vector2();         this.raycaster = new THREE.Raycaster();         this.selectMesh = null;         this.onMouse();         this.onTouchMove();           this.createScene()         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();             }       onTouchMove() {         document.addEventListener(\"touchmove\", (event) => {             const x = (event?.touches[0].clientX \/ window.innerWidth) * 2 - 1;             const y = -(event?.touches[0].clientY \/ window.innerHeight) * 2 + 1;             this.mouseCoordinates = { x, y: window.innerWidth > 450 ? y : 0. };         })     }       onMouse() {         document.addEventListener(\"mousemove\", (event) => {             const x = (event.clientX \/ window.innerWidth) * 2 - 1;             const y = -(event.clientY \/ window.innerHeight) * 2 + 1;             this.mouseCoordinates = { x, y }         })     }       createMesh() {         const imagesLoaded = this.images.map((image) => {             const meshItem = new MeshItem(image, this.scene);             this.meshItems.push(meshItem);             return this.imageLoaded(image.src);         })           Promise.all(imagesLoaded).then(() => {             document.body.style.height = ${this.scrollable.getBoundingClientRect().height}px`;         })           this.scene.traverse((item) => {             if (item.isMesh) {                 this.planeItems.push(item);             }         })     }       render() {         this.smoothScroll();         this.raycaster.setFromCamera(this.mouseCoordinates, this.camera);         const intersects = this.raycaster.intersectObjects(this.planeItems, true);         if (intersects.length > 0) {             this.selectMesh = intersects[0].object;         } else {             if (this.selectMesh !== null) {                 this.selectMesh = null;             }         }         const velocity = (this.target - this.current);         for (let i = 0; i &lt; this.meshItems.length; i++) {             this.meshItems[i].render(velocity, this.mouseCoordinates, this.selectMesh);         }         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }   new Sketch() <\/code><\/pre>\n<p><strong>this.mouseCoordinates <\/strong>\u2014 \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u0432\u0435\u043a\u0442\u043e\u0440 \u0441\u00a0\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438 x, y, \u043a\u0443\u0434\u0430 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c\u0441\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043c\u044b\u0448\u0438;<\/p>\n<p><strong>Raycaster<\/strong>\u00a0\u2014 \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u043a\u043b\u0430\u0441\u0441 Three. \u0418\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u0434\u043b\u044f\u00a0\u0432\u044b\u0431\u043e\u0440\u0430 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432 \u0432\u00a0\u0442\u0440\u0435\u0445\u043c\u0435\u0440\u043d\u043e\u0439 \u0441\u0446\u0435\u043d\u0435;<\/p>\n<p><strong>selectMesh<\/strong>\u00a0\u2014 \u043e\u0431\u044a\u0435\u043a\u0442, \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0430 \u043c\u044b\u0448\u044c;<\/p>\n<p><strong>onTouchMove, onMouse <\/strong>\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u0438, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043c\u044b\u0448\u0438 \u0438\u043b\u0438\u00a0\u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0434\u0432\u0438\u0436\u0435\u043d\u0438\u044f \u043f\u0430\u043b\u044c\u0446\u0430 \u043d\u0430\u00a0\u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u043c \u0432\u00a0\u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e this.mouseCoordinates. \u041d\u043e\u00a0\u043f\u0435\u0440\u0435\u0434 \u044d\u0442\u0438\u043c \u043c\u044b \u0434\u043e\u043b\u0436\u043d\u044b \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u0442\u044c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043c\u044b\u0448\u0438 \u0432\u00a0\u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043e\u0442 -1\u00a0\u0434\u043e 1;<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w780q1\/getpro\/habr\/upload_files\/df5\/f4a\/1d0\/df5f4a1d0fb254441d66bcc1ff5af1ce.jpg\" width=\"610\" height=\"402\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/df5\/f4a\/1d0\/df5f4a1d0fb254441d66bcc1ff5af1ce.jpg\" data-blurred=\"true\"\/><\/figure>\n<p><strong>this.scene.traverse<\/strong>\u00a0\u2014 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u043e\u00a0\u043c\u0430\u0441\u0441\u0438\u0432\u0443 \u0441\u0446\u0435\u043d\u044b \u0438, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044f, \u0442\u043e \u0431\u0443\u0434\u0435\u043c \u0435\u0435 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0442\u044c \u0432\u00a0this.planeItems;<\/p>\n<p><strong>this.raycaster.setFromCamera(this.mouseCoordinates, this.camera) <\/strong>\u2014 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043c\u044b\u0448\u0438 \u0438 \u043a\u0430\u043c\u0435\u0440\u0443 \u0432\u00a0raycaster;<\/p>\n<p><strong>this.raycaster.intersectObjects <\/strong>\u2014 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u00a0\u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0435\u0439 (this.planeItems) \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u0435 \u0441\u00a0\u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0441\u043b\u0435\u0436\u0438\u0432\u0430\u0442\u044c. <strong>this.raycaster.intersectObjects <\/strong>\u0432\u0435\u0440\u043d\u0435\u0442 \u043e\u0442\u0441\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 \u0441\u00a0\u043e\u0431\u044a\u0435\u043a\u0442\u0430\u043c\u0438 \u0441\u0446\u0435\u043d\u044b, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043f\u0435\u0440\u0435\u0441\u0435\u043a\u043b\u0438\u0441\u044c \u0441\u00a0\u043c\u044b\u0448\u044c\u044e, \u043d\u0430\u0447\u0438\u043d\u0430\u044f \u043e\u0442\u00a0\u0441\u0430\u043c\u043e\u0433\u043e \u0431\u043b\u0438\u0436\u043d\u0435\u0433\u043e \u043a\u00a0\u043a\u0430\u043c\u0435\u0440\u0435. \u0412\u00a0\u043d\u0443\u043b\u0435\u0432\u043e\u0439 \u0438\u043d\u0434\u0435\u043a\u0441 \u0437\u0430\u043f\u0438\u0448\u0435\u0442\u0441\u044f \u043f\u0435\u0440\u0432\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043d\u0430\u0445\u043e\u0434\u0438\u043b\u0441\u044f \u0431\u043b\u0438\u0436\u0435 \u043a\u00a0\u043a\u0430\u043c\u0435\u0440\u0435, \u0438 \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0439\u00a0\u0431\u044b\u043b\u0430 \u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0430 \u043c\u044b\u0448\u044c.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u0442\u044c \u043b\u0438 \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u0435, \u0438 \u0437\u0430\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044e \u043f\u0435\u0440\u0435\u0441\u0435\u0447\u0435\u043d\u0438\u044f \u0432 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e selectMesh. \u0412 \u043f\u0440\u043e\u0442\u0438\u0432\u043d\u043e\u043c \u0441\u043b\u0443\u0447\u0430\u0435 \u043e\u0431\u043d\u0443\u043b\u044f\u0435\u043c \u044d\u0442\u0443 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u0443\u044e.<\/p>\n<p><strong>this.meshItems[i].render(velocity, this.mouseCoordinates, this.selectMesh) <\/strong>\u2013 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438, \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043c\u044b\u0448\u0438 \u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u0443\u044e \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044e \u0432 \u043c\u0435\u0442\u043e\u0434 render-\u043a\u043b\u0430\u0441\u0441\u0430 meshItems \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0448\u0435\u0439\u0434\u0435\u0440\u0430:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code>\u00a0 \u043a\u043b\u0430\u0441\u0441: <code>MeshItem<\/code><\/p>\n<pre><code class=\"javascript\">class MeshItem {     render(velocity = 0, mouseCoordinates, selectMesh) {         this.getDimensions();         this.mesh.position.set(this.offset.x, this.offset.y, 0);         this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);           this.uniforms.uOffset.value.set(this.offset.x * 0.5, -(velocity) * 0.0003);           if (this.mesh.uuid === selectMesh?.uuid) {             this.uniforms.u_mouse.value.x = lerp(0.0, mouseCoordinates.x, 0.6);             this.uniforms.u_mouse.value.y = lerp(0.0, mouseCoordinates.y, 0.6);             this.uniforms.u_time.value += 0.05;             return;         }         this.uniforms.u_mouse.value.x = lerp(this.uniforms.u_mouse.value.x, 0.0, 0.02);         this.uniforms.u_mouse.value.y = lerp(this.uniforms.u_mouse.value.y, 0.0, 0.02);         this.uniforms.u_time.value = lerp(this.uniforms.u_time.value, 0.0, 0.02);     } }<\/code><\/pre>\n<p><strong>this.uniforms.uOffset.value.set(this.offset.x * 0.5, -(velocity) * 0.0003)<\/strong> \u2014 \u043f\u0435\u0440\u0435\u0434\u0430\u0435\u043c \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0438 \u0432\u00a0\u0448\u0435\u0439\u0434\u0435\u0440;<\/p>\n<p><strong>this.mesh.uuid === selectMesh?.uuid <\/strong>\u2014 \u043f\u0440\u043e\u0432\u0435\u0440\u044f\u0435\u043c, \u0435\u0441\u043b\u0438 \u0432\u044b\u0431\u0440\u0430\u043d\u043d\u044b\u0439 uuid \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442 \u0441\u00a0uuid \u0442\u0435\u043a\u0443\u0449\u0435\u0439 \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438, \u0442\u043e \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0448\u0435\u0439\u0434\u0435\u0440\u0430 \u0447\u0435\u0440\u0435\u0437 \u0444\u0443\u043d\u043a\u0446\u0438\u044e\u00a0\u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438. \u0415\u0441\u043b\u0438 uuid \u043d\u0435\u00a0\u0441\u043e\u0432\u043f\u0430\u0434\u0430\u0435\u0442, \u0442\u043e \u0432\u043e\u0437\u0432\u0440\u0430\u0449\u0430\u0435\u043c \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0448\u0435\u0439\u0434\u0435\u0440\u0430 \u0432\u00a0\u043f\u0435\u0440\u0432\u043e\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e\u0435 \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435;<\/p>\n<p>\u041d\u0430\u0433\u043b\u044f\u0434\u043d\u044b\u0439 \u043f\u0440\u0438\u043c\u0435\u0440 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0431\u0435\u0437 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438, \u0435\u0441\u043b\u0438 \u0431\u044b \u043c\u044b \u0441\u0440\u0430\u0437\u0443 \u043f\u0440\u0438\u0441\u0432\u0430\u0438\u0432\u0430\u043b\u0438 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0438 \u043f\u043b\u0430\u0432\u043d\u043e\u0435 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d\u0438\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0441 \u0444\u0443\u043d\u043a\u0446\u0438\u0435\u0439 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438.<\/p>\n<p>\u0411\u0435\u0437 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0438:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4a2\/b75\/700\/4a2b75700d8e449b247ddb4c3d9b5d00.gif\" width=\"400\" height=\"321\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/4a2\/b75\/700\/4a2b75700d8e449b247ddb4c3d9b5d00.gif\"\/><\/figure>\n<p>\u0421 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0435\u0439:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8a1\/b95\/c34\/8a1b95c3468beed8de6aa9e3330b7ba5.gif\" width=\"350\" height=\"281\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/8a1\/b95\/c34\/8a1b95c3468beed8de6aa9e3330b7ba5.gif\"\/><\/figure>\n<h2>\u0417\u0430\u043a\u043b\u044e\u0447\u0435\u043d\u0438\u0435  <\/h2>\n<p>\u0412\u00a0\u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043b\u0438 \u043f\u0440\u0438\u043c\u0435\u0440 \u0432\u0441\u0442\u0440\u0430\u0438\u0432\u0430\u043d\u0438\u044f WebGL \u0432\u00a0HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0443, \u0441\u00a0\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u0438\u0437\u0430\u0446\u0438\u0435\u0439 \u043f\u043e\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435. \u041a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0437\u0430\u043c\u0435\u043d\u0435\u043d\u044b \u043d\u0430\u00a0\u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 Three.js, \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u043c\u043e\u0436\u0435\u043c \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u0435\u043c \u043d\u0430\u0448\u0438\u0445 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f CSS (\u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u0442\u0435, \u043a\u0430\u043a\u00a0\u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u043e\u0435 <a href=\"https:\/\/alex277777.github.io\/demo\/\"><u>\u0434\u0435\u043c\u043e<\/u><\/a> \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 \u043d\u0430\u00a0\u043c\u043e\u0431\u0438\u043b\u044c\u043d\u043e\u0439 \u0432\u0435\u0440\u0441\u0438\u0438 \u044d\u043a\u0440\u0430\u043d\u0430). \u0411\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044e 3D\u2011\u0433\u0440\u0430\u0444\u0438\u043a\u0438 \u0438 \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432 \u043c\u043e\u0436\u043d\u043e \u0434\u0435\u043b\u0430\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438, \u0447\u0442\u043e\u00a0\u0434\u0430\u0435\u0442 \u0431\u043e\u043b\u044c\u0448\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u0435\u0439 \u0434\u043b\u044f\u00a0\u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u043e\u0439 \u0440\u0435\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u0438. \u0415\u0434\u0438\u043d\u0441\u0442\u0432\u0435\u043d\u043d\u043e\u0435 \u043e\u0433\u0440\u0430\u043d\u0438\u0447\u0435\u043d\u0438\u0435\u00a0\u2014 \u044d\u0442\u043e \u0432\u0430\u0448\u0435 \u0432\u043e\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0438 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043c\u0430\u0441\u0442\u0435\u0440\u0441\u0442\u0432\u0430:)<\/p>\n<p>\u0412\u00a0\u043a\u0430\u0447\u0435\u0441\u0442\u0432\u0435 \u0441\u0430\u043c\u043e\u0441\u0442\u043e\u044f\u0442\u0435\u043b\u044c\u043d\u043e\u0433\u043e \u0434\u043e\u043c\u0430\u0448\u043d\u0435\u0433\u043e \u0437\u0430\u0434\u0430\u043d\u0438\u044f \u043c\u043e\u0436\u0435\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043a\u0443\u0440\u0441\u043e\u0440, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430\u00a0\u0434\u0432\u0438\u0436\u0435\u043d\u0438\u0435\u043c \u043c\u044b\u0448\u0438 \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eea\/9e5\/f9d\/eea9e5f9d9100cb853ec9f5d2dcda4a7.gif\" width=\"400\" height=\"337\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/eea\/9e5\/f9d\/eea9e5f9d9100cb853ec9f5d2dcda4a7.gif\"\/><\/figure>\n<p><strong>\u0421\u043f\u0430\u0441\u0438\u0431\u043e \u0437\u0430 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435!<\/strong><\/p>\n<p><strong>\u041f\u043e\u043b\u0435\u0437\u043d\u044b\u0435 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u044b \u043e frontend-\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u043c\u044b \u0442\u0430\u043a\u0436\u0435 \u043f\u0443\u0431\u043b\u0438\u043a\u0443\u0435\u043c \u0432 \u043d\u0430\u0448\u0438\u0445 \u0441\u043e\u0446\u0441\u0435\u0442\u044f\u0445 \u2013 <\/strong><a href=\"https:\/\/vk.com\/simbirsoft\"><strong><u>\u0412\u041a\u043e\u043d\u0442\u0430\u043a\u0442\u0435<\/u><\/strong><\/a><strong> \u0438 <\/strong><a href=\"https:\/\/t.me\/simbirsoft_dev\"><strong><u>Telegram<\/u><\/strong><\/a><strong>.<\/strong><\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<p> <!----> <!----><\/div>\n<p> <!----> <!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/company\/simbirsoft\/blog\/721912\/\"> https:\/\/habr.com\/ru\/company\/simbirsoft\/blog\/721912\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<p><a href=\"https:\/\/articles-simbirsoft.github.io\/demo\/\"><strong>demo<\/strong><\/a><strong>, <\/strong><a href=\"https:\/\/github.com\/articles-simbirsoft\/demo\"><strong>github<\/strong>\u00a0<\/a><\/p>\n<p>\u041f\u0440\u0438\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e! \u042f \u0410\u043b\u0435\u043a\u0441\u0435\u0439, frontend\u2011\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a \u0432\u00a0SimbirSoft. \u0412\u044b, \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u0432\u0438\u0434\u0435\u043b\u0438 \u043f\u043e\u0442\u0440\u044f\u0441\u0430\u044e\u0449\u0438\u0435 \u0432\u0435\u0431\u2011\u0441\u0430\u0439\u0442\u044b, \u043f\u0440\u0435\u0434\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u043d\u044b\u0435 \u043d\u0430 <a href=\"https:\/\/www.awwwards.com\">www.awwwards.com<\/a>. \u042d\u0442\u043e \u043e\u043d\u043b\u0430\u0439\u043d\u2011\u043a\u0430\u0442\u0430\u043b\u043e\u0433 \u043b\u0443\u0447\u0448\u0438\u0445 \u0432\u0435\u0431\u2011\u0441\u0430\u0439\u0442\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u0446\u0435\u043d\u0438\u0432\u0430\u0435\u0442 \u0438 \u043d\u0430\u0433\u0440\u0430\u0436\u0434\u0430\u0435\u0442 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u0438\u0435 \u0438 \u0438\u043d\u043d\u043e\u0432\u0430\u0446\u0438\u043e\u043d\u043d\u044b\u0435 \u0440\u0430\u0431\u043e\u0442\u044b \u0432\u0435\u0431\u2011\u0434\u0438\u0437\u0430\u0439\u043d\u0435\u0440\u043e\u0432 \u0438 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u043e\u0432. \u041d\u0430\u00a0\u044d\u0442\u043e\u043c \u0441\u0430\u0439\u0442\u0435 \u043c\u043e\u0436\u043d\u043e \u043d\u0430\u0439\u0442\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043f\u0440\u0438\u043c\u0435\u0440\u043e\u0432 \u043a\u0440\u0435\u0430\u0442\u0438\u0432\u043d\u043e\u0433\u043e \u0432\u0435\u0431\u2011\u0434\u0438\u0437\u0430\u0439\u043d\u0430, \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0439 \u0438 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432. \u0422\u0430\u043a\u0438\u0435 \u0443\u0434\u0438\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u043e\u0431\u044b\u0447\u043d\u043e \u0440\u0430\u0437\u0440\u0430\u0431\u0430\u0442\u044b\u0432\u0430\u044e\u0442\u0441\u044f \u0441\u00a0\u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c WebGL. \u042d\u0442\u0430 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u044f \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0438 \u0442\u0432\u043e\u0440\u0447\u0435\u0441\u043a\u0438 \u043f\u043e\u0434\u0445\u043e\u0434\u0438\u0442\u044c \u043a\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044e \u0432\u043f\u0435\u0447\u0430\u0442\u043b\u044f\u044e\u0449\u0438\u0445 \u0432\u0438\u0437\u0443\u0430\u043b\u044c\u043d\u044b\u0445 \u044d\u0444\u0444\u0435\u043a\u0442\u043e\u0432 \u0431\u0435\u0437\u00a0\u0443\u0449\u0435\u0440\u0431\u0430 \u0434\u043b\u044f\u00a0\u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u0414\u043b\u044f\u00a0\u0440\u0430\u0431\u043e\u0442\u044b \u0441\u00a0WebGL \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442\u0441\u044f \u0442\u0430\u043a\u0438\u0435 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438, \u043a\u0430\u043a\u00a0Three.js, PIXIJS \u0438\u043b\u0438\u00a0BABYLON, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0442\u0430\u043a\u0436\u0435 \u043f\u043e\u043f\u0443\u043b\u044f\u0440\u043d\u044b \u043f\u0440\u0438\u00a0\u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0438 \u0438\u0433\u0440.<\/p>\n<p>\u0412\u00a0\u0434\u0430\u043d\u043d\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u043c\u044b \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u043e\u0432\u043c\u0435\u0449\u0435\u043d\u0438\u0435 WebGL\u2011\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u0441\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u043e\u0439 \u0441\u0442\u0440\u0430\u043d\u0438\u0446\u044b HTML, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044f \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 Three.js. \u0420\u0430\u0431\u043e\u0442\u0430 \u0441\u00a0\u043d\u0435\u0439 \u0432\u043e\u00a0\u043c\u043d\u043e\u0433\u043e\u043c \u0441\u0445\u043e\u0436\u0430 \u0441\u00a0\u0440\u0430\u0431\u043e\u0442\u043e\u0439 3D\u2011\u0440\u0435\u0434\u0430\u043a\u0442\u043e\u0440\u0430 (3ds Max, Maya, Blender \u0438\u00a0\u0442.\u00a0\u0434.). \u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430 \u0432\u00a0\u0432\u0438\u0434\u0435 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 \u0438\u043b\u0438\u00a0\u0430\u043d\u0438\u043c\u0430\u0446\u0438\u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0441\u0446\u0435\u043d\u0443, \u043f\u043e\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0432\u00a0\u043d\u0435\u0435 \u043a\u0430\u043c\u0435\u0440\u0443, \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043f\u0440\u0438\u043c\u0438\u0442\u0438\u0432 (\u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u044e \u0438\u043b\u0438 3D\u2011\u043c\u043e\u0434\u0435\u043b\u044c), \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0438\u0441\u0442\u043e\u0447\u043d\u0438\u043a \u043e\u0441\u0432\u0435\u0449\u0435\u043d\u0438\u044f \u0438 \u0437\u0430\u043f\u0443\u0441\u0442\u0438\u0442\u044c \u043f\u0440\u043e\u0446\u0435\u0441\u0441 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433\u0430. <\/p>\n<p>\u042d\u0442\u0430 \u0441\u0442\u0430\u0442\u044c\u044f \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u043b\u0435\u0437\u043d\u0430 middle \u0438 senior \u0444\u0440\u043e\u043d\u0442\u0435\u043d\u0434\u2011\u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u0447\u0438\u043a\u0430\u043c, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0445\u043e\u0442\u044f\u0442 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441\u00a0Three. \u0412\u00a0\u0441\u0442\u0430\u0442\u044c\u0435 \u043e\u0447\u0435\u043d\u044c \u043c\u0430\u043b\u043e \u0442\u0435\u043e\u0440\u0438\u0438 \u0438 \u0432\u0432\u043e\u0434\u043d\u044b\u0445 \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b\u043e\u0432, \u0430\u043a\u0446\u0435\u043d\u0442 \u0441\u0434\u0435\u043b\u0430\u043d \u043d\u0430\u00a0\u043f\u0440\u0430\u043a\u0442\u0438\u0447\u0435\u0441\u043a\u043e\u0439 \u0447\u0430\u0441\u0442\u0438. \u0415\u0441\u043b\u0438 \u0432\u044b \u0441\u043e\u0432\u0441\u0435\u043c \u043d\u0435\u00a0\u0437\u043d\u0430\u0435\u0442\u0435, \u043a\u0430\u043a\u00a0\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442 <a href=\"https:\/\/threejs.org\/\"><u>Three.js<\/u><\/a> \u0438 \u0448\u0435\u0439\u0434\u0435\u0440\u044b, \u0440\u0435\u043a\u043e\u043c\u0435\u043d\u0434\u0443\u044e \u0432\u043d\u0430\u0447\u0430\u043b\u0435 \u043e\u0437\u043d\u0430\u043a\u043e\u043c\u0438\u0442\u044c\u0441\u044f \u0441\u00a0\u044d\u0442\u043e\u0439 \u0442\u0435\u0445\u043d\u043e\u043b\u043e\u0433\u0438\u0435\u0439, \u0430\u00a0\u043f\u043e\u0441\u043b\u0435 \u0432\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u043a\u00a0\u0441\u0442\u0430\u0442\u044c\u0435.<\/p>\n<figure class=\"full-width\"><\/figure>\n<h2>\u041a\u0430\u043a \u044d\u0442\u043e \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u043d\u0430 \u0431\u0430\u0437\u043e\u0432\u043e\u043c \u0443\u0440\u043e\u0432\u043d\u0435  <\/h2>\n<p>\u0423\u00a0\u043d\u0430\u0441 \u0435\u0441\u0442\u044c HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0441\u043e \u0441\u043a\u0440\u044b\u0442\u044b\u043c\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430\u043c\u0438. \u0411\u043b\u043e\u043a canvas \u0430\u0431\u0441\u043e\u043b\u044e\u0442\u043d\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e <strong>body <\/strong>\u0438 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0437\u0430\u00a0HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435\u0439. \u0412\u043d\u0443\u0442\u0440\u0438 \u0441\u0446\u0435\u043d\u044b \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u044b \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438, \u043d\u0430\u00a0\u043a\u043e\u0442\u043e\u0440\u044b\u0445 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438 (\u0442\u0435\u00a0\u0436\u0435 \u0441\u0430\u043c\u044b\u0435, \u0447\u0442\u043e\u00a0\u0438 \u043d\u0430\u00a0HTML\u2011\u0441\u0442\u0440\u0430\u043d\u0438\u0446\u0435). \u0422\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u0431\u0443\u0434\u0443\u0442 \u0440\u0435\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u044b \u0441\u00a0\u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0445 \u0438 \u043f\u0438\u043a\u0441\u0435\u043b\u044c\u043d\u044b\u0445 \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432, \u0431\u043b\u0430\u0433\u043e\u0434\u0430\u0440\u044f \u044d\u0442\u043e\u043c\u0443 \u043c\u044b \u0441\u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0440\u0430\u0437\u043b\u0438\u0447\u043d\u044b\u0435 \u044d\u0444\u0444\u0435\u043a\u0442\u044b \u043f\u0440\u0438\u00a0\u043d\u0430\u0432\u0435\u0434\u0435\u043d\u0438\u0438 \u043c\u044b\u0448\u0438. \u0414\u043b\u044f\u00a0\u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043c\u044b \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u0444\u0443\u043d\u043a\u0446\u0438\u044e <strong>getBoundingClientRect <\/strong>\u0438 \u043f\u0440\u0438\u043c\u0435\u043d\u0438\u043c \u044d\u0442\u0438 \u0434\u0430\u043d\u043d\u044b\u0435 \u043a\u00a0\u043d\u0430\u0448\u0438\u043c \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044f\u043c \u0432\u00a0\u0441\u0446\u0435\u043d\u0435, \u0447\u0442\u043e\u00a0\u043f\u043e\u0437\u0432\u043e\u043b\u0438\u0442 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u0442\u044c \u0438\u0445 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u0438 \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0445 \u0442\u0430\u043a\u043e\u0433\u043e\u00a0\u0436\u0435 \u0440\u0430\u0437\u043c\u0435\u0440\u0430, \u043a\u0430\u043a\u00a0\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u041f\u0440\u0438\u00a0\u043f\u0440\u043e\u043a\u0440\u0443\u0442\u043a\u0435 \u0442\u0430\u043a\u0436\u0435 \u0431\u0443\u0434\u0435\u043c \u043f\u043e\u043b\u0443\u0447\u0430\u0442\u044c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u0441\u043a\u0440\u043e\u043b\u043b\u0430 \u0438 \u0434\u0432\u0438\u0433\u0430\u0442\u044c \u043d\u0430\u0448\u0438 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u0432\u0435\u0440\u0445.<\/p>\n<figure class=\"full-width\"><\/figure>\n<h2>\u041d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/h2>\n<p>\u0414\u043b\u044f \u0441\u0431\u043e\u0440\u043a\u0438 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c vite. \u041a\u0430\u043a \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c vite, \u043c\u043e\u0436\u043d\u043e \u043f\u043e\u0447\u0438\u0442\u0430\u0442\u044c <a href=\"https:\/\/vitejs.dev\/guide\/\"><u>\u0442\u0443\u0442. <\/u><\/a>\u041a\u043e\u043c\u0430\u043d\u0434\u0430\u00a0<\/p>\n<p><code>npm create vite@latest app --template vanilla<\/code><\/p>\n<p>\u0441\u043e\u0437\u0434\u0430\u0441\u0442 \u0431\u0430\u0437\u043e\u0432\u044b\u0439 \u0448\u0430\u0431\u043b\u043e\u043d \u0434\u043b\u044f \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0438 \u043d\u0430 JavaScript. \u0421\u0440\u0430\u0437\u0443 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u043c Three.js, \u0432\u044b\u043f\u043e\u043b\u043d\u0438\u0432 \u043a\u043e\u043c\u0430\u043d\u0434\u0443:<\/p>\n<p><code>npm install three<\/code><\/p>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u0432\u043e\u0431\u043e\u0434\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0448\u0435\u0439\u0434\u0435\u0440\u044b, \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0442\u044c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 <a href=\"https:\/\/www.npmjs.com\/package\/vite-plugin-glsl\"><u>vite-plugin-glsl<\/u><\/a>, \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u043c \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0443 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043e\u043c\u0430\u043d\u0434\u044b:<\/p>\n<p><code>npm i vite-plugin-glsl --save-dev<\/code><\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0444\u0430\u0439\u043b vite.config.js \u0438 \u043f\u0440\u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u043c \u0442\u0430\u043c \u043a\u043e\u043d\u0444\u0438\u0433:<\/p>\n<p><code>\/\/ vite.config.js<\/code><\/p>\n<pre><code class=\"javascript\">import glsl from 'vite-plugin-glsl'; import { defineConfig } from 'vite';  export default defineConfig({   plugins: [glsl()] });<\/code><\/pre>\n<p>\u0421\u043e\u0433\u043b\u0430\u0441\u043d\u043e \u044d\u0442\u043e\u0439 \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0435, \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430 \u0434\u043e\u043b\u0436\u043d\u0430 \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<figure class=\"full-width\">\n<div><figcaption>\u0421\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 \u043f\u0440\u043e\u0435\u043a\u0442\u0430<\/figcaption><\/div>\n<\/figure>\n<p>\u041d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043f\u0443\u0442\u044c \u0434\u043e \u0444\u0430\u0439\u043b\u0430 main.js \u0432 \u0444\u0430\u0439\u043b\u0435 index.html, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0434\u043e\u043b\u0436\u0435\u043d \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u043f\u043a\u0435 src.<\/p>\n<h2>\u0411\u0430\u0437\u043e\u0432\u044b\u0435 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0441\u0446\u0435\u043d\u044b<\/h2>\n<p>\u0412 \u043d\u0430\u0448\u0435\u0439 \u0441\u0446\u0435\u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u043b\u0435\u043d\u0430 <a href=\"https:\/\/threejs.org\/docs\/#api\/en\/cameras\/PerspectiveCamera\"><u>\u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u043d\u0430\u044f \u043a\u0430\u043c\u0435\u0440\u0430<\/u><\/a>. \u0427\u0442\u043e\u0431\u044b \u043a\u0430\u043c\u0435\u0440\u0430 \u0437\u0430\u0445\u0432\u0430\u0442\u044b\u0432\u0430\u043b\u0430 \u0432\u0441\u044e \u0448\u0438\u0440\u0438\u043d\u0443 \u0438 \u0432\u044b\u0441\u043e\u0442\u0443 \u044d\u043a\u0440\u0430\u043d\u0430, \u0440\u0430\u0441\u0441\u0447\u0438\u0442\u044b\u0432\u0430\u0435\u043c \u0443\u0433\u043e\u043b \u043e\u0431\u0437\u043e\u0440\u0430 <strong>fov<\/strong>:<\/p>\n<figure class=\"full-width\"><\/figure>\n<p>\u0410 \u0442\u0435\u043f\u0435\u0440\u044c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u043e\u0439 \u0443\u0440\u043e\u043a \u0448\u043a\u043e\u043b\u044c\u043d\u043e\u0439 \u0442\u0440\u0438\u0433\u043e\u043d\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \ud83d\ude42 \u041a\u0430\u043c\u0435\u0440\u0430 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0430 \u043d\u0430 \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0438 1000 px \u043e\u0442 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 (\u043d\u0430 \u0440\u0438\u0441\u0443\u043d\u043a\u0435 \u043f\u043e\u043a\u0430\u0437\u0430\u043d\u0430 \u043a\u0430\u043a canvas), \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u0439 \u0431\u0443\u0434\u0443\u0442 \u0440\u0430\u0441\u043f\u043e\u043b\u0430\u0433\u0430\u0442\u044c\u0441\u044f \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438. \u0410\u0440\u043a\u0442\u0430\u043d\u0433\u0435\u043d\u0441 fov\/2 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0432\u0435\u043d (\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u043f\u0440\u043e\u0442\u0438\u0432\u043e\u043b\u0435\u0436\u0430\u0449\u0435\u0433\u043e \u043a\u0430\u0442\u0435\u0442\u0430 \u043a \u043f\u0440\u0438\u043b\u0435\u0436\u0430\u0449\u0435\u043c\u0443) window.innerHeight \/ 2 \/ perspective. \u0427\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u0443\u0433\u043e\u043b \u0432 \u0433\u0440\u0430\u0434\u0443\u0441\u0430\u0445, \u0443\u043c\u043d\u043e\u0436\u0438\u043c \u0443\u0434\u0432\u043e\u0435\u043d\u043d\u043e\u0435 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0430 180 \u0438 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043c \u043d\u0430 PI:<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0432\u043e\u0439 \u043f\u0440\u043e\u0435\u043a\u0442 \u0438\u0437 <a href=\"https:\/\/github.com\/articles-simbirsoft\/demo\"><u>\u043c\u043e\u0435\u0433\u043e \u0440\u0435\u043f\u043e\u0437\u0438\u0442\u043e\u0440\u0438\u044f \u043d\u0430 github<\/u><\/a>, \u0442\u043e \u0432\u0430\u043c \u0441\u043b\u0435\u0434\u0443\u0435\u0442 \u0437\u0430\u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043a\u043b\u0430\u0441\u0441 main \u0432 css, \u0447\u0442\u043e\u0431\u044b \u0432 \u0434\u0430\u043b\u044c\u043d\u0435\u0439\u0448\u0435\u043c \u0432\u0441\u0435 \u0441\u043e\u0432\u043f\u0430\u0434\u0430\u043b\u043e.<\/p>\n<p>\u0418\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0441\u043a\u0440\u043e\u0435\u043c \u0432\u0441\u0435 \u043d\u0430\u0448\u0438 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0438, \u0443\u0441\u0442\u0430\u043d\u043e\u0432\u0438\u0432 \u0438\u043c \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0435\u043f\u0440\u043e\u0437\u0440\u0430\u0447\u043d\u043e\u0441\u0442\u0438 (opacity: 0). \u0414\u043b\u044f \u0442\u0435\u0433\u0430 canvas \u0437\u0430\u0434\u0430\u0434\u0438\u043c \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0435 \u0441\u0442\u0438\u043b\u0438:<\/p>\n<figure class=\"\"><\/figure>\n<p>\u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 Sketch \u0438 \u0432\u044b\u0437\u043e\u0432\u0435\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u044d\u0442\u043e\u0433\u043e \u043a\u043b\u0430\u0441\u0441\u0430:\u00a0<\/p>\n<p>\u0444\u0430\u0439\u043b:<code>src\/main.js<\/code>  <\/p>\n<pre><code class=\"javascript\">import \"\/src\/assets\/style.scss\"; import * as THREE from 'three';   class Sketch {     constructor() {         this.body = document.querySelector('body');         this.createScene();         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();     }       get viewport() {         const width = window.innerWidth;         const height = window.innerHeight;         const aspectRatio = width \/ height;         return { width, height, aspectRatio };     }       createScene() {         this.scene = new THREE.Scene();     }       createCamera() {         const perspective = 1000;         const fov = (180 * (2 * Math.atan(window.innerHeight \/ 2 \/ perspective))) \/ Math.PI;         this.camera = new THREE.PerspectiveCamera(fov, this.viewport.aspectRatio, 1, 1000)         this.camera.position.set(0, 0, perspective);     }       createMesh() {         const geometry = new THREE.PlaneGeometry( 250, 250, 10, 10 );         const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         const mesh = new THREE.Mesh(geometry, material);         this.scene.add(mesh);     }       onWindowResize() {                 this.camera.aspect = this.viewport.aspectRatio;         this.createCamera();         this.camera.updateProjectionMatrix();         this.renderer.setSize(this.viewport.width, this.viewport.height);     }       initRenderer() {         this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });         this.renderer.setSize(this.viewport.width, this.viewport.height);         this.renderer.setPixelRatio(window.devicePixelRatio);         this.body.appendChild(this.renderer.domElement);     }       render() {         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }   new Sketch();<\/code><\/pre>\n<p>\u041e\u043f\u0438\u0441\u0430\u043d\u0438\u0435 \u043c\u0435\u0442\u043e\u0434\u043e\u0432 \u043a\u043b\u0430\u0441\u0441\u0430 Sketch:<\/p>\n<p><strong>viewport<\/strong>\u00a0\u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440\u044b \u0438 \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u0441\u0442\u043e\u0440\u043e\u043d \u044d\u043a\u0440\u0430\u043d\u0430;<\/p>\n<p><strong>createScene<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0441\u0446\u0435\u043d\u0443;<\/p>\n<p><strong>createCamera<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043a\u0430\u043c\u0435\u0440\u0443 \u0441\u00a0\u0440\u0430\u0441\u0447\u0435\u0442\u043d\u044b\u043c\u0438 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u0430\u043c\u0438;<\/p>\n<p><strong>createMesh<\/strong>\u00a0\u2014 \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u043c\u0430\u0442\u0435\u0440\u0438\u0430\u043b, \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0447\u0435\u0441\u043a\u0443\u044e \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u044c \u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0435\u043c \u0435\u0435 \u0432\u00a0\u043d\u0430\u0448\u0443 \u0441\u0446\u0435\u043d\u0443;<\/p>\n<p><strong>THREE.PlaneGeometry (250, 250, 10, 10)<\/strong>\u00a0\u2014 \u0437\u0430\u0434\u0430\u0435\u043c \u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 250\u00d7250\u00a0\u0438 10\u00d710\u00a0\u0440\u0430\u0437\u043c\u0435\u0440 \u043f\u043e\u043b\u0438\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u043a\u0438 \u0434\u0435\u043b\u0435\u043d\u0438\u044f \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u043f\u043e\u00a0\u0433\u043e\u0440\u0438\u0437\u043e\u043d\u0442\u0430\u043b\u0438 \u0438 \u0432\u0435\u0440\u0442\u0438\u043a\u0430\u043b\u0438;<\/p>\n<p><strong>THREE.MeshBasicMaterial({ color: 0\u00d700ff00, wireframe: true })<\/strong>\u00a0\u2014 \u0437\u0430\u0434\u0430\u0435\u043c \u0446\u0432\u0435\u0442 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u00a0\u0444\u043e\u0440\u043c\u0430\u0442\u0435 Hex Color. wireframe: true\u00a0\u2014 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438 \u0432\u00a0\u0432\u0438\u0434\u0435 \u043f\u043e\u043b\u0438\u0433\u043e\u043d\u0430\u043b\u044c\u043d\u043e\u0439 \u0441\u0435\u0442\u043a\u0438 (\u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u043c);<\/p>\n<p><strong>onWindowResize<\/strong>\u00a0\u2014 \u0444\u0443\u043d\u043a\u0446\u0438\u044f \u0434\u043b\u044f\u00a0\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u0432 \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u043f\u0440\u0438\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u044d\u043a\u0440\u0430\u043d\u0430;<\/p>\n<p><strong>initRenderer<\/strong>\u00a0\u2014 \u0438\u043d\u0438\u0446\u0438\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430, \u043f\u0440\u0438\u00a0\u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0438 \u0440\u0430\u0437\u043c\u0435\u0440\u043e\u0432 \u044d\u043a\u0440\u0430\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0437\u044b\u0432\u0430\u0442\u044c \u0444\u0443\u043d\u043a\u0446\u0438\u044e onWindowResize;<\/p>\n<p><a href=\"https:\/\/threejs.org\/docs\/#api\/en\/renderers\/WebGLRenderer.setPixelRatio\"><strong><u>setPixelRatio<\/u><\/strong><\/a>\u00a0\u2014 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442 \u0441\u043e\u043e\u0442\u043d\u043e\u0448\u0435\u043d\u0438\u0435 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439 \u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0435\u0434\u043e\u0442\u0432\u0440\u0430\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u043c\u044b\u0442\u0438\u0435 \u0432\u044b\u0445\u043e\u0434\u043d\u043e\u0433\u043e \u0445\u043e\u043b\u0441\u0442\u0430. \u0412\u043e\u0437\u043c\u043e\u0436\u043d\u043e, \u043d\u0430\u00a0\u0443\u0441\u0442\u0440\u043e\u0439\u0441\u0442\u0432\u0430\u0445 mac retina \u044d\u0442\u043e \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435 \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u0437\u043c\u0435\u043d\u0438\u0442\u044c, \u0447\u0442\u043e\u0431\u044b \u0430\u043d\u0438\u043c\u0430\u0446\u0438\u044f \u043d\u0435\u00a0\u0442\u043e\u0440\u043c\u043e\u0437\u0438\u043b\u0430.<\/p>\n<p><strong>render <\/strong>\u2014 \u0440\u0435\u043a\u0443\u0440\u0441\u0438\u0432\u043d\u0430\u044f \u0444\u0443\u043d\u043a\u0446\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u0430\u044f \u0431\u0443\u0434\u0435\u0442 \u0437\u0430\u043f\u0443\u0441\u043a\u0430\u0442\u044c \u0441\u0430\u043c\u0443 \u0441\u0435\u0431\u044f \u0434\u043b\u044f\u00a0\u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u044f \u0440\u0435\u043d\u0434\u0435\u0440\u0430 \u0441\u0446\u0435\u043d\u044b.<\/p>\n<p>\u0421\u0442\u0440\u0430\u043d\u0438\u0446\u0430 \u0434\u043e\u043b\u0436\u043d\u044b \u0432\u044b\u0433\u043b\u044f\u0434\u0435\u0442\u044c \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\"><\/figure>\n<h2>\u0417\u0430\u043c\u0435\u043d\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a \u043d\u0430 Three.js \u043f\u043b\u043e\u0441\u043a\u043e\u0441\u0442\u0438<\/h2>\n<p>\u0414\u043b\u044f \u043d\u0430\u0447\u0430\u043b\u0430 \u0434\u043e\u0440\u0430\u0431\u043e\u0442\u0430\u0435\u043c \u043d\u0430\u0448 \u043a\u043b\u0430\u0441\u0441 Sketch.<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">\u0441lass Sketch{     constructor(){         this.body = document.querySelector('body');          this.images = [...document.querySelectorAll('img')];         this.meshItems = [];          this.createScene();         this.createCamera();         this.createMesh();         this.initRenderer();         this.render();      }      initRenderer() {         window.addEventListener('resize', this.onWindowResize.bind(this), false);         this.renderer = new THREE.WebGL1Renderer({ antialias: true, alpha: true });         this.renderer.setSize(this.viewport.width, this.viewport.height);         this.renderer.setPixelRatio(window.devicePixelRatio);         this.body.appendChild(this.renderer.domElement);     } ...     createMesh() {         \/\/ const geometry = new THREE.PlaneGeometry( 250, 250, 10, 10 );         \/\/ const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         \/\/ const mesh = new THREE.Mesh(geometry, material);         \/\/ this.scene.add(mesh);         this.images.map((image) => {             const meshItem = new MeshItem(image, this.scene);             this.meshItems.push(meshItem);         })     }      ...     render(){         for(let i = 0; i &lt; this.meshItems.length; i++){             this.meshItems[i].render();         }         this.renderer.render(this.scene, this.camera);         requestAnimationFrame(this.render.bind(this));     } }<\/code><\/pre>\n<p><strong>this.images<\/strong>\u00a0\u2014 \u043f\u043e\u043b\u0443\u0447\u0430\u0435\u043c \u043c\u0430\u0441\u0441\u0438\u0432 \u043a\u0430\u0440\u0442\u0438\u043d\u043e\u043a;<\/p>\n<p><strong>this.meshItems<\/strong>\u00a0\u2014 \u0438\u0437\u043d\u0430\u0447\u0430\u043b\u044c\u043d\u043e \u0440\u0430\u0432\u0435\u043d \u043f\u0443\u0441\u0442\u043e\u043c\u0443 \u043c\u0430\u0441\u0441\u0438\u0432\u0443;<\/p>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 createMesh \u0443\u0434\u0430\u043b\u0438\u043c \u0440\u0430\u043d\u0435\u0435 \u0441\u0443\u0449\u0435\u0441\u0442\u0432\u043e\u0432\u0430\u0432\u0448\u0438\u0439 \u043a\u043e\u0434 \u0438 \u0432\u0441\u0442\u0430\u0432\u0438\u043c \u0446\u0438\u043a\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043e\u0431\u0445\u043e\u0434\u0438\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439. \u0421\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440 \u043a\u043b\u0430\u0441\u0441\u0430 MeshItem \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0444\u043e\u0442\u043e\u0433\u0440\u0430\u0444\u0438\u0438 \u0438 \u0440\u0430\u0437\u043c\u0435\u0441\u0442\u0438\u043c \u0435\u0433\u043e \u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0435 this.meshItems. \u041f\u0440\u0438\u0441\u0432\u043e\u0438\u043c \u043a\u0430\u0436\u0434\u043e\u0439 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0435 \u0443\u043d\u0438\u043a\u0430\u043b\u044c\u043d\u044b\u0439 id, \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0439 \u0435\u0435 \u0438\u043d\u0434\u0435\u043a\u0441\u0443 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432\u0435. \u0414\u043b\u044f \u043c\u0435\u0442\u043e\u0434\u0430 render \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u0446\u0438\u043a\u043b, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u043e\u0445\u043e\u0434\u0438\u0442\u044c \u043f\u043e \u043c\u0430\u0441\u0441\u0438\u0432\u0443 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0439 \u0438 \u0434\u0435\u043b\u0430\u0442\u044c \u0438\u0445 \u043f\u0435\u0440\u0435\u0440\u0435\u043d\u0434\u0435\u0440 \u043d\u0430 \u043a\u0430\u0436\u0434\u043e\u043c \u044d\u0442\u0430\u043f\u0435 requestAnimationFrame;<\/p>\n<p>\u0412\u0441\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0432 \u043a\u043b\u0430\u0441\u0441\u0435 Sketch \u043e\u0441\u0442\u0430\u043d\u0443\u0442\u0441\u044f \u0431\u0435\u0437 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u0439.<\/p>\n<p>\u0414\u0430\u043b\u0435\u0435 \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c \u043a\u043b\u0430\u0441\u0441 MeshItem:<\/p>\n<p>\u0444\u0430\u0439\u043b: <code>src\/main.js<\/code><\/p>\n<pre><code class=\"javascript\">class MeshItem{     constructor(element, scene){         this.element = element;         this.scene = scene;         this.offset = new THREE.Vector2(0,0);         this.sizes = new THREE.Vector2(0,0);         this.createMesh();     }      getDimensions(){         const {width, height, top, left} = this.element.getBoundingClientRect();         this.sizes.set(width, height);         this.offset.set(left - window.innerWidth \/ 2 + width \/ 2, -top + window.innerHeight \/ 2 - height \/ 2);     }      createMesh(){         const geometry = new THREE.PlaneGeometry( 1, 1, 10, 10 );         const material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: true });         this.mesh = new THREE.Mesh(geometry, material);         this.scene.add(this.mesh);     }      render(){         this.getDimensions();         this.mesh.position.set(this.offset.x, this.offset.y, 0);         this.mesh.scale.set(this.sizes.x, this.sizes.y, 1);     } }<\/code><\/pre>\n<p><a href=\"https:\/\/threejs.org\/docs\/#api\/en\/math\/Vector2\"><strong><u>THREE.Vector2<\/u><\/strong><\/a>\u00a0<\/p>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-346705","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346705","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=346705"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/346705\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=346705"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=346705"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=346705"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}