THREEJS Напиши код и вертексный шейдер с униформ п...
Создано: 1 апреля 2025 г.
Создано: 1 апреля 2025 г.
THREEJS Напиши код и вертексный шейдер с униформ переменной для MeshStandardMaterial и InstansedMesh чтоб геометрия определенного экземпляра инстансед меша сдвигалась к камере на .3 единицы
Для того чтобы реализовать сдвиг геометрии инстанса в InstancedMesh
с использованием Three.js, нам нужно сделать следующее:
InstancedMesh
для рендеринга нескольких экземпляров одной и той же геометрии.Пример кода:
javascript// Инициализация сцены, камеры и рендерера const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000); const renderer = new THREE.WebGLRenderer(); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Геометрия и материал для инстансированного меша const geometry = new THREE.BoxGeometry(1, 1, 1); const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 }); // Создание InstancedMesh const count = 10; // Количество инстансов const instancedMesh = new THREE.InstancedMesh(geometry, material, count); // Униформ для сдвига const shiftUniform = new THREE.Uniform(new THREE.Vector3(0, 0, 0.3)); // Добавление шейдера с униформом material.onBeforeCompile = (shader) => { // Добавляем униформу в шейдер shader.uniforms.shift = shiftUniform; // Изменяем вершинный шейдер для сдвига инстанса shader.vertexShader = ` uniform vec3 shift; ${shader.vertexShader} `; shader.vertexShader = shader.vertexShader.replace( '#include <begin_vertex>', ` vec3 transformed = position; transformed += shift; // Сдвигаем позицию в пространстве ${shader.vertexShader} ` ); }; // Добавление инстансированного меша в сцену scene.add(instancedMesh); // Камера camera.position.z = 5; // Анимация function animate() { requestAnimationFrame(animate); // Обновление сдвига, если требуется динамическое изменение shiftUniform.value.set(0, 0, 0.3 * Math.sin(Date.now() * 0.001)); renderer.render(scene, camera); } animate();
BoxGeometry
и материалом MeshStandardMaterial
. Это позволяет рендерить несколько экземпляров одного объекта с оптимизацией.shift
, который задает сдвиг для всех экземпляров в виде вектора (в данном случае только по оси Z).Math.sin()
для создания эффекта анимации.Этот код сдвигает все инстансы на .3
единицы по оси Z, но можно легко модифицировать его для работы с другими осями или для изменения сдвига для конкретных инстансов.
Нужно сдвигать не все элементы с материалом а только один. Видимо надо сделать сдвиг не униформой а аттрибутами
Ниже приведён пример, как можно сдвигать только один (или несколько выбранных) экземпляр(ы) в InstancedMesh
на 0.3 единицы к камере (или по любой другой оси). Для этого вместо единых uniform
мы будем использовать инстанс-атрибут.
InstancedBufferGeometry
, чтобы у нас была возможность добавить собственный атрибут (например instanceShift
).#include <begin_vertex>
, добавив instanceShift
к координате вершины.javascriptimport * as THREE from 'three'; // Создаём сцену, камеру, рендерер const scene = new THREE.Scene(); const camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 ); camera.position.z = 5; const renderer = new THREE.WebGLRenderer({ antialias: true }); renderer.setSize(window.innerWidth, window.innerHeight); document.body.appendChild(renderer.domElement); // Базовая геометрия const baseGeometry = new THREE.BoxGeometry(1, 1, 1); // Количество экземпляров const count = 10; // Чтобы добавить собственные атрибуты для инстансирования, // нам нужна InstancedBufferGeometry на базе нашей geometry const instancedGeometry = new THREE.InstancedBufferGeometry().copy(baseGeometry); // Создаём массив для хранения сдвига (x,y,z) каждого экземпляра const shiftArray = new Float32Array(count * 3); // Допустим, хотим сдвинуть только экземпляр с индексом indexToShift const indexToShift = 3; // например, 4-й объект for (let i = 0; i < count; i++) { if (i === indexToShift) { // Сдвигаем его по оси Z на 0.3 shiftArray[i * 3 + 0] = 0.0; shiftArray[i * 3 + 1] = 0.0; shiftArray[i * 3 + 2] = 0.3; } else { // Остальные не сдвигаем shiftArray[i * 3 + 0] = 0.0; shiftArray[i * 3 + 1] = 0.0; shiftArray[i * 3 + 2] = 0.0; } } // Создаём InstancedBufferAttribute на 3 компоненты (x, y, z) const shiftAttribute = new THREE.InstancedBufferAttribute(shiftArray, 3); instancedGeometry.setAttribute('instanceShift', shiftAttribute); // Стандартный материал, который будем патчить шейдером const material = new THREE.MeshStandardMaterial({ color: 0x00ff00 }); // Патчим вершинный шейдер, чтобы использовать наш instanceShift material.onBeforeCompile = (shader) => { // 1. Объявляем атрибут shader.vertexShader = `attribute vec3 instanceShift;\n` + shader.vertexShader; // 2. Вставляем добавку в стандартный чанк, отвечающий за вычисление позиции вершины shader.vertexShader = shader.vertexShader.replace( `#include <begin_vertex>`, /* glsl */` // Начальная позиция (изначально: transformed = position) vec3 transformed = position; // Прибавляем сдвиг для данного экземпляра transformed += instanceShift; ` ); }; // Теперь создаём InstancedMesh. // Важно: THREE.InstancedMesh ждёт обычную (не instanced) геометрию, но // если нам нужен custom-атрибут, можно "притвориться" обычной геометрией // и использовать instancedGeometry. В Three.js 0.125+ это будет работать. const instancedMesh = new THREE.InstancedMesh(instancedGeometry, material, count); scene.add(instancedMesh); // Расставляем instanceMatrix для каждого экземпляра // (т.е. позиция, вращение, масштаб). // Можно просто поставить их в разные места. const dummy = new THREE.Object3D(); for (let i = 0; i < count; i++) { dummy.position.set(i * 2 - (count), 0, 0); // в ряд по X dummy.updateMatrix(); instancedMesh.setMatrixAt(i, dummy.matrix); } // Свет const light = new THREE.DirectionalLight(0xffffff, 1); light.position.set(0, 10, 10).normalize(); scene.add(light); function animate() { requestAnimationFrame(animate); renderer.render(scene, camera); } animate();
InstancedBufferGeometry:
BoxGeometry
копируется в InstancedBufferGeometry
, чтобы мы могли добавить атрибут.InstancedBufferAttribute instanceShift
:
Float32Array
на count * 3
, где для каждого экземпляра хранится (x, y, z) сдвиг.InstancedBufferAttribute
.instancedGeometry
под именем instanceShift
.Кастомизация шейдера (onBeforeCompile
):
attribute vec3 instanceShift;
к началу vertexShader
.instanceShift
к координатам вершин, заменяя стандартный блок #include <begin_vertex>
.modelViewMatrix
/ instanceMatrix
) остаётся без изменений.InstancedMesh:
instancedGeometry
и материал.instanceMatrix
, чтобы расставить объекты в разные места.Таким образом, только экземпляр с индексом indexToShift
получит сдвиг в 0.3 по оси Z, а остальные останутся на месте. Если нужно динамически менять сдвиг, можно обновлять shiftAttribute
каждую анимацию (и затем, если нужно, помечать .needsUpdate = true
).