Exemple #1
0
        // HDR color pipeline is rendered to a 2D strip lut (works on HDR platforms without compute
        // and 3D texture support). Precision is sliiiiiiightly lower than when using a 3D texture
        // LUT (33^3 -> 32^3) but most of the time it's imperceptible.
        void RenderHDRPipeline2D(PostProcessRenderContext context)
        {
            // For the same reasons as in RenderHDRPipeline3D, regen LUT on evey frame
            {
                CheckInternalStripLut();

                // Lut setup
                var lutSheet = context.propertySheets.Get(context.resources.shaders.lut2DBaker);
                lutSheet.ClearKeywords();

                lutSheet.properties.SetVector(ShaderIDs.Lut2D_Params, new Vector4(k_Lut2DSize, 0.5f / (k_Lut2DSize * k_Lut2DSize), 0.5f / k_Lut2DSize, k_Lut2DSize / (k_Lut2DSize - 1f)));

                var colorBalance = ColorUtilities.ComputeColorBalance(settings.temperature.value, settings.tint.value);
                lutSheet.properties.SetVector(ShaderIDs.ColorBalance, colorBalance);
                lutSheet.properties.SetVector(ShaderIDs.ColorFilter, settings.colorFilter.value);

                float hue = settings.hueShift.value / 360f;         // Remap to [-0.5;0.5]
                float sat = settings.saturation.value / 100f + 1f;  // Remap to [0;2]
                float con = settings.contrast.value / 100f + 1f;    // Remap to [0;2]
                lutSheet.properties.SetVector(ShaderIDs.HueSatCon, new Vector3(hue, sat, con));

                var channelMixerR = new Vector3(settings.mixerRedOutRedIn, settings.mixerRedOutGreenIn, settings.mixerRedOutBlueIn);
                var channelMixerG = new Vector3(settings.mixerGreenOutRedIn, settings.mixerGreenOutGreenIn, settings.mixerGreenOutBlueIn);
                var channelMixerB = new Vector3(settings.mixerBlueOutRedIn, settings.mixerBlueOutGreenIn, settings.mixerBlueOutBlueIn);
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerRed, channelMixerR / 100f);            // Remap to [-2;2]
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerGreen, channelMixerG / 100f);
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerBlue, channelMixerB / 100f);

                var lift     = ColorUtilities.ColorToLift(settings.lift.value * 0.2f);
                var gain     = ColorUtilities.ColorToGain(settings.gain.value * 0.8f);
                var invgamma = ColorUtilities.ColorToInverseGamma(settings.gamma.value * 0.8f);
                lutSheet.properties.SetVector(ShaderIDs.Lift, lift);
                lutSheet.properties.SetVector(ShaderIDs.InvGamma, invgamma);
                lutSheet.properties.SetVector(ShaderIDs.Gain, gain);

                lutSheet.properties.SetTexture(ShaderIDs.Curves, GetCurveTexture(false));

                var tonemapper = settings.tonemapper.value;
                if (tonemapper == Tonemapper.Custom)
                {
                    lutSheet.EnableKeyword("TONEMAPPING_CUSTOM");

                    m_HableCurve.Init(
                        settings.toneCurveToeStrength.value,
                        settings.toneCurveToeLength.value,
                        settings.toneCurveShoulderStrength.value,
                        settings.toneCurveShoulderLength.value,
                        settings.toneCurveShoulderAngle.value,
                        settings.toneCurveGamma.value
                        );

                    lutSheet.properties.SetVector(ShaderIDs.CustomToneCurve, m_HableCurve.uniforms.curve);
                    lutSheet.properties.SetVector(ShaderIDs.ToeSegmentA, m_HableCurve.uniforms.toeSegmentA);
                    lutSheet.properties.SetVector(ShaderIDs.ToeSegmentB, m_HableCurve.uniforms.toeSegmentB);
                    lutSheet.properties.SetVector(ShaderIDs.MidSegmentA, m_HableCurve.uniforms.midSegmentA);
                    lutSheet.properties.SetVector(ShaderIDs.MidSegmentB, m_HableCurve.uniforms.midSegmentB);
                    lutSheet.properties.SetVector(ShaderIDs.ShoSegmentA, m_HableCurve.uniforms.shoSegmentA);
                    lutSheet.properties.SetVector(ShaderIDs.ShoSegmentB, m_HableCurve.uniforms.shoSegmentB);
                }
                else if (tonemapper == Tonemapper.ACES)
                {
                    lutSheet.EnableKeyword("TONEMAPPING_ACES");
                }
                else if (tonemapper == Tonemapper.Neutral)
                {
                    lutSheet.EnableKeyword("TONEMAPPING_NEUTRAL");
                }

                // Generate the lut
                context.command.BeginSample("HdrColorGradingLut2D");
                context.command.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_InternalLdrLut, lutSheet, (int)Pass.LutGenHDR2D);
                context.command.EndSample("HdrColorGradingLut2D");
            }

            var lut       = m_InternalLdrLut;
            var uberSheet = context.uberSheet;

            uberSheet.EnableKeyword("COLOR_GRADING_HDR_2D");
            uberSheet.properties.SetVector(ShaderIDs.Lut2D_Params, new Vector3(1f / lut.width, 1f / lut.height, lut.height - 1f));
            uberSheet.properties.SetTexture(ShaderIDs.Lut2D, lut);
            uberSheet.properties.SetFloat(ShaderIDs.PostExposure, RuntimeUtilities.Exp2(settings.postExposure.value));
        }
Exemple #2
0
        // LDR color pipeline is rendered to a 2D strip lut (works on every platform)
        void RenderLDRPipeline2D(PostProcessRenderContext context)
        {
            // For the same reasons as in RenderHDRPipeline3D, regen LUT on every frame
            {
                CheckInternalStripLut();

                // Lut setup
                var lutSheet = context.propertySheets.Get(context.resources.shaders.lut2DBaker);
                lutSheet.ClearKeywords();

                lutSheet.properties.SetVector(ShaderIDs.Lut2D_Params,
                                              new Vector4(k_Lut2DSize, 0.5f / (k_Lut2DSize * k_Lut2DSize), 0.5f / k_Lut2DSize,
                                                          k_Lut2DSize / (k_Lut2DSize - 1f)));

                var colorBalance =
                    ColorUtilities.ComputeColorBalance(settings.temperature.value, settings.tint.value);
                lutSheet.properties.SetVector(ShaderIDs.ColorBalance, colorBalance);
                lutSheet.properties.SetVector(ShaderIDs.ColorFilter, settings.colorFilter.value);

                float hue = settings.hueShift.value / 360f;                 // Remap to [-0.5;0.5]
                float sat = settings.saturation.value / 100f + 1f;          // Remap to [0;2]
                float con = settings.contrast.value / 100f + 1f;            // Remap to [0;2]
                lutSheet.properties.SetVector(ShaderIDs.HueSatCon, new Vector3(hue, sat, con));

                var channelMixerR = new Vector3(settings.mixerRedOutRedIn, settings.mixerRedOutGreenIn,
                                                settings.mixerRedOutBlueIn);
                var channelMixerG = new Vector3(settings.mixerGreenOutRedIn, settings.mixerGreenOutGreenIn,
                                                settings.mixerGreenOutBlueIn);
                var channelMixerB = new Vector3(settings.mixerBlueOutRedIn, settings.mixerBlueOutGreenIn,
                                                settings.mixerBlueOutBlueIn);
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerRed, channelMixerR / 100f);                   // Remap to [-2;2]
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerGreen, channelMixerG / 100f);
                lutSheet.properties.SetVector(ShaderIDs.ChannelMixerBlue, channelMixerB / 100f);

                var lift     = ColorUtilities.ColorToLift(settings.lift.value);
                var gain     = ColorUtilities.ColorToGain(settings.gain.value);
                var invgamma = ColorUtilities.ColorToInverseGamma(settings.gamma.value);
                lutSheet.properties.SetVector(ShaderIDs.Lift, lift);
                lutSheet.properties.SetVector(ShaderIDs.InvGamma, invgamma);
                lutSheet.properties.SetVector(ShaderIDs.Gain, gain);

                lutSheet.properties.SetFloat(ShaderIDs.Brightness, (settings.brightness.value + 100f) / 100f);
                lutSheet.properties.SetTexture(ShaderIDs.Curves, GetCurveTexture(false));

                // Generate the lut
                context.command.BeginSample("LdrColorGradingLut2D");

                var userLut = settings.ldrLut.value;
                if (userLut == null || userLut.width != userLut.height * userLut.height)
                {
                    context.command.BlitFullscreenTriangle(BuiltinRenderTextureType.None, m_InternalLdrLut, lutSheet,
                                                           (int)Pass.LutGenLDRFromScratch);
                }
                else
                {
                    lutSheet.properties.SetVector(ShaderIDs.UserLut2D_Params,
                                                  new Vector4(1f / userLut.width, 1f / userLut.height, userLut.height - 1f,
                                                              settings.ldrLutContribution));
                    context.command.BlitFullscreenTriangle(userLut, m_InternalLdrLut, lutSheet, (int)Pass.LutGenLDR);
                }

                context.command.EndSample("LdrColorGradingLut2D");
            }

            var lut       = m_InternalLdrLut;
            var uberSheet = context.uberSheet;

            uberSheet.EnableKeyword("COLOR_GRADING_LDR_2D");
            uberSheet.properties.SetVector(ShaderIDs.Lut2D_Params,
                                           new Vector3(1f / lut.width, 1f / lut.height, lut.height - 1f));
            uberSheet.properties.SetTexture(ShaderIDs.Lut2D, lut);
        }
Exemple #3
0
        // HDR color pipeline is rendered to a 3D lut; it requires Texture3D & compute shaders
        // support - Desktop / Consoles / Some high-end mobiles
        void RenderHDRPipeline3D(PostProcessRenderContext context)
        {
            // Unfortunately because AnimationCurve doesn't implement GetHashCode and we don't have
            // any reliable way to figure out if a curve data is different from another one we can't
            // skip regenerating the Lut if nothing has changed. So it has to be done on every
            // frame...
            // It's not a very expensive operation anyway (we're talking about filling a 33x33x33
            // Lut on the GPU) but every little thing helps, especially on mobile.
            {
                CheckInternalLogLut();

                // Lut setup
                var compute = context.resources.computeShaders.lut3DBaker;
                int kernel  = 0;

                switch (settings.tonemapper.value)
                {
                case Tonemapper.None: kernel = compute.FindKernel("KGenLut3D_NoTonemap");
                    break;

                case Tonemapper.Neutral: kernel = compute.FindKernel("KGenLut3D_NeutralTonemap");
                    break;

                case Tonemapper.ACES: kernel = compute.FindKernel("KGenLut3D_AcesTonemap");
                    break;

                case Tonemapper.Custom: kernel = compute.FindKernel("KGenLut3D_CustomTonemap");
                    break;
                }

                int groupSizeXY = Mathf.CeilToInt(k_Lut3DSize / 8f);
                int groupSizeZ  = Mathf.CeilToInt(k_Lut3DSize / (RuntimeUtilities.isAndroidOpenGL ? 2f : 8f));
                var cmd         = context.command;
                cmd.SetComputeTextureParam(compute, kernel, "_Output", m_InternalLogLut);
                cmd.SetComputeVectorParam(compute, "_Size", new Vector4(k_Lut3DSize, 1f / (k_Lut3DSize - 1f), 0f, 0f));

                var colorBalance = ColorUtilities.ComputeColorBalance(settings.temperature.value, settings.tint.value);
                cmd.SetComputeVectorParam(compute, "_ColorBalance", colorBalance);
                cmd.SetComputeVectorParam(compute, "_ColorFilter", settings.colorFilter.value);

                float hue = settings.hueShift.value / 360f;         // Remap to [-0.5;0.5]
                float sat = settings.saturation.value / 100f + 1f;  // Remap to [0;2]
                float con = settings.contrast.value / 100f + 1f;    // Remap to [0;2]
                cmd.SetComputeVectorParam(compute, "_HueSatCon", new Vector4(hue, sat, con, 0f));

                var channelMixerR = new Vector4(settings.mixerRedOutRedIn, settings.mixerRedOutGreenIn, settings.mixerRedOutBlueIn, 0f);
                var channelMixerG = new Vector4(settings.mixerGreenOutRedIn, settings.mixerGreenOutGreenIn, settings.mixerGreenOutBlueIn, 0f);
                var channelMixerB = new Vector4(settings.mixerBlueOutRedIn, settings.mixerBlueOutGreenIn, settings.mixerBlueOutBlueIn, 0f);
                cmd.SetComputeVectorParam(compute, "_ChannelMixerRed", channelMixerR / 100f); // Remap to [-2;2]
                cmd.SetComputeVectorParam(compute, "_ChannelMixerGreen", channelMixerG / 100f);
                cmd.SetComputeVectorParam(compute, "_ChannelMixerBlue", channelMixerB / 100f);

                var lift     = ColorUtilities.ColorToLift(settings.lift.value * 0.2f);
                var gain     = ColorUtilities.ColorToGain(settings.gain.value * 0.8f);
                var invgamma = ColorUtilities.ColorToInverseGamma(settings.gamma.value * 0.8f);
                cmd.SetComputeVectorParam(compute, "_Lift", new Vector4(lift.x, lift.y, lift.z, 0f));
                cmd.SetComputeVectorParam(compute, "_InvGamma", new Vector4(invgamma.x, invgamma.y, invgamma.z, 0f));
                cmd.SetComputeVectorParam(compute, "_Gain", new Vector4(gain.x, gain.y, gain.z, 0f));

                cmd.SetComputeTextureParam(compute, kernel, "_Curves", GetCurveTexture(true));

                if (settings.tonemapper.value == Tonemapper.Custom)
                {
                    m_HableCurve.Init(
                        settings.toneCurveToeStrength.value,
                        settings.toneCurveToeLength.value,
                        settings.toneCurveShoulderStrength.value,
                        settings.toneCurveShoulderLength.value,
                        settings.toneCurveShoulderAngle.value,
                        settings.toneCurveGamma.value
                        );

                    cmd.SetComputeVectorParam(compute, "_CustomToneCurve", m_HableCurve.uniforms.curve);
                    cmd.SetComputeVectorParam(compute, "_ToeSegmentA", m_HableCurve.uniforms.toeSegmentA);
                    cmd.SetComputeVectorParam(compute, "_ToeSegmentB", m_HableCurve.uniforms.toeSegmentB);
                    cmd.SetComputeVectorParam(compute, "_MidSegmentA", m_HableCurve.uniforms.midSegmentA);
                    cmd.SetComputeVectorParam(compute, "_MidSegmentB", m_HableCurve.uniforms.midSegmentB);
                    cmd.SetComputeVectorParam(compute, "_ShoSegmentA", m_HableCurve.uniforms.shoSegmentA);
                    cmd.SetComputeVectorParam(compute, "_ShoSegmentB", m_HableCurve.uniforms.shoSegmentB);
                }

                // Generate the lut
                context.command.BeginSample("HdrColorGradingLut3D");
                cmd.DispatchCompute(compute, kernel, groupSizeXY, groupSizeXY, groupSizeZ);
                context.command.EndSample("HdrColorGradingLut3D");
            }

            var lut       = m_InternalLogLut;
            var uberSheet = context.uberSheet;

            uberSheet.EnableKeyword("COLOR_GRADING_HDR_3D");
            uberSheet.properties.SetTexture(ShaderIDs.Lut3D, lut);
            uberSheet.properties.SetVector(ShaderIDs.Lut3D_Params, new Vector2(1f / lut.width, lut.width - 1f));
            uberSheet.properties.SetFloat(ShaderIDs.PostExposure, RuntimeUtilities.Exp2(settings.postExposure.value));

            context.logLut = lut;
        }
Exemple #4
0
/*
 *      void Update()
 *      {
 *          // Unfortunately we need to track the current layer to update the volume manager in
 *          // real-time as the user could change it at any time in the editor or at runtime.
 *          // Because no event is raised when the layer changes, we have to track it on every
 *          // frame :/
 *          int layer = gameObject.layer;
 *          if (layer != m_PreviousLayer)
 *          {
 *              PostProcessManager.instance.UpdateVolumeLayer(this, m_PreviousLayer, layer);
 *              m_PreviousLayer = layer;
 *          }
 *
 *          // Same for `priority`. We could use a property instead, but it doesn't play nice with
 *          // the serialization system. Using a custom Attribute/PropertyDrawer for a property is
 *          // possible but it doesn't work with Undo/Redo in the editor, which makes it useless.
 *          if (priority != m_PreviousPriority)
 *          {
 *              PostProcessManager.instance.SetLayerDirty(layer);
 *              m_PreviousPriority = priority;
 *          }
 *      }
 */
        // TODO: Look into a better volume previsualization system
        void OnDrawGizmos()
        {
            var colliders = m_TempColliders;

            GetComponents(colliders);

            if (isGlobal || colliders == null)
            {
                return;
            }

#if UNITY_EDITOR
            // Can't access the UnityEditor.Rendering.PostProcessing namespace from here, so
            // we'll get the preferred color manually
            unchecked
            {
                int value = UnityEditor.EditorPrefs.GetInt("PostProcessing.Volume.GizmoColor", (int)0x8033cc1a);
                Gizmos.color = ColorUtilities.ToRGBA((uint)value);
            }
#endif

            var scale    = transform.lossyScale;
            var invScale = new Vector3(1f / scale.x, 1f / scale.y, 1f / scale.z);
            Gizmos.matrix = Matrix4x4.TRS(transform.position, transform.rotation, scale);

            // Draw a separate gizmo for each collider
            foreach (var collider in colliders)
            {
                if (!collider.enabled)
                {
                    continue;
                }

                // We'll just use scaling as an approximation for volume skin. It's far from being
                // correct (and is completely wrong in some cases). Ultimately we'd use a distance
                // field or at least a tesselate + push modifier on the collider's mesh to get a
                // better approximation, but the current Gizmo system is a bit limited and because
                // everything is dynamic in Unity and can be changed at anytime, it's hard to keep
                // track of changes in an elegant way (which we'd need to implement a nice cache
                // system for generated volume meshes).
                var type = collider.GetType();

                if (type == typeof(BoxCollider))
                {
                    var c = (BoxCollider)collider;
                    Gizmos.DrawCube(c.center, c.size);
                    Gizmos.DrawWireCube(c.center, c.size + invScale * blendDistance * 4f);
                }
                else if (type == typeof(SphereCollider))
                {
                    var c = (SphereCollider)collider;
                    Gizmos.DrawSphere(c.center, c.radius);
                    Gizmos.DrawWireSphere(c.center, c.radius + invScale.x * blendDistance * 2f);
                }
                else if (type == typeof(MeshCollider))
                {
                    var c = (MeshCollider)collider;

                    // Only convex mesh colliders are allowed
                    if (!c.convex)
                    {
                        c.convex = true;
                    }

                    // Mesh pivot should be centered or this won't work
                    Gizmos.DrawMesh(c.sharedMesh);
                    Gizmos.DrawWireMesh(c.sharedMesh, Vector3.zero, Quaternion.identity, Vector3.one + invScale * blendDistance * 4f);
                }

                // Nothing for capsule (DrawCapsule isn't exposed in Gizmo), terrain, wheel and
                // other colliders...
            }

            colliders.Clear();
        }