UNPKG

@react-three/drei

Version:

useful add-ons for react-three-fiber

116 lines (98 loc) 3.28 kB
import * as THREE from 'three'; import { version } from '../helpers/constants.js'; class SpotLightMaterial extends THREE.ShaderMaterial { constructor() { super({ uniforms: { depth: { value: null }, opacity: { value: 1 }, attenuation: { value: 2.5 }, anglePower: { value: 12 }, spotPosition: { value: new THREE.Vector3(0, 0, 0) }, lightColor: { value: new THREE.Color('white') }, cameraNear: { value: 0 }, cameraFar: { value: 1 }, resolution: { value: new THREE.Vector2(0, 0) } }, transparent: true, depthWrite: false, vertexShader: /* glsl */` varying vec3 vNormal; varying float vViewZ; varying float vIntensity; uniform vec3 spotPosition; uniform float attenuation; #include <common> #include <logdepthbuf_pars_vertex> void main() { // compute intensity vNormal = normalize(normalMatrix * normal); vec4 worldPosition = modelMatrix * vec4(position, 1); vec4 viewPosition = viewMatrix * worldPosition; vViewZ = viewPosition.z; vIntensity = 1.0 - saturate(distance(worldPosition.xyz, spotPosition) / attenuation); gl_Position = projectionMatrix * viewPosition; #include <logdepthbuf_vertex> } `, fragmentShader: /* glsl */` varying vec3 vNormal; varying float vViewZ; varying float vIntensity; uniform vec3 lightColor; uniform float anglePower; uniform sampler2D depth; uniform vec2 resolution; uniform float cameraNear; uniform float cameraFar; uniform float opacity; #include <packing> #include <logdepthbuf_pars_fragment> float readDepth(sampler2D depthSampler, vec2 uv) { float fragCoordZ = texture(depthSampler, uv).r; // https://github.com/mrdoob/three.js/issues/23072 #ifdef USE_LOGDEPTHBUF float viewZ = 1.0 - exp2(fragCoordZ * log(cameraFar + 1.0) / log(2.0)); #else float viewZ = perspectiveDepthToViewZ(fragCoordZ, cameraNear, cameraFar); #endif return viewZ; } void main() { #include <logdepthbuf_fragment> vec3 normal = vec3(vNormal.x, vNormal.y, abs(vNormal.z)); float angleIntensity = pow(dot(normal, vec3(0, 0, 1)), anglePower); float intensity = vIntensity * angleIntensity; // fades when z is close to sampled depth, meaning the cone is intersecting existing geometry bool isSoft = resolution[0] > 0.0 && resolution[1] > 0.0; if (isSoft) { vec2 uv = gl_FragCoord.xy / resolution; intensity *= smoothstep(0.0, 1.0, vViewZ - readDepth(depth, uv)); } gl_FragColor = vec4(lightColor, intensity * opacity); #include <tonemapping_fragment> #include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}> } ` }); } } export { SpotLightMaterial };