142 lines
4.8 KiB
C#
142 lines
4.8 KiB
C#
using UnityEngine;
|
|
|
|
|
|
namespace CritterVolumetricLighting
|
|
{
|
|
static public class PreComputedCloudBufferHandler
|
|
{
|
|
private static ComputeShader _computeShader;
|
|
private static VolumetricLightManager _vlManager;
|
|
|
|
private static RenderTexture _cloudDataTexture;
|
|
private static RenderTexture _preComputedTexture;
|
|
|
|
private static Material _vLMat;
|
|
private static int _screenHeight, _screenWidth;
|
|
|
|
private static int _mainCSKernel;
|
|
private static int _clearerKernel;
|
|
|
|
private static Vector2Int _preCalcTextBounds;
|
|
|
|
private static int _n; // 'N' is the amount of pixels on Y axis in the precomputed cloud data texture. Each individual pixel in this texture represents a chunk of pixels in the raw cloud data texture. X in this texture is same as in raw texture
|
|
private static int _nSizeInCloudDataTex;
|
|
|
|
private static bool _firstInit = false;
|
|
|
|
public static RenderTexture Init(ref RenderTexture cloudTexture, VolumetricLightManager vlManager)
|
|
{
|
|
_vlManager = vlManager;
|
|
_cloudDataTexture = cloudTexture;
|
|
|
|
if (!_firstInit) {
|
|
_computeShader = Resources.Load<ComputeShader>("CloudTexturePreCalculationCS");
|
|
_mainCSKernel = _computeShader.FindKernel("CSMain");
|
|
_clearerKernel = _computeShader.FindKernel("ClearKernel");
|
|
_firstInit = true;
|
|
}
|
|
|
|
_n = CalculateN(cloudTexture.width, cloudTexture.height);
|
|
_nSizeInCloudDataTex = Mathf.CeilToInt(cloudTexture.height / _n);
|
|
|
|
GetPreCalTextBounds();
|
|
InitPreCalcTexture(vlManager);
|
|
InitCs();
|
|
|
|
RunCS();
|
|
return _preComputedTexture;
|
|
}
|
|
|
|
/**
|
|
Calculates optimal amount of splits for the precomputed cloud data texture that maximizes optimization benefits.
|
|
N needs to be dividable by 8 and the texture width needs to be dividable by N.
|
|
|
|
The final N is as close to the most performatic "ideal N" as possible, while still adhering to the constraints described above.
|
|
*/
|
|
private static int CalculateN(int dataTextureWidth, int dataTextureHeight)
|
|
{
|
|
int nIdeal = VolumetricLightResources.GetIdealN(dataTextureHeight);
|
|
int n = 16;
|
|
bool foundOptimal = false;
|
|
for (int i = 0; i < 8; i++)
|
|
{
|
|
float investigatedN = nIdeal + i * 8;
|
|
if (dataTextureWidth % investigatedN == 0)
|
|
{
|
|
foundOptimal = true;
|
|
n = Mathf.RoundToInt(investigatedN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Because texture width is configured to be compatible with optimization, nearest optimal N should be quite close to ideal N.
|
|
// Exhausting all combinations of screen and data resolutions did not find a single case where optimal was not found.
|
|
// Still... If for some reason optimal is not found, we can try to find a working but slightly suboptimal solution.
|
|
if (!foundOptimal)
|
|
{
|
|
int iterationsToIdealN = Mathf.RoundToInt((nIdeal - 8) / 8);
|
|
bool foundSubOptimalN = false;
|
|
|
|
for (int i = 0; i < iterationsToIdealN; i++)
|
|
{
|
|
float investigatedN = nIdeal - i * 8;
|
|
if (dataTextureWidth % investigatedN == 0)
|
|
{
|
|
foundSubOptimalN = true;
|
|
n = Mathf.RoundToInt(investigatedN);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!foundSubOptimalN)
|
|
{
|
|
Debug.LogWarning("Stylized Volumetric Lighting could not properly optimize the sampling of Clouds with this screen resolution. Artifacts in cloud shadows may occur in some cases.");
|
|
}
|
|
}
|
|
return n;
|
|
}
|
|
|
|
private static void InitPreCalcTexture(VolumetricLightManager vlManager)
|
|
{
|
|
if (_preComputedTexture != null)
|
|
{
|
|
_preComputedTexture.Release();
|
|
_preComputedTexture = null;
|
|
}
|
|
|
|
_preComputedTexture = new RenderTexture(_preCalcTextBounds.x, _preCalcTextBounds.y, 0, RenderTextureFormat.ARGB64);
|
|
_preComputedTexture.wrapMode = TextureWrapMode.Clamp;
|
|
_preComputedTexture.filterMode = FilterMode.Point;
|
|
_preComputedTexture.enableRandomWrite = true;
|
|
_preComputedTexture.Create();
|
|
|
|
_vLMat = vlManager.godrayMat;
|
|
_vLMat.SetFloat("_PreCalcN", _n);
|
|
_vLMat.SetFloat("_PreCalcWorldUnitSizeN", VolumetricLightResources.GetPlaneHeightWorldUnits() / _n);
|
|
_vLMat.SetTexture("_PreComputedTexture", _preComputedTexture);
|
|
}
|
|
|
|
private static void GetPreCalTextBounds()
|
|
{
|
|
int textureHeight = _n;
|
|
int textureWidth = _vlManager.cloudDataTexture.width;
|
|
_preCalcTextBounds = new Vector2Int(textureWidth, textureHeight);
|
|
}
|
|
|
|
|
|
private static void InitCs()
|
|
{
|
|
_computeShader.SetTexture(_clearerKernel, "_PreCalcTexture", _preComputedTexture);
|
|
_computeShader.SetTexture(_mainCSKernel, "_PreCalcTexture", _preComputedTexture);
|
|
_computeShader.SetTexture(_mainCSKernel, "_CloudShadowMap", _cloudDataTexture);
|
|
_computeShader.SetFloat("_nSizeInCloudDataTex", _nSizeInCloudDataTex);
|
|
}
|
|
|
|
|
|
public static void RunCS()
|
|
{
|
|
_computeShader.Dispatch(_clearerKernel, _preCalcTextBounds.x / 8, _preCalcTextBounds.y / 8, 1);
|
|
_computeShader.Dispatch(_mainCSKernel, _preCalcTextBounds.x / 8, _preCalcTextBounds.y / 8, 1);
|
|
}
|
|
}
|
|
} |