Goal
I want to dynamically adjust the geometry of a 3D model based on the position of a tracked point (e.g., "headPosition"), with the nature of this adjustment depending on the model's height (z-axis value). For now the headPosition is fixed variable but will eventually be passed dynamically.
This transformation is influenced by two main factors:
Direction Vector: A vector originating from a fixed point (origin) towards a tracked position in space (referred to as "headPosition"). This vector dictates the direction of the transformation in the XY plane.
Height Factor: The magnitude of the transformation is modulated by the vertex's height (z-coordinate) relative to a base plane (commonly z=0). As a vertex's height increases, the intensity of the transformation—its skew or shift in the XY plane—also increases.
Transformation Strategy
Calculate Direction Vector: Determine the direction vector from the origin (or a predefined base point) to the headPosition. This vector provides the directional basis for the transformation. The vector is normalized to ensure uniform application across all vertices regardless of their distance from the headPosition.
Modulate Transformation by Height: For each vertex, calculate a "height factor" based on its z-coordinate. This factor scales the transformation, causing vertices at higher elevations to experience a more pronounced transformation. The exact relationship (linear, quadratic, etc.) between height and transformation magnitude can be adjusted based on the desired visual effect.
Apply Transformation: Vertices are transformed (translated) in the XY plane according to the direction vector. The extent of this transformation for each vertex is modulated by its height factor, ensuring that the transformation's visibility or intensity increases with height.
import { useGLTF } from '@react-three/drei'
import * as THREE from 'three';
export default function Model(props) {
const { nodes, materials } = useGLTF('/spa/spa.gltf')
const [change, setChange] = useState(true)
const headPosition = new THREE.Vector3(0, 0, 0);
useEffect(() => {
// Custom vertex shader code
// Normalize firection from origin to headPosition
// Height Factor that increases with increasing z coordinate
// Adjust vertex Position
// Transform position with model-view-projection matrix
const customVertexShader = `
uniform vec3 headPosition;
varying vec2 vUv;
void main() {
vUv = uv;
vec3 direction = normalize(headPosition - vec3(0.0, 0.0, 0.0));
float heightFactor = 1.0 + position.z * 1000;
vec3 adjustedPosition = vec3(position.x + direction.x * heightFactor, position.y + direction.y * heightFactor, position.z);
gl_Position = projectionMatrix * modelViewMatrix * vec4(adjustedPosition, 1.0);
}
`;
// Iterate over each mesh and modify its material
Object.values(nodes).forEach(node => {
if (node.material) {
const originalMaterial = node.material;
// Clone the original material and its properties
const newMaterial = originalMaterial.clone();
// Replace the vertex shader while keeping the fragment shader and uniforms
newMaterial.onBeforeCompile = (shader) => {
shader.vertexShader = customVertexShader;
shader.uniforms.headPosition = { value: headPosition };
};
newMaterial.needsUpdate = true
// Update the material of the mesh
node.material = newMaterial;
originalMaterial.dispose();
}
});
}, [nodes, headPosition]);
return (
<group {...props} dispose={null}>
<group position={[1527.063, 1, -1529.891]}>
<mesh geometry={nodes.Mesh_0110.geometry} material={materials['Material_0.110']} />
<mesh geometry={nodes.Mesh_0110_1.geometry} material={materials.Material_0} />
<mesh geometry={nodes.Mesh_0110_2.geometry} material={materials['Material_0.001']} />
<mesh geometry={nodes.Mesh_0110_3.geometry} material={materials['Material_0.002']} />
<mesh geometry={nodes.Mesh_0110_4.geometry} material={materials['Material_0.003']} />
<mesh geometry={nodes.Mesh_0110_5.geometry} material={materials['Material_0.004']} />
<mesh geometry={nodes.Mesh_0110_6.geometry} material={materials['Material_0.005']} />
<mesh geometry={nodes.Mesh_0110_7.geometry} material={materials['Material_0.006']} />
<mesh geometry={nodes.Mesh_0110_8.geometry} material={materials['Material_0.007']} />
<mesh geometry={nodes.Mesh_0110_9.geometry} material={materials['Material_0.008']} />
<mesh geometry={nodes.Mesh_0110_10.geometry} material={materials['Material_0.009']} />
<mesh geometry={nodes.Mesh_0110_11.geometry} material={materials['Material_0.010']} />
<mesh geometry={nodes.Mesh_0110_12.geometry} material={materials['Material_0.011']} />
<mesh geometry={nodes.Mesh_0110_13.geometry} material={materials['Material_0.012']} />
<mesh geometry={nodes.Mesh_0110_14.geometry} material={materials['Material_0.013']} />
<mesh geometry={nodes.Mesh_0110_15.geometry} material={materials['Material_0.014']} />
<mesh geometry={nodes.Mesh_0110_16.geometry} material={materials['Material_0.015']} />
<mesh geometry={nodes.Mesh_0110_17.geometry} material={materials['Material_0.016']} />
<mesh geometry={nodes.Mesh_0110_18.geometry} material={materials['Material_0.017']} />
<mesh geometry={nodes.Mesh_0110_19.geometry} material={materials['Material_0.018']} />
<mesh geometry={nodes.Mesh_0110_20.geometry} material={materials['Material_0.019']} />
<mesh geometry={nodes.Mesh_0110_21.geometry} material={materials['Material_0.020']} />
<mesh geometry={nodes.Mesh_0110_22.geometry} material={materials['Material_0.021']} />
<mesh geometry={nodes.Mesh_0110_23.geometry} material={materials['Material_0.022']} />
<mesh geometry={nodes.Mesh_0110_24.geometry} material={materials['Material_0.023']} />
<mesh geometry={nodes.Mesh_0110_25.geometry} material={materials['Material_0.024']} />
<mesh geometry={nodes.Mesh_0110_26.geometry} material={materials['Material_0.025']} />
<mesh geometry={nodes.Mesh_0110_27.geometry} material={materials['Material_0.026']} />
<mesh geometry={nodes.Mesh_0110_28.geometry} material={materials['Material_0.027']} />
<mesh geometry={nodes.Mesh_0110_29.geometry} material={materials['Material_0.028']} />
<mesh geometry={nodes.Mesh_0110_30.geometry} material={materials['Material_0.029']} />
<mesh geometry={nodes.Mesh_0110_31.geometry} material={materials['Material_0.030']} />
<mesh geometry={nodes.Mesh_0110_32.geometry} material={materials['Material_0.031']} />
<mesh geometry={nodes.Mesh_0110_33.geometry} material={materials['Material_0.032']} />
<mesh geometry={nodes.Mesh_0110_34.geometry} material={materials['Material_0.033']} />
<mesh geometry={nodes.Mesh_0110_35.geometry} material={materials['Material_0.034']} />
<mesh geometry={nodes.Mesh_0110_36.geometry} material={materials['Material_0.035']} />
<mesh geometry={nodes.Mesh_0110_37.geometry} material={materials['Material_0.036']} />
<mesh geometry={nodes.Mesh_0110_38.geometry} material={materials['Material_0.037']} />
<mesh geometry={nodes.Mesh_0110_39.geometry} material={materials['Material_0.038']} />
<mesh geometry={nodes.Mesh_0110_40.geometry} material={materials['Material_0.039']} />
<mesh geometry={nodes.Mesh_0110_41.geometry} material={materials['Material_0.040']} />
<mesh geometry={nodes.Mesh_0110_42.geometry} material={materials['Material_0.041']} />
<mesh geometry={nodes.Mesh_0110_43.geometry} material={materials['Material_0.042']} />
<mesh geometry={nodes.Mesh_0110_44.geometry} material={materials['Material_0.043']} />
<mesh geometry={nodes.Mesh_0110_45.geometry} material={materials['Material_0.044']} />
<mesh geometry={nodes.Mesh_0110_46.geometry} material={materials['Material_0.045']} />
<mesh geometry={nodes.Mesh_0110_47.geometry} material={materials['Material_0.046']} />
<mesh geometry={nodes.Mesh_0110_48.geometry} material={materials['Material_0.047']} />
<mesh geometry={nodes.Mesh_0110_49.geometry} material={materials['Material_0.048']} />
<mesh geometry={nodes.Mesh_0110_50.geometry} material={materials['Material_0.049']} />
<mesh geometry={nodes.Mesh_0110_51.geometry} material={materials['Material_0.050']} />
<mesh geometry={nodes.Mesh_0110_52.geometry} material={materials['Material_0.051']} />
<mesh geometry={nodes.Mesh_0110_53.geometry} material={materials['Material_0.052']} />
<mesh geometry={nodes.Mesh_0110_54.geometry} material={materials['Material_0.053']} />
<mesh geometry={nodes.Mesh_0110_55.geometry} material={materials['Material_0.054']} />
<mesh geometry={nodes.Mesh_0110_56.geometry} material={materials['Material_0.055']} />
<mesh geometry={nodes.Mesh_0110_57.geometry} material={materials['Material_0.056']} />
<mesh geometry={nodes.Mesh_0110_58.geometry} material={materials['Material_0.057']} />
<mesh geometry={nodes.Mesh_0110_59.geometry} material={materials['Material_0.058']} />
<mesh geometry={nodes.Mesh_0110_60.geometry} material={materials['Material_0.059']} />
<mesh geometry={nodes.Mesh_0110_61.geometry} material={materials['Material_0.060']} />
<mesh geometry={nodes.Mesh_0110_62.geometry} material={materials['Material_0.061']} />
<mesh geometry={nodes.Mesh_0110_63.geometry} material={materials['Material_0.062']} />
<mesh geometry={nodes.Mesh_0110_64.geometry} material={materials['Material_0.063']} />
<mesh geometry={nodes.Mesh_0110_65.geometry} material={materials['Material_0.064']} />
<mesh geometry={nodes.Mesh_0110_66.geometry} material={materials['Material_0.065']} />
<mesh geometry={nodes.Mesh_0110_67.geometry} material={materials['Material_0.066']} />
<mesh geometry={nodes.Mesh_0110_68.geometry} material={materials['Material_0.067']} />
<mesh geometry={nodes.Mesh_0110_69.geometry} material={materials['Material_0.068']} />
<mesh geometry={nodes.Mesh_0110_70.geometry} material={materials['Material_0.069']} />
<mesh geometry={nodes.Mesh_0110_71.geometry} material={materials['Material_0.070']} />
<mesh geometry={nodes.Mesh_0110_72.geometry} material={materials['Material_0.071']} />
<mesh geometry={nodes.Mesh_0110_73.geometry} material={materials['Material_0.072']} />
<mesh geometry={nodes.Mesh_0110_74.geometry} material={materials['Material_0.073']} />
<mesh geometry={nodes.Mesh_0110_75.geometry} material={materials['Material_0.074']} />
<mesh geometry={nodes.Mesh_0110_76.geometry} material={materials['Material_0.075']} />
<mesh geometry={nodes.Mesh_0110_77.geometry} material={materials['Material_0.076']} />
<mesh geometry={nodes.Mesh_0110_78.geometry} material={materials['Material_0.077']} />
<mesh geometry={nodes.Mesh_0110_79.geometry} material={materials['Material_0.078']} />
<mesh geometry={nodes.Mesh_0110_80.geometry} material={materials['Material_0.079']} />
<mesh geometry={nodes.Mesh_0110_81.geometry} material={materials['Material_0.080']} />
<mesh geometry={nodes.Mesh_0110_82.geometry} material={materials['Material_0.081']} />
<mesh geometry={nodes.Mesh_0110_83.geometry} material={materials['Material_0.082']} />
<mesh geometry={nodes.Mesh_0110_84.geometry} material={materials['Material_0.083']} />
<mesh geometry={nodes.Mesh_0110_85.geometry} material={materials['Material_0.084']} />
<mesh geometry={nodes.Mesh_0110_86.geometry} material={materials['Material_0.085']} />
<mesh geometry={nodes.Mesh_0110_87.geometry} material={materials['Material_0.086']} />
<mesh geometry={nodes.Mesh_0110_88.geometry} material={materials['Material_0.087']} />
<mesh geometry={nodes.Mesh_0110_89.geometry} material={materials['Material_0.088']} />
<mesh geometry={nodes.Mesh_0110_90.geometry} material={materials['Material_0.089']} />
<mesh geometry={nodes.Mesh_0110_91.geometry} material={materials['Material_0.090']} />
<mesh geometry={nodes.Mesh_0110_92.geometry} material={materials['Material_0.091']} />
<mesh geometry={nodes.Mesh_0110_93.geometry} material={materials['Material_0.092']} />
<mesh geometry={nodes.Mesh_0110_94.geometry} material={materials['Material_0.093']} />
<mesh geometry={nodes.Mesh_0110_95.geometry} material={materials['Material_0.094']} />
<mesh geometry={nodes.Mesh_0110_96.geometry} material={materials['Material_0.095']} />
<mesh geometry={nodes.Mesh_0110_97.geometry} material={materials['Material_0.096']} />
<mesh geometry={nodes.Mesh_0110_98.geometry} material={materials['Material_0.097']} />
<mesh geometry={nodes.Mesh_0110_99.geometry} material={materials['Material_0.098']} />
<mesh geometry={nodes.Mesh_0110_100.geometry} material={materials['Material_0.099']} />
<mesh geometry={nodes.Mesh_0110_101.geometry} material={materials['Material_0.100']} />
<mesh geometry={nodes.Mesh_0110_102.geometry} material={materials['Material_0.101']} />
<mesh geometry={nodes.Mesh_0110_103.geometry} material={materials['Material_0.102']} />
<mesh geometry={nodes.Mesh_0110_104.geometry} material={materials['Material_0.103']} />
<mesh geometry={nodes.Mesh_0110_105.geometry} material={materials['Material_0.104']} />
<mesh geometry={nodes.Mesh_0110_106.geometry} material={materials['Material_0.105']} />
<mesh geometry={nodes.Mesh_0110_107.geometry} material={materials['Material_0.106']} />
<mesh geometry={nodes.Mesh_0110_108.geometry} material={materials['Material_0.107']} />
<mesh geometry={nodes.Mesh_0110_109.geometry} material={materials['Material_0.108']} />
<mesh geometry={nodes.Mesh_0110_110.geometry} material={materials['Material_0.109']} />
</group>
</group>
)
}
useGLTF.preload('/spa.gltf')