Closed finnbear closed 7 months ago
I have a multi-group, multi-material Mesh and one of the groups/materials is windows/transparent glass. I would like that one material to not cast shadows. How can I accomplish this?
Are you aware of the ShadowMaterial? https://threejs.org/docs/#api/en/materials/ShadowMaterial
Are you aware of the ShadowMaterial?
While I meant partially transparent, at first ShadowMaterial
seemed promising as a partial solution for glass (to make it slightly visible but not cast shadows). However, it appears that even ShadowMaterial
casts shadows. It also visually disappears when the shadow map lacks data.
Try mesh.onBeforeRender( renderer, scene, camera, geometry, material )
and set .castShadow
to false
for the desired material only. Restore with .onAfterRender()
.
Try
mesh.onBeforeRender( renderer, scene, camera, geometry, material )
and set.castShadow
tofalse
for the desired material only. Restore with.onAfterRender()
.
Thanks @WestLangley! I tried this:
mesh.onBeforeRender = (renderer, scene, camera, geometry, material) => {
console.log(geometry, material);
if (material.transparent) {
mesh.castShadow = false;
}
};
mesh.onAfterRender = (renderer, scene, camera, geometry, material) => {
if (material.transparent) {
mesh.castShadow = true;
}
};
console.log
confirmed the code (including mesh.castShadow = false;
ran properly, but I observed no visual difference.
It is best if you provide a live example so we can avoid guessing... Is your material both transparent and double-sided?
If so, try adding material.forceSinglePass = true
when the material is instantiated.
It is best if you provide a live example so we can avoid guessing...
@WestLangley Sorry, here you go: https://playcode.io/1725402 Edit: https://playcode.io/1725402
Edit: I updated the example to toggle materials every 2s, so it is clear which one(s) cast shadows on the other(s)
Is your material both transparent and double-sided
My window material is transparent but none are double-sided (as in THREE.BothSides
). However my geometry is double-sided (as in solid volume, for physics reasons).
Unfortunately, the suggested workarounds are not working for me, either.
considering that a single Object3D may have multiple materials with different needs.
Something that definitely works is to use SceneUtils.createMeshesFromMultiMaterialMesh() which splits up a multi-material mesh into separate instances of Mesh
. You can then configure the shadow properties as usual.
As mentioned above, there is no performance difference since multi-material meshes ends up as separate render items anyway.
Splitting into multiple objects would be annoying.
At least you have a routine as a workaround. If you would use GLTFLoader
, you would never get meshes with group
data defined. The BufferGeoemtry.groups
/multi-material API is somewhat controversial and we have already thought about alternatives.
Something that definitely works is to use SceneUtils.createMeshesFromMultiMaterialMesh() which splits up a multi-material mesh into separate instances of Mesh. You can then configure the shadow properties as usual.
Thanks for linking to that API, which would definitely make a workaround easier. I might use it for the time being. Does require code to keep the physics using a single mesh, but that shouldn't be that hard.
The BufferGeoemtry.groups/multi-material API is somewhat controversial and we have already thought about alternatives.
I personally like it. Makes my life easier (except for this issue). Curious if you have a link to discussions about replacing it.
It also depends on your workflow to generate those 3D models. I'm using 3DS Max and the BabylonJS exporter and the results are always the best: a mesh with multiple materials is exported as a group with multiple meshes (a single material per mesh). So in this case, to get what you're looking for is clean and fast. Example: https://necromanthus.com/Test/html5/Glass.html
myBox.traverse( function ( child ) {
if ( child.isMesh ) {
if ( child.material.name != "glass") {
child.castShadow = true;
child.receiveShadow = true;
}
}
});
I personally like it. Makes my life easier (except for this issue). Curious if you have a link to discussions about replacing it.
The discussion is unfortunately scattered around different issues and PRs.
However, there is no concrete plan to remove groups/multi-materials. But maybe implement it differently. Other engines and DCC tools support similar concepts and certain developers rely on it.
It also depends on your workflow to generate those 3D models.
I'm well aware of the ability of my workflow (obj for now, but also gltf) to export different meshes for each material. I intentionally avoided doing that so my scene would be simpler. I might switch to doing that as a workaround, but not a full solution to this issue.
Since this issue is all about shadows of transparent objects:
Would you still need more fain-grained shadow configuration if #15999 would be merged? This PR would make sure shadows of transparent objects look better.
Conceptually, turning on/off shadows is something that you ideally want to configure per object. Simply because from a physically correct point of view it is not plausible when only parts of an object cast (or receive) shadows.
IMO, it makes more sense to improve the shadow quality instead so devs do not want to turn of shadows because the result doesn't look good. Hopefully https://github.com/mrdoob/three.js/pull/15999 is a first step towards this goal.
I'm with you Michael, In the real world only an object (mesh) can generate a shadow, not a material (which is an object property). In my opinion the best solution here is to keep the current logic and to add the real missing feature: the shadow intensity depends on the material opacity.
the shadow intensity depends on the material opacity
I must say, that's a much better solution (didn't know it was possible) :smile:
Will close this for now.
Description
Please make
Material
able to control shadow casting behavior (in addition to or instead ofObject3D
), considering that a singleObject3D
may have multiple materials with different needs.If I understand correctly, each material/group is rendered in a separate draw call, so they could be filtered during shadow rendering.
While I don't personally need each material to also control whether to receive shadows, it could be a logical addition to this proposal.
Solution
Downside: harder to reuse same material if some uses need shadows and others don't.
Alternatives
or (only for shadow casting)
or (also only for shadow casting, specifically windows)
or (this doesn't work right now for some reason)
or (thought of by
Ghost
)or (thought of by WestLangley) https://github.com/mrdoob/three.js/issues/27564#issuecomment-1892715348
Additional context
I have a multi-group, multi-material
Mesh
and one of the groups/materials is windows/transparent glass. I would like that one material to not cast shadows. How can I accomplish this? (castShadows is a property ofObject3D
, notMaterial
). Splitting into multiple objects would be annoying.I asked for advice on Discord but received no responses. I'm open to attempting workarounds that don't require splitting up my
Mesh
into multiple.