-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow directional lights to automatically follow shadows #5025
Changes from 3 commits
8351215
23d1ffc
fa13819
9584a46
c3177a0
930d269
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head> | ||
<meta charset="utf-8"> | ||
<title>Hello, World! • A-Frame</title> | ||
<meta name="description" content="Hello, World! • A-Frame"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
<script src="../../../dist/aframe-master.js"></script> | ||
<script> | ||
AFRAME.registerComponent('follow-shadow', { | ||
schema: {type: 'selector'}, | ||
init() {this.el.object3D.renderOrder = -1;}, | ||
tick() { | ||
if (this.data) { | ||
this.el.object3D.position.copy(this.data.object3D.position); | ||
this.el.object3D.position.y-=0.001; // stop z-fighting | ||
} | ||
} | ||
}); | ||
</script> | ||
</head> | ||
<body> | ||
<a-scene | ||
reflection="directionalLight:a-light[type=directional]" | ||
ar-hit-test="target:#objects;" | ||
renderer="physicallyCorrectLights:true;colorManagement:true;exposure:1;toneMapping:ACESFilmic;" | ||
shadow="type:pcfsoft" | ||
> | ||
<a-light type="directional" light="castShadow:true;" position="1 1 1" intensity="0.5" shadow-camera-automatic="#objects"></a-light> | ||
<a-camera position="0 0.4 0" wasd-controls="acceleration:10;"></a-camera> | ||
<a-entity id="objects" scale="0.2 0.2 0.2" position="0 0 -1" shadow> | ||
<a-box position="-1 0.5 1" rotation="0 45 0" color="#4CC3D9"></a-box> | ||
<a-sphere position="0 1.25 -1" radius="1.25" color="#EF2D5E"></a-sphere> | ||
<a-cylinder position="1 0.75 1" radius="0.5" height="1.5" color="#FFC65D"></a-cylinder> | ||
</a-entity> | ||
<a-plane follow-shadow="#objects" material="shader:shadow" shadow="cast:false;" rotation="-90 0 0" width="2" height="2"></a-plane> | ||
<a-sky color="#ECECEC" hide-on-enter-ar></a-sky> | ||
</a-scene> | ||
</body> | ||
</html> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,23 @@ var CubeLoader = new THREE.CubeTextureLoader(); | |
|
||
var probeCache = {}; | ||
|
||
function distanceOfPointFromPlane (positionOnPlane, planeNormal, p1) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe factor out into utils/math.js to keep this tidier. |
||
// the d value in the plane equation a*x + b*y + c*z=d | ||
var d = planeNormal.dot(positionOnPlane); | ||
|
||
// distance of point from plane | ||
return (d - planeNormal.dot(p1)) / planeNormal.length(); | ||
} | ||
|
||
function nearestPointInPlane (positionOnPlane, planeNormal, p1, out) { | ||
var t = distanceOfPointFromPlane(positionOnPlane, planeNormal, p1); | ||
// closest point on the plane | ||
out.copy(planeNormal); | ||
out.multiplyScalar(t); | ||
out.add(p1); | ||
return out; | ||
} | ||
|
||
/** | ||
* Light component. | ||
*/ | ||
|
@@ -42,6 +59,7 @@ module.exports.Component = registerComponent('light', { | |
shadowCameraBottom: {default: -5, if: {castShadow: true}}, | ||
shadowCameraLeft: {default: -5, if: {castShadow: true}}, | ||
shadowCameraVisible: {default: false, if: {castShadow: true}}, | ||
shadowCameraAutomatic: {default: '', if: {type: ['directional']}}, | ||
shadowMapHeight: {default: 512, if: {castShadow: true}}, | ||
shadowMapWidth: {default: 512, if: {castShadow: true}}, | ||
shadowRadius: {default: 1, if: {castShadow: true}} | ||
|
@@ -111,7 +129,7 @@ module.exports.Component = registerComponent('light', { | |
} | ||
|
||
case 'envMap': | ||
this.updateProbeMap(data, light); | ||
self.updateProbeMap(data, light); | ||
break; | ||
|
||
case 'castShadow': | ||
|
@@ -133,6 +151,14 @@ module.exports.Component = registerComponent('light', { | |
} | ||
break; | ||
|
||
case 'shadowCameraAutomatic': | ||
if (data.shadowCameraAutomatic) { | ||
self.shadowCameraAutomaticEls = Array.from(document.querySelectorAll(data.shadowCameraAutomatic)); | ||
} else { | ||
self.shadowCameraAutomaticEls = []; | ||
} | ||
break; | ||
|
||
default: { | ||
light[key] = value; | ||
} | ||
|
@@ -146,6 +172,50 @@ module.exports.Component = registerComponent('light', { | |
this.updateShadow(); | ||
}, | ||
|
||
tick: (function tickSetup () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to name this function |
||
var bbox = new THREE.Box3(); | ||
var normal = new THREE.Vector3(); | ||
var cameraWorldPosition = new THREE.Vector3(); | ||
var tempMat = new THREE.Matrix4(); | ||
var sphere = new THREE.Sphere(); | ||
var tempVector = new THREE.Vector3(); | ||
|
||
return function tick () { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. no need to name this function |
||
if ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. anyway to simplify this condition and do an early return for readability?
|
||
this.data.type === 'directional' && | ||
this.light.shadow && | ||
this.light.shadow.camera instanceof THREE.OrthographicCamera && | ||
this.shadowCameraAutomaticEls.length | ||
) { | ||
var camera = this.light.shadow.camera; | ||
camera.getWorldDirection(normal); | ||
camera.getWorldPosition(cameraWorldPosition); | ||
tempMat.copy(camera.matrixWorld); | ||
tempMat.invert(); | ||
|
||
camera.near = 1; | ||
camera.left = 100000; | ||
camera.right = -100000; | ||
camera.top = -100000; | ||
camera.bottom = 100000; | ||
this.shadowCameraAutomaticEls.forEach(function (el) { | ||
bbox.setFromObject(el.object3D); | ||
bbox.getBoundingSphere(sphere); | ||
var distanceToPlane = distanceOfPointFromPlane(cameraWorldPosition, normal, sphere.center); | ||
var pointOnCameraPlane = nearestPointInPlane(cameraWorldPosition, normal, sphere.center, tempVector); | ||
|
||
var pointInXYPlane = pointOnCameraPlane.applyMatrix4(tempMat); | ||
camera.near = Math.min(-distanceToPlane - sphere.radius - 1, camera.near); | ||
camera.left = Math.min(-sphere.radius + pointInXYPlane.x, camera.left); | ||
camera.right = Math.max(sphere.radius + pointInXYPlane.x, camera.right); | ||
camera.top = Math.max(sphere.radius + pointInXYPlane.y, camera.top); | ||
camera.bottom = Math.min(-sphere.radius + pointInXYPlane.y, camera.bottom); | ||
}); | ||
camera.updateProjectionMatrix(); | ||
} | ||
}; | ||
}()), | ||
|
||
setLight: function (data) { | ||
var el = this.el; | ||
var newLight = this.getLight(data); | ||
|
@@ -168,6 +238,12 @@ module.exports.Component = registerComponent('light', { | |
el.setObject3D('light-target', this.defaultTarget); | ||
el.getObject3D('light-target').position.set(0, 0, -1); | ||
} | ||
|
||
if (data.shadowCameraAutomatic) { | ||
this.shadowCameraAutomaticEls = Array.from(document.querySelectorAll(data.shadowCameraAutomatic)); | ||
} else { | ||
this.shadowCameraAutomaticEls = []; | ||
} | ||
} | ||
}, | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hello, AR World!
perhaps to differentiate