Fancy, lightweight, drop-in day-night sky component for A-Frame. Confirmed working with A-Frame 1.4.1.
Utilizes a-sun-sky and aframe-star-system for A-Frame <1.2.0 compatible stars; Also borrowed heavily from aframe-environment-component's "starry" preset, a static scene, that I used for the 1.2.0 stars and as a starting point to extrapolate fog color to light color. Beyond those, this library adds controls to enable and control animation, fog to create a more appealing (imo) star fade in/out effect, stronger sunset effect, and also adds a directional light entity that roughly tracks the sun/moon to allow shining a directional shadow-casting light source from the shader's "sun". Significant effort has been spent creating smooth transitions between all of these moving parts to create a coherent environment.
- highly performant out of the box even with maximum settings enabled, and auto-throttles self if sky is slow enough to allow.
- sunrise, daytime, and sunset feature beautiful rayleigh scattering colors in the sky
fog
creates feeling of creeping shadowy darkness after sunset, that then retreats as stars slowly fade into view and light the scene, without the cost of the equivalent shadows- subtle 'colored' fog and lighting on the horizon adds a sense of depth and realism to the day and night's different stages
- moon also rises and sets, creating a blue rayleigh glow in the sky
- at dawn, stars gently fade out, sky stars to go from soft blues to pre-sawn reds, and then fog again comes in just to create a feeling of shadows retreating as sun rises
- now with real time shadow-casting lighting from the sun and moon!
- intensity-matching hemisphere light for a natural/appropriate ambient lighting to match the directional lighting
- all light sources can be enabled/disabled/adjusted, and have sane, natural feeling defaults (e.g., softer light from stars, soft light from moon)
- pause, play, speed up, slow down all possible
- calculate sky to align with any fixed epoch time, thereby making multi-user sync'd skies simple, as well as in-world consistency over many sessions
- play with live functioning code on glitch (as a page)
- this repo's demo html: index.html in this repo live
- runs easily in oculus quest 2's native browser. this even runs smoothly in google cardboard, it seems.
- to improve performance, set
orbitduration
as high as you are willing or want to, and thenthrottle
as high as you can tolerate` before stuttering occurs. - you can remove light sources and remove or reduce the shadow-casting sun to further lighten the load.
- you could also just use this statically instead of dynamically--when doing so, you can also pre-bake shadows for the environment, and only live calc shadows for e.g. your player's character, etc.
- Tested working with 1.0.4, 1.1.0, 1.2.0, 1.3.0.
add sources to project:
add to sources:
<script src="https://cdn.jsdelivr.net/npm/[email protected]/super-sky.js"></script>
add to the above:
<script src="https://cdn.jsdelivr.net/gh/matthewbryancurtis/aframe-star-system-component@db4f1030/index.js"></script>
then add a super-sky entity to your scene:
(it's recommended that you set sunbeamTarget
to the selector that matches your user's camera)
<a-super-sky
orbitduration="1"
groundcolor="#7BC8A4"
></a-super-sky>
if using the very popular aframe environment component, make sure you tell it to not set a sky:
<a-entity environment="skyType:none; lighting:none; "></a-entity>
orbitduration
is how long 1 sun loop takes (in minutes). By default, a full day is twice this length. (ifmooncycle="false"
, it's 1x this length.)- optional: set
throttle
as high as you can before it starts looking choppy. By default, it'll target 40 updates per second (throttle=25), but will proportionately auto-tune down from that iforbitduration
starts to be longer than10
(because that's when you stop needing 40 calcs per second to still get enough frames per arc angle in the sky for it to look smooth). You can setsuper-sky-debug="true"
to see logging info about the auto-tune throttle state, or rundocument.querySelector('[super-sky]').components['super-sky'].throttle
in your console. Net result: the longer your day, the lighter the computational load, and it'll always look smooth. groundcolor
should be set manually, and is used to calculate more realistic light color.- see super-sky.js schema for other options. comments explain their use.
- see glitch demo or repo index.html (referenced above in this readme) for live examples if unclear.
targetfpd="0"
will mean the scene will freeze and not run any update calculationsstartpercent=".25"
means you want the scene to be at the 25% of a full day/night cycle, while.99
would be just before sunrise.0
(default) is sunrise itself.49
would be 1% before moon rises,.75
would be 'moonset'. (Ifmooncycle=false
, it's percent of just the sun rotation, instead of sun+moon rotations.)
if you want shadows, add the shadow
component to entities that you want to cast shadows and receive shadows (allow shadows to be casted upon):
<a-sphere shadow="cast:true; receive:true;"></a-sphere>
<a-plane shadow="cast:false; receive:true;"></a-plane>
- Set
orbitduration
to whatever you want. The higher the number, the slower the sky. The slower the sky, the less often calculations need to be done to update it while still looking smooth. - By default will auto-throttles sky calculation frequency according to duration--sets a 40fps cap by default, but will drop below that if 33 frames per degree of motion are being attained, which happens if the
orbitduration
starts to be above11
(unit is in minutes), which would be 20 minutes for a day/night cycle ifmooncycle="true"
. You can see logged output regarding the tuning, throttle, fps, fpd (frames per degree), etc. whensuper-sky-debug="true"
/. - You can manually set the throttle if desired, instead, which sets the minimum milliseconds to wait between calculations. Alternatively, you can set
targetfpd
(target frames per degree) if desired.
I want to assume it's somehow my fault, but for some reason in my tests oldData
is almost always coming in as an empty object, and in general I'm observing very strange behavior when trying to use the 'correct' update()
functionality for A-Frame components? I'll file an issue here, but in the meantime I've just worked around the issue...
document.querySelector('[super-sky]').components['super-sky'].updateOrbitDuration(.1)
document.querySelector('[super-sky]').components['super-sky'].updateOrbitDuration(10000)
document.querySelector('[super-sky]').components['super-sky'].updateOrbitDuration(.1)
Easiest way? Pick hardcoded 'starttime', 'orbitduration' and 'mooncycle' values for your app. Voila, if their browser has correct time, then it'll always be in sync for all users. Use the correct/same values at instantiation.
snap/sync after the fact can be done for now this way:
// comp1
let user1Sky = JSON.stringify(document.querySelector('[super-sky]').components['super-sky'].shareSky())
// "{\"mooncycle\":true,\"orbitduration\":1.1,\"starttime\":1628045331326}"
// comp2
let user1Sky = JSON.parse(
"{\"mooncycle\":true,\"orbitduration\":1.1,\"starttime\":1628045331326}"
);
document.querySelector('[super-sky]').components['super-sky'].data.mooncycle = user1Sky.mooncycle
document.querySelector('[super-sky]').components['super-sky'].updateOrbitDuration(user1Sky.orbitduration)
document.querySelector('[super-sky]').components['super-sky'].data.starttime = user1Sky.starttime
document.querySelector('[super-sky]').components['super-sky'].updateSkyEpoch()
Add 'roughness' to the material to reduce shininess. In my demos, for example, I use material="roughness:.633"
on the <a-plane>
that acts as the ground
- Shadows aren't trivial and come with a cost. You can up the shadow render square with the
shadowsize
attribute, which underneath affects thelight.shadowCamera
left/right/top/bottom values. This is how many meters from thesunbeamtarget
you calculate shadows from. Keep in mind that too large and the shadows get lower res... you'll need to start crafting some custom light/shadowCamera values here. - You might also change the
sunbeamtarget
attribute, depending on your scene. This is the center of the invisible box around which shadows are calculated. By default, in this app, it's the user's view (so,[camera]
), and the default is theshadowsize
attribute of 15m (so, shadows are calculated within a 30m box around the user). If you only have things shadows should be cast from in the middle of the map, but want to see them even from far away, setting the target to the center of your map would work. This will make surface reflections during sunrise/sunset be a bit off, though, so probably compensate for that (e.g., add roughness to the appropriate material) to cover that up. - You can consider obscuring the user's field of view so that they can rarely see out in the distance, as well, to prevent this from being an issue. (e.g., high features surround you, instead of wide open vistas).
Add a light source if your scene can handle it, or adjust starlightintensity
to a higher than default (.1) value (like .2) if you want to use fewer lights for higher performance/simplicity.
Look into shadow bias, and if you changed the default values for the sunbeamdistance
, that's probably the cause.
showshadowlight='false'
- make
shadowsize
smaller (default is 15) - make
orbitduration
as high as you are willing to, and then makethrottle
(default 10ms) as high as you can before the updates look choppy. - change
shadowMapHeight
andshadowMapWidth
of thesunbeam
to a lower value (default is 1024; try 512, or even further down to 256). - remove other light sources, and/or reduce the number of items that
cast
andreceive
shadow
.
You can file an issue, I'd be interested to hear. But honestly, you should probably see the docs to dig deeper into this. I'm not an expert.
As per the docs, add material="fog:false;"
to X.
document.querySelector('[super-sky]').components['super-sky'].timeOfDay()
- figure out why A-Frame's
update()
functionality seems completely borked? Have worked around it for now, but mostly updating values in a-frame inspector, for example, won't work. Perhaps related to settingthis.data
directly? - add in features from https://github.com/EX3D/aframe-daylight-system !
- some minor stuff gets out of sync if you leave the tab and come back, or change the cycle duration--specifically, for example, the 'timeOfDay' functionality.
- better method for changing moon rise/set position than a-scene rotation? finishing implementing rotation option
- slightly rotate stars over the course of a night
- enable better control of sun/moon trajectory through sky (allow e.g. lower moon)
- make it so that you can assign functions to be triggered at a given certain time of day.
- add in default example for crickets audio playing at night
- add in default example for shooting stars at night
- currently night is 3x the length of day. This would imitate only northern winters/southern summers that have 8 hours of daylight, e.g. 10am to 6pm. ability to tweak this would be desirable--maybe speeding up or skipping phases when neither sun nor moon
- environment component has ability to put sun on the shader anywhere in the sky (using 3x 0-360 inputs), but this code needs updating to allow that. Doing so would allow moon to not follow exact same path as sun, which would be nice.
- finish implementing existing options, and add some more of them
- try exponential fog instead of linear.