I am using Three.js to load a GLTF model and implement an overlay effect with ShaderMaterial
. However, when I group the meshes using THREE.Group
, the meshes disappear when applying the overlay.
Issue:
- When the meshes are not grouped, they are displayed correctly.
- When the meshes are grouped, they are not displayed correctly (they disappear).
I would appreciate advice on the following points:
- How to properly handle the impact of
modelMatrix
andnormalMatrix
when grouping meshes. - How to manage local and world coordinates within a group.
- How to address UV mapping or texture issues that might arise when meshes are grouped.
Current observations:
- Using
MeshBasicMaterial
instead ofShaderMaterial
displays the meshes correctly, but the texture is missing. - If the fragment shader uses
gl_FragColor = vec4(vWorldPosition.xyz * 0.1, 1.0);
, the meshes appear in the correct position. - The texture is already included in the GLTF material, so the fragment shader doesn’t need to sample it in order for the model to display.
initializeObj(object: THREE.Object3D) {
// Process textures
if (object instanceof THREE.Mesh) {
this.meshs.push(object);
// Create a mesh for overlay display
const overlayMesh = object.clone();
// Configure the shader material for the overlay
const matOvelay = new THREE.ShaderMaterial({
vertexShader,
fragmentShader,
depthTest: false, // Disable Z-test to render on top of the original mesh
transparent: true,
});
// Uncomment this block to test with a basic material
/*
const matOvelay = new THREE.MeshBasicMaterial({
color: 0xff0000, // Red color for testing
depthTest: false,
transparent: true,
opacity: 0.5, // Semi-transparent for testing
});
*/
overlayMesh.material = matOvelay;
// Set render order to ensure the overlay is drawn after the original
overlayMesh.renderOrder = object.renderOrder + 1;
// Add the overlay mesh to the parent
object.parent?.add(overlayMesh);
this.materialsOverlay.push(matOvelay);
} else if (object instanceof THREE.Camera) {
// Set the camera position for the camera dot mesh
this.cameraDotMesh.position.copy(object.position);
}
// Recursively process child objects if they exist
if (object.children && object.children.length > 0) {
// Clone the children to avoid modifying the original array
const children = [...object.children];
for (const child of children) {
this.initializeObj(child);
}
}
}
// Fragment Shader
// ====================
uniform vec3 p0;
uniform vec3 p1;
uniform vec3 p2;
uniform vec3 p3;
uniform vec3 p4;
uniform vec3 p5;
uniform vec3 p6;
uniform vec3 p7;
uniform vec3 eVec;
uniform int mode;
varying vec4 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
// Function to check if a point is inside a triangle
bool isPointInside(vec3 p, vec3 pA, vec3 pB, vec3 pC) {
return dot(cross(pB - pA, pC - pA), p - pA) < 0.0;
}
void main() {
// Debug: Visualize world position as color
gl_FragColor = vec4(vWorldPosition.xyz * 0.1, 1.0);
return;
// Calculate the dot product between the mesh normal and the container normal (eVec)
// Map the result from [-1, 1] to [0, 1]
float dotProduct = dot(normalize(vNormal), normalize(eVec));
float brightness = dotProduct * 0.5 + 0.5;
// Allowable angle: 45 degrees
bool isPlane = brightness > (180.0 - 45.0) / 180.0;
vec3 point = vWorldPosition.xyz;
// Check if the point is inside the defined container (formed by triangles)
bool isInside = isPointInside(point, p0, p1, p2) &&
isPointInside(point, p2, p1, p5) &&
isPointInside(point, p2, p6, p7) &&
isPointInside(point, p5, p1, p0) &&
isPointInside(point, p7, p4, p0) &&
isPointInside(point, p6, p5, p4);
// Overlay effect: If the conditions are met, render red, otherwise discard
if (isPlane && isInside) {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
} else {
discard;
}
}
// Vertex Shader
// =================
varying vec4 vWorldPosition;
varying vec3 vNormal;
varying vec2 vUv;
void main() {
// Transform vertex position to world coordinates
vec4 worldPos = modelMatrix * vec4(position, 1.0);
vWorldPosition = worldPos;
// Calculate the normal vector (transformed using normalMatrix)
vNormal = normalize(normalMatrix * normal);
// Pass through UV coordinates
vUv = uv;
// Transform vertex to clip space
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}