Skip to content
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

Underwater Fog Input #867

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
79 changes: 79 additions & 0 deletions crest/Assets/Crest/Crest/Scripts/Helpers/LightProbeUtility.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Taken from:
// https://github.com/keijiro/LightProbeUtility/blob/85c93577338e10a52dd53f263056de08d883337a/Assets/LightProbeUtility.cs

namespace Crest
{
using UnityEngine;
using UnityEngine.Rendering;

public static class LightProbeUtility
{
// Set SH coefficients to MaterialPropertyBlock
public static void SetSHCoefficients(
Vector3 position, MaterialPropertyBlock properties
)
{
SphericalHarmonicsL2 sh;
LightProbes.GetInterpolatedProbe(position, null, out sh);

// Constant + Linear
for (var i = 0; i < 3; i++)
properties.SetVector(_idSHA[i], new Vector4(
sh[i, 3], sh[i, 1], sh[i, 2], sh[i, 0] - sh[i, 6]
));

// Quadratic polynomials
for (var i = 0; i < 3; i++)
properties.SetVector(_idSHB[i], new Vector4(
sh[i, 4], sh[i, 6], sh[i, 5] * 3, sh[i, 7]
));

// Final quadratic polynomial
properties.SetVector(_idSHC, new Vector4(
sh[0, 8], sh[2, 8], sh[1, 8], 1
));
}

// Set SH coefficients to Material
public static void SetSHCoefficients(
Vector3 position, Material material
)
{
SphericalHarmonicsL2 sh;
LightProbes.GetInterpolatedProbe(position, null, out sh);

// Constant + Linear
for (var i = 0; i < 3; i++)
material.SetVector(_idSHA[i], new Vector4(
sh[i, 3], sh[i, 1], sh[i, 2], sh[i, 0] - sh[i, 6]
));

// Quadratic polynomials
for (var i = 0; i < 3; i++)
material.SetVector(_idSHB[i], new Vector4(
sh[i, 4], sh[i, 6], sh[i, 5] * 3, sh[i, 7]
));

// Final quadratic polynomial
material.SetVector(_idSHC, new Vector4(
sh[0, 8], sh[2, 8], sh[1, 8], 1
));
}

static int[] _idSHA = {
Shader.PropertyToID("unity_SHAr"),
Shader.PropertyToID("unity_SHAg"),
Shader.PropertyToID("unity_SHAb")
};

static int[] _idSHB = {
Shader.PropertyToID("unity_SHBr"),
Shader.PropertyToID("unity_SHBg"),
Shader.PropertyToID("unity_SHBb")
};

static int _idSHC =
Shader.PropertyToID("unity_SHC");
}
}

12 changes: 12 additions & 0 deletions crest/Assets/Crest/Crest/Scripts/Helpers/LightProbeUtility.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Crest Ocean System

// This file is subject to the MIT License as seen in the root of this folder structure (LICENSE)

namespace Crest
{
using System.Collections.Generic;
using UnityEngine;

[AddComponentMenu(Internal.Constants.MENU_PREFIX_SCRIPTS + "Apply Underwater Fog To Transparent")]
public class ApplyUnderwaterFogToTransparent : MonoBehaviour
{
[Tooltip("If enabled, the depth fog will be correctly blended with the object's color at the performance cost of an extra texture copy/blit.")]
[SerializeField]
internal bool _highQuality;
public static readonly List<ApplyUnderwaterFogToTransparent> s_Renderers = new List<ApplyUnderwaterFogToTransparent>();
internal Renderer _renderer;

[Tooltip("The shader pass to use when rendering this object.")]
[SerializeField]
internal int _shaderPass;

[Tooltip("The property name of the main texture which will be used for alpha blending.")]
[SerializeField]
internal string _textureProperty = "_MainTex";

internal string _texturePropertyST;

bool _isEnabled;
public bool IsEnabled => _isEnabled;

void OnEnable()
{
if (TryGetComponent(out _renderer) && !s_Renderers.Contains(this))
{
// If the shader has other passes (like shadows) then this will stop them from working.
_isEnabled = _renderer.enabled;
_texturePropertyST = $"{_textureProperty}_ST";
s_Renderers.Add(this);
}
}

void LateUpdate()
{
// Quick and dirty optimisation.
var maxWaterHeight = OceanRenderer.Instance.SeaLevel + OceanRenderer.Instance.MaxVertDisplacement;
// TODO: Throws exceptions when renderer is disabled for ParticleSystem.
// TODO: Probably a better to check this.
_isEnabled = _renderer.enabled && !(_renderer.bounds.ClosestPoint(transform.position + Vector3.down * 10000f).y > maxWaterHeight);
}

void OnDisable()
{
if (_renderer != null && s_Renderers.Contains(this))
{
_isEnabled = false;
s_Renderers.Remove(this);
}
}
}
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Crest
{
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
Expand All @@ -25,20 +26,36 @@ public partial class UnderwaterRenderer
CommandBuffer _underwaterEffectCommandBuffer;
PropertyWrapperMaterial _underwaterEffectMaterial;
internal readonly UnderwaterSphericalHarmonicsData _sphericalHarmonicsData = new UnderwaterSphericalHarmonicsData();
MaterialPropertyBlock _materialPropertyBlock;

internal class UnderwaterSphericalHarmonicsData
{
internal Color[] _ambientLighting = new Color[1];
internal Vector3[] _shDirections = { new Vector3(0.0f, 0.0f, 0.0f) };
}

CrestSortedList<float, ApplyUnderwaterFogToTransparent> _registry = new CrestSortedList<float, ApplyUnderwaterFogToTransparent>(new TransparentRenderOrderComparer());

internal class TransparentRenderOrderComparer : IComparer<float>
{
int IComparer<float>.Compare(float x, float y)
{
return x.CompareTo(y) * -1;
}
}

void SetupUnderwaterEffect()
{
if (_underwaterEffectMaterial?.material == null)
{
_underwaterEffectMaterial = new PropertyWrapperMaterial(SHADER_UNDERWATER_EFFECT);
}

if (_materialPropertyBlock == null)
{
_materialPropertyBlock = new MaterialPropertyBlock();
}

if (_underwaterEffectCommandBuffer == null)
{
_underwaterEffectCommandBuffer = new CommandBuffer()
Expand Down Expand Up @@ -83,24 +100,94 @@ void OnPreRenderUnderwaterEffect()

_underwaterEffectCommandBuffer.Clear();

if (_camera.allowMSAA)
CopyTexture(_underwaterEffectCommandBuffer, temporaryColorBuffer, _camera);

_underwaterEffectMaterial.SetTexture(sp_CrestCameraColorTexture, temporaryColorBuffer);

_underwaterEffectCommandBuffer.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
_underwaterEffectCommandBuffer.DrawProcedural(Matrix4x4.identity, _underwaterEffectMaterial.material,
shaderPass: 0, MeshTopology.Triangles, vertexCount: 3, instanceCount: 1);

// NOTE: Sorting Transparent Objects Manually
// We are sorting manually but Unity might provide a way as we still need to take TransparencySortMode
// into account.

// Add renderers if within frustum and sort transparency as Unity does.
_registry.Clear();
foreach (var input in ApplyUnderwaterFogToTransparent.s_Renderers)
{
// Disabled renderer means we control the rendering.
if (input.IsEnabled && GeometryUtility.TestPlanesAABB(_cameraFrustumPlanes, input._renderer.bounds))
{
_registry.Add(Vector3.Distance(_camera.transform.position, input.transform.position), input);
}
}

// Enable probe sampling.
_underwaterEffectCommandBuffer.EnableShaderKeyword("LIGHTPROBE_SH");

foreach (var registered in _registry)
{
var input = registered.Value;
var renderer = input._renderer;

renderer.GetPropertyBlock(_materialPropertyBlock);

// Set _MainTex so we can get the alpha channel for blending.
var texture = renderer.sharedMaterial.HasProperty(input._textureProperty) ? renderer.sharedMaterial.GetTexture(input._textureProperty) : null;
if (texture != null)
{
_materialPropertyBlock.SetTexture(input._textureProperty, renderer.sharedMaterial.GetTexture(input._textureProperty));
_materialPropertyBlock.SetVector(input._texturePropertyST, renderer.sharedMaterial.GetVector(input._texturePropertyST));
}
else
{
_materialPropertyBlock.SetTexture(input._textureProperty, Texture2D.whiteTexture);
}

// Add missing probe data.
// LightProbeUtility.SetSHCoefficients(renderer.gameObject.transform.position, _materialPropertyBlock);
renderer.SetPropertyBlock(_materialPropertyBlock);

if (input._highQuality)
{
// Render into temporary render texture so the effect shader will have colour to work with. I could not
// work out how to use GPU blending to apply the underwater fog correctly.
CopyTexture(_underwaterEffectCommandBuffer, temporaryColorBuffer, _camera);
_underwaterEffectCommandBuffer.SetRenderTarget(temporaryColorBuffer, 0, CubemapFace.Unknown, -1);
}
else
{
_underwaterEffectCommandBuffer.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
}

_underwaterEffectCommandBuffer.DrawRenderer(renderer, renderer.sharedMaterial, submeshIndex: 0, shaderPass: input._shaderPass);

int shaderPass = input._highQuality ? 2 : 1;

// Render the fog and apply to camera target.
_underwaterEffectCommandBuffer.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
_underwaterEffectCommandBuffer.DrawRenderer(renderer, _underwaterEffectMaterial.material, submeshIndex: 0, shaderPass);
}

_underwaterEffectCommandBuffer.DisableShaderKeyword("LIGHTPROBE_SH");

RenderTexture.ReleaseTemporary(temporaryColorBuffer);
}

static void CopyTexture(CommandBuffer buffer, RenderTexture texture, Camera camera)
{
if (camera.allowMSAA)
{
// Use blit if MSAA is active because transparents were not included with CopyTexture.
// Not sure if we need an MSAA resolve? Not sure how to do that...
_underwaterEffectCommandBuffer.Blit(BuiltinRenderTextureType.CameraTarget, temporaryColorBuffer);
buffer.Blit(BuiltinRenderTextureType.CameraTarget, texture);
}
else
{
// Copy the frame buffer as we cannot read/write at the same time. If it causes problems, replace with Blit.
_underwaterEffectCommandBuffer.CopyTexture(BuiltinRenderTextureType.CameraTarget, temporaryColorBuffer);
buffer.CopyTexture(BuiltinRenderTextureType.CameraTarget, texture);
}

_underwaterEffectMaterial.SetTexture(sp_CrestCameraColorTexture, temporaryColorBuffer);

_underwaterEffectCommandBuffer.SetRenderTarget(BuiltinRenderTextureType.CameraTarget, 0, CubemapFace.Unknown, -1);
_underwaterEffectCommandBuffer.DrawProcedural(Matrix4x4.identity, _underwaterEffectMaterial.material, -1, MeshTopology.Triangles, 3, 1);

RenderTexture.ReleaseTemporary(temporaryColorBuffer);
}

internal static void UpdatePostProcessMaterial(
Expand Down
Loading