Boat Attack使用了Universal RP的许多新图形功能,可以用于探索 Universal RP 的使用方式和技巧。
您最多选择25个主题 主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

465 行
18 KiB

Shader "Hidden/LookDev/Compositor"
{
Properties
{
_Tex0WithSun("First View", 2D) = "white" {}
_Tex0WithoutSun("First View without sun", 2D) = "white" {}
_Tex0Shadows("First View shadow mask", 2D) = "white" {}
_ShadowColor0("Shadow Color for first view", Color) = (1.0, 1.0, 1.0, 1.0)
_Tex1WithSun("Second View", 2D) = "white" {}
_Tex1WithoutSun("Second View without sun", 2D) = "white" {}
_Tex1Shadows("Second View shadow mask", 2D) = "white" {}
_ShadowColor1("Shadow Color for second view", Color) = (1.0, 1.0, 1.0, 1.0)
_CompositingParams("Blend Factor, exposure for first and second view, and current selected side", Vector) = (0.0, 1.0, 1.0, 1.0)
_CompositingParams2("Drag and drop zone and shadow multipliers", Vector) = (0.0, 1.0, 1.0, 1.0) // Drag and Drop zone Left == 1.0, Right == -1.0, None == 0.0
_FirstViewColor("Gizmo Color for first view", Color) = (0.5, 0.5, 0.5, 0.5)
_SecondViewColor("Gizmo Color for second view", Color) = (0.5, 0.5, 0.5, 0.5)
_GizmoPosition("Position of split view gizmo", Vector) = (0.5, 0.5, 0.0, 0.0)
_GizmoZoneCenter("Center of Zone view gizmo", Vector) = (0.5, 0.5, 0.0, 0.0)
_GizmoSplitPlane("2D plane of the gizmo", Vector) = (1.0, 1.0, 0.0, 0.0)
_GizmoSplitPlaneOrtho("2D plane orthogonal to the gizmo", Vector) = (1.0, 1.0, 0.0, 0.0)
_GizmoLength("Gizmo Length", Float) = 0.2
_GizmoThickness("Gizmo Thickness", Vector) = (0.01, 0.08, 0.0, 0.0)
_GizmoCircleRadius("Gizmo extremities radius", Vector) = (0.05, 0.4, 0.0, 0.0)
_GizmoRenderMode("Render gizmo mode", Float) = 0.0
_GetBlendFactorMaxGizmoDistance("Distance on the gizmo where the blend circle stops", Float) = 0.2
_BlendFactorCircleRadius("Visual radius of the blend factor gizmo", Float) = 0.01
_ScreenRatio("Screen ratio", Vector) = (1.0, 1.0, 0.0, 0.0) // xy screen ratio, zw screen size
_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)
}
CGINCLUDE
#include "UnityCG.cginc"
#pragma vertex vert
// Enum matching GizmoOperationType in LookDevViews.cs
#define kNone 0.0f
#define kTranslation 1.0f
#define kRotationZone1 2.0f
#define kRotationZone2 3.0f
#define kAll 4.0f
sampler2D _Tex0WithSun;
sampler2D _Tex0WithoutSun;
sampler2D _Tex0Shadows;
float4 _ShadowColor0;
sampler2D _Tex1WithSun;
sampler2D _Tex1WithoutSun;
sampler2D _Tex1Shadows;
float4 _ShadowColor1;
float4 _CompositingParams; // x BlendFactor, yz ExposureValue (first/second view), w current selected side
float4 _CompositingParams2; // x current drag context, y apply tonemap (bool), z shadow multiplier
float4 _FirstViewColor;
float4 _SecondViewColor;
float4 _GizmoPosition;
float4 _GizmoZoneCenter;
float4 _GizmoThickness;
float4 _GizmoCircleRadius;
float4 _GizmoSplitPlane;
float4 _GizmoSplitPlaneOrtho;
float _GizmoLength;
float _GizmoRenderMode;
float _GetBlendFactorMaxGizmoDistance;
float _BlendFactorCircleRadius;
float4 _ScreenRatio;
float4 _ToneMapCoeffs1;
float4 _ToneMapCoeffs2;
float4 _Tex0WithSun_ST;
#define ShadowMultiplier0 _CompositingParams2.z
#define ShadowMultiplier1 _CompositingParams2.w
#define ExposureValue1 _CompositingParams.y
#define ExposureValue2 _CompositingParams.z
#define InBlack _ToneMapCoeffs1.x
#define OutBlack _ToneMapCoeffs1.y
#define InWhite _ToneMapCoeffs1.z
#define OutWhite _ToneMapCoeffs1.w
#define WhiteLevel _ToneMapCoeffs2.z
#define WhiteClip _ToneMapCoeffs2.w
struct appdata_t
{
float4 vertex : POSITION;
float2 texcoord : TEXCOORD0;
};
struct v2f
{
float2 texcoord : TEXCOORD0;
float4 vertex : SV_POSITION;
};
float DistanceToSplit(float2 pos, float3 splitPlane)
{
return dot(float3(pos, 1), splitPlane);
}
bool IsInsideGizmo(float2 normalizedCoord, float absDistanceToPlane, float distanceFromCenter, float side, float3 orthoPlane, float gizmoCircleRadius, float gizmoThickness, out float outSmoothing, float mode)
{
bool result = false;
outSmoothing = 0.0;
if (absDistanceToPlane < gizmoCircleRadius) // First "thick" bar, as large as the radius at extremities.
{
if (distanceFromCenter < (_GizmoLength + gizmoCircleRadius))
{
// side < 0 is cyan circle, side > 0 is orange widget
if (mode == kAll ||
(mode == kRotationZone1 && side > 0) ||
(mode == kRotationZone2 && side < 0))
{
if (distanceFromCenter >= (_GizmoLength - gizmoCircleRadius)) // Inside circle at the extremities ?
{
float2 circleCenter = _GizmoPosition.xy + side * orthoPlane.xy * _GizmoLength;
float d = length(normalizedCoord - circleCenter);
if (d <= gizmoCircleRadius)
{
outSmoothing = smoothstep(1.0, 0.8, d / gizmoCircleRadius);
result = true;
}
}
}
if (mode == kAll || mode == kTranslation)
{
if (absDistanceToPlane < gizmoThickness && distanceFromCenter < _GizmoLength)
{
outSmoothing = max(outSmoothing, smoothstep(1.0, 0.0, absDistanceToPlane / gizmoThickness));
result = true;
}
}
}
}
return result;
}
float4 GetGizmoColor(float2 normalizedCoord, float3 splitPlane, float3 orthoPlane)
{
float distanceToPlane = DistanceToSplit(normalizedCoord, splitPlane);
float absDistanceToPlane = abs(distanceToPlane);
float distanceFromCenter = length(normalizedCoord.xy - _GizmoPosition.xy);
float distanceToOrtho = DistanceToSplit(normalizedCoord, orthoPlane);
float4 result = float4(0.0, 0.0, 0.0, 0.0);
float side = 0.0;
if (distanceToOrtho > 0.0)
{
result.rgb = _FirstViewColor.rgb;
side = 1.0;
}
else
{
result.rgb = _SecondViewColor.rgb;
side = -1.0;
}
result.a = 0.0;
// "normal" gizmo
float smoothing = 1.0;
if (IsInsideGizmo(normalizedCoord, absDistanceToPlane, distanceFromCenter, side, orthoPlane, _GizmoCircleRadius.x, _GizmoThickness.x, smoothing, kAll))
{
result.a = 1.0 * smoothing;
}
// large gizmo when in translation mode
if (IsInsideGizmo(normalizedCoord, absDistanceToPlane, distanceFromCenter, side, orthoPlane, _GizmoCircleRadius.y, _GizmoThickness.y, smoothing, _GizmoRenderMode))
{
result.a = max(result.a, 0.25 * smoothing);
}
// Blend factor selection disc
float2 blendCircleCenter = _GizmoPosition.xy - _CompositingParams.x * orthoPlane.xy * _GetBlendFactorMaxGizmoDistance;
float distanceToBlendCircle = length(normalizedCoord.xy - blendCircleCenter);
if (distanceToBlendCircle < _BlendFactorCircleRadius)
{
float alpha = smoothstep(1.0, 0.6, distanceToBlendCircle / _BlendFactorCircleRadius);
result = lerp(result, float4(1.0, 1.0, 1.0, alpha), alpha);
}
// Display transparent disc if near the center where the blend factor selection disc will automatically snap back
if (abs(_CompositingParams.x) < _GizmoCircleRadius.y / _GetBlendFactorMaxGizmoDistance)
{
if (distanceFromCenter < _BlendFactorCircleRadius)
{
float alpha = smoothstep(1.0, 0.6, distanceFromCenter / _BlendFactorCircleRadius) * 0.75;
result = lerp(result, float4(1.0, 1.0, 1.0, alpha), alpha);
}
}
return result;
}
float GetZoneViewFeedbackCircleFactor(float2 normalizedCoord, float radius, float circleSize)
{
float distanceToCenter = abs(length(_GizmoZoneCenter.xy - normalizedCoord) - radius);
return saturate((circleSize - distanceToCenter) / circleSize);
}
float ComputeBorderFactor(float borderSize, float2 screenPos, bool sideBySideView)
{
float4 borderSize4 = float4(borderSize, borderSize, borderSize, borderSize);
float4 distanceToBorder = float4(screenPos.x, screenPos.y, abs(_ScreenRatio.z - screenPos.x), abs(_ScreenRatio.w - screenPos.y));
float4 factors = saturate((borderSize4 - distanceToBorder) / borderSize4); // Lerp from 1.0 to 0.0 alpha from screen border to border size
float factor = max(factors.x, max(factors.y, max(factors.z, factors.w)));
// Add middle of the screen for side by side view
if (sideBySideView)
{
float distanceToCenterLine = abs(_ScreenRatio.z * 0.5 - screenPos.x);
float factorForCenterLine = saturate((borderSize - distanceToCenterLine) / borderSize);
factor = max(factor, factorForCenterLine);
}
return factor;
}
float ComputeSelectedSideColorFactor(float side, float2 screenPos, float2 normalizedCoord, bool sideBySideView, bool zoneView)
{
float borderSize = 2.0;
bool selectedSide = side * _CompositingParams.w > 0.0;
float factor = ComputeBorderFactor(borderSize, screenPos, sideBySideView);
// Add circle for zone view
if (zoneView)
{
float selectionCircleFeedbackFactor = GetZoneViewFeedbackCircleFactor(normalizedCoord, _GizmoCircleRadius.y, 0.002);
factor = max(factor, selectionCircleFeedbackFactor);
}
// If not on the selected side, make it more transparent
if (!selectedSide)
{
factor = factor * 0.2;
}
return factor;
}
float4 ComputeDragColorFactor(float side, float2 screenPos, float2 normalizedCoord, bool sideBySideView, bool zoneView)
{
float factor = 0;
float borderSize = 40.0;
bool sideIsDragZone = (side > 0.0 && _CompositingParams2.x > 0.0) || (side < 0.0 && _CompositingParams2.x < 0.0);
if (sideIsDragZone)
{
factor = ComputeBorderFactor(borderSize, screenPos, sideBySideView);
// Add circle for zone view
if (zoneView && side < 0.0)
{
float feedbackRadius = _GizmoLength * 2.0 * 0.3; // make it proprtional to selection zone
factor = max(factor, GetZoneViewFeedbackCircleFactor(normalizedCoord, _GizmoLength * 2.0, feedbackRadius));
}
factor = pow(factor, 8) * 0.7; // Casimir magics values for optimum fadeout :)
}
return factor;
}
float4 ComputeFeedbackColor(float4 inputColor, float side, float2 screenPos, float2 normalizedCoord, bool sideBySideView, bool zoneView)
{
float factor = ComputeSelectedSideColorFactor(side, screenPos, normalizedCoord, sideBySideView, zoneView);
factor = max(factor, ComputeDragColorFactor(side, screenPos, normalizedCoord, sideBySideView, zoneView));
float4 result = float4(0.0, 0.0, 0.0, 0.0);
if (side > 0.0)
{
result = lerp(inputColor, _FirstViewColor, factor);
}
else
{
result = lerp(inputColor, _SecondViewColor, factor);
}
return result;
}
v2f vert(appdata_t IN)
{
v2f OUT;
OUT.vertex = UnityObjectToClipPos(IN.vertex);
OUT.texcoord = TRANSFORM_TEX(IN.texcoord, _Tex0WithSun);
return OUT;
}
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;
}
float3 applyTonemapFilmicAD(float3 linearColor)
{
float blackRatio = InBlack / OutBlack;
float whiteRatio = InWhite / OutWhite;
// 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);
// constants
float A = 0.2;
float E = 0.02;
float F = 0.30;
// 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 curr*whiteScale;
}
float3 remapWhite(float3 inPixel, float whitePt)
{
// 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);
}
float3 NeutralTonemap(float3 x)
{
float3 finalColor = applyTonemapFilmicAD(x); // curve (dynamic coeffs differ per level)
finalColor = remapWhite(finalColor, WhiteClip); // post-curve white point adjustment
finalColor = saturate(finalColor);
return finalColor;
}
float3 ApplyToneMap(float3 color)
{
if (_CompositingParams2.y > 0.0)
{
return NeutralTonemap(color);
}
else
{
return saturate(color);
}
}
float3 ComputeColor(sampler2D texNormal, sampler2D texWithoutSun, sampler2D texShadowMask, float shadowMultiplier, float4 shadowColor, float2 texcoord)
{
// Explanation of how this work:
// To simulate the shadow of a directional light, we want to interpolate between two environments. One with a skybox without sun for shadowed area and the other with the sun.
// To create the lerp mask we render the scene with a white diffuse material and a single shadow casting directional light.
// This will create a mask where the shadowed area is 0 and the lit area is 1 with a smooth NDotL transition in-between.
// However, the DNotL will create an unwanted darkening of the scene (it's not actually part of the lighting equation)
// so we sort it in order to avoid too much darkening.
float3 color = tex2D(texNormal, texcoord).rgb;
if (shadowMultiplier < 0.0)
{
// no need to composite as we do not want shadow in this case
return color;
}
else
{
float3 colorWithoutsun = tex2D(texWithoutSun, texcoord).rgb;
float3 shadowMask = sqrt(tex2D(texShadowMask, texcoord).rgb);
return lerp(colorWithoutsun * shadowColor.rgb * shadowMultiplier, color, saturate(shadowMask.r));
}
}
ENDCG
SubShader
{
Tags
{
"ForceSupported" = "True"
}
Lighting Off
Cull Off
ZTest Always
ZWrite Off
Blend One Zero
// Single view 1
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma target 3.0
float4 frag(float2 texcoord : TEXCOORD0,
UNITY_VPOS_TYPE vpos : VPOS) : COLOR
{
float4 color = float4(ComputeColor(_Tex0WithSun, _Tex0WithoutSun, _Tex0Shadows, ShadowMultiplier0, _ShadowColor0, texcoord) * exp2(ExposureValue1), 1.0);
color.rgb = ApplyToneMap(color.rgb);
color = ComputeFeedbackColor(color, 1.0, vpos.xy, float2(0.0, 0.0), false, false);
return color;
}
ENDCG
}
// Single view 2
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma target 3.0
float4 frag(float2 texcoord : TEXCOORD0,
UNITY_VPOS_TYPE vpos : VPOS) : COLOR
{
float4 color = float4(ComputeColor(_Tex1WithSun, _Tex1WithoutSun, _Tex1Shadows, ShadowMultiplier1, _ShadowColor1, texcoord) * exp2(ExposureValue2), 1.0);
color.rgb = ApplyToneMap(color.rgb);
color = ComputeFeedbackColor(color, -1.0, vpos.xy, float2(0.0, 0.0), false, false);
return color;
}
ENDCG
}
// split
Pass
{
CGPROGRAM
#pragma fragment frag
#pragma target 3.0
float4 frag(float2 texcoord : TEXCOORD0,
UNITY_VPOS_TYPE vpos : VPOS) : COLOR
{
float3 color1 = ComputeColor(_Tex0WithSun, _Tex0WithoutSun, _Tex0Shadows, ShadowMultiplier0, _ShadowColor0, texcoord) * exp2(ExposureValue1);
float3 color2 = ComputeColor(_Tex1WithSun, _Tex1WithoutSun, _Tex1Shadows, ShadowMultiplier1, _ShadowColor1, texcoord) * exp2(ExposureValue2);
float2 normalizedCoord = ((texcoord * 2.0 - 1.0) * _ScreenRatio.xy);
float side = DistanceToSplit(normalizedCoord, _GizmoSplitPlane) < 0.0f ? -1.0f : 1.0f;
float blendFactor = 0.0f;
if (side < 0.0)
{
blendFactor = 1.0 - saturate(side * _CompositingParams.x);
}
else
{
blendFactor = saturate(side * _CompositingParams.x);
}
float4 finalColor = float4(lerp(color1, color2, blendFactor), 1.0);
finalColor.rgb = ApplyToneMap(finalColor.rgb);
float4 gizmoColor = GetGizmoColor(normalizedCoord, _GizmoSplitPlane, _GizmoSplitPlaneOrtho);
finalColor = lerp(finalColor, gizmoColor, gizmoColor.a);
finalColor = ComputeFeedbackColor(finalColor, side, vpos.xy, float2(0.0, 0.0), false, false);
return finalColor;
}
ENDCG
}
}
}