Cast a shadow from a semi-transparent texture?

Hello, if I have a texture such as this:

Currently I am using the black section as transparent and the white as what should be rendered to a plane. It works well, but I’m curious if I can also have that white section cast a shadow, as though it were a mesh?

Thanks

Maybe need material.alphaMap=texture;

Use it as alphaMap and set alphaTest to 0.5, you could also use it as map if the black parts are zero alpha.

2 Likes

Thank you both for your help. Unfortunately I had already set those properties. Here is my material:

        this.material = new THREE.MeshStandardMaterial({
            map: tex,
            alphaMap: tex,
            alphaTest: .05,
            transparent: true,
        });

I get the following result:

As you can see, the cube successfully casts a shadow, while the plane+texture items do not.

this.material=new THREE.MeshBasicMaterial({
map:tex,
alphaMap:tex,
alphaTest:0.5, // <-- not 0.05
transparent:false
});
mesh.castShadow=true;

1 Like

I really appreciate you taking the time to respond. I must be doing something dumb. No worries if this is too much to ask, but here is a cleaned up full version of the code…


import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

import { TWEEN } from 'three/examples/jsm/libs/tween.module.min'



// ----------------------- DATA
const assetsDir = "map-assets/";
const glyphDir = assetsDir + "/glyphs_jpg/";
const glyphData =
    [
        {
            "image": glyphDir + 'glyph_1.jpg',
            "position": { x: -4.0, y: 3.7, z: .5 },
        },
        {
            "image": glyphDir + 'glyph_2.jpg',
            "position": { x: 3.92, y: 4, z: .5 },
        },
        {
            "image": glyphDir + 'glyph_3.jpg',
            "position": { x: -0.35, y: 2.70, z: .4 },
        },
        
    ];

// ----------------------- SCENE
const scene = new THREE.Scene();

// ----------------------- CAMERA
const camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, .1, 10000);
camera.position.set(0, 0, 5);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setClearColor(0x111111);
document.body.appendChild(renderer.domElement);
renderer.setPixelRatio(window.devicePixelRatio);

// ----------------------- CONTROLS
const controls = new OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render);



// ----------------------- LIGHTING
const dirLight = new THREE.DirectionalLight(0xffffff, .5);
dirLight.position.setScalar(5);  // get it away from center
dirLight.position.set(0, 0, 15);
dirLight.castShadow = true;
scene.add(dirLight);
// const helper = new THREE.CameraHelper(dirLight.shadow.camera)
// scene.add(helper)


const geometry = new THREE.BoxGeometry(11, 11, 1);
const material = new THREE.MeshStandardMaterial({ color: 0xbbccaa });
const cube = new THREE.Mesh(geometry, material);
cube.position.set(0, 0, -1);
cube.receiveShadow = true;
scene.add(cube);

const geometry2 = new THREE.BoxGeometry(3, 3, 3);
const material2 = new THREE.MeshStandardMaterial({ color: 0x555555 });
const cube2 = new THREE.Mesh(geometry2, material2);
cube2.position.set(1, 0, 5);
cube2.receiveShadow = true;
cube2.castShadow = true;
scene.add(cube2);




// ----------------------- GLYPHS

class Glyph extends THREE.Mesh {
    constructor(data) {

        super()

        this.position.set(data.position.x, data.position.y, data.position.z);

        this.geometry = new THREE.PlaneGeometry(1, 1);
        this.scale.setScalar(3);

        const tex = new THREE.TextureLoader().load(data.image);

        this.material = new THREE.MeshBasicMaterial({
            map: tex,
            alphaMap: tex,
            alphaTest: .5,
            transparent: false,

        });
        this.castShadow = true;
    }

}



let glyphs = [];
for (let i = 0; i < glyphData.length; i++) {

    glyphs[i] = new Glyph(glyphData[i]);
    scene.add(glyphs[i]);

}


renderer.shadowMap.enabled = true;



// ----------------------- RENDER
render();
function render() {

        renderer.render(scene, camera);

}

animate();
function animate() {
    // console.log("!");
    requestAnimationFrame(animate);
    TWEEN.update();
}

I can’t figure out why it won’t work!

renderer.shadowMap.type=THREE.VSMShadowMap;
Another shadow types not working for me.
image

Thank you!

It turns out that Plane geometries do not cast shadows by default, since the shadow is generated using the back face of a given mesh. The solution above works, as well as using shadowSide: THREE.DoubleSide on the Plane material.

1 Like

I used material.side:2;
Added color:

this.material = new THREE.MeshBasicMaterial({
color:data.color,
side:2,
alphaMap: tex,
alphaTest: .5,
transparent: true,
});

How can we make the shadow more or less depending on the amount of transparency a texture has?

For example, if part of the mesh has opacity 1, and the other part has opacity 0.5, we would want one part of the shadow to full strength (no light goes through at places with opacity 1), and part of the shadow to be half strength (some light goes through at places with opacity 0.5).