I have this scene and for the half transparent box geometry I want to create a shader that gradually reduced the alpha value of gl_FragColor going over the long axis (in this case the z axis in three js).
I am a bit at a loss on how to do that. I thought I could do that with UVs but they run along the wrong axis.
I basically need a way to find out at which percentage point I am on the z axis relative to the mesh size.
Is that even possible in a shader?
I could also send the length size via a uniform. But then again, I missing the overall concept on how to reach the goal. Any help would be greatly appreciated.
Yes it is possible to be done with shaders, here is function which will return material which you can set for your mesh to achieve effect you want:
function materialGradientByAxis(mesh, color = 0xff_ff_00, axis = 0, reverse = false) {
/*- mesh: must be instanceof THREE.Mesh
mesh must have geometry (mesh.geometry)
expected static geometry attribute, but dynamic transform matrix
- axis: 0: x , 1: y, 2: z (in mesh.geometry space)
- reverse:
false: axis local min: alpha = 0, max: alpha = 1
true: axis local min: alpha = 1, max: alpha = 0
*/
if ( !(mesh?.geometry?.boundingBox instanceof THREE.Box3) ) {
mesh.geometry.computeBoundingBox()
}
const shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
u_bboxMin: { value: mesh.geometry.boundingBox.min },
u_bboxDelta: { value: mesh.geometry.boundingBox.max.clone().sub(mesh.geometry.boundingBox.min) },
u_axisIndex: { value: axis },
u_reverse: { value: reverse },
u_color: { value: new THREE.Color(color) }
},
vertexShader:`
varying vec3 vPosition;
void main() {
vPosition = position;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}`,
fragmentShader:`
varying vec3 vPosition;
uniform vec3 u_bboxMin;
uniform vec3 u_bboxDelta;
uniform int u_axisIndex;
uniform bool u_reverse;
uniform vec3 u_color;
void main() {
float pos = ((vPosition - u_bboxMin)/u_bboxDelta)[u_axisIndex];
vec4 color = vec4(
u_color,
u_reverse ? 1.0 - pos : pos
);
gl_FragColor = color;
}`
});
shaderMaterial.transparent = true;
shaderMaterial.side = THREE.DoubleSide;
shaderMaterial.depthWrite = false;
return shaderMaterial;
}
you can use it like this:
const geometry = new THREE.BoxGeometry( 2, 2, 2 );
const cube = new THREE.Mesh( geometry );
cube.material = materialGradientByAxis( cube, 0xff_ff_00, 0, false )
and here is a result:
and without box helper:
you can check full code and view result from different angles here:
https://codepen.io/andromeda2/full/LEYJMEN