2022-07-13 04:15:20 +01:00
using System ;
using System.Linq ;
using System.Collections ;
using System.Collections.Generic ;
using UnityEngine ;
using UnityEngine.Rendering ;
/// <summary>
/// Based on github.com/yangrc1234/VolumeCloud
/// Generate halton sequence.
/// code from unity post-processing stack.
/// </summary>
2022-07-14 05:21:16 +01:00
public class HaltonSequence
{
2022-07-13 04:15:20 +01:00
public int radix = 3 ;
private int storedIndex = 0 ;
public float Get ( ) {
float result = 0f ;
float fraction = 1f / ( float ) radix ;
int index = storedIndex ;
while ( index > 0 ) {
result + = ( float ) ( index % radix ) * fraction ;
index / = radix ;
fraction / = ( float ) radix ;
}
storedIndex + + ;
return result ;
}
}
/// <summary>
/// Cloud renderer post processing.
/// </summary>
2022-07-16 23:14:25 +01:00
[ImageEffectAllowedInSceneView]
2022-07-13 04:15:20 +01:00
[ExecuteInEditMode,RequireComponent(typeof(Camera))]
[ImageEffectOpaque]
2022-07-14 05:21:16 +01:00
public class VolumeCloudRenderer : EffectBase
{
2022-07-13 04:15:20 +01:00
[Header("Render Settings")]
[Range(0, 2)]
public int downSample = 1 ;
public Quality quality ;
private Material mat ;
private RenderTexture [ ] fullBuffer ;
private int fullBufferIndex ;
private RenderTexture undersampleBuffer ;
private Matrix4x4 prevV ;
private Camera mcam ;
private HaltonSequence sequence = new HaltonSequence ( ) { radix = 3 } ;
// The index of 4x4 pixels.
private int frameIndex = 0 ;
private bool firstFrame = true ;
[SerializeField]
private Shader cloudShader ;
void EnsureMaterial ( bool force = false ) {
if ( mat = = null | | force ) {
mat = new Material ( cloudShader ) ;
}
}
private void OnDestroy ( ) {
if ( this . fullBuffer ! = null ) {
for ( int i = 0 ; i < fullBuffer . Length ; i + + ) {
fullBuffer [ i ] . Release ( ) ;
fullBuffer [ i ] = null ;
}
}
if ( this . undersampleBuffer ! = null ) {
this . undersampleBuffer . Release ( ) ;
this . undersampleBuffer = null ;
}
}
private void Start ( ) {
this . EnsureMaterial ( true ) ;
2022-07-18 08:44:54 +01:00
if ( Application . isPlaying )
quality = ActionGameManager . PlayerSettings . GraphicsSettings . NebulaQuality ;
2022-07-13 04:15:20 +01:00
}
[ImageEffectOpaque]
private void OnRenderImage ( RenderTexture source , RenderTexture destination ) {
if ( cloudShader = = null ) {
Graphics . Blit ( source , destination ) ;
return ;
}
mcam = GetComponent < Camera > ( ) ;
var width = mcam . pixelWidth > > downSample ;
var height = mcam . pixelHeight > > downSample ;
this . EnsureMaterial ( ) ;
EnsureArray ( ref fullBuffer , 2 ) ;
firstFrame | = EnsureRenderTarget ( ref fullBuffer [ 0 ] , width , height , RenderTextureFormat . ARGBFloat , FilterMode . Bilinear ) ;
firstFrame | = EnsureRenderTarget ( ref fullBuffer [ 1 ] , width , height , RenderTextureFormat . ARGBFloat , FilterMode . Bilinear ) ;
firstFrame | = EnsureRenderTarget ( ref undersampleBuffer , width , height , RenderTextureFormat . ARGBFloat , FilterMode . Bilinear ) ;
frameIndex = ( frameIndex + 1 ) % 16 ;
fullBufferIndex = ( fullBufferIndex + 1 ) % 2 ;
/* Some code is from playdead TAA. */
2022-07-17 05:27:50 +01:00
//1. Pass1, Render a undersampled buffer. The buffer is dithered using blue noise and halton sequence.
2022-07-13 04:15:20 +01:00
//If it's first frame, force a high quality sample to make the initial history buffer good enough.
if ( firstFrame | | quality = = Quality . Ultra ) {
mat . EnableKeyword ( "ULTRA_QUALITY" ) ;
mat . DisableKeyword ( "HIGH_QUALITY" ) ;
mat . DisableKeyword ( "MEDIUM_QUALITY" ) ;
mat . DisableKeyword ( "LOW_QUALITY" ) ;
} else if ( quality = = Quality . High ) {
mat . DisableKeyword ( "ULTRA_QUALITY" ) ;
mat . EnableKeyword ( "HIGH_QUALITY" ) ;
mat . DisableKeyword ( "MEDIUM_QUALITY" ) ;
mat . DisableKeyword ( "LOW_QUALITY" ) ;
} else if ( quality = = Quality . Normal ) {
mat . DisableKeyword ( "ULTRA_QUALITY" ) ;
mat . DisableKeyword ( "HIGH_QUALITY" ) ;
mat . EnableKeyword ( "MEDIUM_QUALITY" ) ;
mat . DisableKeyword ( "LOW_QUALITY" ) ;
} else if ( quality = = Quality . Low ) {
mat . DisableKeyword ( "ULTRA_QUALITY" ) ;
mat . DisableKeyword ( "HIGH_QUALITY" ) ;
mat . DisableKeyword ( "MEDIUM_QUALITY" ) ;
mat . EnableKeyword ( "LOW_QUALITY" ) ;
}
2022-07-16 23:14:25 +01:00
mat . SetMatrix ( "_CamInvProj" , ( Camera . current . projectionMatrix * Camera . current . worldToCameraMatrix ) . inverse ) ;
2022-07-13 04:15:20 +01:00
mat . SetVector ( "_ProjectionExtents" , mcam . GetProjectionExtents ( ) ) ;
mat . SetFloat ( "_RaymarchOffset" , sequence . Get ( ) ) ;
mat . SetVector ( "_TexelSize" , undersampleBuffer . texelSize ) ;
Graphics . Blit ( null , undersampleBuffer , mat , 0 ) ;
//2. Pass 2, blend undersampled image with history buffer to new buffer.
mat . SetTexture ( "_UndersampleCloudTex" , undersampleBuffer ) ;
mat . SetMatrix ( "_PrevVP" , GL . GetGPUProjectionMatrix ( mcam . projectionMatrix , false ) * prevV ) ;
mat . SetVector ( "_ProjectionExtents" , mcam . GetProjectionExtents ( ) ) ;
if ( firstFrame ) { //Wait, is this the first frame? If it is, the history buffer is empty yet. It will cause glitch if we use it directly. Fill it using the undersample buffer.
Graphics . Blit ( undersampleBuffer , fullBuffer [ fullBufferIndex ] ) ;
}
Graphics . Blit ( fullBuffer [ fullBufferIndex ] , fullBuffer [ fullBufferIndex ^ 1 ] , mat , 1 ) ;
//3. Pass3, Calculate lighting, blend final cloud image with final image.
mat . SetTexture ( "_CloudTex" , fullBuffer [ fullBufferIndex ^ 1 ] ) ;
Graphics . Blit ( source , destination , mat , 2 ) ;
//4. Cleanup
prevV = mcam . worldToCameraMatrix ;
firstFrame = false ;
}
}