当前位置:网站首页>Study notes: unity customsrp-13-colorgrading
Study notes: unity customsrp-13-colorgrading
2022-04-23 05:16:00 【Niu Shenzi】
original text ----- translate ----- Source code
This section is also about image processing : Color grading
Introduce several common image processing effects
summary :
1. Common effect : Post exposure 、 white balance 、 Contrast 、 Color filtering 、 Color separation 、 Channel mixing 、 Shadow center highlight 、 Hue shift 、 saturation .
notes : The hue separation is similar to the shadow middle highlight , The difference is that tone separation deals with shadows and highlights , And can't control the scope , The shadow center highlight is three areas , And can control the range .
2.LUT: The above calculation is performed for each pixel, which consumes a lot , So using LUT(lookup table), Render to map first , Sampling is enough .
Part of the source code :
using UnityEngine;
using UnityEngine.Rendering;
using static PostFXSettings;
partial class PostFXStack
{
//shader In this order, we should also put Pass
enum Pass
{
BloomAdd,
BloomHorizontal,
BloomPrefilter,
BloomPrefilterFireflies,
BloomScatter,
BloomScatterFinal,
BloomVertical,
Copy,
ColorGradingNone,
ColorGradingACES,
ColorGradingNeutral,
ColorGradingReinhard,
Final
}
const string bufferName = "Post FX";
CommandBuffer buffer = new CommandBuffer
{
name = bufferName
};
ScriptableRenderContext context;
Camera camera;
PostFXSettings settings;
public bool IsActive => settings != null;
int
bloomBicubicUpsamplingId = Shader.PropertyToID("_BloomBicubicUpsampling"),
bloomPrefilterId = Shader.PropertyToID("_BloomPrefilter"),
bloomThresholdId = Shader.PropertyToID("_BloomThreshold"),
bloomIntensityId = Shader.PropertyToID("_BloomIntensity"),
bloomResultId = Shader.PropertyToID("_BloomResult"),
fxSourceId = Shader.PropertyToID("_PostFXSource"),
fxSource2Id = Shader.PropertyToID("_PostFXSource2"),
colorAdjustmentsId = Shader.PropertyToID("_ColorAdjustments"),
colorFilterId = Shader.PropertyToID("_ColorFilter"),
whiteBalanceId = Shader.PropertyToID("_WhiteBalance"),
splitToningShadowsId = Shader.PropertyToID("_SplitToningShadow"),
splitToningHighlightsId = Shader.PropertyToID("_SplitToningHighlights"),
channelMixerRedId = Shader.PropertyToID("_ChannelMixerRed"),
channelMixerGreenId = Shader.PropertyToID("_ChannelMixerGreen"),
channelMixerBlueId = Shader.PropertyToID("_ChannelMixerBlue"),
smhShadowsId = Shader.PropertyToID("_SMHShadows"),
smhMidtonesId = Shader.PropertyToID("_SMHMidtones"),
smhHighlightsId = Shader.PropertyToID("_SMHHighlights"),
smhRangeId = Shader.PropertyToID("_SMHRange"),
colorGradingLUTId = Shader.PropertyToID("_ColorGradingLUT"),
colorGradingLUTParametersId = Shader.PropertyToID("_ColorGradingLUTParameters"),
colorGradingLUTInLogCId = Shader.PropertyToID("_ColorGradingLUTInLogC");
const int maxBloomPyramidLevels = 16;
int bloomPyramidId;
bool useHDR;
int colorLUTResolution;
public PostFXStack()
{
bloomPyramidId = Shader.PropertyToID("_BloomPyramid0");
for (int i = 1; i < maxBloomPyramidLevels * 2; i++)
{
Shader.PropertyToID("_BloomPyramid" + i);
}
}
public void Setup(ScriptableRenderContext context, Camera camera, PostFXSettings settings, bool useHDR, int colorLUTResolution)
{
this.colorLUTResolution = colorLUTResolution;
this.useHDR = useHDR;
this.context = context;
this.camera = camera;
this.settings = camera.cameraType <= CameraType.SceneView ? settings : null;
ApplySceneViewState();
}
public void Render(int sourceId)
{
//buffer.Blit(sourceId, BuiltinRenderTextureType.CameraTarget);
//Draw(sourceId, BuiltinRenderTextureType.CameraTarget, Pass.Copy);
if (DoBloom(sourceId))
{
DoColorGradingAndToneMapping(bloomResultId);
buffer.ReleaseTemporaryRT(bloomResultId);
}
else
{
DoColorGradingAndToneMapping(sourceId);
}
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
}
void Draw(RenderTargetIdentifier from, RenderTargetIdentifier to, Pass pass)
{
buffer.SetGlobalTexture(fxSourceId, from);
buffer.SetRenderTarget(to, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store);
buffer.DrawProcedural(Matrix4x4.identity, settings.Material, (int)pass, MeshTopology.Triangles, 3);
}
bool DoBloom(int sourceId)
{
//buffer.BeginSample("Bloom");
PostFXSettings.BloomSettings bloom = settings.Bloom;
int width = camera.pixelWidth / 2, height = camera.pixelHeight / 2;
if (bloom.maxIterations == 0 || bloom.intensity <= 0f || height < bloom.downscaleLimit * 2 || width < bloom.downscaleLimit * 2)
{
//Draw(sourceId, BuiltinRenderTextureType.CameraTarget, Pass.Copy);
//buffer.EndSample("Bloom");
return false;
}
buffer.BeginSample("Bloom");
Vector4 threshold;
threshold.x = Mathf.GammaToLinearSpace(bloom.threshold);
threshold.y = threshold.x * bloom.thresholdKnee;
threshold.z = 2f * threshold.y;
threshold.w = 0.25f / (threshold.y + 0.00001f);
threshold.y -= threshold.x;
buffer.SetGlobalVector(bloomThresholdId, threshold);
RenderTextureFormat format = useHDR ? RenderTextureFormat.DefaultHDR : RenderTextureFormat.Default;
// Half resolution
buffer.GetTemporaryRT(bloomPrefilterId, width, height, 0, FilterMode.Bilinear, format);
Draw(sourceId, bloomPrefilterId, bloom.fadeFireflies ? Pass.BloomPrefilterFireflies : Pass.BloomPrefilter);
width /= 2;
height /= 2;
int fromId = bloomPrefilterId, toId = bloomPyramidId + 1;
int i;
for (i = 0; i < bloom.maxIterations; i++)
{
if (height < bloom.downscaleLimit || width < bloom.downscaleLimit)
{
break;
}
int midId = toId - 1;
buffer.GetTemporaryRT(midId, width, height, 0, FilterMode.Bilinear, format);
buffer.GetTemporaryRT(toId, width, height, 0, FilterMode.Bilinear, format);
Draw(fromId, midId, Pass.BloomHorizontal);
Draw(midId, toId, Pass.BloomVertical);
fromId = toId;
toId += 2;
width /= 2;
height /= 2;
}
buffer.ReleaseTemporaryRT(bloomPrefilterId);
//Draw(fromId, BuiltinRenderTextureType.CameraTarget, Pass.Copy);
buffer.SetGlobalFloat(bloomBicubicUpsamplingId, bloom.bicubicUpsampling ? 1f : 0f);
Pass combinePass, finalPass;
float finalIntensity;
if (bloom.mode == PostFXSettings.BloomSettings.Mode.Additive)
{
combinePass = finalPass = Pass.BloomAdd;
buffer.SetGlobalFloat(bloomIntensityId, 1f);
finalIntensity = bloom.intensity;
}
else
{
combinePass = Pass.BloomScatter;
finalPass = Pass.BloomScatterFinal;
buffer.SetGlobalFloat(bloomIntensityId, bloom.scatter);
finalIntensity = Mathf.Min(bloom.intensity, 0.95f);
}
if (i > 1)
{
buffer.ReleaseTemporaryRT(fromId - 1);
toId -= 5;
for (i -= 1; i > 0; i--)
{
buffer.SetGlobalTexture(fxSource2Id, toId + 1);
Draw(fromId, toId, combinePass);
buffer.ReleaseTemporaryRT(fromId);
buffer.ReleaseTemporaryRT(fromId + 1);
fromId = toId;
toId -= 2;
}
}
else
{
buffer.ReleaseTemporaryRT(bloomPyramidId);
}
buffer.SetGlobalFloat(bloomIntensityId, finalIntensity);
buffer.SetGlobalTexture(fxSource2Id, sourceId);
buffer.GetTemporaryRT(bloomResultId, camera.pixelWidth, camera.pixelHeight, 0, FilterMode.Bilinear, format);
Draw(fromId, bloomResultId, finalPass);
buffer.ReleaseTemporaryRT(fromId);
buffer.EndSample("Bloom");
return true;
}
void ConfigureColorAdjustments()
{
ColorAdjustmentsSetttings colorAdjustments = settings.ColorAdjustments;
buffer.SetGlobalVector(colorAdjustmentsId, new Vector4(
Mathf.Pow(2f, colorAdjustments.postExposure), // Exposure value
colorAdjustments.contrast * 0.01f + 1f, // Contrast 0~2
colorAdjustments.hueShift * (1f / 360f), // Hue 0~2
colorAdjustments.saturation * 0.01f + 1f // saturation -1~1
));
buffer.SetGlobalColor(colorFilterId, colorAdjustments.colorFilter.linear);
}
void ConfigureWhiteBalance()
{
// white balance :temperature adjustment : blue <-> yellow
// tint: magenta <-> green
WhiteBalanceSettings whiteBalance = settings.WhiteBalance;
buffer.SetGlobalVector(whiteBalanceId, ColorUtils.ColorBalanceToLMSCoeffs(whiteBalance.temperature, whiteBalance.tint));
}
void ConfigureSplitToning()
{
SplitToningSettings splitToning = settings.SplitToning;
Color splitColor = splitToning.shadows;
splitColor.a = splitToning.balance * 0.01f;
buffer.SetGlobalColor(splitToningShadowsId, splitColor);
buffer.SetGlobalColor(splitToningHighlightsId, splitToning.highlights);
}
void ConfigureChannelMixer()
{
ChannelMixerSettings channelMixer = settings.ChannelMixer;
buffer.SetGlobalVector(channelMixerRedId, channelMixer.red);
buffer.SetGlobalVector(channelMixerGreenId, channelMixer.green);
buffer.SetGlobalVector(channelMixerBlueId, channelMixer.blue);
}
void ConfigureShadowsMidtonesHighlights()
{
ShadowsMidtonesHighlightsSettings smh = settings.ShadowsMidtonesHighlights;
buffer.SetGlobalColor(smhShadowsId, smh.shadows.linear);
buffer.SetGlobalColor(smhMidtonesId, smh.midtones.linear);
buffer.SetGlobalColor(smhHighlightsId, smh.highlights.linear);
buffer.SetGlobalVector(smhRangeId, new Vector4(
smh.shadowsStart, smh.shadowsEnd, smh.highlightsStart, smh.highLightsEnd
));
}
void DoColorGradingAndToneMapping(int sourceId)
{
ConfigureColorAdjustments();
ConfigureWhiteBalance();
ConfigureSplitToning();
ConfigureChannelMixer();
ConfigureShadowsMidtonesHighlights();
int lutHeight = colorLUTResolution;
int lutWidth = lutHeight * lutHeight;
buffer.GetTemporaryRT(colorGradingLUTId, lutWidth, lutHeight, 0, FilterMode.Bilinear, RenderTextureFormat.DefaultHDR);
buffer.SetGlobalVector(colorGradingLUTParametersId, new Vector4(
lutHeight, 0.5f / lutWidth, 0.5f / lutHeight, lutHeight / (lutHeight - 1f)
));
//ToneMappingSettings.Mode mode = settings.ToneMapping.mode;
//Pass pass = Pass.ColorGradingNone + (int)mode;
//Draw(sourceId, BuiltinRenderTextureType.CameraTarget, pass);
ToneMappingSettings.Mode mode = settings.ToneMapping.mode;
Pass pass = Pass.ColorGradingNone + (int)mode;
// Enable hdr Tone mapping , Enable LogC Space
buffer.SetGlobalFloat(colorGradingLUTInLogCId, useHDR && pass != Pass.ColorGradingNone ? 1f : 0f);
//first render to LUT
Draw(sourceId, colorGradingLUTId, pass);
// Modify parameter value
buffer.SetGlobalVector(colorGradingLUTParametersId, new Vector4(1f / lutWidth, 1f / lutHeight, lutHeight - 1f));
//second copy to camera
Draw(sourceId, BuiltinRenderTextureType.CameraTarget, Pass.Final);
buffer.ReleaseTemporaryRT(colorGradingLUTId);
}
}
#ifndef CUSTOM_POST_FX_PASSES_INCLUDED
#define CUSTOM_POST_FX_PASSES_INCLUDED
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
TEXTURE2D(_PostFXSource);
TEXTURE2D(_PostFXSource2);
SAMPLER(sampler_linear_clamp);
float4 _PostFXSource_TexelSize;
float4 GetSourceTexelSize()
{
return _PostFXSource_TexelSize;
}
float4 GetSource(float2 screenUV)
{
return SAMPLE_TEXTURE2D_LOD(_PostFXSource, sampler_linear_clamp, screenUV, 0);
}
float4 GetSourceBicubic(float2 screenUV)
{
return SampleTexture2DBicubic(TEXTURE2D_ARGS(_PostFXSource, sampler_linear_clamp), screenUV, _PostFXSource_TexelSize.zwxy, 1.0, 0.0);
}
float4 GetSource2(float2 screenUV)
{
return SAMPLE_TEXTURE2D_LOD(_PostFXSource2, sampler_linear_clamp, screenUV, 0);
}
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 screenUV : VAR_SCREEN_UV;
};
Varyings DefaultPassVertex(uint vertexID : SV_VertexID)
{
Varyings output;
output.positionCS = float4(
vertexID <= 1 ? -1.0 : 3.0,
vertexID == 1 ? 3.0 : -1.0,
0.0, 1.0
);
output.screenUV = float2(
vertexID <= 1 ? 0.0 : 2.0,
vertexID == 1 ? 2.0 : 0.0
);
if (_ProjectionParams.x < 0.0)
{
output.screenUV.y = 1.0 - output.screenUV.y;
}
return output;
}
bool _BloomBicubicUpSampling;
float _BloomIntensity;
float4 BloomAddPassFragment(Varyings input) :SV_TARGET
{
float3 lowRes;
if (_BloomBicubicUpSampling)
{
lowRes = GetSourceBicubic(input.screenUV).rgb;
}
else
{
lowRes = GetSource(input.screenUV).rgb;
}
float3 highRes = GetSource2(input.screenUV).rgb;
return float4(lowRes * _BloomIntensity + highRes, 1.0);
}
float4 BloomHorizontalPassFragment(Varyings input) :SV_TARGET
{
float3 color = 0.0;
float offsets[] =
{
-4.0,-3.0,-2.0,-1.0,0.0,1.0,2.0,3.0,4.0
};
float weights[] =
{
0.01621622, 0.05405405, 0.12162162, 0.19459459, 0.22702703,
0.19459459, 0.12162162, 0.05405405, 0.01621622
};
for (int i = 0; i < 9; i++)
{
float offset = offsets[i] * 2.0 * GetSourceTexelSize().x;
color += GetSource(input.screenUV + float2(offset, 0.0)).rgb * weights[i];
}
return float4(color, 1.0);
}
float4 _BloomThreshold;
float3 ApplyBloomThreshold(float3 color)
{
float brightness = Max3(color.r, color.g, color.b);
float soft = brightness + _BloomThreshold.y;
soft = clamp(soft, 0.0, _BloomThreshold.z);
soft = soft * soft * _BloomThreshold.w;
float contribution = max(soft, brightness - _BloomThreshold.x);
contribution /= max(brightness, 0.00001);
return color * contribution;
}
float4 BloomPrefilterPassFragment(Varyings input) :SV_TARGET
{
float3 color = ApplyBloomThreshold(GetSource(input.screenUV).rgb);
return float4(color, 1.0);
}
float4 BloomPrefilterFirefliesPassFragment(Varyings input) :SV_TARGET
{
float3 color = 0.0;
float weightSum = 0.0;
float2 offsets[] = {
float2(0.0, 0.0),
float2(-1.0, -1.0), float2(-1.0, 1.0), float2(1.0, -1.0), float2(1.0, 1.0)//,
//float2(-1.0, 0.0), float2(1.0, 0.0), float2(0.0, -1.0), float2(0.0, 1.0)
};
for (int i = 0; i < 5; i++)
{
float3 c = GetSource(input.screenUV + offsets[i] * GetSourceTexelSize().xy * 2.0).rgb;
c = ApplyBloomThreshold(c);
float w = 1.0 / (Luminance(c) + 1.0);
color += c * w;
weightSum += w;
}
//color *= 1.0 / 9.0;
color /= weightSum;
return float4(color, 1.0);
}
float4 BloomScatterPassFragment(Varyings input) :SV_TARGET
{
float3 lowRes;
if (_BloomBicubicUpSampling)
{
lowRes = GetSourceBicubic(input.screenUV).rgb;
}
else
{
lowRes = GetSource(input.screenUV).rgb;
}
float3 highRes = GetSource2(input.screenUV).rgb;
return float4(lerp(highRes,lowRes,_BloomIntensity), 1.0);
}
float4 BloomScatterFinalPassFragment(Varyings input) :SV_TARGET
{
float3 lowRes;
if (_BloomBicubicUpSampling)
{
lowRes = GetSourceBicubic(input.screenUV).rgb;
}
else
{
lowRes = GetSource(input.screenUV).rgb;
}
float3 highRes = GetSource2(input.screenUV).rgb;
lowRes += highRes - ApplyBloomThreshold(highRes);
return float4(lerp(highRes,lowRes,_BloomIntensity), 1.0);
}
float4 BloomVerticalPassFragment(Varyings input) :SV_TARGET
{
float3 color = 0.0;
float offsets[] =
{
-3.23076923, -1.38461538, 0.0, 1.38461538, 3.23076923
};
float weights[] =
{
0.07027027, 0.31621622, 0.22702703, 0.31621622, 0.07027027
};
for (int i = 0; i < 5; i++)
{
float offset = offsets[i] * GetSourceTexelSize().y;
color += GetSource(input.screenUV + float2(0.0, offset)).rgb * weights[i];
}
return float4(color, 1.0);
}
float4 CopyPassFragment(Varyings input) : SV_TARGET
{
return GetSource(input.screenUV);
}
float4 _ColorAdjustments;
float4 _ColorFilter;
float4 _WhiteBalance;
float4 _SplitToningShadows, _SplitToningHighlights;
float4 _ChannelMixerRed, _ChannelMixerGreen, _ChannelMixerBlue;
float4 _SMHShadows, _SMHMidtones, _SMHHighlights, _SMHRange;
float Luminance(float3 color, bool useACES)
{
return useACES ? AcesLuminance(color) : Luminance(color);
}
// Exposure
float3 ColorGradePostExposure(float3 color)
{
return color * _ColorAdjustments.x;
}
// white balance
float3 ColorGradeWhiteBalance(float3 color)
{
// The switch to LMS Space calculation ,LMS It is the color that describes the reaction of three kinds of photosensitive cones in human eyes
color = LinearToLMS(color);
color *= _WhiteBalance.rgb;
return LMSToLinear(color);
}
// Contrast
float3 ColorGradingContrast(float3 color, bool useACES)
{
// The transformation is in Log C in , First convert to Log C The value in
color = useACES ? ACES_to_ACEScc(unity_to_ACES(color)) : LinearToLogC(color);
// Contrast : In the middle is the interpolation between the gray and the original color , In fact, the following formula is lerp(ACEScc_MIDGRAY,color,_ColorAdjustments.y);
// Contrast here y The range of values :0~2, Color range : Middle grey <-> 2 * Primary colors - Middle grey
color = (color - ACEScc_MIDGRAY) * _ColorAdjustments.y + ACEScc_MIDGRAY;
// It's switching back
return useACES ? ACES_to_ACEScg(ACEScc_to_ACES(color)) : LogCToLinear(color);
}
// Color filtering
float3 ColorGradeColorFilter(float3 color)
{
// Color filtering : Control the original color The strength of the color channel
//1. For example, the filter color is (1,1,0), The result is that the original blue channel color disappears
//2. The filter color is (0.5,1,1), The result is that the original red channel color is halved
return color * _ColorFilter.rgb;
}
// Color separation : Shadows and highlights
float3 ColorGradeSplitToning(float3 color, bool useACES)
{
// Convert linear colors to gamma Space
color = PositivePow(color, 1.0 / 2.2);
float t = saturate(Luminance(saturate(color), useACES) + _SplitToningShadows.w);
float3 shadows = lerp(0.5, _SplitToningShadows.rgb, 1.0 - t);
float3 highlights = lerp(0.5, _SplitToningHighlights.rgb, t);
// Mix colors
color = SoftLight(color, shadows);
color = SoftLight(color, highlights);
// Go back to linear space
return PositivePow(color, 2.2);
}
// Hue shift
//HSV(Hue, Saturation, Value) tonal (H)、 saturation (S) And lightness (V)
float3 ColorGradingHueShift(float3 color)
{
color = RgbToHsv(color);
// Hue shift is tone h, That is to say x, The hue range is 0~1
// The adjustment range is :-1~1
float hue = color.x + _ColorAdjustments.z;
// Adjust the hue value at 0~1 Range , Specific to see RotateHue Source code
color.x = RotateHue(hue, 0.0, 1.0);
return HsvToRgb(color);
}
// saturation
float3 ColorGradingSaturation(float3 color, bool useACES)
{
// Calculate the brightness of the color
float luminance = Luminance(color, useACES);
// This is the same as the contrast , It's also interpolation ,w The scope is also 0~2
// Color range : brightness <-> 2* Original color - brightness
return (color - luminance) * _ColorAdjustments.w + luminance;
}
// Channel mixing : Matrix multiplication
float3 ColorGradingChannelMixer(float3 color)
{
return mul(
float3x3(_ChannelMixerRed.rgb, _ChannelMixerGreen.rgb, _ChannelMixerBlue.rgb),
color
);
}
// Shadow center highlight
float3 ColorGradingShadowsMidtonesHighlights(float3 color, bool useACES)
{
float luminance = Luminance(color, useACES);
float shadowsWeight = 1.0 - smoothstep(_SMHRange.x, _SMHRange.y, luminance);
float highlightsWeight = smoothstep(_SMHRange.z, _SMHRange.w, luminance);
float midtonesWeight = 1.0 - shadowsWeight - highlightsWeight;
return
color * _SMHShadows.rgb * shadowsWeight +
color * _SMHMidtones.rgb * midtonesWeight +
color * _SMHHighlights.rgb * highlightsWeight;
}
// Color grading
float3 ColorGrade(float3 color, bool useACES = false)
{
//color = min(color, 60.0); // use LUT after , suffer LUT The limitation of
color = ColorGradePostExposure(color); // Exposure effect
color = ColorGradeWhiteBalance(color); // white balance
color = ColorGradingContrast(color, useACES); // Contrast adjustment
color = ColorGradeColorFilter(color); // Color filtering
color = max(color, 0.0); // The contrast may be negative after adjustment
color = ColorGradeSplitToning(color, useACES); // Color separation
color = ColorGradingChannelMixer(color); // Channel mixing
color = max(color, 0.0); // Exclude negative values
color = ColorGradingShadowsMidtonesHighlights(color, useACES);// Shadow center highlight ( Same tone separation )
color = ColorGradingHueShift(color); // After excluding colors that are not negative , Then perform hue shift
color = ColorGradingSaturation(color, useACES); // Apply saturation
color = max(useACES ? ACEScg_to_ACES(color) : color, 0.0); // Applying saturation produces a negative value , Limit
return color;
}
float4 _ColorGradingLUTParameters;
bool _ColorGradingLUTInLogC;
float3 GetColorGradedLUT(float2 uv, bool useACES = false)
{
float3 color = GetLutStripValue(uv, _ColorGradingLUTParameters);
// hold color Interpreted as Log C Spatial value , Expand the scope , Turned out to be 0~1, The extended 0~59
color = _ColorGradingLUTInLogC ? LogCToLinear(color) : color;
return ColorGrade(color, useACES);
}
float4 ColorGradingNonePassFragment(Varyings input) : SV_TARGET{
/*float4 color = GetSource(input.screenUV); color.rgb = ColorGrade(color.rgb);*/
//return color;
float3 color = GetColorGradedLUT(input.screenUV);
return float4(color, 1.0);
}
float4 ColorGradingACESPassFragment(Varyings input) : SV_TARGET
{
//float4 color = GetSource(input.screenUV);
///*color.rgb = ColorGrade(color.rgb);
//color.rgb = AcesTonemap(unity_to_ACES(color.rgb));*/
//color.rgb = ColorGrade(color.rgb,true);
//color.rgb = AcesTonemap(color.rgb);
//return color;
float3 color = GetColorGradedLUT(input.screenUV, true);
color = AcesTonemap(color);
return float4(color, 1.0);
}
float4 ColorGradingNeutralPassFragment(Varyings input) :SV_TARGET
{
/*float4 color = GetSource(input.screenUV); color.rgb = ColorGrade(color.rgb); color.rgb = NeutralTonemap(color.rgb); return color;*/
float3 color = GetColorGradedLUT(input.screenUV);
color = NeutralTonemap(color);
return float4(color, 1.0);
}
float4 ColorGradingReinhardPassFragment(Varyings input) :SV_TARGET
{
/*float4 color = GetSource(input.screenUV); color.rgb = ColorGrade(color.rgb); color.rgb /= color.rgb + 1.0; return color;*/
float3 color = GetColorGradedLUT(input.screenUV);
color /= color + 1.0;
return float4(color, 1.0);
}
TEXTURE2D(_ColorGradingLUT);
float3 ApplyColorGradingLUT(float3 color)
{
// hold 2D LUT Strip graph Interpreted as 3D chart : The first two parameters are LUT Maps and samplers , Behind it is the color ( Linear or LogC Space ), Finally, the parameters xyz
return ApplyLut2D(TEXTURE2D_ARGS(_ColorGradingLUT, sampler_linear_clamp),
saturate(_ColorGradingLUTInLogC ? LinearToLogC(color) : color),
_ColorGradingLUTParameters.xyz);
}
float4 FinalPassFragment(Varyings input) : SV_TARGET{
float4 color = GetSource(input.screenUV);
color.rgb = ApplyColorGradingLUT(color.rgb);
return color;
}
#endif
using System;
using UnityEngine;
[CreateAssetMenu(menuName = "Rendering/Custom Post FX Settings")]
public class PostFXSettings : ScriptableObject
{
[SerializeField]
Shader shader = default;
[NonSerialized]
Material material;
public Material Material
{
get
{
if (material == null && shader != null)
{
material = new Material(shader);
material.hideFlags = HideFlags.HideAndDontSave;
}
return material;
}
}
#region Bloom
[System.Serializable]
public struct BloomSettings
{
[Range(0f, 16f)]
public int maxIterations;
[Min(0f)]
public int downscaleLimit;
public bool bicubicUpsampling;
[Min(0f)]
public float threshold;
[Range(0f, 1f)]
public float thresholdKnee;
[Min(0f)]
public float intensity;
public bool fadeFireflies;
public enum Mode {
Additive, Scattering }
public Mode mode;
[Range(0.05f, 0.95f)]
public float scatter;
}
[SerializeField]
BloomSettings bloom = new BloomSettings {
scatter = 0.7f };
public BloomSettings Bloom => bloom;
#endregion
#region Color grading
[Serializable]
public struct ColorAdjustmentsSetttings
{
public float postExposure;
[Range(-100f, 100f)]
public float contrast;
[ColorUsage(false, true)]
public Color colorFilter;
[Range(-180f, 180f)]
public float hueShift;
[Range(-100f, 100f)]
public float saturation;
}
[SerializeField]
ColorAdjustmentsSetttings colorAdjustments = new ColorAdjustmentsSetttings
{
colorFilter = Color.white
};
public ColorAdjustmentsSetttings ColorAdjustments => colorAdjustments;
#endregion
#region white balance
[Serializable]
public struct WhiteBalanceSettings
{
[Range(-100f, 100f)]
public float temperature, tint;
}
[SerializeField]
WhiteBalanceSettings whiteBalance = default;
public WhiteBalanceSettings WhiteBalance => whiteBalance;
#endregion
#region Color separation
[Serializable]
public struct SplitToningSettings
{
// A common example is to make shadows tend to blue , Highlights tend to orange
[ColorUsage(false)]
public Color shadows, highlights;
[Range(-100f, 100f)]
public float balance;
}
[SerializeField]
SplitToningSettings splitToning = new SplitToningSettings
{
shadows = Color.gray,
highlights = Color.gray
};
public SplitToningSettings SplitToning => splitToning;
#endregion
#region Channel mixing
[Serializable]
public struct ChannelMixerSettings
{
public Vector3 red, green, blue;
}
[SerializeField]
ChannelMixerSettings channelMixer = new ChannelMixerSettings
{
red = Vector3.right,
green = Vector3.up,
blue = Vector3.forward
};
public ChannelMixerSettings ChannelMixer => channelMixer;
#endregion
#region Shadow center highlight
[Serializable]
public struct ShadowsMidtonesHighlightsSettings
{
[ColorUsage(false, true)]
public Color shadows, midtones, highlights;
[Range(0f, 2f)]
public float shadowsStart, shadowsEnd, highlightsStart, highLightsEnd;
}
[SerializeField]
ShadowsMidtonesHighlightsSettings shadowsMidtonesHighlights = new ShadowsMidtonesHighlightsSettings
{
shadows = Color.white,
midtones = Color.white,
highlights = Color.white,
shadowsEnd = 0.3f,
highlightsStart = 0.55f,
highLightsEnd = 1f
};
public ShadowsMidtonesHighlightsSettings ShadowsMidtonesHighlights =>
shadowsMidtonesHighlights;
#endregion
#region Tone mapping
[Serializable]
public struct ToneMappingSettings
{
public enum Mode {
None, ACES, Neutral, Reinhard }
public Mode mode;
}
[SerializeField]
ToneMappingSettings toneMapping = default;
public ToneMappingSettings ToneMapping => toneMapping;
#endregion
}
Shader "Custom RP/Post FX Stack" {
SubShader
{
Cull Off
ZTest Always
ZWrite Off
HLSLINCLUDE
#include "../ShaderLibrary/Common.hlsl"
#include "PostFXStackPasses.hlsl"
ENDHLSL
Pass
{
Name "Bloom Add"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomAddPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Horizontal"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomHorizontalPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Prefilter"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomPrefilterPassFragment
ENDHLSL
}
Pass
{
Name "Bloom PrefilterFireflies"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomPrefilterFirefliesPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Scatter"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomScatterPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Scatter Final"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomScatterFinalPassFragment
ENDHLSL
}
Pass
{
Name "Bloom Vertical"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment BloomVerticalPassFragment
ENDHLSL
}
Pass
{
Name "Copy"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment CopyPassFragment
ENDHLSL
}
Pass
{
Name "ColorGrading None"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ColorGradingNonePassFragment
ENDHLSL
}
Pass
{
Name "ColorGrading ACES"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ColorGradingACESPassFragment
ENDHLSL
}
Pass
{
Name "ColorGrading Neutral"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ColorGradingNeutralPassFragment
ENDHLSL
}
Pass
{
Name "ColorGrading Reinhard"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment ColorGradingReinhardPassFragment
ENDHLSL
}
Pass
{
Name "Final"
HLSLPROGRAM
#pragma target 3.5
#pragma vertex DefaultPassVertex
#pragma fragment FinalPassFragment
ENDHLSL
}
}
}
版权声明
本文为[Niu Shenzi]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/04/202204230511336061.html
边栏推荐
- This call when the transaction does not take effect
- SCP command details
- Basic concepts of multithreading (concurrency and parallelism, threads and processes) and entry cases
- 无线网怎么用手机验证码登录解决方案
- Chapter III project schedule management of information system project manager summary
- The 8 diagrams let you see the execution sequence of async / await and promise step by step
- MySQL uses or to query SQL, and SQL execution is very slow
- Power consumption parameters of Jinbei household mute box series
- Unique primary key ID of tidb sub table -- solution to failure of sequence and Gorm to obtain primary key
- MySQL slow query
猜你喜欢
源码剖析Redis中如何使用跳表的
Using PHP post temporary file mechanism to upload arbitrary files
数字化转型失败,有哪些原因?
工具在数字化转型中扮演了什么样的角色?
Define defines constants and macros, pointers and structures
JS Array常见方法
深度学习笔记 —— 物体检测和数据集 + 锚框
改进DevSecOps框架的 5 大关键技术
Transaction isolation level of MySQL transactions
Deep learning notes - semantic segmentation and data sets
随机推荐
Traversal of tree
Let the LAN group use the remote device
改进DevSecOps框架的 5 大关键技术
工具在数字化转型中扮演了什么样的角色?
[database] MySQL single table query
【openh264】cmake: msopenh264-static
Interview summary
Mariadb的半同步复制
数字化转型失败,有哪些原因?
Differences between redis and MySQL
[database] MySQL multi table query (I)
Minimum spanning tree -- unblocked project hdu1863
青岛敏捷之旅,来了!
Discussion on flow restriction
Nacos source code startup error report solution
Swing display time (click once to display once)
Golang select priority execution
Golang memory escape
MySQL views the SQL statement details executed by the optimizer
The difference between static pipeline and dynamic pipeline