{"id":341045,"date":"2022-11-09T21:00:20","date_gmt":"2022-11-09T21:00:20","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=341045"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=341045","title":{"rendered":"<span>\u0420\u0430\u0431\u043e\u0442\u0430 \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0439 \u043d\u0430 \u044f\u0437\u044b\u043a\u0435 Rust. \u0427\u0430\u0441\u0442\u044c 2<\/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<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/6f6\/56b\/a64\/6f656ba649fdda6a1ecb60747dbe462b.png\" width=\"1278\" height=\"891\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/6f6\/56b\/a64\/6f656ba649fdda6a1ecb60747dbe462b.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0438 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 <em>WGPU<\/em>, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u044f\u0437\u044b\u043a\u0430 Rust \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0439.<\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043d\u0435 \u0447\u0438\u0442\u0430\u043b <a href=\"https:\/\/habr.com\/ru\/post\/690514\/\" rel=\"noopener noreferrer nofollow\"><u>\u043f\u0435\u0440\u0432\u0443\u044e<\/u><\/a> \u0441\u0442\u0430\u0442\u044c\u044e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0432\u0432\u043e\u0434\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f.<\/p>\n<p><em>WGPU<\/em> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0432\u0438\u0434\u0435\u043e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u2014 <em>WebGPU \u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u043d\u044b\u0435 backend-\u044b (OpenGL, DirectX12, Metal, Vulkan, WebGL). \u041e\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u0449\u0435 \u0434\u043b\u044f \u043e\u0441\u0432\u043e\u0435\u043d\u0438\u044f, \u0447\u0435\u043c Vulkan \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0443\u043c\u0430\u043d\u043d\u043e\u0435 \u0430\u043f\u0438, \u0447\u0435\u043c OpenGL.<\/em><\/p>\n<p>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c!<\/p>\n<h3>\u0423\u0440\u043e\u043a 4. \u0422\u0435\u043a\u0441\u0442\u0443\u0440\u044b<\/h3>\n<p>\u0422\u0435\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u0432\u0435\u0440\u0445 \u0441\u0435\u0442\u043a\u0438 (\u0444\u0438\u0433\u0443\u0440\u044b) \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0434\u0430\u0442\u044c \u0435\u0439 \u0444\u0430\u043a\u0442\u0443\u0440\u0443 \u0438\/\u0438\u043b\u0438 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<h4>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0430\u043c\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u0430\u043f\u043a\u0443 <code>assets<\/code> \u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0442\u0443\u0434\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/5e7\/9c0\/0d3\/5e79c00d39b37a77efbca2d9bb04d0ec.png\" width=\"640\" height=\"640\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/5e7\/9c0\/0d3\/5e79c00d39b37a77efbca2d9bb04d0ec.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0414\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>image<\/code> crate:<\/p>\n<p><code>image = \"0.24\"<\/code><\/p>\n<p>\u0414\u0430\u043b\u0435\u0435, \u044f \u0441\u0434\u0435\u043b\u0430\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u2014 <code>texture<\/code>:<\/p>\n<pre><code class=\"rust\">use std::error::Error;  pub struct Texture {    pub texture: wgpu::Texture,    pub view: wgpu::TextureView,    pub sampler: wgpu::Sampler, }<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>Texture::from_image<\/code>:<\/p>\n<pre><code class=\"rust\">pub fn from_image(    device: &amp;wgpu::Device,    queue: &amp;wgpu::Queue,    img: &amp;image::DynamicImage,    label: Option&lt;&amp;str> ) -> Self {    let diffuse_rgba = diffuse_image.to_rgba8();                            \/\/ 1       use image::GenericImageView;    let dimensions = diffuse_image.dimensions();       let size = wgpu::Extent3d {                                             \/\/ 2        width: dimensions.0,        height: dimensions.1,        \/\/ \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 2D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0441\u043b\u043e\u0439        depth_or_array_layers: 1,    };    let texture = device.create_texture(                                    \/\/ 3        &amp;wgpu::TextureDescriptor {            label,            size,            mip_level_count: 1,            sample_count: 1,            dimension: wgpu::TextureDimension::D2,            \/\/ \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0436\u0438\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0446\u0432\u0435\u0442\u0430 \u0438\u0437 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 sRGB \u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0435 float [0, 1] \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435            format: wgpu::TextureFormat::Rgba8UnormSrgb,            \/\/ TEXTURE_BINDING \u0444\u043b\u0430\u0433 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u0448\u0435\u0439\u0434\u0435\u0440\u0435            \/\/ COPY_DST \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u044d\u0442\u0443 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,        }    ); ...<\/code><\/pre>\n<ol>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 rgba \u0431\u0430\u0439\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c (\u044d\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c) \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u044c \u0432 3D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<\/ol>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430 \u043f\u0443\u0441\u0442\u0430\u044f \u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"rust\">...    \/\/ \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0432\u044b\u0448\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443    queue.write_texture(        \/\/ \u0417\u0430\u0434\u0430\u0435\u043c, \u043a\u0443\u0434\u0430 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435        wgpu::ImageCopyTexture {            aspect: wgpu::TextureAspect::All,            texture: &amp;texture,            mip_level: 0,            origin: wgpu::Origin3d::ZERO,        },        \/\/ \u041c\u0430\u0441\u0441\u0438\u0432 rgba \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439        &amp;rgba,        \/\/ \u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445        wgpu::ImageDataLayout {            offset: 0,            bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),            rows_per_image: std::num::NonZeroU32::new(dimensions.1),        },        size,    );     Self { texture, view, sampler } }<\/code><\/pre>\n<h4>\u0424\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440<\/h4>\n<p>\u042d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c \u044f \u0434\u043e\u043b\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u043b, \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0435 \u0438\u043c\u0435\u043b \u0434\u0435\u043b\u043e \u0441 <code>Sampler<\/code>-\u0430\u043c\u0438. \u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043e\u043c \u043a\u043e\u0434\u0430, \u0434\u0430\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0432\u0432\u043e\u0434\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0422\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0439 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u0438\u043b\u0438 \u044d\u043a\u0440\u0430\u043d\u0430. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0433\u0434\u0435-\u0442\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <em>\u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0445 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439<\/em> (\u0438\u043d\u043e\u0433\u0434\u0430 \u0438\u0445 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 <strong>\u0442\u0435\u043a\u0441\u0435\u043b\u0438<\/strong>) \u0441 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u043c\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438. \u0418 <code>Sampler<\/code> \u043a\u0430\u043a \u0440\u0430\u0437 \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u044d\u0442\u0438\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0436\u0438\u043c\u043e\u0432 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<pre><code class=\"rust\">let view = texture.create_view(&amp;wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler(    &amp;wgpu::SamplerDescriptor {        address_mode_u: wgpu::AddressMode::ClampToEdge,     \/\/ 1        address_mode_v: wgpu::AddressMode::ClampToEdge,        address_mode_w: wgpu::AddressMode::ClampToEdge,        mag_filter: wgpu::FilterMode::Linear,               \/\/ 2        min_filter: wgpu::FilterMode::Nearest,              \/\/ 3        mipmap_filter: wgpu::FilterMode::Nearest,        ..Default::default()    } );<\/code><\/pre>\n<ol>\n<li>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0434\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0440\u0435\u0436\u0438\u043c\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 <code>address_mode_*<\/code><\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0441\u0442\u044f\u043d\u0443\u0442\u044c (magnify) \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0441\u0436\u0430\u0442\u044c (minify) \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<\/ol>\n<p><code>FilterMode<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0432\u0435 \u043e\u043f\u0446\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><code>Linear<\/code> \u0441\u043c\u0435\u0448\u0438\u0432\u0430\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u043a \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u0435\u0439. \u0427\u0435\u043c \u0431\u043b\u0438\u0436\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u044c \u043a \u0437\u0430\u0434\u0430\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u0439 \u0432\u043a\u043b\u0430\u0434 \u043e\u043d \u0432\u043d\u0435\u0441\u0435\u0442 \u0432 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0446\u0432\u0435\u0442. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0435\u0449\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0431\u0438\u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0435\u0439<\/p>\n<\/li>\n<li>\n<p><code>Nearest<\/code> \u0431\u0435\u0440\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0433\u043e \u043a \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u044f. \u0412 \u044d\u0442\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u043f\u0440\u0438 \u0431\u043b\u0438\u0437\u043a\u043e\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0443\u0433\u043b\u043e\u0432\u0430\u0442\u044b\u0435 \u0443\u0437\u043e\u0440\u044b \u0438 \u043f\u0438\u043a\u0441\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0440\u0435\u0436\u0438\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u044b <code>Sampler<\/code> (<em>AddressMode<\/em>):<\/p>\n<ul>\n<li>\n<p><code>ClampToEdge<\/code> \u043b\u044e\u0431\u043e\u0439 \u0442\u0435\u043a\u0441\u0435\u043b\u044c, \u0432\u044b\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442, \u043f\u0440\u0438\u043c\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0433\u043e \u043a \u0433\u0440\u0430\u043d\u0438\u0446\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u043f\u0438\u043a\u0441\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p><code>Repeat<\/code> \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u2014 \u0437\u0430\u043c\u043e\u0441\u0442\u0438\u0442\u044c, \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u0439 \u043f\u043b\u043e\u0449\u0430\u0434\u0438 \u0444\u0438\u0433\u0443\u0440\u044b<\/p>\n<\/li>\n<li>\n<p><code>MirrorRepeat<\/code> \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 <code>Repeat<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u043e, \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0437\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/493\/fb2\/088\/493fb2088141de7db149cb37bb8beb49.png\" width=\"1592\" height=\"596\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/493\/fb2\/088\/493fb2088141de7db149cb37bb8beb49.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"rust\">pub fn from_bytes(    device: &amp;wgpu::Device,    queue: &amp;wgpu::Queue,    bytes: &amp;[u8],    label: &amp;str ) -> anyhow::Result&lt;Self> {    let img = image::load_from_memory(bytes)?;    Ok(Self::from_image(device, queue, &amp;img, Some(label))) } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b <code>anyhow::Result<\/code> \u0438\u0437 <a href=\"https:\/\/docs.rs\/anyhow\/latest\/anyhow\/\" rel=\"noopener noreferrer nofollow\">\u043a\u0440\u0435\u0439\u0442\u0430<\/a> <code>anyhow = &amp;quot;1.0.64&amp;quot;<\/code>, \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::new<\/code>:<\/p>\n<pre><code class=\"rust\">surface.configure(&amp;device, &amp;config);  \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 let diffuse_bytes = include_bytes!(\"..\/assets\/tree.png\"); let diffuse_texture = Texture::from_bytes(&amp;device, &amp;queue, diffuse_bytes, \"tree.png\").unwrap(); <\/code><\/pre>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443, <code>view<\/code>, <code>sampler<\/code>, \u043d\u043e \u043e\u043d\u0438 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u044a\u0435\u0434\u0435\u043d\u0438\u044e \u0438\u0445 \u0432\u043c\u0435\u0441\u0442\u0435!<\/p>\n<h4>BindGroup<\/h4>\n<p><code>BindGroup<\/code> \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0431\u043e\u0440 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438 \u0441\u0445\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432. \u041c\u044b \u043e\u0431\u044a\u0435\u0434\u0435\u043d\u0438\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0441\u0434\u0435\u043b\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c <code>BindGroup<\/code>, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>BindGroupLayout<\/code>:<\/p>\n<pre><code class=\"rust\">... let texture_bind_group_layout =    device.create_bind_group_layout(&amp;wgpu::BindGroupLayoutDescriptor {        entries: &amp;[            wgpu::BindGroupLayoutEntry {                binding: 0,                                 \/\/ 1                visibility: wgpu::ShaderStages::FRAGMENT,                ty: wgpu::BindingType::Texture {                    multisampled: false,                    view_dimension: wgpu::TextureViewDimension::D2,                    sample_type: wgpu::TextureSampleType::Float { filterable: true },                },                count: None,            },            wgpu::BindGroupLayoutEntry {                binding: 1,                                 \/\/ 2                visibility: wgpu::ShaderStages::FRAGMENT,                \/\/ \u0417\u0434\u0435\u0441\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c SamplerBindingType::Filtering, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u044b\u0448\u0435 \u0432 sample_type \u0443\u043a\u0430\u0437\u0430\u043d\u043e filterable: true                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),                count: None,            },        ],        label: Some(\"texture_bind_group_layout\"),    }); <\/code><\/pre>\n<p>\u042d\u0442\u0430 \u0441\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 2 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u044f: \u043f\u0435\u0440\u0432\u043e\u0435 \u0434\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b, \u0432\u0442\u043e\u0440\u043e\u0435 \u0434\u043b\u044f <code>Sampler<\/code>. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u0438\u0434\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u043e\u043c (<code>ShaderStages::FRAGMENT<\/code>) \u0448\u0435\u0439\u0434\u0435\u0440\u0435.\u00a0<\/p>\n<p>\u041f\u043e\u043b\u0435 <code>visibility<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u0438\u0442\u043e\u0432\u0443\u044e \u043c\u0430\u0441\u043a\u0443 \u0438\u0437 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 <code>NONE<\/code>, <code>VERTEX<\/code>, <code>FRAGMENT<\/code>, \u0438\u043b\u0438 <code>COMPUTE<\/code>. \u0414\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>FRAGMENT<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>BindGroup<\/code>:<\/p>\n<pre><code class=\"rust\">... let diffuse_bind_group = device.create_bind_group(    &amp;wgpu::BindGroupDescriptor {        layout: &amp;texture_bind_group_layout,        entries: &amp;[            wgpu::BindGroupEntry {                binding: 0,                resource: wgpu::BindingResource::TextureView(&amp;diffuse_texture.view),    \/\/ 1            },            wgpu::BindGroupEntry {                binding: 1,                resource: wgpu::BindingResource::Sampler(&amp;diffuse_texture.sampler),     \/\/ 2            }        ],        label: Some(\"diffuse_bind_group\"),    } ); <\/code><\/pre>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>diffuse_texture.view<\/code> \u0438 <code>diffuse_texture.sampler<\/code>, \u0441\u0434\u0435\u043b\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435. <code>BindGroup<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u043d\u044b\u043c \u0441\u043b\u0443\u0447\u0430\u0435\u043c <code>BindGroupLayout<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0445\u043e\u0434\u0441\u0442\u0432\u043e. \u0412 <em>WGPU<\/em> \u0438\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b\u0438 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438\u0445 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b), \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0443 \u043d\u0438\u0445 \u0431\u044b\u043b\u0430 \u043e\u0434\u043d\u0430 \u0441\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (BindGroupLayout). \u041f\u043e\u0437\u0434\u043d\u0435\u0435, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u0441\u0432\u043e\u0435\u043c <code>BindGroup<\/code>.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>State<\/code>:<\/p>\n<pre><code class=\"rust\">struct State {    surface: wgpu::Surface,    device: wgpu::Device,    queue: wgpu::Queue,    config: wgpu::SurfaceConfiguration,    size: winit::dpi::PhysicalSize&lt;u32>,    render_pipeline: wgpu::RenderPipeline,    vertex_buffer: wgpu::Buffer,    index_buffer: wgpu::Buffer,    num_indices: u32,    \/\/ NEW!    diffuse_bind_group: wgpu::BindGroup,    diffuse_texture: texture::Texture, }<\/code><\/pre>\n<p>\u0418 \u0432\u0435\u0440\u043d\u0443\u0442\u044c <code>diffuse_bind_group<\/code> \u0438 <code>diffuse_texture<\/code>:<\/p>\n<pre><code class=\"rust\">impl State {    async fn new() -> Self {        \/\/ ...        Self {            surface,            device,            queue,            config,            size,            render_pipeline,            vertex_buffer,            index_buffer,            num_indices,            \/\/ NEW!            diffuse_bind_group,            diffuse_texture,        }    } }<\/code><\/pre>\n<p>\u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <code>diffuse_bind_group<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433:<\/p>\n<pre><code class=\"rust\">\/\/ render() \/\/ ... render_pass.set_pipeline(&amp;self.render_pipeline); render_pass.set_bind_group(0, &amp;self.diffuse_bind_group, &amp;[]); \/\/ NEW! render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); <\/code><\/pre>\n<h4>PipelineLayout<\/h4>\n<p>\u041f\u043e\u043c\u043d\u0438\u0442\u0435 <code>PipelineLayout<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0432\u043e <a href=\"https:\/\/..\/..\/lesson2\/docs\/index.md\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u043c<\/a> \u0443\u0440\u043e\u043a\u0435? \u0412 \u043d\u0435\u043c \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0432\u0441\u0435 <code>BindGroupLayout<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.<\/p>\n<pre><code class=\"rust\">async fn new(...) {    \/\/ ...    let render_pipeline_layout = device.create_pipeline_layout(        &amp;wgpu::PipelineLayoutDescriptor {            label: Some(\"Render Pipeline Layout\"),            bind_group_layouts: &amp;[&amp;texture_bind_group_layout], \/\/ NEW!            push_constant_ranges: &amp;[],        }    );    \/\/ ... <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>texture_bind_group_layout<\/code> \u0432 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435!<\/p>\n<h4>\u041d\u043e\u0432\u044b\u0439 \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0439 \u0431\u0443\u0444\u0435\u0440<\/h4>\n<p>\u041f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>Vertex<\/code>:<\/p>\n<pre><code class=\"rust\">#[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct Vertex {    position: [f32; 3],    color: [f32; 3], }<\/code><\/pre>\n<p>\u041c\u044b \u043d\u0430\u043f\u0440\u044f\u043c\u0443\u044e \u0437\u0430\u0434\u0430\u0432\u0430\u043b\u0438 \u0446\u0432\u0435\u0442 \u0432 \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u043e\u043c \u0431\u0443\u0444\u0435\u0440\u0435. \u0417\u0430\u043c\u0435\u043d\u0438\u043c \u043f\u043e\u043b\u0435 <code>color<\/code> \u043d\u0430 <code>tex_coords<\/code>, \u0442\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043f\u0435\u0440\u044c \u0446\u0432\u0435\u0442 \u0431\u0443\u0434\u0435\u0442 \u0432\u044b\u0447\u0438\u0441\u043b\u044f\u0442\u044c\u0441\u044f \u0432\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u043e\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u0435 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b:<\/p>\n<pre><code class=\"rust\">#[repr(C)] #[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)] struct Vertex {    position: [f32; 3],    tex_coords: [f32; 2], } <\/code><\/pre>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043a\u043e\u0434\u0438\u0440\u0443\u044e\u0442\u0441\u044f \u0434\u0432\u0443\u043c\u044f \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u044f\u043c\u0438 f32, \u043d\u0443\u0436\u043d\u043e \u0432\u043d\u0435\u0441\u0442\u0438 \u0441\u043e\u043e\u0442\u0432\u0435\u0442\u0441\u0442\u0432\u0443\u044e\u0449\u0438\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u0438 \u0432 <code>Vertex::description<\/code>:<\/p>\n<pre><code class=\"rust\">... wgpu::VertexAttribute {    offset: std::mem::size_of::&lt;[f32; 3]>() as wgpu::BufferAddress,    shader_location: 1,    format: wgpu::VertexFormat::Float32x2,      \/\/ \u0431\u044b\u043b\u043e Float32x3 } ...<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u0441\u0430\u043c \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0439 \u0431\u0443\u0444\u0435\u0440:<\/p>\n<pre><code class=\"rust\">const VERTICES: &amp;[Vertex] = &amp;[    Vertex { position: [-0.0868241, 0.49240386, 0.0], tex_coords: [0.4131759, 0.99240386], },     \/\/ A    Vertex { position: [-0.49513406, 0.06958647, 0.0], tex_coords: [0.0048659444, 0.56958647], }, \/\/ B    Vertex { position: [-0.21918549, -0.44939706, 0.0], tex_coords: [0.28081453, 0.05060294], },  \/\/ C    Vertex { position: [0.35966998, -0.3473291, 0.0], tex_coords: [0.85967, 0.1526709], },        \/\/ D    Vertex { position: [0.44147372, 0.2347359, 0.0], tex_coords: [0.9414737, 0.7347359], },       \/\/ E ];<\/code><\/pre>\n<h4>\u0428\u0435\u0439\u0434\u0435\u0440\u044b<\/h4>\n<p>\u0417\u0430\u043a\u043b\u044e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u044d\u0442\u0430\u043f, \u043f\u0435\u0440\u0435\u0434 \u0442\u0435\u043c, \u043a\u0430\u043a \u043c\u044b \u0443\u0432\u0438\u0434\u0438\u043c \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u2014 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u043d\u0430 \u043d\u0430\u0448\u0435\u043c \u043f\u0435\u043d\u0442\u0430\u0433\u043e\u043d\u0435. \u041c\u044b \u0438\u0437\u043c\u0435\u043d\u0438\u043b\u0438 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>Vertex<\/code>, \u044d\u0442\u0438 \u0436\u0435 \u0438\u0437\u043c\u0435\u043d\u0435\u043d\u0438\u044f \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0440\u0430\u0437\u0438\u0442\u044c \u0432 \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u043e\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u0435:<\/p>\n<pre><code>\/\/ \u0412\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0439 \u0448\u0435\u0439\u0434\u0435\u0440  struct VertexInput {    @location(0) position: vec3&lt;f32>,    @location(1) tex_coords: vec2&lt;f32>,             \/\/ NEW! };  struct VertexOutput {    @builtin(position) clip_position: vec4&lt;f32>,    @location(0) tex_coords: vec2&lt;f32>,             \/\/ NEW! };  @vertex fn vs_main(    model: VertexInput, ) -> VertexOutput {    var out: VertexOutput;    out.tex_coords = vec2&lt;f32>(model.tex_coords.x, 1.0 - model.tex_coords.y);   \/\/ NEW!    out.clip_position = vec4&lt;f32>(model.position, 1.0);    return out; }<\/code><\/pre>\n<p>\u041e\u0431\u0440\u0430\u0442\u0438\u0442\u0435 \u0432\u043d\u0438\u043c\u0430\u043d\u0438\u0435 \u043d\u0430 \u0441\u0442\u0440\u043e\u043a\u0443 <code>out.tex_coords = vec2&lt;f32>(model.tex_coords.x, 1.0 - model.tex_coords.y)<\/code>.<\/p>\n<p>\u0412 \u043d\u0435\u0439 \u044f \u0438\u043d\u0432\u0435\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u043b \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0443 <code>y<\/code>, \u0447\u0442\u043e\u0431\u044b \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043d\u0435 \u0431\u044b\u043b\u0430 \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u0430\u044f. \u042d\u0442\u043e \u0441\u0432\u044f\u0437\u0430\u043d\u043e \u0442\u0435\u043c, \u0447\u0442\u043e \u0432 <em>WGPU<\/em> \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430 <code>y<\/code> \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u0432\u0435\u0440\u0445, \u0430 \u0432 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430\u0445 \u043e\u043d\u0430 \u043d\u0430\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0430 \u0432\u043d\u0438\u0437.<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/afe\/02b\/1c4\/afe02b1c403ccbf775f0b5535426fe48.png\" width=\"634\" height=\"636\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/afe\/02b\/1c4\/afe02b1c403ccbf775f0b5535426fe48.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u043c \u0442\u0430\u043a\u0436\u0435 \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u044b\u0439 \u0448\u0435\u0439\u0434\u0435\u0440:<\/p>\n<pre><code>\/\/ \u0424\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u044b\u0439 \u0448\u0435\u0439\u0434\u0435\u0440  @group(0) @binding(0) var t_diffuse: texture_2d&lt;f32>; @group(0) @binding(1) var s_diffuse: sampler;  @fragment fn fs_main(in: VertexOutput) -> @location(0) vec4&lt;f32> {    return textureSample(t_diffuse, s_diffuse, in.tex_coords); }<\/code><\/pre>\n<p>\u0414\u043e\u043b\u0436\u043d\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f \u0432\u043e\u0442 \u0442\u0430\u043a:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7a2\/9e3\/d01\/7a29e3d01f9676b2da1311b4dbd07f70.png\" width=\"786\" height=\"590\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7a2\/9e3\/d01\/7a29e3d01f9676b2da1311b4dbd07f70.png\"\/><figcaption><\/figcaption><\/figure>\n<h4>\u0414\u043e\u043c\u0430\u0448\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u043d\u0438\u0435<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u043c\u0435\u043d\u044f\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u043f\u043e \u043d\u0430\u0436\u0430\u0442\u0438\u044e \u043d\u0430 \u043a\u043d\u043e\u043f\u043a\u0443 space.<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/abritov\/rust-wgpu-tutorial\/tree\/master\/lesson4\" rel=\"noopener noreferrer nofollow\"><u>\u043a\u043e\u0434<\/u><\/a> \u0443\u0440\u043e\u043a\u0430<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/sotrh.github.io\/learn-wgpu\/beginner\/tutorial5-textures\/#loading-an-image-from-a-file\" rel=\"noopener noreferrer nofollow\"><u>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/u><\/a><\/p>\n<h3>\u0423\u0440\u043e\u043a 5. Uniform \u0431\u0443\u0444\u0435\u0440 \u0438 3D \u043a\u0430\u043c\u0435\u0440\u0430<\/h3>\n<p>\u0425\u043e\u0442\u044f \u0434\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u044b \u0440\u0438\u0441\u043e\u0432\u0430\u043b\u0438 \u0442\u043e\u043b\u044c\u043a\u043e \u0434\u0432\u0443\u0445\u043c\u0435\u0440\u043d\u044b\u0435 \u0444\u0438\u0433\u0443\u0440\u044b, \u043d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u043c\u044b \u0440\u0430\u0431\u043e\u0442\u0430\u043b\u0438 \u0432 \u0442\u0440\u0435\u0445\u043c\u0435\u0440\u043d\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435. \u0418\u043c\u0435\u043d\u043d\u043e \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0430 <code>Vertex::position<\/code> \u0438\u043c\u0435\u0435\u0442 3 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b, \u0430 \u043d\u0435 2. \u041d\u043e \u043c\u044b \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043c\u043e\u0436\u0435\u043c \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0442\u0440\u0435\u0445\u043c\u0435\u0440\u043d\u043e\u0435 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043a\u0430\u043c\u0435\u0440\u0443.<\/p>\n<h4>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u0443<\/h4>\n<p>\u042d\u0442\u043e\u0442 \u0442\u0443\u0442\u043e\u0440\u0438\u0430\u043b \u0431\u043e\u043b\u044c\u0448\u0435 \u043f\u0440\u043e <em>WGPU<\/em>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c \u043d\u0435 \u0431\u0443\u0434\u0435\u0442 \u0432 \u0434\u0435\u0442\u0430\u043b\u044f\u0445 \u0440\u0430\u0441\u0441\u043c\u0430\u0442\u0440\u0438\u0432\u0430\u0442\u044c\u0441\u044f \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0435 \u0430\u0441\u043f\u0435\u043a\u0442\u044b \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0430\u043c\u0435\u0440\u043e\u0439, \u0442\u043e\u043b\u044c\u043a\u043e \u043f\u0440\u0430\u043a\u0442\u0438\u043a\u0430. \u0415\u0441\u043b\u0438 \u0436\u0435 \u0432\u044b \u0445\u043e\u0442\u0438\u0442\u0435 \u043b\u0443\u0447\u0448\u0435 \u0440\u0430\u0437\u043e\u0431\u0440\u0430\u0442\u044c\u0441\u044f \u0432 \u044d\u0442\u043e\u0439 \u0442\u0435\u043c\u0435 \u0432\u0430\u043c <a href=\"https:\/\/ravesli.com\/urok-9-kamera-v-opengl\/#toc-0\" rel=\"noopener noreferrer nofollow\"><u>\u0441\u044e\u0434\u0430<\/u><\/a> \u0438 <a href=\"https:\/\/ravesli.com\/urok-7-transformatsii-v-opengl\/\" rel=\"noopener noreferrer nofollow\"><u>\u0441\u044e\u0434\u0430<\/u><\/a>. \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <a href=\"https:\/\/docs.rs\/cgmath\/latest\/cgmath\/\" rel=\"noopener noreferrer nofollow\"><u>cgmath<\/u><\/a>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0432\u043e\u0437\u044c\u043c\u0435\u0442 \u043d\u0430 \u0441\u0435\u0431\u044f \u0432\u0441\u044e \u0440\u0430\u0431\u043e\u0442\u0443 \u0441 \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f\u043c\u0438. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0435\u0433\u043e \u0432 \u0437\u0430\u0432\u0438\u0441\u0438\u043c\u043e\u0441\u0442\u0438 \u043f\u0440\u043e\u0435\u043a\u0442\u0430:<\/p>\n<pre><code>cgmath = \"0.18.0\"<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0430\u043c\u0435\u0440\u043e\u0439. \u042f \u0441\u0434\u0435\u043b\u0430\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0444\u0430\u0439\u043b <code>camera.rs<\/code>:<\/p>\n<pre><code class=\"rust\">#[rustfmt::skip] pub const OPENGL_TO_WGPU_MATRIX: cgmath::Matrix4&lt;f32> = cgmath::Matrix4::new(     1.0, 0.0, 0.0, 0.0,     0.0, 1.0, 0.0, 0.0,     0.0, 0.0, 0.5, 0.0,     0.0, 0.0, 0.5, 1.0, );  pub struct Camera {     pub eye: cgmath::Point3&lt;f32>,     pub target: cgmath::Point3&lt;f32>,     pub up: cgmath::Vector3&lt;f32>,     pub aspect: f32,     pub fovy: f32,     pub znear: f32,     pub zfar: f32, }  impl Camera {     pub fn build_view_projection_matrix(&amp;self) -> cgmath::Matrix4&lt;f32> {         let view = cgmath::Matrix4::look_at_rh(self.eye, self.target, self.up);                     \/\/ 1         let proj = cgmath::perspective(cgmath::Deg(self.fovy), self.aspect, self.znear, self.zfar); \/\/ 2         return OPENGL_TO_WGPU_MATRIX * proj * view;                                                 \/\/ 3     } }<\/code><\/pre>\n<p>\u0421\u0430\u043c\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0435\u0441\u043d\u043e\u0435 \u043f\u0440\u043e\u0438\u0441\u0445\u043e\u0434\u0438\u0442 \u0432 <code>build_view_projection_matrix<\/code>:<\/p>\n<ol>\n<li>\n<p>\u041c\u0430\u0442\u0440\u0438\u0446\u0430 <code>view<\/code> \u043f\u0435\u0440\u0435\u043c\u0435\u0449\u0430\u0435\u0442 \u043c\u0438\u0440 \u0432 \u043f\u043e\u0437\u0438\u0446\u0438\u044e \u043a\u0430\u043c\u0435\u0440\u044b. \u041e\u043d\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u044b\u0432\u0430\u0442\u044c \u043c\u0438\u0440\u043e\u0432\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0430 \u043e\u043a\u043d\u0430<\/p>\n<\/li>\n<li>\n<p>\u041c\u0430\u0442\u0440\u0438\u0446\u0430 <code>proj<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u0442 \u044d\u0444\u0444\u0435\u043a\u0442 \u043f\u0435\u0440\u0441\u043f\u0435\u043a\u0442\u0438\u0432\u044b. \u0411\u0435\u0437 \u043d\u0435\u0435 \u0431\u043b\u0438\u0437\u043a\u0438\u0435 \u0438 \u0434\u0430\u043b\u0435\u043a\u0438\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b \u0431\u044b\u043b\u0438 \u0431\u044b \u043e\u0434\u043d\u043e\u0433\u043e \u0440\u0430\u0437\u043c\u0435\u0440\u0430<\/p>\n<\/li>\n<li>\n<p>\u0412 <em>WGPU<\/em> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043d\u0430\u044f \u0441\u0438\u0441\u0442\u0435\u043c\u0430 DirectX &amp; Metal. \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0432 \u043d\u043e\u0440\u043c\u0430\u043b\u0438\u0437\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u0445 (\u043d\u0435\u0437\u0430\u0432\u0438\u0441\u0438\u043c\u044b\u0445 \u043e\u0442 \u0440\u0430\u0437\u043c\u0435\u0440\u0430 \u044d\u043a\u0440\u0430\u043d\u0430) <em>WGPU<\/em> \u043e\u0441\u0438 <code>x<\/code> \u0438 <code>y<\/code> \u043d\u0430\u0445\u043e\u0434\u044f\u0442\u0441\u044f \u0432 \u043f\u0440\u043e\u043c\u0435\u0436\u0443\u0442\u043a\u0435 <code>[-1.0, +1.0]<\/code>, \u0430 \u043e\u0441\u044c <code>z<\/code> \u0432 <code>[0.0, +1.0]<\/code>. \u0412 \u0442\u043e \u0436\u0435 \u0432\u0440\u0435\u043c\u044f <code>cgmath<\/code> \u0441\u0434\u0435\u043b\u0430\u043d \u0434\u043b\u044f \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u043d\u043e\u0439 \u0441\u0438\u0441\u0442\u0435\u043c\u044b <em>OpenGL<\/em>. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043c\u0430\u0442\u0440\u0438\u0446\u0443 <code>OPENGL_TO_WGPU_MATRIX<\/code> \u0434\u043b\u044f \u0442\u0440\u0430\u043d\u0441\u043b\u044f\u0446\u0438\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u043a\u0430\u043c\u0435\u0440\u0443 \u0432 \u043c\u0435\u0442\u043e\u0434 <code>State::new<\/code>:<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"rust\">struct State {     \/\/ ...     camera: Camera,     \/\/ ... }  async fn new(window: &amp;Window) -> Self {     \/\/ let diffuse_bind_group ...      let camera = Camera {         \/\/ \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043a\u0430\u043c\u0435\u0440\u044b         eye: (0.0, 1.0, 4.0).into(),         \/\/ \u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0446\u0435\u043d\u0442\u0440         target: (0.0, 0.0, 0.0).into(),         up: cgmath::Vector3::unit_y(),         aspect: config.width as f32 \/ config.height as f32,         fovy: 45.0,         znear: 0.1,         zfar: 100.0,     };      Self {         \/\/ ...         camera,         \/\/ ...     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u043a\u0430\u043c\u0435\u0440\u0430 \u0438 \u043f\u0440\u043e\u0435\u043a\u0446\u0438\u044f, \u043d\u0443\u0436\u043d\u043e \u043a\u0430\u043a\u0438\u043c-\u0442\u043e \u043e\u0431\u0440\u0430\u0437\u043e\u043c \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0435\u0435 \u0432 \u0448\u0435\u0439\u0434\u0435\u0440.<\/p>\n<h4>Uniform \u0431\u0443\u0444\u0435\u0440<\/h4>\n<p>\u0414\u043e \u044d\u0442\u043e\u0433\u043e \u043c\u043e\u043c\u0435\u043d\u0442\u0430 \u043c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0431\u0443\u0444\u0435\u0440\u044b \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u043e\u0433\u043e, \u0438\u043d\u0434\u0435\u043a\u0441\u043d\u044b\u0445 \u043c\u0430\u0441\u0441\u0438\u0432\u043e\u0432 \u0438 \u0434\u0430\u0436\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440. \u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u044b\u0439 \u0431\u0443\u0444\u0435\u0440 \u2014 <em>uniform<\/em>. \u041e\u043d \u043e\u0442\u043b\u0438\u0447\u0430\u044e\u0442\u0441\u044f \u0442\u0435\u043c, \u0447\u0442\u043e \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0432 \u043b\u044e\u0431\u043e\u043c \u043c\u0435\u0441\u0442\u0435 \u0432 \u0448\u0435\u0439\u0434\u0435\u0440\u0430\u0445 (\u043a\u0430\u043a \u0433\u043b\u043e\u0431\u0430\u043b\u044c\u043d\u044b\u0435 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0435). \u041d\u0430 \u0441\u0430\u043c\u043e\u043c \u0434\u0435\u043b\u0435, \u043c\u044b \u0443\u0436\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b\u0438 \u0435\u0433\u043e \u0434\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u0438 <em>sampler<\/em>-\u0430. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f \u0445\u0440\u0430\u043d\u0435\u043d\u0438\u044f \u043c\u0430\u0442\u0440\u0438\u0446\u044b \u043f\u0440\u043e\u0435\u043a\u0446\u0438\u0438!<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432 \u0444\u0430\u0439\u043b <code>camera.rs<\/code> \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434:<\/p>\n<pre><code class=\"rust\">\/\/ \u0414\u0430\u043d\u043d\u044b\u0435, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c\u0441\u044f \u0432 \u0448\u0435\u0439\u0434\u0435\u0440 \u043d\u0443\u0436\u043d\u043e \u043f\u043e\u043c\u0435\u0442\u0438\u0442\u044c \u0441\u043f\u0435\u0446\u0438\u0430\u043b\u044c\u043d\u043e\u0439 \u0430\u043d\u043d\u043e\u0442\u0430\u0446\u0438\u0435\u0439 #[repr(C)] \/\/ Pod &amp; Zeroable \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0433\u043e \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u044f \u0442\u0438\u043f\u043e\u0432 \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u043f\u0440\u0430\u0432\u043a\u043e\u0439 \u0432 WGPU #[derive(Debug, Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct CameraUniform {     \/\/ We can't use cgmath with bytemuck directly so we'll have     \/\/ to convert the Matrix4 into a 4x4 f32 array     view_proj: [[f32; 4]; 4], }  impl CameraUniform {     fn new() -> Self {         use cgmath::SquareMatrix;         Self {             view_proj: cgmath::Matrix4::identity().into(),         }     }      fn update_view_proj(&amp;mut self, camera: &amp;Camera) {         self.view_proj = camera.build_view_projection_matrix().into();     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0430\u043c \u0431\u0443\u0444\u0435\u0440 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::new<\/code>:<\/p>\n<pre><code class=\"rust\">\/\/ \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435 \u043a\u0430\u043c\u0435\u0440\u044b  let mut camera_uniform = CameraUniform::new(); camera_uniform.update_view_proj(&amp;camera);  let camera_buffer = device.create_buffer_init(     &amp;wgpu::util::BufferInitDescriptor {         label: Some(\"Camera Buffer\"),         contents: bytemuck::cast_slice(&amp;[camera_uniform]),         usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,     } ); \/\/ ...<\/code><\/pre>\n<h4>Uniform \u0431\u0443\u0444\u0435\u0440 \u0438 BindGroup<\/h4>\n<p>\u0427\u0442\u043e\u0431\u044b \u043c\u044b \u043c\u043e\u0433\u043b\u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u0438\u0442\u044c \u0431\u0443\u0444\u0435\u0440 \u0432 \u0432\u0438\u0434\u0435\u043e\u043a\u0430\u0440\u0442\u0443, \u043d\u0443\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0443 (\u0441\u0445\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445, layout):<\/p>\n<pre><code class=\"rust\">\/\/ ... let camera_bind_group_layout = device.create_bind_group_layout(&amp;wgpu::BindGroupLayoutDescriptor {     entries: &amp;[         wgpu::BindGroupLayoutEntry {             binding: 0,             visibility: wgpu::ShaderStages::VERTEX,     \/\/ 1             ty: wgpu::BindingType::Buffer {                 ty: wgpu::BufferBindingType::Uniform,                 has_dynamic_offset: false,              \/\/ 2                 min_binding_size: None,             },             count: None,         }     ],     label: Some(\"camera_bind_group_layout\"), }); \/\/ ...<\/code><\/pre>\n<ol>\n<li>\n<p>\u0411\u0443\u0444\u0435\u0440 \u043a\u0430\u043c\u0435\u0440\u044b \u043d\u0443\u0436\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0432 \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u043e\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u0435<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u043b\u0435 <code>dynamic<\/code> \u043e\u0431\u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0431\u0443\u0434\u0435\u0442 \u043b\u0438 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c\u0441\u044f \u0440\u0430\u0437\u043c\u0435\u0440 \u044d\u0442\u043e\u0433\u043e \u0431\u0443\u0444\u0435\u0440\u0430. \u042d\u0442\u043e \u043f\u043e\u043b\u0435\u0437\u043d\u043e, \u0435\u0441\u043b\u0438 \u0432\u044b \u0445\u0440\u0430\u043d\u0438\u0442\u0435 \u043c\u0430\u0441\u0441\u0438\u0432\u044b<\/p>\n<\/li>\n<\/ol>\n<p>\u041f\u043e\u0441\u043b\u0435 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u0441\u0445\u0435\u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0445, \u0441\u043e\u0437\u0434\u0430\u0434\u0438\u043c <code>BindGroup<\/code> \u0434\u043b\u044f \u0431\u0443\u0444\u0435\u0440\u0430 \u043a\u0430\u043c\u0435\u0440\u044b:<\/p>\n<pre><code class=\"rust\">\/\/ ... let camera_bind_group = device.create_bind_group(&amp;wgpu::BindGroupDescriptor {     layout: &amp;camera_bind_group_layout,     entries: &amp;[         wgpu::BindGroupEntry {             binding: 0,             resource: camera_buffer.as_entire_binding(),         }     ],     label: Some(\"camera_bind_group\"), }); \/\/ ...<\/code><\/pre>\n<p>\u0422\u0430\u043a \u0436\u0435, \u043a\u0430\u043a \u0438 \u0441 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043e\u0439, \u043d\u0443\u0436\u043d\u043e \u0437\u0430\u0440\u0435\u0433\u0438\u0441\u0442\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0441\u0445\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0431\u0443\u0444\u0435\u0440\u0430 (\u0434\u043b\u044f \u0435\u0441\u043b\u0438 \u0432\u0434\u0440\u0443\u0433 \u0432\u044b \u0437\u0430\u0445\u043e\u0442\u0438\u0442\u0435 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u0431\u0443\u0444\u0435\u0440 \u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b):<\/p>\n<pre><code class=\"rust\">let render_pipeline_layout = device.create_pipeline_layout(     &amp;wgpu::PipelineLayoutDescriptor {         label: Some(\"Render Pipeline Layout\"),         bind_group_layouts: &amp;[             &amp;texture_bind_group_layout,             \/\/ NEW!             &amp;camera_bind_group_layout,         ],         push_constant_ranges: &amp;[],     } );<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>State<\/code>:<\/p>\n<pre><code class=\"rust\">struct State {     \/\/ ...     camera: Camera,     camera_uniform: CameraUniform,     camera_buffer: wgpu::Buffer,     camera_bind_group: wgpu::BindGroup, }  async fn new(window: &amp;Window) -> Self {     \/\/ ...     Self {         \/\/ ...         camera,         camera_uniform,         camera_buffer,         camera_bind_group,     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0432\u0441\u0435 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043d\u043e\u0432\u0443\u044e \u043a\u0430\u043c\u0435\u0440\u0443 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::render<\/code>:<\/p>\n<pre><code class=\"rust\">render_pass.set_pipeline(&amp;self.render_pipeline); render_pass.set_bind_group(0, &amp;self.diffuse_bind_group, &amp;[]); \/\/ NEW! render_pass.set_bind_group(1, &amp;self.camera_bind_group, &amp;[]); render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);  render_pass.draw_indexed(0..self.num_indices, 0, 0..1);<\/code><\/pre>\n<h4>\u041e\u0431\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u043a uniform \u0431\u0443\u0444\u0435\u0440\u0443 \u0432 \u0448\u0435\u0439\u0434\u0435\u0440\u0435<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432 \u0432\u0430\u0448 \u0448\u0435\u0439\u0434\u0435\u0440 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>CameraUniform<\/code> (1), uniform \u0431\u0443\u0444\u0435\u0440 (2) \u0438 \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u0435 <code>clip_position<\/code> (3) \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c:<\/p>\n<pre><code>\/\/ \u0412\u0435\u0440\u0448\u0438\u043d\u043d\u044b\u0439 \u0448\u0435\u0439\u0434\u0435\u0440 struct CameraUniform {     view_proj: mat4x4&lt;f32>, };  \/\/ 1 @group(1) @binding(0) var&lt;uniform> camera: CameraUniform;  struct VertexInput {     @location(0) position: vec3&lt;f32>,     @location(1) tex_coords: vec2&lt;f32>, }  struct VertexOutput {     @builtin(position) clip_position: vec4&lt;f32>,     @location(0) tex_coords: vec2&lt;f32>, }  @vertex fn vs_main(     model: VertexInput, ) -> VertexOutput {     var out: VertexOutput;     out.tex_coords = vec2&lt;f32>(model.tex_coords.x, 1.0 - model.tex_coords.y);     out.clip_position = camera.view_proj * vec4&lt;f32>(model.position, 1.0);      \/\/ 2     return out; }<\/code><\/pre>\n<ol>\n<li>\n<p>\u0422\u0430\u043a \u043a\u0430\u043a \u043c\u044b \u0434\u043e\u0431\u0430\u0432\u0438\u043b\u0438 \u043d\u043e\u0432\u0443\u044e <code>BindGroup<\/code>, \u043d\u0443\u0436\u043d\u043e \u044f\u0432\u043d\u043e \u0443\u043a\u0430\u0437\u0430\u0442\u044c \u0438\u043d\u0434\u0435\u043a\u0441, \u043a\u0430\u043a\u043e\u0439 \u0438\u043c\u0435\u043d\u043d\u043e <code>BindGroup<\/code> \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0434\u043b\u044f \u0434\u0430\u043d\u043d\u043e\u0439 \u043f\u0435\u0440\u0435\u043c\u0435\u043d\u043d\u043e\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442\u0441\u044f \u0432 <code>render_pipeline_layout<\/code>. <code>texture_bind_group_layout<\/code> \u0438\u0434\u0435\u0442 \u0432 \u0441\u043f\u0438\u0441\u043a\u0435 \u043f\u0435\u0440\u0432\u043e\u0439, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u043c\u0435\u0435\u0442 \u0438\u043d\u0434\u0435\u043a\u0441 0. \u041f\u043e\u044d\u0442\u043e\u043c\u0443 <code>camera_bind_group<\/code> \u0431\u0443\u0434\u0435\u0442 \u0438\u0434\u0442\u0438 \u043f\u043e\u0434 \u0438\u043d\u0434\u0435\u043a\u0441\u043e\u043c 1<\/p>\n<\/li>\n<li>\n<p>\u041f\u043e\u0440\u044f\u0434\u043e\u043a \u0443\u043c\u043d\u043e\u0436\u0435\u043d\u0438\u044f \u0432\u0430\u0436\u0435\u043d \u043f\u0440\u0438 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0438 \u043c\u0430\u0442\u0440\u0438\u0446. <code>matrix_a<\/code> * <code>matrix_b<\/code> != <code>matrix_b<\/code> * <code>matrix_a<\/code><\/p>\n<\/li>\n<\/ol>\n<h4>\u041a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043a\u0430\u043c\u0435\u0440\u044b<\/h4>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/0c2\/c5d\/544\/0c2c5d544b61e3896b4043ce2af66b59.png\" width=\"942\" height=\"712\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/0c2\/c5d\/544\/0c2c5d544b61e3896b4043ce2af66b59.png\"\/><figcaption><\/figcaption><\/figure>\n<p>\u0422\u0430\u043a\u043e\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 \u0434\u043e\u043b\u0436\u0435\u043d \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c\u0441\u044f. \u041c\u044b \u043a\u0430\u043a \u0431\u0443\u0434\u0442\u043e \u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0438\u0437\u0434\u0430\u043b\u0435\u043a\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u0443\u043c\u0435\u043d\u044c\u0448\u0438\u043b\u0430\u0441\u044c. \u041d\u043e \u043f\u043e\u043a\u0430 \u0447\u0442\u043e \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u043f\u043e\u043d\u044f\u0442\u043d\u043e, \u0447\u0442\u043e \u043c\u044b \u0432 3D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435.<\/p>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0432\u043e\u0437\u043c\u043e\u0436\u043d\u043e\u0441\u0442\u044c \u0432\u0440\u0430\u0449\u0430\u0442\u044c \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443:<\/p>\n<pre><code class=\"rust\">pub struct CameraController {     speed: f32,     is_forward_pressed: bool,     is_backward_pressed: bool,     is_left_pressed: bool,     is_right_pressed: bool, }  impl CameraController {     pub fn new(speed: f32) -> Self {         Self {             speed,             is_forward_pressed: false,             is_backward_pressed: false,             is_left_pressed: false,             is_right_pressed: false,         }     }      pub fn process_event(&amp;mut self, event: &amp;WindowEvent) -> bool {         match event {             WindowEvent::KeyboardInput {                 input: KeyboardInput {                     state,                     virtual_keycode: Some(keycode),                     ..                 },                 ..             } => {                 let is_pressed = *state == ElementState::Pressed;                 match keycode {                     VirtualKeyCode::W | VirtualKeyCode::Up => {                         self.is_forward_pressed = is_pressed;                         true                     }                     VirtualKeyCode::A | VirtualKeyCode::Left => {                         self.is_left_pressed = is_pressed;                         true                     }                     VirtualKeyCode::S | VirtualKeyCode::Down => {                         self.is_backward_pressed = is_pressed;                         true                     }                     VirtualKeyCode::D | VirtualKeyCode::Right => {                         self.is_right_pressed = is_pressed;                         true                     }                     _ => false,                 }             }             _ => false,         }     }      pub(crate) fn update_camera(&amp;self, camera: &amp;mut Camera) {         use cgmath::InnerSpace;         let forward = camera.target - camera.eye;         let forward_norm = forward.normalize();         let forward_mag = forward.magnitude();          if self.is_forward_pressed &amp;&amp; forward_mag > self.speed {             camera.eye += forward_norm * self.speed;         }         if self.is_backward_pressed {             camera.eye -= forward_norm * self.speed;         }          let right = forward_norm.cross(camera.up);          \/\/ \u041a\u043e\u0440\u0440\u0435\u043a\u0442\u0438\u0440\u043e\u0432\u043a\u0430 \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 \u043d\u0430\u0436\u0430\u0442\u044b \u043a\u043d\u043e\u043f\u043a\u0438 \u0432\u043f\u0435\u0440\u0435\u0434\/\u043d\u0430\u0437\u0430\u0434         let forward = camera.target - camera.eye;         let forward_mag = forward.magnitude();          if self.is_right_pressed {             \/\/ \u041d\u0443\u0436\u043d\u043e \u043f\u0435\u0440\u0435\u0441\u0447\u0438\u0442\u0430\u0442\u044c \u0440\u0430\u0441\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043c\u0435\u0436\u0434\u0443 target \u0438 eye \u0447\u0442\u043e\u0431\u044b \u043e\u043d\u043e \u043d\u0435 \u043c\u0435\u043d\u044f\u043b\u043e\u0441\u044c             \/\/ \u0422\u0430\u043a\u0438\u043c \u043e\u0431\u0440\u0430\u0437\u043e\u043c eye \u0431\u0443\u0434\u0435\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u044c\u0441\u044f \u0432 \u0434\u0438\u0430\u043f\u0430\u0437\u043e\u043d\u0435 \u043c\u0435\u0436\u0434\u0443 target \u0438 eye             camera.eye = camera.target - (forward + right * self.speed).normalize() * forward_mag;         }         if self.is_left_pressed {             camera.eye = camera.target - (forward - right * self.speed).normalize() * forward_mag;         }     } }<\/code><\/pre>\n<p>\u042d\u0442\u043e\u0442 \u043a\u043e\u0434 \u043d\u0435 \u0438\u0434\u0435\u0430\u043b\u0435\u043d, \u043d\u043e \u0432\u0441\u0435 \u0436\u0435 \u043e\u043d \u0440\u0430\u0431\u043e\u0442\u0430\u0435\u0442. \u041c\u043e\u0436\u0435\u0442\u0435 \u0434\u043e\u043f\u0438\u043b\u0438\u0442\u044c \u0435\u0433\u043e \u043f\u043e\u0434 \u0441\u0432\u043e\u0438 \u043d\u0443\u0436\u0434\u044b!<\/p>\n<p>\u0418, \u043a\u0430\u043a \u043e\u0431\u044b\u0447\u043d\u043e, \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043a\u043e\u043d\u0442\u0440\u043e\u043b\u043b\u0435\u0440 \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>State<\/code>:<\/p>\n<pre><code class=\"rust\">struct State {     \/\/ ...     camera: Camera,     \/\/ NEW!     camera_controller: CameraController,     \/\/ ... } \/\/ ... impl State {     async fn new(window: &amp;Window) -> Self {         \/\/ ...         let camera_controller = CameraController::new(0.2);         \/\/ ...          Self {             \/\/ ...             camera_controller,             \/\/ ...         }     } }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u043c\u043e\u0436\u0435\u043c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u043e\u0431\u044b\u0442\u0438\u0439 \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u043c\u0435\u0442\u043e\u0434 <code>State::input<\/code>:<\/p>\n<pre><code class=\"rust\">fn input(&amp;mut self, event: &amp;WindowEvent) -> bool {     self.camera_controller.process_events(event) }<\/code><\/pre>\n<p>\u041d\u043e, \u043a\u0430\u043a \u043d\u0438 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u043a\u0430\u043c\u0435\u0440\u0430 \u0432\u0441\u0435 \u0435\u0449\u0435 \u043d\u0435 \u0434\u0432\u0438\u0433\u0430\u0435\u0442\u0441\u044f! \u041f\u043e \u0444\u0430\u043a\u0442\u0443, \u043c\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u043c\u0435\u043d\u044f\u0435\u043c \u0441\u043e\u0441\u0442\u043e\u044f\u043d\u0438\u0435 \u043a\u0430\u043c\u0435\u0440\u044b \u0432 \u043f\u0430\u043c\u044f\u0442\u0438 \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u0430, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0442\u043e\u0433\u043e\u0432\u0430\u044f \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0430 \u043d\u0438\u043a\u0430\u043a \u043d\u0435 \u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f. \u0427\u0442\u043e\u0431\u044b \u044d\u0442\u043e \u0438\u0441\u043f\u0440\u0430\u0432\u0438\u0442\u044c, \u043d\u0443\u0436\u043d\u043e \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c uniform \u0431\u0443\u0444\u0435\u0440 \u043a\u0430\u043c\u0435\u0440\u044b. \u0415\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432, \u043a\u0430\u043a \u044d\u0442\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c:<\/p>\n<ol>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u0431\u0443\u0444\u0435\u0440 \u0438 \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u043c\u043e\u0435 <code>camera_buffer<\/code>. \u041d\u043e\u0432\u044b\u0439 \u0431\u0443\u0444\u0435\u0440 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 staging \u0431\u0443\u0444\u0435\u0440. \u041e\u0431\u044b\u0447\u043d\u043e, \u043f\u0440\u0438\u043c\u0435\u043d\u044f\u0435\u0442\u0441\u044f \u0438\u043c\u0435\u043d\u043d\u043e \u044d\u0442\u043e\u0442 \u0441\u043f\u043e\u0441\u043e\u0431, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u2018camera_buffer\u2019 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043d\u0443\u0442\u0440\u0438 \u0432\u0438\u0434\u0435\u043e\u043a\u0430\u0440\u0442\u044b, \u0447\u0442\u043e \u043f\u043e\u0437\u0432\u043e\u043b\u044f\u0435\u0442 \u0441\u0434\u0435\u043b\u0430\u0442\u044c GPU \u043e\u043f\u0442\u0438\u043c\u0438\u0437\u0430\u0446\u0438\u044e \u0441\u043a\u043e\u0440\u043e\u0441\u0442\u0438<\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0435 \u043c\u0435\u0442\u043e\u0434\u044b \u0431\u0443\u0444\u0435\u0440\u0430 <code>map_read_async<\/code> \u0438 <code>map_read_async<\/code>. \u0412 \u0446\u0435\u043b\u043e\u043c, \u044d\u0442\u043e \u0431\u043e\u043b\u0435\u0435 \u0441\u043b\u043e\u0436\u043d\u044b\u0439 \u043f\u043e\u0434\u0445\u043e\u0434 (\u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0430\u0441\u0438\u043d\u0445\u0440\u043e\u043d\u043d\u044b\u0439), \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u043a\u043e\u0442\u043e\u0440\u044b\u043c \u043d\u0443\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u0435\u0449\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>BufferUsages::MAP_READ<\/code> \u0438\/\u0438\u043b\u0438 <code>BufferUsages::MAP_WRITE<\/code><\/p>\n<\/li>\n<li>\n<p>\u0418\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <code>queue.write_buffer<\/code><\/p>\n<\/li>\n<\/ol>\n<p>\u042f \u0432\u044b\u0431\u0438\u0440\u0430\u044e \u043d\u043e\u043c\u0435\u0440 \u0442\u0440\u0438:<\/p>\n<pre><code>fn update(&amp;mut self) {     self.camera_controller.update_camera(&amp;mut self.camera);     self.camera_uniform.update_view_proj(&amp;self.camera);     self.queue.write_buffer(&amp;self.camera_buffer, 0, bytemuck::cast_slice(&amp;[self.camera_uniform])); }<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0432\u0441\u0435 \u0433\u043e\u0442\u043e\u0432\u043e, \u043d\u0443\u0436\u043d\u043e \u0432\u044b\u0437\u0432\u0430\u0442\u044c \u043c\u0435\u0442\u043e\u0434 <code>State::update<\/code> \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u043c \u0446\u0438\u043a\u043b\u0435 <code>main.rs<\/code>:<\/p>\n<pre><code class=\"rust\">Event::RedrawRequested(window_id) if window_id == window.id() => {     \/\/ NEW!     state.update();     match state.render() {         Ok(_) => {}         Err(wgpu::SurfaceError::Lost) => state.resize(state.size),         Err(wgpu::SurfaceError::OutOfMemory) => *control_flow = ControlFlow::Exit,         \/\/ \u0412\u0441\u0435 \u043e\u0441\u0442\u0430\u043b\u044c\u043d\u044b\u0435 \u043e\u0448\u0438\u0431\u043a\u0438 \u0431\u0443\u0434\u0443\u0442 \u043e\u0431\u0440\u0430\u0431\u043e\u0442\u0430\u043d\u044b \u0432 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0435\u043c \u043a\u0430\u0434\u0440\u0435         Err(e) => eprintln!(\"{:?}\", e),     } }<\/code><\/pre>\n<p>\u042d\u0442\u043e \u0432\u0441\u0435, \u0447\u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0431\u044b\u043b\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c. \u0422\u0435\u043f\u0435\u0440\u044c \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u0443\u043f\u0440\u0430\u0432\u043b\u044f\u0442\u044c \u043a\u0430\u043c\u0435\u0440\u043e\u0439 \u0441 \u043f\u043e\u043c\u043e\u0449\u044c\u044e \u043a\u043d\u043e\u043f\u043e\u043a <em>wsad<\/em>. \u042d\u0442\u043e\u0442 \u043f\u0435\u043d\u0442\u0430\u0433\u043e\u043d \u043d\u0430\u043f\u043e\u043c\u0438\u043d\u0430\u0435\u0442 \u043c\u043d\u0435 \u043a\u0430\u0440\u0442\u043e\u0447\u043a\u0438 \u0438\u0437 \u0438\u0433\u0440\u044b \u0413\u0430\u0440\u0440\u0438 \u041f\u043e\u0442\u0442\u0435\u0440, \u0441\u0440\u0430\u0432\u043d\u0438\u0442\u0435:<\/p>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/59a\/ff1\/424\/59aff14240ac2724de6719256fc3e4bc.gif\" width=\"378\" height=\"286\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/59a\/ff1\/424\/59aff14240ac2724de6719256fc3e4bc.gif\"\/><figcaption><\/figcaption><\/figure>\n<figure class=\"\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d28\/b56\/380\/d28b56380a70eb4afafec789d197c00b.gif\" width=\"320\" height=\"228\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/d28\/b56\/380\/d28b56380a70eb4afafec789d197c00b.gif\"\/><figcaption><\/figcaption><\/figure>\n<h4>\u0414\u043e\u043c\u0430\u0448\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u043d\u0438\u0435<\/h4>\n<p>\u0421\u0434\u0435\u043b\u0430\u0439\u0442\u0435 \u0442\u0430\u043a, \u0447\u0442\u043e\u0431\u044b \u043c\u043e\u0434\u0435\u043b\u044c \u0432\u0440\u0430\u0449\u0430\u043b\u0430\u0441\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e \u043e\u0442 \u043a\u0430\u043c\u0435\u0440\u044b (\u043a\u0430\u043a \u043d\u0430 \u0433\u0438\u0444\u043a\u0435). \u041f\u043e\u0434\u0441\u043a\u0430\u0437\u043a\u0430: \u0432\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u043e\u0434\u043d\u0430 \u043c\u0430\u0442\u0440\u0438\u0446\u0430.<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/abritov\/rust-wgpu-tutorial\/tree\/master\/lesson4\" rel=\"noopener noreferrer nofollow\"><u>\u043a\u043e\u0434<\/u><\/a> \u0443\u0440\u043e\u043a\u0430<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/sotrh.github.io\/learn-wgpu\/beginner\/tutorial6-uniforms\/#a-perspective-camera\" rel=\"noopener noreferrer nofollow\"><u>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/u><\/a><\/p>\n<h3>\u0423\u0440\u043e\u043a 6. Instancing<\/h3>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u043d\u0430\u0448\u0430 \u0441\u0446\u0435\u043d\u0430 \u043e\u0447\u0435\u043d\u044c \u043f\u0440\u043e\u0441\u0442\u0430\u044f, \u043e\u0434\u0438\u043d \u043e\u0431\u044a\u0435\u043a\u0442 \u0432 \u0442\u043e\u0447\u043a\u0435 (0, 0, 0). \u041a\u0430\u043a \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u0431\u043e\u043b\u044c\u0448\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432? \u0422\u0435\u0445\u043d\u0438\u043a\u0430 \u043a\u043b\u043e\u043d\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f (\u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438 \u043e\u0434\u043d\u043e\u0433\u043e \u0438 \u0442\u043e\u0433\u043e \u0436\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0432 \u0440\u0430\u0437\u043d\u044b\u0445 \u043c\u0435\u0441\u0442\u0430\u0445) \u043d\u0430\u0437\u044b\u0432\u0430\u0435\u0442\u0441\u044f <strong>instancing<\/strong>.<\/p>\n<p>\u0415\u0441\u0442\u044c \u043c\u043d\u043e\u0433\u043e \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u0432, \u043a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0440\u0430\u0437\u043c\u043d\u043e\u0436\u0438\u0442\u044c \u043e\u0431\u044a\u0435\u043a\u0442\u044b. \u041d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u043c\u043e\u0436\u043d\u043e \u0432\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0432 <em>uniform<\/em> \u0431\u0443\u0444\u0435\u0440 \u0438\u0437\u043c\u0435\u043d\u044f\u044e\u0449\u0438\u0435\u0441\u044f \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b (\u043f\u043e\u0437\u0438\u0446\u0438\u044f \u0438 \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435) \u0438 \u043e\u0431\u043d\u043e\u0432\u043b\u044f\u0442\u044c \u0438\u0445 \u043a\u0430\u0436\u0434\u044b\u0439 \u0440\u0430\u0437, \u043a\u043e\u0433\u0434\u0430 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u044b\u0432\u0430\u0435\u0442\u0441\u044f \u043e\u0431\u044a\u0435\u043a\u0442.\u00a0<\/p>\n<p>\u042d\u0442\u043e \u0440\u0430\u0441\u0442\u043e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438. \u041e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u0438\u0435 uniform \u0431\u0443\u0444\u0435\u0440\u0430 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430 \u0443\u0432\u0435\u043b\u0438\u0447\u0438\u0442\u044c \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0439 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u043d\u0438\u044f \u0432 <em>\u043a\u0430\u0436\u0434\u043e\u043c \u043a\u0430\u0434\u0440\u0435<\/em>. \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u043f\u043e\u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u0431\u0443\u0444\u0435\u0440 \u0434\u043b\u044f \u043e\u0431\u043d\u043e\u0432\u043b\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445.\u00a0<\/p>\n<p>\u041a\u0430\u043a \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c \u043b\u0443\u0447\u0448\u0435?<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043f\u043e\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u043d\u0430 \u0441\u0438\u0433\u043d\u0430\u0442\u0443\u0440\u0443 draw_indexed:<\/p>\n<pre><code class=\"rust\">pub fn draw_indexed(     &amp;mut self,     indices: Range&lt;u32>,     base_vertex: i32,     instances: Range&lt;u32> \/\/ &lt;-- \u0412\u043e\u0442 \u0441\u044e\u0434\u0430 )<\/code><\/pre>\n<p>\u0410\u0440\u0433\u0443\u043c\u0435\u043d\u0442 <code>instances<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0441\u043f\u0438\u0441\u043e\u043a \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c. \u042d\u0442\u043e \u0438 \u0435\u0441\u0442\u044c \u0440\u0435\u0448\u0435\u043d\u0438\u0435! \u0415\u0441\u043b\u0438 \u044f \u0443\u043a\u0430\u0436\u0443 <code>0..5<\/code>, \u0442\u043e\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u043e 5 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432.<\/p>\n<p>\u0422\u0438\u043f <code>Range&lt;u32><\/code> \u0432\u044b\u0433\u043b\u044f\u0434\u0438\u0442 \u0441\u0442\u0440\u0430\u043d\u043d\u043e, \u043f\u043e\u0447\u0435\u043c\u0443 \u043f\u0440\u043e\u0441\u0442\u043e \u043d\u0435 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>u32<\/code>, \u0435\u0441\u043b\u0438 \u044d\u0442\u043e\u0442 \u0430\u0440\u0433\u0443\u043c\u0435\u043d\u0442 \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u044f\u0435\u0442 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432?\u00a0<\/p>\n<p>\u041f\u0440\u0438\u0447\u0438\u043d\u0430 \u0432 \u0442\u043e\u043c, \u0447\u0442\u043e \u0438\u043d\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0444\u0438\u0433\u0443\u0440\u044b, \u0447\u0435\u0433\u043e \u043d\u0435\u043b\u044c\u0437\u044f \u0431\u044b\u043b\u043e \u0431\u044b \u0434\u043e\u0441\u0442\u0438\u0447\u044c \u0441 \u043f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u043e\u043c <code>u32<\/code>.\u00a0<\/p>\n<p>\u0425\u043e\u0440\u043e\u0448\u043e, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u044b \u0437\u043d\u0430\u0435\u043c, \u043a\u0430\u043a \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u0430. \u041a\u0430\u043a \u0443\u043a\u0430\u0437\u0430\u0442\u044c, \u043a\u0430\u043a \u0438\u043c\u0435\u043d\u043d\u043e \u043e\u043d\u0438 \u0434\u043e\u043b\u0436\u043d\u044b \u0431\u044b\u0442\u044c \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u043d\u044b? \u041c\u044b \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c <em>instance \u0431\u0443\u0444\u0435\u0440.<\/em><\/p>\n<h4>Instance \u0431\u0443\u0444\u0435\u0440<\/h4>\n<p><em>Instance<\/em> \u0431\u0443\u0444\u0435\u0440 \u0441\u043e\u0437\u0434\u0430\u0435\u0442\u0441\u044f \u0442\u0435\u043c \u0436\u0435 \u0441\u043f\u043e\u0441\u043e\u0431\u043e\u043c, \u043a\u0430\u043a \u0438 uniform \u0431\u0443\u0444\u0435\u0440.<\/p>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c, \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>Instance<\/code>:<\/p>\n<pre><code class=\"rust\">\/\/ instance.rs  struct Instance {     position: cgmath::Vector3&lt;f32>,     rotation: cgmath::Quaternion&lt;f32>, }  impl Instance {    pub fn new(position: cgmath::Vector3&lt;f32>, rotation: cgmath::Quaternion&lt;f32>) -> Self {        Self {            position, rotation        }    } }<\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e \u043d\u043e\u0432\u044b\u0439 \u043c\u0430\u0442\u0435\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 \u2014 <em>Quaternion. <\/em>\u0418\u0445 \u0447\u0430\u0441\u0442\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e\u0442 \u0434\u043b\u044f \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u044f \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435\u043c. <a href=\"https:\/\/mathworld.wolfram.com\/Quaternion.html\" rel=\"noopener noreferrer nofollow\"><u>\u0417\u0434\u0435\u0441\u044c<\/u><\/a> \u0432\u044b \u043c\u043e\u0436\u0435\u0442\u0435 \u043f\u043e\u0434\u0440\u043e\u0431\u043d\u0435\u0435 \u0438\u0437\u0443\u0447\u0438\u0442\u044c \u044d\u0442\u0443 \u0442\u0435\u043c\u0443.<\/p>\n<p>WGSL \u0448\u0435\u0439\u0434\u0435\u0440\u044b \u043d\u0435 \u0438\u043c\u0435\u044e\u0442 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440 \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043a\u0432\u0430\u0442\u0435\u0440\u043d\u0438\u043e\u043d\u0430\u043c\u0438, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0443\u044e \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443, \u0433\u0434\u0435 \u0431\u0443\u0434\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0442\u043a\u0430 \u0441\u0445\u0435\u043c\u044b \u0434\u0430\u043d\u043d\u044b\u0445:<\/p>\n<pre><code class=\"rust\">#[repr(C)] #[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)] struct InstanceRaw {     model: [[f32; 4]; 4], }  impl InstanceRaw {    pub(crate) fn description&lt;'a>() -> wgpu::VertexBufferLayout&lt;'a> {        use std::mem;        wgpu::VertexBufferLayout {            array_stride: mem::size_of::&lt;InstanceRaw>() as wgpu::BufferAddress, \/\/ \u041f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0435\u043c \u0440\u0435\u0436\u0438\u043c \u0440\u0430\u0431\u043e\u0442\u044b \u0441 VertexStepMode::Vertex \u043d\u0430 VertexStepMode::Instance \/\/ \u042d\u0442\u043e \u0437\u043d\u0430\u0447\u0438\u0442, \u0447\u0442\u043e \u0448\u0435\u0439\u0434\u0435\u0440 \u0431\u0443\u0434\u0435\u0442 \u0438\u0442\u0435\u0440\u0438\u0440\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u043f\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u0430\u043c, \u0430 \u043d\u0435 \u0432\u0435\u043a\u0442\u043e\u0440\u0430\u043c            step_mode: wgpu::VertexStepMode::Instance,            attributes: &amp;[                \/\/ mat4 \u0438\u043c\u0435\u0435\u0442 \u0440\u0430\u0437\u043c\u0435\u0440 4 \u0432\u0435\u043a\u0442\u043e\u0440\u043e\u0432 vec4, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u044f \u0434\u0435\u043b\u0430\u044e 4 \u0441\u043b\u043e\u0442\u0430 \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0433\u043e \u0432\u0435\u043a\u0442\u043e\u0440\u0430                wgpu::VertexAttribute {                    offset: 0,                    \/\/ \u041f\u043e\u043a\u0430 \u0447\u0442\u043e \u0432 \u0432\u0435\u0440\u0448\u0438\u043d\u043d\u043e\u043c \u0448\u0435\u0439\u0434\u0435\u0440\u0435 \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e 0 \u0438 1 location.                    \/\/ 2, 3 \u0438 4 \u0437\u0430\u0440\u0435\u0437\u0435\u0440\u0432\u0438\u0440\u0443\u044e \u043d\u0430 \u0431\u0443\u0434\u0443\u0449\u0435\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c shader_location = 5                    shader_location: 5,                    format: wgpu::VertexFormat::Float32x4,                },                wgpu::VertexAttribute {                    offset: mem::size_of::&lt;[f32; 4]>() as wgpu::BufferAddress,                    shader_location: 6,                    format: wgpu::VertexFormat::Float32x4,                },                wgpu::VertexAttribute {                    offset: mem::size_of::&lt;[f32; 8]>() as wgpu::BufferAddress,                    shader_location: 7,                    format: wgpu::VertexFormat::Float32x4,                },                wgpu::VertexAttribute {                    offset: mem::size_of::&lt;[f32; 12]>() as wgpu::BufferAddress,                    shader_location: 8,                    format: wgpu::VertexFormat::Float32x4,                },            ],        }    } }<\/code><\/pre>\n<p>\u0414\u0430\u043d\u043d\u044b\u0435 \u0438\u0437 \u044d\u0442\u043e\u0439 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u044b \u043f\u043e\u043f\u0430\u0434\u0443\u0442 \u0432 <code>wgpu::Buffer<\/code>. \u0414\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0435 \u0440\u0430\u0437\u0434\u0435\u043b\u0435\u043d\u0438\u0435 <code>Instance<\/code> \u0438 <code>InstanceRaw<\/code> \u0438\u0433\u0440\u0430\u0435\u0442 \u043d\u0430\u043c \u043d\u0430 \u0440\u0443\u043a\u0443, \u0442\u043a \u043c\u043e\u0436\u043d\u043e \u0431\u0443\u0434\u0435\u0442 \u043c\u0435\u043d\u044f\u0442\u044c \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u043e <code>Instance<\/code> \u0431\u0435\u0437 \u0432\u043e\u0437\u043d\u0438 \u0441 \u043c\u0430\u0442\u0440\u0438\u0446\u0430\u043c\u0438. \u041d\u0443\u0436\u043d\u043e \u0442\u043e\u043b\u044c\u043a\u043e \u043d\u0435 \u0437\u0430\u0431\u044b\u0442\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u0442\u044c \u0434\u0430\u043d\u043d\u044b\u0435 \u0432 <code>InstanceRaw<\/code> \u043f\u0435\u0440\u0435\u0434 \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u043e\u0439.<\/p>\n<p>\u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u0441\u0434\u0435\u043b\u0430\u0435\u043c \u043c\u0435\u0442\u043e\u0434 \u0434\u043b\u044f \u043a\u043e\u043d\u0432\u0435\u0440\u0442\u0430\u0446\u0438\u0438 <code>Instance<\/code> \u0432 <code>InstanceRaw<\/code>:<\/p>\n<pre><code class=\"rust\">impl Instance {     \/\/ \u2026     fn to_raw(&amp;self) -> InstanceRaw {         InstanceRaw {             model: (cgmath::Matrix4::from_translation(self.position) * cgmath::Matrix4::from(self.rotation)).into(),         }     } }<\/code><\/pre>\n<p>\u0414\u043e\u0431\u0430\u0432\u0438\u043c \u0442\u0435\u043f\u0435\u0440\u044c \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u044b \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>State<\/code>:<\/p>\n<pre><code class=\"rust\">struct State {     \/\/ \u2026     instances: Vec&lt;Instance>,     instance_buffer: wgpu::Buffer, }<\/code><\/pre>\n<p>\u042f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0440\u0435\u0439\u0442\u044b \u0432\u044b\u0447\u0438\u0441\u043b\u0435\u043d\u0438\u044f \u0438\u0437 <code>cgmath<\/code> , \u0438\u0445 \u043d\u0443\u0436\u043d\u043e \u0438\u043c\u043f\u043e\u0440\u0442\u0438\u0440\u043e\u0432\u0430\u0442\u044c:<\/p>\n<pre><code>use cgmath::prelude::*;<\/code><\/pre>\n<p>\u0427\u0442\u043e\u0431\u044b \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432 10 \u0445 10, \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0438\u0432 \u0432 \u043d\u0430\u0447\u0430\u043b\u0435\u00a0 \u043a\u043e\u043d\u0441\u0442\u0430\u043d\u0442\u044b:<\/p>\n<pre><code>const NUM_INSTANCES_PER_ROW: u32 = 10; const INSTANCE_DISPLACEMENT: cgmath::Vector3&lt;f32> = cgmath::Vector3::new(NUM_INSTANCES_PER_ROW as f32 * 0.5, 0.0, NUM_INSTANCES_PER_ROW as f32 * 0.5);<\/code><\/pre>\n<p>\u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::new<\/code> \u0441\u043e\u0437\u0434\u0430\u0435\u043c \u0434\u0432\u0443\u043c\u0435\u0440\u043d\u044b\u0439 \u043c\u0430\u0441\u0441\u0438\u0432:<\/p>\n<pre><code class=\"rust\">\/\/ Instancing let instances = (0..NUM_INSTANCES_PER_ROW).flat_map(|z| {    (0..NUM_INSTANCES_PER_ROW).map(move |x| {        let position = cgmath::Vector3 { x: x as f32, y: 0.0, z: z as f32 } - INSTANCE_DISPLACEMENT;         let rotation = if position.is_zero() {            \/\/ \u041a\u0432\u0430\u0442\u0435\u0440\u043d\u0438\u043e\u043d\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u0437\u043c\u0435\u043d\u044f\u0442\u044c \u043c\u0430\u0441\u0448\u0442\u0430\u0431, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0437\u0434\u0435\u0441\u044c \u0434\u043e\u043f\u043e\u043b\u043d\u0438\u0442\u0435\u043b\u044c\u043d\u0430\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u043a\u0430            \/\/ \u043d\u0430 \u0441\u043b\u0443\u0447\u0430\u0439, \u0435\u0441\u043b\u0438 \u043e\u0431\u044a\u0435\u043a\u0442 \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0442\u043e\u0447\u043a\u0435 (0, 0, 0)            \/\/ \u0422\u0430\u043a \u043c\u044b \u0438\u0437\u0431\u0435\u0436\u0438\u043c \u0441\u0438\u0442\u0443\u0430\u0446\u0438\u0438, \u043a\u043e\u0433\u0434\u0430 \u043c\u0430\u0441\u0448\u0442\u0430\u0431 \u043e\u0431\u044a\u0435\u043a\u0442\u0430 \u0441\u0442\u0430\u043b \u043d\u0443\u043b\u0435\u0432\u043e\u0439            cgmath::Quaternion::from_axis_angle(cgmath::Vector3::unit_z(), cgmath::Deg(0.0))        } else {            cgmath::Quaternion::from_axis_angle(position.normalize(), cgmath::Deg(45.0))        };         Instance::new(position, rotation)    }) }).collect::&lt;Vec&lt;_>>();<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c, \u043a\u043e\u0433\u0434\u0430 \u0435\u0441\u0442\u044c \u043c\u0430\u0441\u0441\u0438\u0432 \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432, \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u043f\u0438\u0441\u0430\u0442\u044c \u0438\u0445 \u0432 \u0431\u0443\u0444\u0435\u0440:<\/p>\n<pre><code>let instance_data = instances.iter().map(Instance::to_raw).collect::&lt;Vec&lt;_>>(); let instance_buffer = device.create_buffer_init(    &amp;wgpu::util::BufferInitDescriptor {        label: Some(\"Instance Buffer\"),        contents: bytemuck::cast_slice(&amp;instance_data),        usage: wgpu::BufferUsages::VERTEX,    } );<\/code><\/pre>\n<p>\u0411\u0443\u0444\u0435\u0440 \u0433\u043e\u0442\u043e\u0432, \u043e\u0431\u043d\u043e\u0432\u0438\u043c <code>render_pipeline<\/code>:<\/p>\n<pre><code>let render_pipeline = device.create_render_pipeline(&amp;wgpu::RenderPipelineDescriptor {     \/\/ ...     vertex: wgpu::VertexState {         \/\/ ...         \/\/ UPDATED!         buffers: &amp;[Vertex::description(), InstanceRaw::description()],     },     \/\/ ... });<\/code><\/pre>\n<p>\u041f\u043e\u0441\u043b\u0435\u0434\u043d\u0438\u0439 \u0448\u0442\u0440\u0438\u0445 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::new<\/code>, \u0434\u043e\u0431\u0430\u0432\u0438\u043c \u043d\u043e\u0432\u044b\u0435 \u043f\u043e\u043b\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443:<\/p>\n<pre><code class=\"rust\">Self {     \/\/ ...     \/\/ NEW!     instances,     instance_buffer, }<\/code><\/pre>\n<h4>\u0420\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433<\/h4>\n<p>\u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043c\u043d\u043e\u0436\u0435\u0441\u0442\u0432\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043c\u043e\u0436\u043d\u043e \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u0430\u0442\u044c. \u0412 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::render<\/code> \u044f \u0434\u043e\u0431\u0430\u0432\u043b\u044e \u043d\u043e\u0432\u044b\u0439 <code>instance_buffer<\/code> \u0432 <code>render_pass<\/code> \u0438 \u0443\u043a\u0430\u0436\u0443 \u043a\u043e\u043b\u0438\u0447\u0435\u0441\u0442\u0432\u043e \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432 \u0434\u043b\u044f \u043e\u0442\u0440\u0438\u0441\u043e\u0432\u043a\u0438:<\/p>\n<pre><code class=\"rust\">\/\/ NEW! render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);  \/\/ UPDATED! render_pass.draw_indexed(0..self.num_indices, 0, 0..self.instances.len() as _);<\/code><\/pre>\n<p>\u0415\u0441\u043b\u0438 \u0432\u044b \u0431\u0443\u0434\u0435\u0442\u0435 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u043d\u043e\u0432\u044b\u0435 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u044b, \u043d\u0435 \u0437\u0430\u0431\u0443\u0434\u044c\u0442\u0435 \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0435\u0449\u0451 \u0440\u0430\u0437 <code>instance_buffer<\/code> \u0438 <code>camera_bind_group<\/code>, \u0438\u043d\u0430\u0447\u0435 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u044b \u043d\u0435 \u0431\u0443\u0434\u0443\u0442 \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0430\u0442\u044c\u0441\u044f \u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u043e.<\/p>\n<p>\u041e\u0431\u043d\u043e\u0432\u0438\u043c \u0448\u0435\u0439\u0434\u0435\u0440. \u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u0439 \u043a\u043e\u0434 \u0432 \u043d\u0430\u0447\u0430\u043b\u043e \u0444\u0430\u0439\u043b\u0430:<\/p>\n<pre><code>struct InstanceInput {     @location(5) model_matrix_0: vec4&lt;f32>,     @location(6) model_matrix_1: vec4&lt;f32>,     @location(7) model_matrix_2: vec4&lt;f32>,     @location(8) model_matrix_3: vec4&lt;f32>, };<\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043e\u0431\u043d\u043e\u0432\u0438\u043c \u043c\u0435\u0442\u043e\u0434 <code>vs_main<\/code>:<\/p>\n<pre><code>fn vs_main(    model: VertexInput,    instance: InstanceInput, ) -> VertexOutput {    var out: VertexOutput;     let model_matrix = mat4x4&lt;f32>(        instance.model_matrix_0,        instance.model_matrix_1,        instance.model_matrix_2,        instance.model_matrix_3,    );     out.tex_coords = vec2&lt;f32>(model.tex_coords.x, 1.0 - model.tex_coords.y);    out.clip_position = camera.view_proj * model_matrix * vec4&lt;f32>(model.position, 1.0);    return out; }<\/code><\/pre>\n<p>\u042f \u0432 \u043d\u0430\u0447\u0430\u043b\u0435 \u0443\u043c\u043d\u043e\u0436\u0430\u044e <code>camera.view_proj<\/code>, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e <code>camera.view_proj<\/code> \u043c\u0435\u043d\u044f\u0435\u0442 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u0432 \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u043e \u043a\u0430\u043c\u0435\u0440\u044b, \u0430 <code>model_matrix<\/code> \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u043c\u0438\u0440\u043e\u0432\u043e\u043c \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435. \u0415\u0441\u043b\u0438 \u0438\u0445 \u043f\u043e\u043c\u0435\u043d\u044f\u0442\u044c \u043c\u0435\u0441\u0442\u0430\u043c\u0438, \u0442\u043e \u043f\u043e\u043b\u0443\u0447\u0438\u043c \u043d\u0435\u043a\u043e\u0440\u0440\u0435\u043a\u0442\u043d\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442 (\u044f \u043f\u0440\u043e\u0432\u0435\u0440\u0438\u043b!).<\/p>\n<p>\u0418\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/7a8\/deb\/b34\/7a8debb343a817529ef8923d64322661.png\" width=\"858\" height=\"658\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/7a8\/deb\/b34\/7a8debb343a817529ef8923d64322661.png\"\/><figcaption><\/figcaption><\/figure>\n<h4>\u0414\u043e\u043c\u0430\u0448\u043d\u0435\u0435 \u0437\u0430\u0434\u0430\u043d\u0438\u0435<\/h4>\n<p>\u0414\u043e\u0431\u0430\u0432\u044c\u0442\u0435 \u0432\u0440\u0430\u0449\u0435\u043d\u0438\u0435 \u0438\u043d\u0441\u0442\u0430\u043d\u0441\u043e\u0432.<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/github.com\/abritov\/rust-wgpu-tutorial\/tree\/master\/lesson6\" rel=\"noopener noreferrer nofollow\"><u>\u043a\u043e\u0434<\/u><\/a> \u0443\u0440\u043e\u043a\u0430<\/p>\n<p>\u0421\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 <a href=\"https:\/\/sotrh.github.io\/learn-wgpu\/beginner\/tutorial7-instancing\/#the-instance-buffer\" rel=\"noopener noreferrer nofollow\"><u>\u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b<\/u><\/a> \u0441\u0442\u0430\u0442\u044c\u0438<\/p>\n<\/p>\n<\/div>\n<\/div>\n<\/div>\n<div class=\"v-portal\" style=\"display:none;\"><\/div>\n<\/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\/post\/698426\/\"> https:\/\/habr.com\/ru\/post\/698426\/<\/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<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0412 \u044d\u0442\u043e\u0439 \u0441\u0442\u0430\u0442\u044c\u0435 \u044f \u043f\u0440\u043e\u0434\u043e\u043b\u0436\u0443 \u043f\u0435\u0440\u0435\u0432\u043e\u0434 \u0438 \u0438\u0441\u0441\u043b\u0435\u0434\u043e\u0432\u0430\u043d\u0438\u0435 <em>WGPU<\/em>, \u0431\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0438 \u044f\u0437\u044b\u043a\u0430 Rust \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0433\u0440\u0430\u0444\u0438\u043a\u043e\u0439.<\/p>\n<p>\u0414\u043b\u044f \u0442\u0435\u0445, \u043a\u0442\u043e \u043d\u0435 \u0447\u0438\u0442\u0430\u043b <a href=\"https:\/\/habr.com\/ru\/post\/690514\/\" rel=\"noopener noreferrer nofollow\"><u>\u043f\u0435\u0440\u0432\u0443\u044e<\/u><\/a> \u0441\u0442\u0430\u0442\u044c\u044e \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0430\u044f \u0432\u0432\u043e\u0434\u043d\u0430\u044f \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044f.<\/p>\n<p><em>WGPU<\/em> \u0440\u0435\u0430\u043b\u0438\u0437\u0443\u0435\u0442 \u0441\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0441\u0442\u0430\u043d\u0434\u0430\u0440\u0442 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u0432\u0438\u0434\u0435\u043e \u043f\u043e\u0434\u0441\u0438\u0441\u0442\u0435\u043c\u0430\u043c\u0438 \u2014 <em>WebGPU \u0438 \u043a\u043e\u043c\u043f\u0438\u043b\u0438\u0440\u0443\u0435\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u043d\u044b\u0435 backend-\u044b (OpenGL, DirectX12, Metal, Vulkan, WebGL). \u041e\u043d \u043e\u0434\u043d\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u043e \u043f\u0440\u043e\u0449\u0435 \u0434\u043b\u044f \u043e\u0441\u0432\u043e\u0435\u043d\u0438\u044f, \u0447\u0435\u043c Vulkan \u0438 \u0438\u043c\u0435\u0435\u0442 \u0431\u043e\u043b\u0435\u0435 \u043f\u0440\u043e\u0434\u0443\u043c\u0430\u043d\u043d\u043e\u0435 \u0430\u043f\u0438, \u0447\u0435\u043c OpenGL.<\/em><\/p>\n<p>\u041f\u0440\u0438\u0441\u0442\u0443\u043f\u0438\u043c!<\/p>\n<h3>\u0423\u0440\u043e\u043a 4. \u0422\u0435\u043a\u0441\u0442\u0443\u0440\u044b<\/h3>\n<p>\u0422\u0435\u043a\u0441\u0442\u0443\u0440\u0430\u043c\u0438 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u043d\u0430\u043a\u043b\u0430\u0434\u044b\u0432\u0430\u044e\u0442 \u043f\u043e\u0432\u0435\u0440\u0445 \u0441\u0435\u0442\u043a\u0438 (\u0444\u0438\u0433\u0443\u0440\u044b) \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0440\u0438\u0434\u0430\u0442\u044c \u0435\u0439 \u0444\u0430\u043a\u0442\u0443\u0440\u0443 \u0438\/\u0438\u043b\u0438 \u0434\u0435\u0442\u0430\u043b\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<h4>\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f<\/h4>\n<p>\u041f\u0435\u0440\u0432\u044b\u043c \u0434\u0435\u043b\u043e\u043c, \u043d\u0430\u043c \u043d\u0443\u0436\u043d\u043e \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0441\u0430\u043c\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u043f\u0430\u043f\u043a\u0443 \u0441 \u043f\u0440\u043e\u0435\u043a\u0442\u043e\u043c.<\/p>\n<p>\u0414\u043b\u044f \u044d\u0442\u043e\u0433\u043e, \u044f \u0441\u0434\u0435\u043b\u0430\u043b \u043f\u0430\u043f\u043a\u0443 <code>assets<\/code> \u0438 \u043f\u043e\u043b\u043e\u0436\u0438\u043b \u0442\u0443\u0434\u0430 \u043a\u0430\u0440\u0442\u0438\u043d\u043a\u0443:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u0414\u043b\u044f \u0447\u0442\u0435\u043d\u0438\u044f \u0444\u0430\u0439\u043b\u0430 \u044f \u0431\u0443\u0434\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c <code>image<\/code> crate:<\/p>\n<p><code>image = \"0.24\"<\/code><\/p>\n<p>\u0414\u0430\u043b\u0435\u0435, \u044f \u0441\u0434\u0435\u043b\u0430\u044e \u043e\u0442\u0434\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u043e\u0434\u0443\u043b\u044c \u2014 <code>texture<\/code>:<\/p>\n<pre><code class=\"rust\">use std::error::Error;  pub struct Texture {    pub texture: wgpu::Texture,    pub view: wgpu::TextureView,    pub sampler: wgpu::Sampler, }<\/code><\/pre>\n<p>\u0421\u0434\u0435\u043b\u0430\u0435\u043c \u043d\u043e\u0432\u044b\u0439 \u043c\u0435\u0442\u043e\u0434 <code>Texture::from_image<\/code>:<\/p>\n<pre><code class=\"rust\">pub fn from_image(    device: &amp;wgpu::Device,    queue: &amp;wgpu::Queue,    img: &amp;image::DynamicImage,    label: Option&lt;&amp;str> ) -> Self {    let diffuse_rgba = diffuse_image.to_rgba8();                            \/\/ 1       use image::GenericImageView;    let dimensions = diffuse_image.dimensions();       let size = wgpu::Extent3d {                                             \/\/ 2        width: dimensions.0,        height: dimensions.1,        \/\/ \u041c\u044b \u0431\u0443\u0434\u0435\u043c \u0440\u0430\u0431\u043e\u0442\u0430\u0442\u044c \u0432 2D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u043c \u0442\u043e\u043b\u044c\u043a\u043e \u043e\u0434\u0438\u043d \u0441\u043b\u043e\u0439        depth_or_array_layers: 1,    };    let texture = device.create_texture(                                    \/\/ 3        &amp;wgpu::TextureDescriptor {            label,            size,            mip_level_count: 1,            sample_count: 1,            dimension: wgpu::TextureDimension::D2,            \/\/ \u0423\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c \u0440\u0435\u0436\u0438\u043c \u043f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0446\u0432\u0435\u0442\u0430 \u0438\u0437 \u0444\u043e\u0440\u043c\u0430\u0442\u0430 sRGB \u0432 \u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0435 float [0, 1] \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0435            format: wgpu::TextureFormat::Rgba8UnormSrgb,            \/\/ TEXTURE_BINDING \u0444\u043b\u0430\u0433 \u0443\u0441\u0442\u0430\u043d\u0430\u0432\u043b\u0438\u0432\u0430\u0435\u0442\u0441\u044f, \u0435\u0441\u043b\u0438 \u043d\u0443\u0436\u043d\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u0448\u0435\u0439\u0434\u0435\u0440\u0435            \/\/ COPY_DST \u0433\u043e\u0432\u043e\u0440\u0438\u0442, \u0447\u0442\u043e \u043f\u043e\u0437\u0436\u0435 \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u043f\u043e\u0437\u0436\u0435 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u044d\u0442\u0443 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443            usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,        }    ); ...<\/code><\/pre>\n<ol>\n<li>\n<p>\u041f\u0440\u0435\u043e\u0431\u0440\u0430\u0437\u0443\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u043c\u0430\u0441\u0441\u0438\u0432 rgba \u0431\u0430\u0439\u0442\u043e\u0432<\/p>\n<\/li>\n<li>\n<p>\u0417\u0430\u0434\u0430\u0435\u043c (\u044d\u043c\u0443\u043b\u0438\u0440\u0443\u0435\u043c) \u0440\u0430\u0437\u043c\u0435\u0440\u043d\u043e\u0441\u0442\u044c \u0432 3D \u043f\u0440\u043e\u0441\u0442\u0440\u0430\u043d\u0441\u0442\u0432\u0435<\/p>\n<\/li>\n<li>\n<p>\u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<\/ol>\n<p>\u0421\u0435\u0439\u0447\u0430\u0441 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430 \u043f\u0443\u0441\u0442\u0430\u044f \u0438 \u043d\u0443\u0436\u043d\u043e \u0441\u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0442\u0443\u0434\u0430 \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"rust\">...    \/\/ \u041a\u043e\u043f\u0438\u0440\u0443\u0435\u043c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0432 \u0441\u043e\u0437\u0434\u0430\u043d\u043d\u0443\u044e \u0432\u044b\u0448\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443    queue.write_texture(        \/\/ \u0417\u0430\u0434\u0430\u0435\u043c, \u043a\u0443\u0434\u0430 \u043a\u043e\u043f\u0438\u0440\u043e\u0432\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435        wgpu::ImageCopyTexture {            aspect: wgpu::TextureAspect::All,            texture: &amp;texture,            mip_level: 0,            origin: wgpu::Origin3d::ZERO,        },        \/\/ \u041c\u0430\u0441\u0441\u0438\u0432 rgba \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439        &amp;rgba,        \/\/ \u0421\u0445\u0435\u043c\u0430 \u0440\u0430\u0441\u043f\u043e\u043b\u043e\u0436\u0435\u043d\u0438\u044f \u0434\u0430\u043d\u043d\u044b\u0445        wgpu::ImageDataLayout {            offset: 0,            bytes_per_row: std::num::NonZeroU32::new(4 * dimensions.0),            rows_per_image: std::num::NonZeroU32::new(dimensions.1),        },        size,    );     Self { texture, view, sampler } }<\/code><\/pre>\n<h4>\u0424\u0438\u043b\u044c\u0442\u0440\u0430\u0446\u0438\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440<\/h4>\n<p>\u042d\u0442\u0443 \u0447\u0430\u0441\u0442\u044c \u044f \u0434\u043e\u043b\u0433\u043e \u043d\u0435 \u0437\u043d\u0430\u043b, \u043a\u0430\u043a \u043f\u0435\u0440\u0435\u0432\u0435\u0441\u0442\u0438, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u043d\u0435 \u0438\u043c\u0435\u043b \u0434\u0435\u043b\u043e \u0441 <code>Sampler<\/code>-\u0430\u043c\u0438. \u041f\u0440\u0435\u0436\u0434\u0435 \u0447\u0435\u043c \u0440\u0430\u0437\u0431\u0438\u0440\u0430\u0442\u044c\u0441\u044f \u0441\u043e \u0441\u043b\u0435\u0434\u0443\u044e\u0449\u0438\u043c \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043e\u043c \u043a\u043e\u0434\u0430, \u0434\u0430\u043c \u043d\u0435\u0431\u043e\u043b\u044c\u0448\u0443\u044e \u0432\u0432\u043e\u0434\u043d\u0443\u044e \u0438\u043d\u0444\u043e\u0440\u043c\u0430\u0446\u0438\u044e.<\/p>\n<p>\u0422\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0435 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u044b \u043d\u0435 \u0437\u0430\u0432\u0438\u0441\u044f\u0442 \u043e\u0442 \u0440\u0430\u0437\u0440\u0435\u0448\u0435\u043d\u0438\u044f \u0441\u0430\u043c\u043e\u0439 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u0438\u043b\u0438 \u044d\u043a\u0440\u0430\u043d\u0430. \u042d\u0442\u043e \u043e\u0437\u043d\u0430\u0447\u0430\u0435\u0442, \u0447\u0442\u043e \u0433\u0434\u0435-\u0442\u043e \u0435\u0441\u0442\u044c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c, \u043e\u0442\u0432\u0435\u0447\u0430\u044e\u0449\u0438\u0439 \u0437\u0430 \u0441\u043e\u043f\u043e\u0441\u0442\u0430\u0432\u043b\u0435\u043d\u0438\u0435 <em>\u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0445 \u043f\u0438\u043a\u0441\u0435\u043b\u0435\u0439<\/em> (\u0438\u043d\u043e\u0433\u0434\u0430 \u0438\u0445 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 <strong>\u0442\u0435\u043a\u0441\u0435\u043b\u0438<\/strong>) \u0441 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u043c\u0438 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0430\u043c\u0438. \u0418 <code>Sampler<\/code> \u043a\u0430\u043a \u0440\u0430\u0437 \u0438 \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u044d\u0442\u0438\u043c \u043c\u0435\u0445\u0430\u043d\u0438\u0437\u043c\u043e\u043c, \u0443 \u043a\u043e\u0442\u043e\u0440\u043e\u0433\u043e \u0435\u0441\u0442\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u043e \u0440\u0435\u0436\u0438\u043c\u043e\u0432 \u0440\u0430\u0431\u043e\u0442\u044b:<\/p>\n<pre><code class=\"rust\">let view = texture.create_view(&amp;wgpu::TextureViewDescriptor::default()); let sampler = device.create_sampler(    &amp;wgpu::SamplerDescriptor {        address_mode_u: wgpu::AddressMode::ClampToEdge,     \/\/ 1        address_mode_v: wgpu::AddressMode::ClampToEdge,        address_mode_w: wgpu::AddressMode::ClampToEdge,        mag_filter: wgpu::FilterMode::Linear,               \/\/ 2        min_filter: wgpu::FilterMode::Nearest,              \/\/ 3        mipmap_filter: wgpu::FilterMode::Nearest,        ..Default::default()    } );<\/code><\/pre>\n<ol>\n<li>\n<p>\u041c\u044b \u043c\u043e\u0436\u0435\u043c \u0437\u0430\u0434\u0430\u0442\u044c \u0440\u0430\u0437\u043d\u044b\u0435 \u0440\u0435\u0436\u0438\u043c\u044b \u0434\u043b\u044f \u043a\u0430\u0436\u0434\u043e\u0439 \u0438\u0437 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442 <code>address_mode_*<\/code><\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0440\u0430\u0441\u0442\u044f\u043d\u0443\u0442\u044c (magnify) \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<li>\n<p>\u041a\u0430\u043a\u043e\u0439 \u0444\u0438\u043b\u044c\u0442\u0440 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c, \u043a\u043e\u0433\u0434\u0430 \u043d\u0443\u0436\u043d\u043e \u0441\u0436\u0430\u0442\u044c (minify) \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443<\/p>\n<\/li>\n<\/ol>\n<p><code>FilterMode<\/code> \u043f\u0440\u0435\u0434\u043e\u0441\u0442\u0430\u0432\u043b\u044f\u0435\u0442 \u0434\u0432\u0435 \u043e\u043f\u0446\u0438\u0438:<\/p>\n<ul>\n<li>\n<p><code>Linear<\/code> \u0441\u043c\u0435\u0448\u0438\u0432\u0430\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0438\u0445 \u043a \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u0435\u0439. \u0427\u0435\u043c \u0431\u043b\u0438\u0436\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u044c \u043a \u0437\u0430\u0434\u0430\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435, \u0442\u0435\u043c \u0431\u043e\u043b\u044c\u0448\u0438\u0439 \u0432\u043a\u043b\u0430\u0434 \u043e\u043d \u0432\u043d\u0435\u0441\u0435\u0442 \u0432 \u0438\u0442\u043e\u0433\u043e\u0432\u044b\u0439 \u0446\u0432\u0435\u0442. \u042d\u0442\u043e\u0442 \u043c\u0435\u0442\u043e\u0434 \u0435\u0449\u0435 \u043d\u0430\u0437\u044b\u0432\u0430\u044e\u0442 \u0431\u0438\u043b\u0438\u043d\u0435\u0439\u043d\u043e\u0439 \u0438\u043d\u0442\u0435\u0440\u043f\u043e\u043b\u044f\u0446\u0438\u0435\u0439<\/p>\n<\/li>\n<li>\n<p><code>Nearest<\/code> \u0431\u0435\u0440\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0433\u043e \u043a \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u043e\u0439 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442\u0435 \u0442\u0435\u043a\u0441\u0435\u043b\u044f. \u0412 \u044d\u0442\u043e\u043c \u0440\u0435\u0436\u0438\u043c\u0435 \u043f\u0440\u0438 \u0431\u043b\u0438\u0437\u043a\u043e\u043c \u0440\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0435\u043d\u0438\u0438 \u043c\u043e\u0436\u043d\u043e \u0443\u0432\u0438\u0434\u0435\u0442\u044c \u0443\u0433\u043b\u043e\u0432\u0430\u0442\u044b\u0435 \u0443\u0437\u043e\u0440\u044b \u0438 \u043f\u0438\u043a\u0441\u0435\u043b\u0438\u0437\u0430\u0446\u0438\u044e.<\/p>\n<\/li>\n<\/ul>\n<p>\u0420\u0430\u0441\u0441\u043c\u043e\u0442\u0440\u0438\u043c \u0440\u0435\u0436\u0438\u043c\u044b \u0440\u0430\u0431\u043e\u0442\u044b <code>Sampler<\/code> (<em>AddressMode<\/em>):<\/p>\n<ul>\n<li>\n<p><code>ClampToEdge<\/code> \u043b\u044e\u0431\u043e\u0439 \u0442\u0435\u043a\u0441\u0435\u043b\u044c, \u0432\u044b\u0445\u043e\u0434\u044f\u0449\u0438\u0439 \u0437\u0430 \u043f\u0440\u0435\u0434\u0435\u043b\u044b \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u043d\u044b\u0445 \u043a\u043e\u043e\u0440\u0434\u0438\u043d\u0430\u0442, \u043f\u0440\u0438\u043c\u0435\u0442 \u0446\u0432\u0435\u0442 \u0431\u043b\u0438\u0436\u0430\u0439\u0448\u0435\u0433\u043e \u043a \u0433\u0440\u0430\u043d\u0438\u0446\u0435 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b \u043f\u0438\u043a\u0441\u0435\u043b\u044f<\/p>\n<\/li>\n<li>\n<p><code>Repeat<\/code> \u043f\u043e-\u0440\u0443\u0441\u0441\u043a\u0438 \u2014 \u0437\u0430\u043c\u043e\u0441\u0442\u0438\u0442\u044c, \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0430 \u0431\u0443\u0434\u0435\u0442 \u043f\u043e\u0432\u0442\u043e\u0440\u044f\u0442\u044c\u0441\u044f \u043f\u043e \u0432\u0441\u0435\u0439 \u043f\u043b\u043e\u0449\u0430\u0434\u0438 \u0444\u0438\u0433\u0443\u0440\u044b<\/p>\n<\/li>\n<li>\n<p><code>MirrorRepeat<\/code> \u043f\u043e\u0445\u043e\u0436\u0435 \u043d\u0430 <code>Repeat<\/code>, \u0442\u043e\u043b\u044c\u043a\u043e \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435 \u0431\u0443\u0434\u0435\u0442 \u043f\u0435\u0440\u0435\u0432\u0435\u0440\u043d\u0443\u0442\u043e, \u043f\u0440\u0438 \u0432\u044b\u0445\u043e\u0434\u0435 \u0437\u0430 \u0433\u0440\u0430\u043d\u0438\u0446\u044b \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b<\/p>\n<\/li>\n<\/ul>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>\u041d\u0430\u043c \u043f\u043e\u043d\u0430\u0434\u043e\u0431\u0438\u0442\u0441\u044f \u0435\u0449\u0435 \u043e\u0434\u0438\u043d \u0432\u0441\u043f\u043e\u043c\u043e\u0433\u0430\u0442\u0435\u043b\u044c\u043d\u044b\u0439 \u043c\u0435\u0442\u043e\u0434, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u0431\u0443\u0434\u0435\u0442 \u0447\u0438\u0442\u0430\u0442\u044c \u0438\u0437\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u0435:<\/p>\n<pre><code class=\"rust\">pub fn from_bytes(    device: &amp;wgpu::Device,    queue: &amp;wgpu::Queue,    bytes: &amp;[u8],    label: &amp;str ) -> anyhow::Result&lt;Self> {    let img = image::load_from_memory(bytes)?;    Ok(Self::from_image(device, queue, &amp;img, Some(label))) } <\/code><\/pre>\n<p>\u0417\u0434\u0435\u0441\u044c \u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043b <code>anyhow::Result<\/code> \u0438\u0437 <a href=\"https:\/\/docs.rs\/anyhow\/latest\/anyhow\/\" rel=\"noopener noreferrer nofollow\">\u043a\u0440\u0435\u0439\u0442\u0430<\/a> <code>anyhow = &amp;quot;1.0.64&amp;quot;<\/code>, \u0434\u043b\u044f \u0443\u0434\u043e\u0431\u043d\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b \u0441 \u043e\u0448\u0438\u0431\u043a\u0430\u043c\u0438.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u043e\u0437\u0434\u0430\u0442\u044c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u043c\u0435\u0442\u043e\u0434\u0435 <code>State::new<\/code>:<\/p>\n<pre><code class=\"rust\">surface.configure(&amp;device, &amp;config);  \/\/ \u0421\u043e\u0437\u0434\u0430\u0435\u043c \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 let diffuse_bytes = include_bytes!(\"..\/assets\/tree.png\"); let diffuse_texture = Texture::from_bytes(&amp;device, &amp;queue, diffuse_bytes, \"tree.png\").unwrap(); <\/code><\/pre>\n<p>\u041c\u044b \u0441\u043e\u0437\u0434\u0430\u043b\u0438 \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443, <code>view<\/code>, <code>sampler<\/code>, \u043d\u043e \u043e\u043d\u0438 \u043f\u043e\u043a\u0430 \u043d\u0435 \u0432\u044b\u043f\u043e\u043b\u043d\u044f\u044e\u0442 \u043d\u0438\u043a\u0430\u043a\u043e\u0439 \u0440\u0430\u0431\u043e\u0442\u044b. \u0414\u0430\u0432\u0430\u0439\u0442\u0435 \u043e\u0431\u044a\u0435\u0434\u0435\u043d\u0438\u044e \u0438\u0445 \u0432\u043c\u0435\u0441\u0442\u0435!<\/p>\n<h4>BindGroup<\/h4>\n<p><code>BindGroup<\/code> \u043e\u043f\u0438\u0441\u044b\u0432\u0430\u0435\u0442 \u043d\u0430\u0431\u043e\u0440 \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432 \u0438 \u0441\u0445\u0435\u043c\u0443 \u0434\u0430\u043d\u043d\u044b\u0445 \u0434\u043b\u044f \u0448\u0435\u0439\u0434\u0435\u0440\u043e\u0432. \u041c\u044b \u043e\u0431\u044a\u0435\u0434\u0435\u043d\u0438\u043c \u0440\u0435\u0441\u0443\u0440\u0441\u044b, \u0441\u0434\u0435\u043b\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435.<\/p>\n<p>\u041f\u0435\u0440\u0435\u0434 \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u0435\u043c <code>BindGroup<\/code>, \u043d\u0443\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>BindGroupLayout<\/code>:<\/p>\n<pre><code class=\"rust\">... let texture_bind_group_layout =    device.create_bind_group_layout(&amp;wgpu::BindGroupLayoutDescriptor {        entries: &amp;[            wgpu::BindGroupLayoutEntry {                binding: 0,                                 \/\/ 1                visibility: wgpu::ShaderStages::FRAGMENT,                ty: wgpu::BindingType::Texture {                    multisampled: false,                    view_dimension: wgpu::TextureViewDimension::D2,                    sample_type: wgpu::TextureSampleType::Float { filterable: true },                },                count: None,            },            wgpu::BindGroupLayoutEntry {                binding: 1,                                 \/\/ 2                visibility: wgpu::ShaderStages::FRAGMENT,                \/\/ \u0417\u0434\u0435\u0441\u044c \u0443\u043a\u0430\u0437\u044b\u0432\u0430\u0435\u043c SamplerBindingType::Filtering, \u043f\u043e\u0442\u043e\u043c\u0443 \u0447\u0442\u043e \u0432\u044b\u0448\u0435 \u0432 sample_type \u0443\u043a\u0430\u0437\u0430\u043d\u043e filterable: true                ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),                count: None,            },        ],        label: Some(\"texture_bind_group_layout\"),    }); <\/code><\/pre>\n<p>\u042d\u0442\u0430 \u0441\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u0434\u0435\u0440\u0436\u0438\u0442 2 \u0441\u0432\u044f\u0437\u044b\u0432\u0430\u043d\u0438\u044f: \u043f\u0435\u0440\u0432\u043e\u0435 \u0434\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u044b, \u0432\u0442\u043e\u0440\u043e\u0435 \u0434\u043b\u044f <code>Sampler<\/code>. \u041e\u043d\u0438 \u0431\u0443\u0434\u0443\u0442 \u0432\u0438\u0434\u043d\u044b \u0442\u043e\u043b\u044c\u043a\u043e \u0432\u043e \u0444\u0440\u0430\u0433\u043c\u0435\u043d\u0442\u043d\u043e\u043c (<code>ShaderStages::FRAGMENT<\/code>) \u0448\u0435\u0439\u0434\u0435\u0440\u0435.\u00a0<\/p>\n<p>\u041f\u043e\u043b\u0435 <code>visibility<\/code> \u043f\u0440\u0438\u043d\u0438\u043c\u0430\u0435\u0442 \u0431\u0438\u0442\u043e\u0432\u0443\u044e \u043c\u0430\u0441\u043a\u0443 \u0438\u0437 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439 <code>NONE<\/code>, <code>VERTEX<\/code>, <code>FRAGMENT<\/code>, \u0438\u043b\u0438 <code>COMPUTE<\/code>. \u0414\u043b\u044f \u0442\u0435\u043a\u0441\u0442\u0443\u0440 \u0447\u0430\u0449\u0435 \u0432\u0441\u0435\u0433\u043e \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u0435\u0442\u0441\u044f <code>FRAGMENT<\/code>.<\/p>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0441\u0434\u0435\u043b\u0430\u0442\u044c <code>BindGroup<\/code>:<\/p>\n<pre><code class=\"rust\">... let diffuse_bind_group = device.create_bind_group(    &amp;wgpu::BindGroupDescriptor {        layout: &amp;texture_bind_group_layout,        entries: &amp;[            wgpu::BindGroupEntry {                binding: 0,                resource: wgpu::BindingResource::TextureView(&amp;diffuse_texture.view),    \/\/ 1            },            wgpu::BindGroupEntry {                binding: 1,                resource: wgpu::BindingResource::Sampler(&amp;diffuse_texture.sampler),     \/\/ 2            }        ],        label: Some(\"diffuse_bind_group\"),    } ); <\/code><\/pre>\n<p>\u042f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u0443\u044e <code>diffuse_texture.view<\/code> \u0438 <code>diffuse_texture.sampler<\/code>, \u0441\u0434\u0435\u043b\u0430\u043d\u043d\u044b\u0435 \u0432\u044b\u0448\u0435. <code>BindGroup<\/code> \u044f\u0432\u043b\u044f\u0435\u0442\u0441\u044f \u0447\u0430\u0441\u0442\u043d\u044b\u043c \u0441\u043b\u0443\u0447\u0430\u0435\u043c <code>BindGroupLayout<\/code>, \u043f\u043e\u044d\u0442\u043e\u043c\u0443 \u043c\u0435\u0436\u0434\u0443 \u043d\u0438\u043c\u0438 \u0435\u0441\u0442\u044c \u043d\u0435\u043a\u043e\u0442\u043e\u0440\u043e\u0435 \u0441\u0445\u043e\u0434\u0441\u0442\u0432\u043e. \u0412 <em>WGPU<\/em> \u0438\u0445 \u0440\u0430\u0437\u0434\u0435\u043b\u0438\u043b\u0438 \u0434\u043b\u044f \u0442\u043e\u0433\u043e, \u0447\u0442\u043e\u0431\u044b \u043f\u0435\u0440\u0435\u043a\u043b\u044e\u0447\u0430\u0442\u044c \u0438\u0445 \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438 (\u0432\u043e \u0432\u0440\u0435\u043c\u044f \u0432\u044b\u043f\u043e\u043b\u043d\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0433\u0440\u0430\u043c\u043c\u044b), \u043d\u043e \u043f\u0440\u0438 \u044d\u0442\u043e\u043c, \u0447\u0442\u043e\u0431\u044b \u0443 \u043d\u0438\u0445 \u0431\u044b\u043b\u0430 \u043e\u0434\u043d\u0430 \u0441\u0445\u0435\u043c\u0430 \u0434\u0430\u043d\u043d\u044b\u0445 (BindGroupLayout). \u041f\u043e\u0437\u0434\u043d\u0435\u0435, \u043c\u044b \u0431\u0443\u0434\u0435\u043c \u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043a\u0430\u0436\u0434\u0443\u044e \u0442\u0435\u043a\u0441\u0442\u0443\u0440\u0443 \u0432 \u0441\u0432\u043e\u0435\u043c <code>BindGroup<\/code>.<\/p>\n<p>\u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u043f\u043e\u043b\u044f \u0432 \u0441\u0442\u0440\u0443\u043a\u0442\u0443\u0440\u0443 <code>State<\/code>:<\/p>\n<pre><code class=\"rust\">struct State {    surface: wgpu::Surface,    device: wgpu::Device,    queue: wgpu::Queue,    config: wgpu::SurfaceConfiguration,    size: winit::dpi::PhysicalSize&lt;u32>,    render_pipeline: wgpu::RenderPipeline,    vertex_buffer: wgpu::Buffer,    index_buffer: wgpu::Buffer,    num_indices: u32,    \/\/ NEW!    diffuse_bind_group: wgpu::BindGroup,    diffuse_texture: texture::Texture, }<\/code><\/pre>\n<p>\u0418 \u0432\u0435\u0440\u043d\u0443\u0442\u044c <code>diffuse_bind_group<\/code> \u0438 <code>diffuse_texture<\/code>:<\/p>\n<pre><code class=\"rust\">impl State {    async fn new() -> Self {        \/\/ ...        Self {            surface,            device,            queue,            config,            size,            render_pipeline,            vertex_buffer,            index_buffer,            num_indices,            \/\/ NEW!            diffuse_bind_group,            diffuse_texture,        }    } }<\/code><\/pre>\n<p>\u041c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u043f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u044b\u0435 \u0434\u0435\u0439\u0441\u0442\u0432\u0438\u044f, \u0442\u0435\u043f\u0435\u0440\u044c \u043c\u043e\u0436\u043d\u043e \u0432\u043e\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f <code>diffuse_bind_group<\/code> \u0438 \u0434\u043e\u0431\u0430\u0432\u0438\u0442\u044c \u0435\u0433\u043e \u0432 \u0440\u0435\u043d\u0434\u0435\u0440\u0438\u043d\u0433:<\/p>\n<pre><code class=\"rust\">\/\/ render() \/\/ ... render_pass.set_pipeline(&amp;self.render_pipeline); render_pass.set_bind_group(0, &amp;self.diffuse_bind_group, &amp;[]); \/\/ NEW! render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..)); render_pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16); <\/code><\/pre>\n<h4>PipelineLayout<\/h4>\n<p>\u041f\u043e\u043c\u043d\u0438\u0442\u0435 <code>PipelineLayout<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0439 \u043c\u044b \u0441\u0434\u0435\u043b\u0430\u043b\u0438 \u0432\u043e <a href=\"https:\/\/..\/..\/lesson2\/docs\/index.md\" rel=\"noopener noreferrer nofollow\">\u0432\u0442\u043e\u0440\u043e\u043c<\/a> \u0443\u0440\u043e\u043a\u0435? \u0412 \u043d\u0435\u043c \u0437\u0430\u0434\u0430\u044e\u0442\u0441\u044f \u0432\u0441\u0435 <code>BindGroupLayout<\/code>, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0431\u0443\u0434\u0443\u0442 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u0442\u044c\u0441\u044f \u0432 \u043f\u0430\u0439\u043f\u043b\u0430\u0439\u043d\u0435.<\/p>\n<pre><code class=\"rust\">async fn new(...) {    \/\/ ...    let render_pipeline_layout = device.create_pipeline_layout(        &amp;wgpu::PipelineLayoutDescriptor {            label: Some(\"Render Pipeline Layout\"),            bind_group_layouts: &amp;[&amp;texture_bind_group_layout], \/\/ NEW!            push_constant_ranges: &amp;[],        }    );    \/\/ ... <\/code><\/pre>\n<p>\u0422\u0435\u043f\u0435\u0440\u044c<\/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-341045","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341045","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=341045"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/341045\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=341045"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=341045"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=341045"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}