I have a scale attribute like that I am applying to different instances of a instance buffered geometry in three js. The shader look like this:
attribute float scale;
uniform vec3 uMeshPosition;
void main() {
vec3 pos = position;
pos.x *= ( uMeshPosition.x - pows.x ) * scale + uMeshPosition.x;
pos.z *= ( uMeshPosition.z - pos.z ) * scale + uMeshPosition.z;
pos.y *= ( uMeshPosition.y - pos.y ) * scale + uMeshPosition.y;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos,1.0);
}
Z is the height in my case!!!
I'd like the scaled cubes to maintain their initial center indicated by the wireframe cube above.
Can scaling applied on all 3 axis be done without calculating and applying scale on the CPU?
Updates:
The way I'm creating the Geometry, normally with multiple cubes, but for this example it will be just one instance:
const createInstancedGeometry = (instanceCount, sizeX = 1, sizeY = 1, sizeZ = 1) => {
const geometry = new InstancedBufferGeometry()
geometry.maxInstancedCount = instanceCount
const shape = new BoxBufferGeometry(0.1 * sizeX, 0.1 * sizeY, 0.1 * sizeZ)
shape.translate(0, 0.4, 0)
const data = shape.attributes
geometry.addAttribute('position', new BufferAttribute(new Float32Array(data.position.array), 3))
geometry.addAttribute('uv', new BufferAttribute(new Float32Array(data.uv.array), 2))
geometry.addAttribute('normal', new BufferAttribute(new Float32Array(data.normal.array), 3))
geometry.setIndex(new BufferAttribute(new Uint16Array(shape.index.array), 1))
shape.dispose()
createInstancedAtrributes(geometry, instanceCount)
return geometry
}
This is the way I'm setting up the shader, I'm not using the colors yet.
const createShader = () => {
const uniforms = {
// uMap: { type: 't', value: null },
uColor1: { type: 'c', value: new Color(0x961800) }, // red
uColor2: { type: 'c', value: new Color(0x4b5828) }, // yellow
uMeshPosition: { type: 'vec3', value: new Vector3(0, 0, 0) },
}
const shader = new ShaderMaterial({
uniforms,
vertexShader,
fragmentShader,
blending: AdditiveBlending,
transparent: true,
depthWrite: false,
})
return shader
}
The constructor for my Particle Fire looks like this:
constructor({ sizeX = 1, sizeY = 1, sizeZ = 1 } = {}) {
const instanceCount = 1
const geometry = createInstancedGeometry(instanceCount, sizeX, sizeY, sizeZ)
const material = createShader()
const mesh = new Mesh(geometry, material)
mesh.frustumCulled = false
this.geometry = geometry
this.material = material
this.mesh = mesh
mesh.up = new Vector3(0, 0, 1)
mesh.position.set(2, 2, 1)
mesh.rotateX(Math.PI / 2)
this.instanceCount = instanceCount
const lineGeo = new EdgesGeometry(geometry) // or WireframeGeometry
const mat = new LineBasicMaterial({ color: 0xffffff, linewidth: 2 })
const wireframe = new LineSegments(lineGeo, mat)
this.mesh.add(wireframe)
}
And the update call:
update() {
const { instanceCount } = this
const { scale, progress, randoms } = this.geometry.attributes
const { uMeshPosition } = this.material.uniforms
uMeshPosition.value = this.mesh.position
for (let i = 0; i < instanceCount; i += 1) {
let value = progress.array[i]
value += 0.025
if (value > 1) {
value -= 1
scale.setX(i, randomValueBetween(0.3, 2, 3))
// randoms.setX(i, randomValueBetween(0, 1, 3))
}
// progress.setX(i, value)
}
scale.needsUpdate = true
// randoms.needsUpdate = true
// progress.needsUpdate = true
}
I'm adding the object to the scene like this:
const pFire = new ParticleFire()
scene.add(pFire.mesh)
And updating it in a render loop like this:
pFire.update({ deltaTime })
renderer.render(scene, cameraController.camera)
requestAnimationFrame(animate)
And cameraController.camera
is a simple camera controller added to the scene as a child to a 'character' that I move around the scene.
configuredCamera = new PerspectiveCamera(
75, window.innerWidth / window.innerHeight, 0.1, 5000,
)
The translate
function of THREE.Geometry
translates each vertex of the mesh. So the mesh it self is displaced and its center is not (0, 0, 0) anymore.
You should set the geoemtry of the instead (position
). See THREE.Object3D
.
This means you have to delete shape.translate(0, 0.4, 0)
and you have to do the whole placement by geometry.position
.
Then the mesh is not displaced anymore and is placed by the modelViewMatrix
alone. And the GLS shader code will work:
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos * scale, 1.0);