浏览代码
Merge pull request #80 from Chman/postfx-hack
Merge pull request #80 from Chman/postfx-hack
Temporary post-processing stack for artists/main
GitHub
8 年前
当前提交
c0f37e55
共有 22 个文件被更改,包括 1748 次插入 和 151 次删除
-
8Assets/ScriptableRenderLoop/HDRenderPipeline/Editor/HDRenderPipelineInspector.cs
-
16Assets/ScriptableRenderLoop/HDRenderPipeline/Editor/HDRenderPipelineMenuItems.cs
-
52Assets/ScriptableRenderLoop/HDRenderPipeline/HDRenderPipeline.cs
-
207Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/FinalPass.shader
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Editor.meta
-
257Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/PostProcessing.cs
-
12Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/PostProcessing.cs.meta
-
77Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/ColorGrading.hlsl
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/ColorGrading.hlsl.meta
-
22Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeAdaptation.hlsl
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeAdaptation.hlsl.meta
-
236Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeAdaptation.shader
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeAdaptation.shader.meta
-
59Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeHistogram.compute
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/EyeHistogram.compute.meta
-
68Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/LutGen.shader
-
9Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Resources/LutGen.shader.meta
-
422Assets/Textures/CoolingFilterLUT.exr
-
108Assets/Textures/CoolingFilterLUT.exr.meta
-
289Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Editor/PostProcessingEditor.cs
-
12Assets/ScriptableRenderLoop/HDRenderPipeline/PostProcess/Editor/PostProcessingEditor.cs.meta
|
|||
// Final compositing pass, just does gamma correction for now. |
|||
|
|||
_ToneMapCoeffs1("Parameters for neutral tonemap", Vector) = (0.0, 0.0, 0.0, 0.0) |
|||
_ToneMapCoeffs2("Parameters for neutral tonemap", Vector) = (0.0, 0.0, 0.0, 0.0) |
|||
_Exposure("Exposure", Range(-32.0, 32.0)) = 0 |
|||
[ToggleOff] _EnableToneMap("Enable Tone Map", Float) = 0 |
|||
SubShader { |
|||
Pass { |
|||
ZTest Always Cull Off ZWrite Off |
|||
HLSLINCLUDE |
|||
HLSLPROGRAM |
|||
#pragma vertex Vert |
|||
#pragma fragment Frag |
|||
#pragma target 5.0 |
|||
#pragma target 5.0 |
|||
#include "Common.hlsl" |
|||
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl" |
|||
#include "ColorGrading.hlsl" |
|||
#include "Common.hlsl" |
|||
#include "Color.hlsl" |
|||
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl" |
|||
TEXTURE2D(_MainTex); |
|||
SAMPLER2D(sampler_MainTex); |
|||
TEXTURE2D(_MainTex); |
|||
SAMPLER2D(sampler_MainTex); |
|||
TEXTURE2D(_AutoExposure); |
|||
SAMPLER2D(sampler_AutoExposure); |
|||
float4 _ToneMapCoeffs1; |
|||
float4 _ToneMapCoeffs2; |
|||
TEXTURE2D(_LogLut); |
|||
SAMPLER2D(sampler_LogLut); |
|||
#define InBlack _ToneMapCoeffs1.x |
|||
#define OutBlack _ToneMapCoeffs1.y |
|||
#define InWhite _ToneMapCoeffs1.z |
|||
#define OutWhite _ToneMapCoeffs1.w |
|||
#define WhiteLevel _ToneMapCoeffs2.z |
|||
#define WhiteClip _ToneMapCoeffs2.w |
|||
float4 _LogLut_Params; |
|||
float _Exposure; |
|||
float _EnableToneMap; |
|||
float _Exposure; |
|||
struct Attributes |
|||
{ |
|||
float3 vertex : POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
float4 _NeutralTonemapperParams1; |
|||
float4 _NeutralTonemapperParams2; |
|||
struct Varyings |
|||
{ |
|||
float4 vertex : SV_POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
struct Attributes |
|||
{ |
|||
float3 vertex : POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
Varyings Vert(Attributes input) |
|||
{ |
|||
Varyings output; |
|||
output.vertex = TransformWorldToHClip(input.vertex); |
|||
output.texcoord = input.texcoord.xy; |
|||
return output; |
|||
} |
|||
struct Varyings |
|||
{ |
|||
float4 vertex : SV_POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
float3 evalCurve(float3 x, float A, float B, float C, float D, float E, float F) |
|||
{ |
|||
return ((x*(A*x + C*B) + D*E) / (x*(A*x + B) + D*F)) - E / F; |
|||
} |
|||
Varyings Vert(Attributes input) |
|||
{ |
|||
Varyings output; |
|||
output.vertex = TransformWorldToHClip(input.vertex); |
|||
output.texcoord = input.texcoord.xy; |
|||
return output; |
|||
} |
|||
float3 applyTonemapFilmicAD(float3 linearColor) |
|||
{ |
|||
float blackRatio = InBlack / OutBlack; |
|||
float whiteRatio = InWhite / OutWhite; |
|||
// Neutral tonemapping (Hable/Hejl/Frostbite) |
|||
// Input is linear RGB |
|||
float3 NeutralCurve(float3 x, float a, float b, float c, float d, float e, float f) |
|||
{ |
|||
return ((x * (a * x + c * b) + d * e) / (x * (a * x + b) + d * f)) - e / f; |
|||
} |
|||
|
|||
float3 NeutralTonemap(float3 x, float4 params1, float4 params2) |
|||
{ |
|||
// ACES supports negative color values and WILL output negative values when coming from ACES or ACEScg |
|||
// Make sure negative channels are clamped to 0.0 as this neutral tonemapper can't deal with them properly |
|||
x = max((0.0).xxx, x); |
|||
|
|||
// Tonemap |
|||
float a = params1.x; |
|||
float b = params1.y; |
|||
float c = params1.z; |
|||
float d = params1.w; |
|||
float e = params2.x; |
|||
float f = params2.y; |
|||
float whiteLevel = params2.z; |
|||
float whiteClip = params2.w; |
|||
// blend tunable coefficients |
|||
float B = lerp(0.57, 0.37, blackRatio); |
|||
float C = lerp(0.01, 0.24, whiteRatio); |
|||
float D = lerp(0.02, 0.20, blackRatio); |
|||
float3 whiteScale = (1.0).xxx / NeutralCurve(whiteLevel, a, b, c, d, e, f); |
|||
x = NeutralCurve(x * whiteScale, a, b, c, d, e, f); |
|||
x *= whiteScale; |
|||
// constants |
|||
float A = 0.2; |
|||
float E = 0.02; |
|||
float F = 0.30; |
|||
// Post-curve white point adjustment |
|||
x /= whiteClip.xxx; |
|||
// eval and correct for white point |
|||
float3 whiteScale = 1.0f / evalCurve(WhiteLevel, A, B, C, D, E, F); |
|||
float3 curr = evalCurve(linearColor * whiteScale, A, B, C, D, E, F); |
|||
return x; |
|||
} |
|||
return curr * whiteScale; |
|||
} |
|||
float4 Frag(Varyings input) : SV_Target |
|||
{ |
|||
float4 color = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.texcoord); |
|||
float3 remapWhite(float3 inPixel, float whitePt) |
|||
#if EYE_ADAPTATION |
|||
// var breakout for readability |
|||
const float inBlack = 0; |
|||
const float outBlack = 0; |
|||
float inWhite = whitePt; |
|||
const float outWhite = 1; |
|||
|
|||
// remap input range to output range |
|||
float3 outPixel = ((inPixel.rgb) - inBlack.xxx) / (inWhite.xxx - inBlack.xxx) * (outWhite.xxx - outBlack.xxx) + outBlack.xxx; |
|||
return (outPixel.rgb); |
|||
float autoExposure = SAMPLE_TEXTURE2D(_AutoExposure, sampler_AutoExposure, input.texcoord).r; |
|||
color *= autoExposure; |
|||
#endif |
|||
float3 NeutralTonemap(float3 x) |
|||
color.rgb *= _Exposure; // Exposure is in ev units (or 'stops'), precomputed CPU-side |
|||
|
|||
#if NEUTRAL_GRADING |
|||
float3 finalColor = applyTonemapFilmicAD(x); // curve (dynamic coeffs differ per level) |
|||
finalColor = remapWhite(finalColor, WhiteClip); // post-curve white point adjustment |
|||
finalColor = saturate(finalColor); |
|||
return finalColor; |
|||
color.rgb = NeutralTonemap(color.rgb, _NeutralTonemapperParams1, _NeutralTonemapperParams2); |
|||
#elif CUSTOM_GRADING |
|||
{ |
|||
float3 uvw = saturate(LinearToLogC(color)); |
|||
float3 ApplyToneMap(float3 color) |
|||
// Strip lut format where `height = sqrt(width)` |
|||
uvw.z *= _LogLut_Params.z; |
|||
half shift = floor(uvw.z); |
|||
uvw.xy = uvw.xy * _LogLut_Params.z * _LogLut_Params.xy + _LogLut_Params.xy * 0.5; |
|||
uvw.x += shift * _LogLut_Params.y; |
|||
uvw.xyz = lerp( |
|||
SAMPLE_TEXTURE2D(_LogLut, sampler_LogLut, uvw.xy).rgb, |
|||
SAMPLE_TEXTURE2D(_LogLut, sampler_LogLut, uvw.xy + half2(_LogLut_Params.y, 0)).rgb, |
|||
uvw.z - shift |
|||
); |
|||
|
|||
color.rgb = uvw; |
|||
} |
|||
#else |
|||
if (_EnableToneMap > 0.0) |
|||
{ |
|||
return NeutralTonemap(color); |
|||
} |
|||
else |
|||
{ |
|||
return saturate(color); |
|||
} |
|||
color = saturate(color); |
|||
#endif |
|||
float4 Frag(Varyings input) : SV_Target |
|||
{ |
|||
float4 c = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.texcoord); |
|||
// Gamma correction |
|||
return color; |
|||
} |
|||
// TODO: Currenlt in the editor there a an additional pass were the result is copyed in a render target RGBA8_sRGB. |
|||
// So we must not correct the sRGB here else it will be done two time. |
|||
// To fix! |
|||
ENDHLSL |
|||
c.rgb = ApplyToneMap(c.rgb * exp2(_Exposure)); |
|||
SubShader |
|||
{ |
|||
Cull Off ZWrite Off ZTest Always |
|||
// return LinearToSRGB(c); |
|||
return c; |
|||
Pass |
|||
{ |
|||
HLSLPROGRAM |
|||
#pragma vertex Vert |
|||
#pragma fragment Frag |
|||
#pragma multi_compile __ NEUTRAL_GRADING CUSTOM_GRADING |
|||
#pragma multi_compile __ EYE_ADAPTATION |
|||
} |
|||
|
|||
|
|||
Fallback Off |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5d4f8c766010cda4b97dbd7bb0905215 |
|||
folderAsset: yes |
|||
timeCreated: 1483612561 |
|||
licenseType: Pro |
|||
DefaultImporter: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System; |
|||
using UnityEngine.Rendering; |
|||
|
|||
// TEMPORARY, minimalist post-processing stack until the fully-featured framework is ready
|
|||
|
|||
namespace UnityEngine.Experimental.Rendering.HDPipeline |
|||
{ |
|||
using GradingType = PostProcessing.ColorGradingSettings.GradingType; |
|||
using EyeAdaptationType = PostProcessing.EyeAdaptationSettings.EyeAdaptationType; |
|||
|
|||
[ExecuteInEditMode] |
|||
public sealed class PostProcessing : MonoBehaviour |
|||
{ |
|||
[Serializable] |
|||
public sealed class ColorGradingSettings |
|||
{ |
|||
public enum GradingType |
|||
{ |
|||
None, |
|||
Neutral, |
|||
Custom |
|||
} |
|||
|
|||
public GradingType type = GradingType.None; |
|||
public float exposure = 0f; |
|||
|
|||
public Texture logLut = null; |
|||
|
|||
[Range(-0.10f, 0.1f)] public float neutralBlackIn = 0.02f; |
|||
[Range( 1.00f, 20.0f)] public float neutralWhiteIn = 10f; |
|||
[Range(-0.09f, 0.1f)] public float neutralBlackOut = 0f; |
|||
[Range( 1.00f, 19.0f)] public float neutralWhiteOut = 10f; |
|||
[Range( 0.10f, 20.0f)] public float neutralWhiteLevel = 5.3f; |
|||
[Range( 1.00f, 10.0f)] public float neutralWhiteClip = 10f; |
|||
} |
|||
|
|||
[Serializable] |
|||
public sealed class EyeAdaptationSettings |
|||
{ |
|||
public enum EyeAdaptationType |
|||
{ |
|||
Progressive, |
|||
Fixed |
|||
} |
|||
|
|||
public bool enabled = false; |
|||
public bool showDebugHistogramInGameView = true; |
|||
|
|||
[Range(1f, 99f)] public float lowPercent = 65f; |
|||
[Range(1f, 99f)] public float highPercent = 95f; |
|||
|
|||
public float minLuminance = 0.03f; |
|||
public float maxLuminance = 2f; |
|||
public float exposureCompensation = 0.5f; |
|||
|
|||
public EyeAdaptationType adaptationType = EyeAdaptationType.Progressive; |
|||
|
|||
public float speedUp = 2f; |
|||
public float speedDown = 1f; |
|||
|
|||
[Range(-16, -1)] public int logMin = -8; |
|||
[Range( 1, 16)] public int logMax = 4; |
|||
} |
|||
|
|||
public EyeAdaptationSettings eyeAdaptation = new EyeAdaptationSettings(); |
|||
public ColorGradingSettings colorGrading = new ColorGradingSettings(); |
|||
|
|||
Material m_EyeAdaptationMaterial; |
|||
Material m_FinalPassMaterial; |
|||
|
|||
ComputeShader m_EyeCompute; |
|||
ComputeBuffer m_HistogramBuffer; |
|||
|
|||
readonly RenderTexture[] m_AutoExposurePool = new RenderTexture[2]; |
|||
int m_AutoExposurePingPing; |
|||
RenderTexture m_CurrentAutoExposure; |
|||
RenderTexture m_DebugHistogram = null; |
|||
|
|||
static uint[] s_EmptyHistogramBuffer = new uint[k_HistogramBins]; |
|||
|
|||
bool m_FirstFrame = true; |
|||
|
|||
// Don't forget to update 'EyeAdaptation.cginc' if you change these values !
|
|||
const int k_HistogramBins = 64; |
|||
const int k_HistogramThreadX = 16; |
|||
const int k_HistogramThreadY = 16; |
|||
|
|||
void OnEnable() |
|||
{ |
|||
m_FinalPassMaterial = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/FinalPass"); |
|||
m_EyeAdaptationMaterial = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/EyeAdaptation"); |
|||
|
|||
m_EyeCompute = Resources.Load<ComputeShader>("EyeHistogram"); |
|||
m_HistogramBuffer = new ComputeBuffer(k_HistogramBins, sizeof(uint)); |
|||
|
|||
m_AutoExposurePool[0] = new RenderTexture(1, 1, 0, RenderTextureFormat.RFloat); |
|||
m_AutoExposurePool[1] = new RenderTexture(1, 1, 0, RenderTextureFormat.RFloat); |
|||
|
|||
m_FirstFrame = true; |
|||
} |
|||
|
|||
void OnDisable() |
|||
{ |
|||
Utilities.Destroy(m_EyeAdaptationMaterial); |
|||
Utilities.Destroy(m_FinalPassMaterial); |
|||
|
|||
foreach (var rt in m_AutoExposurePool) |
|||
Utilities.Destroy(rt); |
|||
|
|||
Utilities.Destroy(m_DebugHistogram); |
|||
Utilities.SafeRelease(m_HistogramBuffer); |
|||
} |
|||
|
|||
public void Render(Camera camera, ScriptableRenderContext context, RenderTargetIdentifier source, RenderTargetIdentifier destination) |
|||
{ |
|||
m_FinalPassMaterial.shaderKeywords = null; |
|||
|
|||
var cmd = new CommandBuffer { name = "Final Pass" }; |
|||
|
|||
if (eyeAdaptation.enabled) |
|||
{ |
|||
int tempRt = Shader.PropertyToID("_Source"); |
|||
|
|||
// Downscale the framebuffer, we don't need an absolute precision for auto exposure
|
|||
// and it helps making it more stable - should be using a previously downscaled pass
|
|||
var scaleOffsetRes = GetHistogramScaleOffsetRes(camera); |
|||
|
|||
cmd.GetTemporaryRT(tempRt, (int)scaleOffsetRes.z, (int)scaleOffsetRes.w, 0, FilterMode.Bilinear, RenderTextureFormat.ARGBHalf); |
|||
cmd.Blit(source, tempRt); |
|||
|
|||
// Clears the buffer on every frame as we use it to accumulate luminance values on each frame
|
|||
m_HistogramBuffer.SetData(s_EmptyHistogramBuffer); |
|||
|
|||
// Gets a log histogram
|
|||
int kernel = m_EyeCompute.FindKernel("KEyeHistogram"); |
|||
|
|||
cmd.SetComputeBufferParam(m_EyeCompute, kernel, "_Histogram", m_HistogramBuffer); |
|||
cmd.SetComputeTextureParam(m_EyeCompute, kernel, "_Source", tempRt); |
|||
cmd.SetComputeVectorParam(m_EyeCompute, "_ScaleOffsetRes", scaleOffsetRes); |
|||
cmd.DispatchCompute(m_EyeCompute, kernel, Mathf.CeilToInt(scaleOffsetRes.z / (float)k_HistogramThreadX), Mathf.CeilToInt(scaleOffsetRes.w / (float)k_HistogramThreadY), 1); |
|||
|
|||
// Cleanup
|
|||
cmd.ReleaseTemporaryRT(tempRt); |
|||
|
|||
// Make sure filtering values are correct to avoid apocalyptic consequences
|
|||
const float kMinDelta = 1e-2f; |
|||
eyeAdaptation.highPercent = Mathf.Clamp(eyeAdaptation.highPercent, 1f + kMinDelta, 99f); |
|||
eyeAdaptation.lowPercent = Mathf.Clamp(eyeAdaptation.lowPercent, 1f, eyeAdaptation.highPercent - kMinDelta); |
|||
|
|||
// Compute auto exposure
|
|||
m_EyeAdaptationMaterial.SetBuffer("_Histogram", m_HistogramBuffer); |
|||
m_EyeAdaptationMaterial.SetVector("_Params", new Vector4(eyeAdaptation.lowPercent * 0.01f, eyeAdaptation.highPercent * 0.01f, eyeAdaptation.minLuminance, eyeAdaptation.maxLuminance)); |
|||
m_EyeAdaptationMaterial.SetVector("_Speed", new Vector2(eyeAdaptation.speedDown, eyeAdaptation.speedUp)); |
|||
m_EyeAdaptationMaterial.SetVector("_ScaleOffsetRes", scaleOffsetRes); |
|||
m_EyeAdaptationMaterial.SetFloat("_ExposureCompensation", eyeAdaptation.exposureCompensation); |
|||
|
|||
if (m_FirstFrame || !Application.isPlaying) |
|||
{ |
|||
// We don't want eye adaptation when not in play mode because the GameView isn't
|
|||
// animated, thus making it harder to tweak. Just use the final audo exposure value.
|
|||
m_CurrentAutoExposure = m_AutoExposurePool[0]; |
|||
cmd.Blit(null, m_CurrentAutoExposure, m_EyeAdaptationMaterial, (int)EyeAdaptationType.Fixed); |
|||
|
|||
// Copy current exposure to the other pingpong target on first frame to avoid adapting from black
|
|||
cmd.Blit(m_AutoExposurePool[0], m_AutoExposurePool[1]); |
|||
} else |
|||
{ |
|||
int pp = m_AutoExposurePingPing; |
|||
var src = m_AutoExposurePool[++pp % 2]; |
|||
var dst = m_AutoExposurePool[++pp % 2]; |
|||
cmd.Blit(src, dst, m_EyeAdaptationMaterial, (int)eyeAdaptation.adaptationType); |
|||
m_AutoExposurePingPing = ++pp % 2; |
|||
m_CurrentAutoExposure = dst; |
|||
} |
|||
|
|||
m_FinalPassMaterial.EnableKeyword("EYE_ADAPTATION"); |
|||
m_FinalPassMaterial.SetTexture("_AutoExposure", m_CurrentAutoExposure); |
|||
|
|||
// Debug histogram visualization
|
|||
if (eyeAdaptation.showDebugHistogramInGameView) |
|||
{ |
|||
if (m_DebugHistogram == null || !m_DebugHistogram.IsCreated()) |
|||
{ |
|||
m_DebugHistogram = new RenderTexture(256, 128, 0, RenderTextureFormat.ARGB32) |
|||
{ |
|||
filterMode = FilterMode.Point, |
|||
wrapMode = TextureWrapMode.Clamp |
|||
}; |
|||
} |
|||
|
|||
m_EyeAdaptationMaterial.SetFloat("_DebugWidth", m_DebugHistogram.width); |
|||
cmd.Blit(null, m_DebugHistogram, m_EyeAdaptationMaterial, 2); |
|||
} |
|||
|
|||
m_FirstFrame = false; |
|||
} |
|||
|
|||
float ev = Mathf.Exp(colorGrading.exposure * 0.6931471805599453f); |
|||
m_FinalPassMaterial.SetFloat("_Exposure", ev); |
|||
|
|||
if (colorGrading.type == GradingType.Neutral) |
|||
{ |
|||
const float kScaleFactor = 20f; |
|||
const float kScaleFactorHalf = kScaleFactor * 0.5f; |
|||
|
|||
float inBlack = colorGrading.neutralBlackIn * kScaleFactor + 1f; |
|||
float outBlack = colorGrading.neutralBlackOut * kScaleFactorHalf + 1f; |
|||
float inWhite = colorGrading.neutralWhiteIn / kScaleFactor; |
|||
float outWhite = 1f - colorGrading.neutralWhiteOut / kScaleFactor; |
|||
float blackRatio = inBlack / outBlack; |
|||
float whiteRatio = inWhite / outWhite; |
|||
|
|||
const float a = 0.2f; |
|||
float b = Mathf.Max(0f, Mathf.LerpUnclamped(0.57f, 0.37f, blackRatio)); |
|||
float c = Mathf.LerpUnclamped(0.01f, 0.24f, whiteRatio); |
|||
float d = Mathf.Max(0f, Mathf.LerpUnclamped(0.02f, 0.20f, blackRatio)); |
|||
const float e = 0.02f; |
|||
const float f = 0.30f; |
|||
|
|||
m_FinalPassMaterial.SetVector("_NeutralTonemapperParams1", new Vector4(a, b, c, d)); |
|||
m_FinalPassMaterial.SetVector("_NeutralTonemapperParams2", new Vector4(e, f, colorGrading.neutralWhiteLevel, colorGrading.neutralWhiteClip / kScaleFactorHalf)); |
|||
m_FinalPassMaterial.EnableKeyword("NEUTRAL_GRADING"); |
|||
} |
|||
else if (colorGrading.type == GradingType.Custom) |
|||
{ |
|||
if (colorGrading.logLut != null) |
|||
{ |
|||
var lut = colorGrading.logLut; |
|||
m_FinalPassMaterial.SetTexture("_LogLut", lut); |
|||
m_FinalPassMaterial.SetVector("_LogLut_Params", new Vector3(1f / lut.width, 1f / lut.height, lut.height - 1f)); |
|||
m_FinalPassMaterial.EnableKeyword("CUSTOM_GRADING"); |
|||
} |
|||
} |
|||
|
|||
cmd.Blit(source, destination, m_FinalPassMaterial, 0); |
|||
context.ExecuteCommandBuffer(cmd); |
|||
cmd.Dispose(); |
|||
} |
|||
|
|||
Vector4 GetHistogramScaleOffsetRes(Camera camera) |
|||
{ |
|||
float diff = eyeAdaptation.logMax - eyeAdaptation.logMin; |
|||
float scale = 1f / diff; |
|||
float offset = -eyeAdaptation.logMin * scale; |
|||
return new Vector4(scale, offset, Mathf.Floor(camera.pixelWidth / 2f), Mathf.Floor(camera.pixelHeight / 2f)); |
|||
} |
|||
|
|||
void OnGUI() |
|||
{ |
|||
if (!eyeAdaptation.enabled || !eyeAdaptation.showDebugHistogramInGameView || m_DebugHistogram == null || !m_DebugHistogram.IsCreated()) |
|||
return; |
|||
|
|||
var rect = new Rect(8f, 8f, m_DebugHistogram.width, m_DebugHistogram.height); |
|||
GUI.DrawTexture(rect, m_DebugHistogram); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: aaa10d337b215d94dbc0ce5467cdc79a |
|||
timeCreated: 1483612023 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef UNITY_COLOR_GRADING |
|||
#define UNITY_COLOR_GRADING |
|||
|
|||
// Set to 1 to use more precise but more expensive log/linear conversions. I haven't found a proper |
|||
// use case for the high precision version yet so I'm leaving this to 0. |
|||
#define COLOR_GRADING_PRECISE_LOG 0 |
|||
|
|||
// |
|||
// Alexa LogC converters (El 1000) |
|||
// See http://www.vocas.nl/webfm_send/964 |
|||
// It's a good fit to store HDR values in log as the range is pretty wide (1 maps to ~58.85666) and |
|||
// is quick enough to compute. |
|||
// |
|||
struct ParamsLogC |
|||
{ |
|||
float cut; |
|||
float a, b, c, d, e, f; |
|||
}; |
|||
|
|||
static const ParamsLogC LogC = |
|||
{ |
|||
0.011361, // cut |
|||
5.555556, // a |
|||
0.047996, // b |
|||
0.244161, // c |
|||
0.386036, // d |
|||
5.301883, // e |
|||
0.092819 // f |
|||
}; |
|||
|
|||
half LinearToLogC_Precise(half x) |
|||
{ |
|||
float o; |
|||
if (x > LogC.cut) |
|||
o = LogC.c * log10(LogC.a * x + LogC.b) + LogC.d; |
|||
else |
|||
o = LogC.e * x + LogC.f; |
|||
return o; |
|||
} |
|||
|
|||
float3 LinearToLogC(float3 x) |
|||
{ |
|||
#if COLOR_GRADING_PRECISE_LOG |
|||
return float3( |
|||
LinearToLogC_Precise(x.x), |
|||
LinearToLogC_Precise(x.y), |
|||
LinearToLogC_Precise(x.z) |
|||
); |
|||
#else |
|||
return LogC.c * log10(LogC.a * x + LogC.b) + LogC.d; |
|||
#endif |
|||
} |
|||
|
|||
float LogCToLinear_Precise(float x) |
|||
{ |
|||
float o; |
|||
if (x > LogC.e * LogC.cut + LogC.f) |
|||
o = (pow(10.0, (x - LogC.d) / LogC.c) - LogC.b) / LogC.a; |
|||
else |
|||
o = (x - LogC.f) / LogC.e; |
|||
return o; |
|||
} |
|||
|
|||
float3 LogCToLinear(float3 x) |
|||
{ |
|||
#if COLOR_GRADING_PRECISE_LOG |
|||
return float3( |
|||
LogCToLinear_Precise(x.x), |
|||
LogCToLinear_Precise(x.y), |
|||
LogCToLinear_Precise(x.z) |
|||
); |
|||
#else |
|||
return (pow(10.0, (x - LogC.d) / LogC.c) - LogC.b) / LogC.a; |
|||
#endif |
|||
} |
|||
|
|||
#endif // UNITY_COLOR_GRADING |
|
|||
fileFormatVersion: 2 |
|||
guid: d353f8a66edd94f4ab3f3c8e2ef7dd61 |
|||
timeCreated: 1483709991 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
#ifndef UNITY_EYE_ADAPTATION |
|||
#define UNITY_EYE_ADAPTATION |
|||
|
|||
// Optimal values for PS4/GCN |
|||
// Using a group size of 32x32 seems to be a bit faster on Kepler/Maxwell |
|||
// Don't forget to update 'EyeAdaptationController.cs' if you change these values ! |
|||
#define HISTOGRAM_BINS 64 |
|||
#define HISTOGRAM_TEXELS HISTOGRAM_BINS / 4 |
|||
#define HISTOGRAM_THREAD_X 16 |
|||
#define HISTOGRAM_THREAD_Y 16 |
|||
|
|||
float GetHistogramBinFromLuminance(float value, float2 scaleOffset) |
|||
{ |
|||
return saturate(log2(value) * scaleOffset.x + scaleOffset.y); |
|||
} |
|||
|
|||
float GetLuminanceFromHistogramBin(float bin, float2 scaleOffset) |
|||
{ |
|||
return exp2((bin - scaleOffset.y) / scaleOffset.x); |
|||
} |
|||
|
|||
#endif // UNITY_EYE_ADAPTATION |
|
|||
fileFormatVersion: 2 |
|||
guid: a34d57fb96cd85e4db20d2b70a6f0b1d |
|||
timeCreated: 1483621596 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/HDRenderPipeline/EyeAdaptation" |
|||
{ |
|||
Properties |
|||
{ |
|||
_MainTex("Texture", any) = "" {} |
|||
} |
|||
|
|||
HLSLINCLUDE |
|||
|
|||
#pragma target 5.0 |
|||
#include "Common.hlsl" |
|||
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl" |
|||
#include "EyeAdaptation.hlsl" |
|||
|
|||
TEXTURE2D(_MainTex); |
|||
SAMPLER2D(sampler_MainTex); |
|||
|
|||
float4 _Params; // x: lowPercent, y: highPercent, z: minBrightness, w: maxBrightness |
|||
float2 _Speed; // x: down, y: up |
|||
float4 _ScaleOffsetRes; // x: scale, y: offset, w: histogram pass width, h: histogram pass height |
|||
float _ExposureCompensation; |
|||
|
|||
StructuredBuffer<uint> _Histogram; |
|||
|
|||
float GetBinValue(uint index, float maxHistogramValue) |
|||
{ |
|||
return float(_Histogram[index]) * maxHistogramValue; |
|||
} |
|||
|
|||
float FindMaxHistogramValue() |
|||
{ |
|||
uint maxValue = 0u; |
|||
|
|||
for (uint i = 0; i < HISTOGRAM_BINS; i++) |
|||
{ |
|||
uint h = _Histogram[i]; |
|||
maxValue = max(maxValue, h); |
|||
} |
|||
|
|||
return float(maxValue); |
|||
} |
|||
|
|||
void FilterLuminance(uint i, float maxHistogramValue, inout float4 filter) |
|||
{ |
|||
float binValue = GetBinValue(i, maxHistogramValue); |
|||
|
|||
// Filter dark areas |
|||
float offset = min(filter.z, binValue); |
|||
binValue -= offset; |
|||
filter.zw -= offset.xx; |
|||
|
|||
// Filter highlights |
|||
binValue = min(filter.w, binValue); |
|||
filter.w -= binValue; |
|||
|
|||
// Luminance at the bin |
|||
float luminance = GetLuminanceFromHistogramBin(float(i) / float(HISTOGRAM_BINS), _ScaleOffsetRes.xy); |
|||
|
|||
filter.xy += float2(luminance * binValue, binValue); |
|||
} |
|||
|
|||
float GetAverageLuminance(float maxHistogramValue) |
|||
{ |
|||
// Sum of all bins |
|||
uint i; |
|||
float totalSum = 0.0; |
|||
|
|||
[loop] |
|||
for (i = 0; i < HISTOGRAM_BINS; i++) |
|||
totalSum += GetBinValue(i, maxHistogramValue); |
|||
|
|||
// Skip darker and lighter parts of the histogram to stabilize the auto exposure |
|||
// x: filtered sum |
|||
// y: accumulator |
|||
// zw: fractions |
|||
float4 filter = float4(0.0, 0.0, totalSum * _Params.xy); |
|||
|
|||
[loop] |
|||
for (i = 0; i < HISTOGRAM_BINS; i++) |
|||
FilterLuminance(i, maxHistogramValue, filter); |
|||
|
|||
// Clamp to user brightness range |
|||
return clamp(filter.x / max(filter.y, 1.0e-4), _Params.z, _Params.w); |
|||
} |
|||
|
|||
float GetExposureMultiplier(float avgLuminance) |
|||
{ |
|||
avgLuminance = max(1.0e-4, avgLuminance); |
|||
|
|||
//half keyValue = 1.03 - (2.0 / (2.0 + log2(avgLuminance + 1.0))); |
|||
half keyValue = _ExposureCompensation; |
|||
half exposure = keyValue / avgLuminance; |
|||
|
|||
return exposure; |
|||
} |
|||
|
|||
float InterpolateExposure(float newExposure, float oldExposure) |
|||
{ |
|||
float delta = newExposure - oldExposure; |
|||
float speed = delta > 0.0 ? _Speed.x : _Speed.y; |
|||
float exposure = oldExposure + delta * (1.0 - exp2(-unity_DeltaTime.x * speed)); |
|||
//float exposure = oldExposure + delta * (unity_DeltaTime.x * speed); |
|||
return exposure; |
|||
} |
|||
|
|||
struct Attributes |
|||
{ |
|||
float4 vertex : POSITION; |
|||
float4 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
struct Varyings |
|||
{ |
|||
float4 pos : SV_POSITION; |
|||
float2 uv : TEXCOORD0; |
|||
}; |
|||
|
|||
Varyings Vert(Attributes v) |
|||
{ |
|||
Varyings o; |
|||
o.pos = TransformWorldToHClip(v.vertex.xyz); |
|||
o.uv = v.texcoord.xy; |
|||
return o; |
|||
} |
|||
|
|||
float4 FragAdaptProgressive(Varyings i) : SV_Target |
|||
{ |
|||
float maxValue = 1.0 / FindMaxHistogramValue(); |
|||
float avgLuminance = GetAverageLuminance(maxValue); |
|||
float exposure = GetExposureMultiplier(avgLuminance); |
|||
float prevExposure = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, (0.5).xx).r; |
|||
exposure = InterpolateExposure(exposure, prevExposure); |
|||
return exposure.xxxx; |
|||
} |
|||
|
|||
float4 FragAdaptFixed(Varyings i) : SV_Target |
|||
{ |
|||
float maxValue = 1.0 / FindMaxHistogramValue(); |
|||
float avgLuminance = GetAverageLuminance(maxValue); |
|||
float exposure = GetExposureMultiplier(avgLuminance); |
|||
return exposure.xxxx; |
|||
} |
|||
|
|||
// Editor stuff (histogram debug visualization) |
|||
int _DebugWidth; |
|||
|
|||
struct VaryingsEditorHisto |
|||
{ |
|||
float4 pos : SV_POSITION; |
|||
float2 uv : TEXCOORD0; |
|||
float maxValue : TEXCOORD1; |
|||
float avgLuminance : TEXCOORD2; |
|||
}; |
|||
|
|||
VaryingsEditorHisto VertEditorHisto(Attributes v) |
|||
{ |
|||
VaryingsEditorHisto o; |
|||
o.pos = TransformWorldToHClip(v.vertex.xyz); |
|||
o.uv = v.texcoord.xy; |
|||
o.maxValue = 1.0 / FindMaxHistogramValue(); |
|||
o.avgLuminance = GetAverageLuminance(o.maxValue); |
|||
return o; |
|||
} |
|||
|
|||
float4 FragEditorHisto(VaryingsEditorHisto i) : SV_Target |
|||
{ |
|||
const float3 kRangeColor = float3(0.05, 0.4, 0.6); |
|||
const float3 kAvgColor = float3(0.8, 0.3, 0.05); |
|||
|
|||
float4 color = float4(0.0, 0.0, 0.0, 0.7); |
|||
|
|||
uint ix = (uint)(round(i.uv.x * HISTOGRAM_BINS)); |
|||
float bin = saturate(float(_Histogram[ix]) * i.maxValue); |
|||
float fill = step(i.uv.y, bin); |
|||
|
|||
// Min / max brightness markers |
|||
float luminanceMin = GetHistogramBinFromLuminance(_Params.z, _ScaleOffsetRes.xy); |
|||
float luminanceMax = GetHistogramBinFromLuminance(_Params.w, _ScaleOffsetRes.xy); |
|||
|
|||
color.rgb += fill.rrr; |
|||
|
|||
if (i.uv.x > luminanceMin && i.uv.x < luminanceMax) |
|||
{ |
|||
color.rgb = fill.rrr * kRangeColor; |
|||
color.rgb += kRangeColor; |
|||
} |
|||
|
|||
// Current average luminance marker |
|||
float luminanceAvg = GetHistogramBinFromLuminance(i.avgLuminance, _ScaleOffsetRes.xy); |
|||
float avgPx = luminanceAvg * _DebugWidth; |
|||
|
|||
if (abs(i.pos.x - avgPx) < 2) |
|||
color.rgb = kAvgColor; |
|||
|
|||
return color; |
|||
} |
|||
|
|||
ENDHLSL |
|||
|
|||
SubShader |
|||
{ |
|||
Cull Off ZWrite Off ZTest Always |
|||
|
|||
Pass |
|||
{ |
|||
HLSLPROGRAM |
|||
|
|||
#pragma vertex Vert |
|||
#pragma fragment FragAdaptProgressive |
|||
|
|||
ENDHLSL |
|||
} |
|||
|
|||
Pass |
|||
{ |
|||
HLSLPROGRAM |
|||
|
|||
#pragma vertex Vert |
|||
#pragma fragment FragAdaptFixed |
|||
|
|||
ENDHLSL |
|||
} |
|||
|
|||
Pass |
|||
{ |
|||
HLSLPROGRAM |
|||
|
|||
#pragma vertex VertEditorHisto |
|||
#pragma fragment FragEditorHisto |
|||
|
|||
ENDHLSL |
|||
} |
|||
} |
|||
|
|||
Fallback Off |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: aeefbc574fd1b81489d0c8f8dcd5f1b7 |
|||
timeCreated: 1483625503 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
// Put the following line to 0 or comment it to disable vignette weighting |
|||
#define USE_VIGNETTE_WEIGHTING 1 |
|||
|
|||
#include "Common.hlsl" |
|||
#include "EyeAdaptation.hlsl" |
|||
|
|||
RWStructuredBuffer<uint> _Histogram; |
|||
Texture2D<float4> _Source; |
|||
|
|||
CBUFFER_START(Params) |
|||
float4 _ScaleOffsetRes; // x: scale, y: offset, z: width, w: height |
|||
CBUFFER_END |
|||
|
|||
groupshared uint gs_histogram[HISTOGRAM_BINS]; |
|||
|
|||
#pragma kernel KEyeHistogram |
|||
[numthreads(HISTOGRAM_THREAD_X,HISTOGRAM_THREAD_Y,1)] |
|||
void KEyeHistogram(uint2 dispatchThreadId : SV_DispatchThreadID, uint2 groupThreadId : SV_GroupThreadID) |
|||
{ |
|||
// Pretty straightforward implementation of histogram gathering using atomic ops. |
|||
// I tried a few methods (no atomic ops / heavy LDS leveraging) but this one turned out to be |
|||
// the fastest on desktop (Nvidia - Kepler/Maxwell) and PS4. Still need to try it on GCN/desktop |
|||
// but considering it runs very fast on PS4 we can expect it to run well (?). |
|||
|
|||
const uint localThreadId = groupThreadId.y * HISTOGRAM_THREAD_X + groupThreadId.x; |
|||
|
|||
// Clears the shared memory |
|||
if (localThreadId < HISTOGRAM_BINS) |
|||
gs_histogram[localThreadId] = 0u; |
|||
|
|||
GroupMemoryBarrierWithGroupSync(); |
|||
|
|||
// Gather local group histogram |
|||
if (dispatchThreadId.x < (uint)_ScaleOffsetRes.z && dispatchThreadId.y < (uint)_ScaleOffsetRes.w) |
|||
{ |
|||
#if USE_VIGNETTE_WEIGHTING |
|||
// Vignette weighting to put more focus on what's in the center of the screen |
|||
float2 uv01 = float2(dispatchThreadId) / float2(_ScaleOffsetRes.z, _ScaleOffsetRes.w); |
|||
float2 d = abs(uv01 - (0.5).xx); |
|||
float vfactor = saturate(1.0 - dot(d, d)); |
|||
vfactor *= vfactor; |
|||
uint weight = (uint)(64.0 * vfactor); |
|||
#else |
|||
uint weight = 1u; |
|||
#endif |
|||
|
|||
float3 color = _Source[dispatchThreadId].xyz; |
|||
float luminance = max(color.r, max(color.g, color.b)); // Looks more natural than using a Rec.709 luminance |
|||
float logLuminance = GetHistogramBinFromLuminance(luminance, _ScaleOffsetRes.xy); |
|||
uint idx = (uint)(logLuminance * (HISTOGRAM_BINS - 1u)); |
|||
InterlockedAdd(gs_histogram[idx], weight); |
|||
} |
|||
|
|||
GroupMemoryBarrierWithGroupSync(); |
|||
|
|||
// Merge everything |
|||
if (localThreadId < HISTOGRAM_BINS) |
|||
InterlockedAdd(_Histogram[localThreadId], gs_histogram[localThreadId]); |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 6ffb363cabef3ec459559197dbeda232 |
|||
timeCreated: 1483621596 |
|||
licenseType: Pro |
|||
ComputeShaderImporter: |
|||
currentAPIMask: 4 |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
Shader "Hidden/HDRenderPipeline/LutGen" |
|||
{ |
|||
HLSLINCLUDE |
|||
|
|||
#pragma target 5.0 |
|||
#include "Common.hlsl" |
|||
#include "Assets/ScriptableRenderLoop/HDRenderPipeline/ShaderVariables.hlsl" |
|||
#include "ColorGrading.hlsl" |
|||
|
|||
float4 _LutParams; |
|||
|
|||
struct Attributes |
|||
{ |
|||
float3 vertex : POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
struct Varyings |
|||
{ |
|||
float4 vertex : SV_POSITION; |
|||
float2 texcoord : TEXCOORD0; |
|||
}; |
|||
|
|||
Varyings Vert(Attributes input) |
|||
{ |
|||
Varyings output; |
|||
output.vertex = TransformWorldToHClip(input.vertex); |
|||
output.texcoord = input.texcoord.xy; |
|||
return output; |
|||
} |
|||
|
|||
float4 Frag(Varyings input) : SV_Target |
|||
{ |
|||
// 2D strip lut |
|||
float2 uv = input.texcoord - _LutParams.yz; |
|||
float3 color; |
|||
color.r = frac(uv.x * _LutParams.x); |
|||
color.b = uv.x - color.r / _LutParams.x; |
|||
color.g = uv.y; |
|||
|
|||
// Lut is in LogC |
|||
float3 colorLogC = color * _LutParams.w; |
|||
|
|||
// Switch back to unity linear |
|||
float3 colorLinear = LogCToLinear(colorLogC); |
|||
|
|||
return float4(colorLinear, 1.0); |
|||
} |
|||
|
|||
ENDHLSL |
|||
|
|||
SubShader |
|||
{ |
|||
Cull Off ZWrite Off ZTest Always |
|||
|
|||
Pass |
|||
{ |
|||
HLSLPROGRAM |
|||
|
|||
#pragma vertex Vert |
|||
#pragma fragment Frag |
|||
|
|||
ENDHLSL |
|||
} |
|||
} |
|||
|
|||
Fallback Off |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: d5ac66d76294f88419a16132df665c37 |
|||
timeCreated: 1483708495 |
|||
licenseType: Pro |
|||
ShaderImporter: |
|||
defaultTextures: [] |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
422
Assets/Textures/CoolingFilterLUT.exr
文件差异内容过多而无法显示
查看文件
文件差异内容过多而无法显示
查看文件
|
|||
fileFormatVersion: 2 |
|||
guid: 36ff124e89908864e932c5a3c7c6d366 |
|||
timeCreated: 1483716363 |
|||
licenseType: Pro |
|||
TextureImporter: |
|||
fileIDToRecycleName: {} |
|||
serializedVersion: 4 |
|||
mipmaps: |
|||
mipMapMode: 0 |
|||
enableMipMap: 0 |
|||
sRGBTexture: 0 |
|||
linearTexture: 0 |
|||
fadeOut: 0 |
|||
borderMipMap: 0 |
|||
mipMapFadeDistanceStart: 1 |
|||
mipMapFadeDistanceEnd: 3 |
|||
bumpmap: |
|||
convertToNormalMap: 0 |
|||
externalNormalMap: 0 |
|||
heightScale: 0.25 |
|||
normalMapFilter: 0 |
|||
isReadable: 0 |
|||
grayScaleToAlpha: 0 |
|||
generateCubemap: 6 |
|||
cubemapConvolution: 0 |
|||
seamlessCubemap: 0 |
|||
textureFormat: 1 |
|||
maxTextureSize: 2048 |
|||
textureSettings: |
|||
filterMode: 1 |
|||
aniso: 0 |
|||
mipBias: -1 |
|||
wrapMode: -1 |
|||
nPOTScale: 0 |
|||
lightmap: 0 |
|||
compressionQuality: 50 |
|||
spriteMode: 0 |
|||
spriteExtrude: 1 |
|||
spriteMeshType: 1 |
|||
alignment: 0 |
|||
spritePivot: {x: 0.5, y: 0.5} |
|||
spriteBorder: {x: 0, y: 0, z: 0, w: 0} |
|||
spritePixelsToUnits: 100 |
|||
alphaUsage: 1 |
|||
alphaIsTransparency: 0 |
|||
spriteTessellationDetail: -1 |
|||
textureType: 0 |
|||
textureShape: 1 |
|||
maxTextureSizeSet: 0 |
|||
compressionQualitySet: 0 |
|||
textureFormatSet: 0 |
|||
platformSettings: |
|||
- buildTarget: DefaultTexturePlatform |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 0 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: Standalone |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 1 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: iPhone |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 1 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: tvOS |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 1 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: Windows Store Apps |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 1 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
- buildTarget: WebGL |
|||
maxTextureSize: 2048 |
|||
textureFormat: -1 |
|||
textureCompression: 1 |
|||
compressionQuality: 50 |
|||
crunchedCompression: 0 |
|||
allowsAlphaSplitting: 0 |
|||
overridden: 0 |
|||
spriteSheet: |
|||
serializedVersion: 2 |
|||
sprites: [] |
|||
outline: [] |
|||
spritePackingTag: |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
|
|||
using System.IO; |
|||
using UnityEngine; |
|||
using UnityEngine.Experimental.Rendering.HDPipeline; |
|||
|
|||
namespace UnityEditor.Experimental.Rendering.HDPipeline |
|||
{ |
|||
using GradingType = PostProcessing.ColorGradingSettings.GradingType; |
|||
using EyeAdaptationType = PostProcessing.EyeAdaptationSettings.EyeAdaptationType; |
|||
|
|||
[CustomEditor(typeof(PostProcessing))] |
|||
public class PostProcessingEditor : Editor |
|||
{ |
|||
public class ColorGradingSettings |
|||
{ |
|||
public SerializedProperty type; |
|||
public SerializedProperty exposure; |
|||
|
|||
public SerializedProperty logLut; |
|||
|
|||
public SerializedProperty neutralBlackIn; |
|||
public SerializedProperty neutralWhiteIn; |
|||
public SerializedProperty neutralBlackOut; |
|||
public SerializedProperty neutralWhiteOut; |
|||
public SerializedProperty neutralWhiteLevel; |
|||
public SerializedProperty neutralWhiteClip; |
|||
} |
|||
|
|||
public class EyeAdaptationSettings |
|||
{ |
|||
public SerializedProperty enabled; |
|||
public SerializedProperty showDebugHistogramInGameView; |
|||
|
|||
public SerializedProperty lowPercent; |
|||
public SerializedProperty highPercent; |
|||
|
|||
public SerializedProperty minLuminance; |
|||
public SerializedProperty maxLuminance; |
|||
public SerializedProperty exposureCompensation; |
|||
|
|||
public SerializedProperty adaptationType; |
|||
|
|||
public SerializedProperty speedUp; |
|||
public SerializedProperty speedDown; |
|||
|
|||
public SerializedProperty logMin; |
|||
public SerializedProperty logMax; |
|||
} |
|||
|
|||
public ColorGradingSettings colorGrading; |
|||
public EyeAdaptationSettings eyeAdaptation; |
|||
|
|||
void OnEnable() |
|||
{ |
|||
colorGrading = new ColorGradingSettings() |
|||
{ |
|||
type = serializedObject.FindProperty("colorGrading.type"), |
|||
exposure = serializedObject.FindProperty("colorGrading.exposure"), |
|||
|
|||
logLut = serializedObject.FindProperty("colorGrading.logLut"), |
|||
|
|||
neutralBlackIn = serializedObject.FindProperty("colorGrading.neutralBlackIn"), |
|||
neutralWhiteIn = serializedObject.FindProperty("colorGrading.neutralWhiteIn"), |
|||
neutralBlackOut = serializedObject.FindProperty("colorGrading.neutralBlackOut"), |
|||
neutralWhiteOut = serializedObject.FindProperty("colorGrading.neutralWhiteOut"), |
|||
neutralWhiteLevel = serializedObject.FindProperty("colorGrading.neutralWhiteLevel"), |
|||
neutralWhiteClip = serializedObject.FindProperty("colorGrading.neutralWhiteClip") |
|||
}; |
|||
|
|||
eyeAdaptation = new EyeAdaptationSettings() |
|||
{ |
|||
enabled = serializedObject.FindProperty("eyeAdaptation.enabled"), |
|||
showDebugHistogramInGameView = serializedObject.FindProperty("eyeAdaptation.showDebugHistogramInGameView"), |
|||
|
|||
lowPercent = serializedObject.FindProperty("eyeAdaptation.lowPercent"), |
|||
highPercent = serializedObject.FindProperty("eyeAdaptation.highPercent"), |
|||
|
|||
minLuminance = serializedObject.FindProperty("eyeAdaptation.minLuminance"), |
|||
maxLuminance = serializedObject.FindProperty("eyeAdaptation.maxLuminance"), |
|||
exposureCompensation = serializedObject.FindProperty("eyeAdaptation.exposureCompensation"), |
|||
|
|||
adaptationType = serializedObject.FindProperty("eyeAdaptation.adaptationType"), |
|||
|
|||
speedUp = serializedObject.FindProperty("eyeAdaptation.speedUp"), |
|||
speedDown = serializedObject.FindProperty("eyeAdaptation.speedDown"), |
|||
|
|||
logMin = serializedObject.FindProperty("eyeAdaptation.logMin"), |
|||
logMax = serializedObject.FindProperty("eyeAdaptation.logMax") |
|||
}; |
|||
} |
|||
|
|||
public override void OnInspectorGUI() |
|||
{ |
|||
serializedObject.Update(); |
|||
|
|||
var camera = (target as PostProcessing).GetComponent<Camera>(); |
|||
if (camera == null) |
|||
EditorGUILayout.HelpBox("Global post-processing settings will be overriden by local camera settings. Global settings are the only one visible in the scene view.", MessageType.Info); |
|||
|
|||
EditorGUILayout.LabelField("Color Grading", EditorStyles.boldLabel); |
|||
|
|||
EditorGUI.indentLevel++; |
|||
|
|||
if (camera != null) |
|||
{ |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.Space(EditorGUI.indentLevel * 15f); |
|||
if (GUILayout.Button("Export frame to EXR", EditorStyles.miniButton)) |
|||
{ |
|||
string path = EditorUtility.SaveFilePanelInProject("Export frame as EXR...", "Frame.exr", "exr", ""); |
|||
|
|||
if (!string.IsNullOrEmpty(path)) |
|||
SaveFrameToEXR(camera, path); |
|||
} |
|||
} |
|||
} |
|||
|
|||
EditorGUILayout.PropertyField(colorGrading.exposure); |
|||
EditorGUILayout.PropertyField(colorGrading.type); |
|||
|
|||
EditorGUI.indentLevel++; |
|||
|
|||
var gradingType = (GradingType)colorGrading.type.intValue; |
|||
|
|||
if (gradingType == GradingType.Custom) |
|||
{ |
|||
EditorGUILayout.PropertyField(colorGrading.logLut); |
|||
|
|||
if (!ValidateLutImportSettings()) |
|||
{ |
|||
EditorGUILayout.HelpBox("Invalid LUT import settings.", MessageType.Warning); |
|||
|
|||
GUILayout.Space(-32); |
|||
using (new EditorGUILayout.HorizontalScope()) |
|||
{ |
|||
GUILayout.FlexibleSpace(); |
|||
if (GUILayout.Button("Fix", GUILayout.Width(60))) |
|||
{ |
|||
SetLUTImportSettings(); |
|||
AssetDatabase.Refresh(); |
|||
} |
|||
GUILayout.Space(8); |
|||
} |
|||
GUILayout.Space(11); |
|||
} |
|||
} |
|||
else if (gradingType == GradingType.Neutral) |
|||
{ |
|||
EditorGUILayout.PropertyField(colorGrading.neutralBlackIn); |
|||
EditorGUILayout.PropertyField(colorGrading.neutralWhiteIn); |
|||
EditorGUILayout.PropertyField(colorGrading.neutralBlackOut); |
|||
EditorGUILayout.PropertyField(colorGrading.neutralWhiteOut); |
|||
EditorGUILayout.PropertyField(colorGrading.neutralWhiteLevel); |
|||
EditorGUILayout.PropertyField(colorGrading.neutralWhiteClip); |
|||
} |
|||
|
|||
EditorGUI.indentLevel--; |
|||
EditorGUI.indentLevel--; |
|||
|
|||
EditorGUILayout.LabelField("Eye Adaptation", EditorStyles.boldLabel); |
|||
|
|||
EditorGUI.indentLevel++; |
|||
|
|||
EditorGUILayout.PropertyField(eyeAdaptation.enabled); |
|||
|
|||
if (eyeAdaptation.enabled.boolValue) |
|||
{ |
|||
EditorGUILayout.PropertyField(eyeAdaptation.showDebugHistogramInGameView); |
|||
|
|||
EditorGUILayout.PropertyField(eyeAdaptation.logMin, new GUIContent("Histogram Log Min")); |
|||
EditorGUILayout.PropertyField(eyeAdaptation.logMax, new GUIContent("Histogram Log Max")); |
|||
EditorGUILayout.Space(); |
|||
|
|||
float low = eyeAdaptation.lowPercent.floatValue; |
|||
float high = eyeAdaptation.highPercent.floatValue; |
|||
|
|||
EditorGUILayout.MinMaxSlider(new GUIContent("Filter"), ref low, ref high, 1f, 99f); |
|||
|
|||
eyeAdaptation.lowPercent.floatValue = low; |
|||
eyeAdaptation.highPercent.floatValue = high; |
|||
|
|||
EditorGUILayout.PropertyField(eyeAdaptation.minLuminance); |
|||
EditorGUILayout.PropertyField(eyeAdaptation.maxLuminance); |
|||
EditorGUILayout.PropertyField(eyeAdaptation.exposureCompensation); |
|||
|
|||
EditorGUILayout.PropertyField(eyeAdaptation.adaptationType); |
|||
|
|||
if (eyeAdaptation.adaptationType.intValue == (int)EyeAdaptationType.Progressive) |
|||
{ |
|||
EditorGUI.indentLevel++; |
|||
EditorGUILayout.PropertyField(eyeAdaptation.speedUp); |
|||
EditorGUILayout.PropertyField(eyeAdaptation.speedDown); |
|||
EditorGUI.indentLevel--; |
|||
} |
|||
} |
|||
|
|||
EditorGUI.indentLevel--; |
|||
|
|||
serializedObject.ApplyModifiedProperties(); |
|||
} |
|||
|
|||
void SetLUTImportSettings() |
|||
{ |
|||
var lut = (target as PostProcessing).colorGrading.logLut; |
|||
var importer = (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(lut)); |
|||
importer.textureType = TextureImporterType.Default; |
|||
importer.filterMode = FilterMode.Bilinear; |
|||
importer.mipmapEnabled = false; |
|||
importer.anisoLevel = 0; |
|||
importer.sRGBTexture = false; |
|||
importer.npotScale = TextureImporterNPOTScale.None; |
|||
importer.textureCompression = TextureImporterCompression.Uncompressed; |
|||
importer.SaveAndReimport(); |
|||
AssetDatabase.Refresh(); |
|||
} |
|||
|
|||
bool ValidateLutImportSettings() |
|||
{ |
|||
var lut = (target as PostProcessing).colorGrading.logLut; |
|||
|
|||
if (lut == null) |
|||
return true; |
|||
|
|||
var importer = (TextureImporter)AssetImporter.GetAtPath(AssetDatabase.GetAssetPath(lut)); |
|||
return importer.anisoLevel == 0 |
|||
&& importer.mipmapEnabled == false |
|||
&& importer.sRGBTexture == false |
|||
&& (importer.textureCompression == TextureImporterCompression.Uncompressed) |
|||
&& importer.filterMode == FilterMode.Bilinear; |
|||
} |
|||
|
|||
void SaveFrameToEXR(Camera camera, string path) |
|||
{ |
|||
// We want a 1024x32 sized render at a minimum so that we have enough space to stamp the lut
|
|||
var aspect = (float)camera.pixelHeight / (float)camera.pixelWidth; |
|||
var width = camera.pixelWidth >= 1024 |
|||
? camera.pixelWidth |
|||
: 1024; |
|||
var height = Mathf.RoundToInt(width * aspect); |
|||
|
|||
var texture = new Texture2D(width, height, TextureFormat.RGBAHalf, false, true); |
|||
var targetRt = RenderTexture.GetTemporary(width, height, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); |
|||
|
|||
// Render the current frame without post processing
|
|||
var oldPPState = (target as PostProcessing).enabled; |
|||
(target as PostProcessing).enabled = false; |
|||
var oldTarget = camera.targetTexture; |
|||
var oldActive = RenderTexture.active; |
|||
camera.targetTexture = targetRt; |
|||
camera.Render(); |
|||
|
|||
// Stamp the log lut in the top left corner
|
|||
const int k_InternalLogLutSize = 32; |
|||
var stampMaterial = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/LutGen"); |
|||
stampMaterial.SetVector("_LutParams", new Vector4( |
|||
k_InternalLogLutSize, |
|||
0.5f / (k_InternalLogLutSize * k_InternalLogLutSize), |
|||
0.5f / k_InternalLogLutSize, |
|||
k_InternalLogLutSize / (k_InternalLogLutSize - 1f)) |
|||
); |
|||
|
|||
var stampRt = RenderTexture.GetTemporary(1024, 32, 0, RenderTextureFormat.ARGBHalf, RenderTextureReadWrite.Linear); |
|||
Graphics.Blit(null, stampRt, stampMaterial, 0); |
|||
|
|||
RenderTexture.active = targetRt; |
|||
GL.PushMatrix(); |
|||
{ |
|||
GL.LoadPixelMatrix(0, width, height, 0); |
|||
Graphics.DrawTexture(new Rect(0, 0, stampRt.width, stampRt.height), stampRt); |
|||
} |
|||
GL.PopMatrix(); |
|||
|
|||
// Read back
|
|||
texture.ReadPixels(new Rect(0, 0, targetRt.width, targetRt.height), 0, 0); |
|||
camera.targetTexture = oldTarget; |
|||
RenderTexture.active = oldActive; |
|||
(target as PostProcessing).enabled = oldPPState; |
|||
|
|||
// Cleanup
|
|||
RenderTexture.ReleaseTemporary(stampRt); |
|||
RenderTexture.ReleaseTemporary(targetRt); |
|||
Utilities.Destroy(stampMaterial); |
|||
|
|||
// Save
|
|||
File.WriteAllBytes(path, texture.EncodeToEXR(Texture2D.EXRFlags.CompressPIZ)); |
|||
AssetDatabase.Refresh(); |
|||
} |
|||
} |
|||
} |
|
|||
fileFormatVersion: 2 |
|||
guid: 5ddd90f68799818409996b6fd52790dc |
|||
timeCreated: 1483612569 |
|||
licenseType: Pro |
|||
MonoImporter: |
|||
serializedVersion: 2 |
|||
defaultReferences: [] |
|||
executionOrder: 0 |
|||
icon: {instanceID: 0} |
|||
userData: |
|||
assetBundleName: |
|||
assetBundleVariant: |
撰写
预览
正在加载...
取消
保存
Reference in new issue