Light-shadows
This guide will help you get started with simple light and shadows in TresJS.
We will build a simple scene with three meshes and a plane but only two will have shadows.
Setting up the scene (optional)
We import all the modules that we need, for comfort we can use the orbit-controls from cientos, check here to know how.
Let's put four objects in our scene, one will be the plane that receive shadows, two of them will cast shadows and the last one will not cast any shadow at all.
I'm going to use MeshToonMaterial. Simply because we can see the "soft shadow" easily.
<script setup lang="ts">
import { OrbitControls } from '@tresjs/cientos'
import { TresCanvas } from '@tresjs/core'
</script>
<template>
<TresCanvas
clear-color="#111"
window-size
>
<OrbitControls />
<TresPerspectiveCamera :position="[5, 7.5, 7.5]" />
<TresMesh
:position="[-2, 2, 0]"
:rotation="[0, Math.PI, 0]"
>
<TresConeGeometry :args="[1, 1.5, 3]" />
<TresMeshToonMaterial color="#82DBC5" />
</TresMesh>
<TresMesh
:position="[0, 0, 0]"
>
<TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
<TresMeshToonMaterial color="#4F4F4F" />
</TresMesh>
<TresMesh
:position="[2, -2, 0]"
>
<TresSphereGeometry />
<TresMeshToonMaterial color="#FBB03B" />
</TresMesh>
<TresMesh
:position="[0, -3, 0]"
:rotation="[-Math.PI / 2, 0, 0]"
>
<TresPlaneGeometry :args="[10, 10, 10, 10]" />
<TresMeshStandardMaterial color="#f7f7f7" />
</TresMesh>
</TresCanvas>
</template>
Lights (explanation)
As you know every instance in ThreeJs is available in TresJs so are all the light types, we just need to add the Tres
prefix to use them.
But not all lights can cast shadows, this definition comes directly from ThreeJs and makes sense, for example the purpose of an ambientLight is to iluminate everysingle side of your scene, so it makes no sense for it to cast shadows, on the contrary, a DirectionalLight immitating the sun can and should cast shadows.
Shadows (explanation)
There are also many types of shadows, for example the "soft shadow" is generated automatially when an object receives more light from one side, but in summary a "ThreeJS default shadow" that is directed towards another surface needs to be cast by a mesh and another mesh needs to receive it. As we see in our example, the Plane
is receiving a shadow but not casting it. Please note that not all materials can cast or receive shadows.
Internally, ThreeJS automatically generates a new mesh with a ShadowMaterial which gets updated in each frame, that is why if you apply animations, the shadow also is animated, but also why you have to use shadows carefully, because they could slow your performance down.
WARNING
The overuse of shadows in this way could drop your performance. However, there are ways to increase your performance, for more information please check out this video
Enabling shadows
We could divide this into three steps:
Activate shadows on the renderer
//...
<template>
<TresCanvas
clear-color="#111"
shadows
window-size
/>
//...
</template>
Set the light to cast shadows
We can simply add the boolean cast-shadow
, Vue understands this as a prop
with a value of true
.
The AmbientLight doesn't generate any type of shadow here
//...
<template>
<TresAmbientLight :intensity="1" />
<TresDirectionalLight
cast-shadow
:position="[0, 2, 0]"
:intensity="1"
/>
//...
</template>
Set the objects to cast or receive shadows
Similarly to the previous step, we set the mesh that we want to cast shadow (our sphere) with the cast-shadow
prop, and set the object to receive shadow (our plane) with the receive-shadow
prop.
//...
<template>
<TresMesh
cast-shadow
:position="[2, -2, 0]"
>
<TresSphereGeometry />
<TresMeshToonMaterial color="#FBB03B" />
</TresMesh>
<TresMesh
receive-shadow
:position="[0, -3, 0]"
:rotation="[-Math.PI / 2, 0, 0]"
>
<TresPlaneGeometry :args="[10, 10, 10, 10]" />
<TresMeshStandardMaterial color="#f7f7f7" />
</TresMesh>
//...
</template>
Now we have all the necessary steps to add shadows to our scene, and if we apply what we learned in basic animations, and we add movement to our cube, you will see the shadow is animated as well. 🤩
<script setup>
import { TresCanvas, useRenderLoop } from '@tresjs/core'
import { shallowRef } from 'vue'
const boxRef = shallowRef()
const { onLoop } = useRenderLoop()
onLoop(() => {
if (boxRef.value) {
boxRef.value.rotation.y += 0.01
}
})
</script>
<template>
//...
<TresMesh
ref="boxRef"
cast-shadow
:position="[0, 0, 0]"
>
<TresBoxGeometry :args="[1.5, 1.5, 1.5]" />
<TresMeshToonMaterial color="#4F4F4F" />
</TresMesh>
//...
</template>
Note that I intentionally did not apply cast-shadow
to the Cone
so it doesn't cast any shadow