0
Лайки
0
Скачивания
0
Комментарии
Описание
OVERVIEW

Horizon Fix is an open-source SKSE plugin that improves the look of the horizon by adding additional water far past the vanilla map to give the illusion of an infinite horizon.
Note that when the term "horizon fix" has been thrown around in the Skyrim modding community it usually refers to the sky blending into the horizon. In real life, however, the sky usually does not blend into the horizon in clear weather. Most previous "horizon fix" attempts tried to blend it because if you didn't it looked like the above before screenshot which is, well, not good. Adding any sort of blending is out of scope of this mod. Weather mod authors, however, are free to continue to do so in conjunction with this mod if that is a desired aesthetic.
Horizon fix is feature complete and is ready for wider testing, so this release will be a beta. If you enjoy this mod please remember to endorse, and if you have a GitHub account I would appreciate a star on the repo!
PERFORMANCE
While I have not done any formal benchmarks at this point, note that this mod will add somewhere on the order of hundreds of water draw calls which will affect your performance if you are CPU-bound (which often is the case with modded Skyrim). In my testing from a worst case scenario it jumped from ~40 water draw calls to ~600. You can reduce this exponentially by reducing the iWaterSkirtRimQuality option in the config. Each step reduced will roughly half the draw calls, just note that you will lose smoothness quality on the horizon line by doing this.
COMPATIBILITY
Game version 1.6.1170 is supported. Versions 1.5.97 and 1.4.15.0 (VR) are offered at a "best effort" capacity as time permits.
Compatible with every mod (Including CS and ENB). Feel free to make a post if you find this to not be the case and I can look into it.
TECHNICAL DETAILS
Building the Water Skirt
The water skirt is what you see as LOD water extending the horizon, this is the most important part of the mod. Building this takes several steps:
Initial Checks - Worlds flagged small world are skipped by default unless manually enabled in the INI. In addition, the blocklist is checked from the INI to see if any worldspaces should be ignored.
Template Discovery - The plugin walks the scenegraph under TES::objLODWaterRoot to try to find a suitable "donor" tile from the vanilla map. Every BSTriShape is scored based on how many vertices it has. The one with the LEAST amount of vertices is chosen as the donor tile. This is to avoid cloning a tile that has extra geometry in it. In the case of a tie-breaker the one with the largest world bound is used.
Layout and Cloning - Now, the skirt will start being build. Each tile is cloned from the donor tile selected in step 1. The tiles are attempted to be placed by radius selected from fWaterSkirtRadius in the config, skipping the immediate LOD32-sized tile the player is in. Each candidate is checked to see if it is fully outside the calculated circle. If it is, it is dropped. If it is halfway between the inside and the outside, it is split up into 4 smaller tiles, then this step is repeated recursively N times, where N is iWaterSkirtRimQuality from the config. This turns the skirt's silhouette into a smooth approximate circle that appears at a circle when looking at it from the map.
Placement - Now that the layout is established and tiles clones, they must be placed. The height of each tile is the same height as the map's ocean (NAM4 water height). All tiles in the layout are placed under the NiNode HSF_WaterSkirtRoot. If bWaterSkirtDrawLast is 1 they are then attached to the scene-graph under TES::lodLandRoot which ensures they are drawn after the game's own LOD water which is important for some ENBSeries effects. Tiles are not placed in areas that already have water or land (the original game world). For areas that overlap game world and the outside, the tiles are subdivided.
Lifecycle - The skirt is rebuilt upon cell detatch events (when player moves or loads into a new cell), as well as when the game initially loads. There is also pathing to ensure the skirt stops rendering when indoors. When updating outside tiles are simply re-placed, the layout is not calculated again.
Water Skirt Culling
Because of the game's default far clip, those placed tiles would ordinarily just be culled. Initial versions of this plugin modified the game's far clip to avoid this. However, this broke some vanilla effects and some effects in CS/ENB so a different approach was needed. Instead, each tile in the water skirt gets its model bound radius set to 1e9, which makes it intersect with every possible frustrum, in addition to the kAlwaysDraw flag. This results in the engine NEVER culling these tiles. But that is also a problem, because now we have to render every tile every frame.
To solve this, a custom culling algorithm was implemented. By hooking SkyObject::Update on Atmosphere, the 6 frustrum planes from NiCamera::world and viewFrustrum are reconstructed every event fire. In addition, each tile's bounding sphere is shrunk along the camera tile ray onto a sphere at half the distance to far plane. Because angular extent is preserved, the four side-planes still cull off-screen tiles correctly, while the ones in view remain.
Water Skirt Depth
Despite all of the above, the tiles STILL will not be rendered. This is because the rasterizer clips fragments with z height beyond the viewport depth range. Therefore, we also hook into BSWaterShader::SetupGeometry and BSWaterShader::RestoreGeometry. When these hooks come across a water skirt tile, they rebind the viewport with maxDepth = 1 - 8/2^24. That constant is 8 quanta of a 24-bit depth buffer below the far plane which is always closer than the 1.0 value. That allows these tiles to WIN the depth test in the empty horizon gap, but LOSE everywhere real geometry exists. This is why tiles can be drawn on the real map and it's not an issue.
ACKNOWLEDGEMENTS AND LINKS
Thanks to alandtse for VR support!
Thanks to po3 and the RE community for their work on commonlib!
Thanks to Community Shaders devs for working with me on some of this.
Thanks to Boris (ENBSeries Author) for working with me to resolve issues this mod had with ENB.