275 lines
9.3 KiB
HLSL
275 lines
9.3 KiB
HLSL
//Unity Toon Shader/HDRP
|
|
//nobuyuki@unity3d.com
|
|
//toshiyuki@unity3d.com (Universal RP/HDRP)
|
|
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/PhysicalCamera.hlsl"
|
|
#include "HDRPToonHead.hlsl"
|
|
|
|
// Channel mask enum.
|
|
// this must be same to UI cs code
|
|
// HDRPToonGUI._ChannelEnum
|
|
int eBaseColor = 0;
|
|
int eFirstShade = 1;
|
|
int eSecondShade = 2;
|
|
int eHighlight = 3;
|
|
int eAngelRing = 4;
|
|
int eRimLight = 5;
|
|
int eOutline = 6;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// not in materials
|
|
int _ToonLightHiCutFilter;
|
|
int _ToonEvAdjustmentCurve;
|
|
float _ToonEvAdjustmentValueArray[128];
|
|
float _ToonEvAdjustmentValueMin;
|
|
float _ToonEvAdjustmentValueMax;
|
|
float _ToonEvAdjustmentCompensation;
|
|
float _ToonIgnoreExposureMultiplier;
|
|
|
|
|
|
// function to rotate the UV: RotateUV()
|
|
//float2 rotatedUV = RotateUV(i.uv0, (_angular_Verocity*3.141592654), float2(0.5, 0.5), _Time.g);
|
|
float2 RotateUV(float2 _uv, float _radian, float2 _piv, float _time)
|
|
{
|
|
float RotateUV_ang = _radian;
|
|
float RotateUV_cos = cos(_time*RotateUV_ang);
|
|
float RotateUV_sin = sin(_time*RotateUV_ang);
|
|
return (mul(_uv - _piv, float2x2(RotateUV_cos, -RotateUV_sin, RotateUV_sin, RotateUV_cos)) + _piv);
|
|
}
|
|
|
|
float3 ConvertFromEV100(float3 EV100)
|
|
{
|
|
#if 1
|
|
float3 value = pow(2, EV100) * 2.5f;
|
|
return value;
|
|
#else
|
|
float3 maxLuminance = 1.2f * pow(2.0f, EV100);
|
|
return 1.0f / maxLuminance;
|
|
#endif
|
|
}
|
|
|
|
float3 ConvertToEV100(float3 value)
|
|
{
|
|
#if 1
|
|
return log2(value*0.4f);
|
|
#else
|
|
return log2(1.0f / (1.2f * value));
|
|
#endif
|
|
}
|
|
|
|
|
|
|
|
float WeightSample(PositionInputs positionInput)
|
|
{
|
|
// Center-weighted
|
|
const float2 kCenter = _ScreenParams.xy * 0.5;
|
|
const float weight = pow(length((kCenter.xy - positionInput.positionSS.xy) / _ScreenParams.xy),1.0) ;
|
|
return 1.0 - saturate(weight);
|
|
}
|
|
|
|
float3 ApplyCompensation(float3 originalColor)
|
|
{
|
|
float3 ev100_Color = ConvertToEV100(originalColor) +_ToonEvAdjustmentCompensation * 0.5f;
|
|
|
|
|
|
float3 resultColor = max(0, ConvertFromEV100(ev100_Color));
|
|
return resultColor;
|
|
}
|
|
|
|
float3 ApplyCurrentExposureMultiplier(float3 color)
|
|
{
|
|
return color * lerp(GetCurrentExposureMultiplier(), 1, _ToonIgnoreExposureMultiplier);
|
|
}
|
|
|
|
|
|
float3 GetExposureAdjustedColor(float3 originalColor)
|
|
{
|
|
if (_ToonEvAdjustmentCurve != 0)
|
|
{
|
|
|
|
float3 ev100_Color = ConvertToEV100(originalColor);
|
|
ev100_Color = clamp(ev100_Color, _ToonEvAdjustmentValueMin, _ToonEvAdjustmentValueMax);
|
|
float3 ev100_remap = (ev100_Color - _ToonEvAdjustmentValueMin) * (128-1) / (_ToonEvAdjustmentValueMax - _ToonEvAdjustmentValueMin);
|
|
ev100_remap = clamp(ev100_remap, 0.0, 127.0);
|
|
int3 ev100_idx = (int3)ev100_remap;
|
|
float3 ev100_lerp = ev100_remap - ev100_idx;
|
|
float3 ev100_remapped;
|
|
|
|
ev100_remapped.r = _ToonEvAdjustmentValueArray[ev100_idx.r] +(_ToonEvAdjustmentValueArray[ev100_idx.r + 1] - _ToonEvAdjustmentValueArray[ev100_idx.r]) * ev100_lerp.r;
|
|
ev100_remapped.g = _ToonEvAdjustmentValueArray[ev100_idx.g] +(_ToonEvAdjustmentValueArray[ev100_idx.g + 1] - _ToonEvAdjustmentValueArray[ev100_idx.g]) * ev100_lerp.g;
|
|
ev100_remapped.b = _ToonEvAdjustmentValueArray[ev100_idx.b] +(_ToonEvAdjustmentValueArray[ev100_idx.b + 1] - _ToonEvAdjustmentValueArray[ev100_idx.b]) * ev100_lerp.b;
|
|
|
|
|
|
float3 resultColor = ConvertFromEV100(ev100_remapped);
|
|
|
|
|
|
return resultColor;
|
|
}
|
|
else // else is neccessary to avoid warrnings.
|
|
{
|
|
return originalColor;
|
|
}
|
|
}
|
|
|
|
|
|
float GetLightAttenuation(float3 lightColor)
|
|
{
|
|
float lightAttenuation = rateR *lightColor.r + rateG *lightColor.g + rateB *lightColor.b;
|
|
return lightAttenuation;
|
|
}
|
|
|
|
|
|
int GetNextDirectionalLightIndex(BuiltinData builtinData, int currentIndex, int mainLightIndex)
|
|
{
|
|
int i = 0; // Declare once to avoid the D3D11 compiler warning.
|
|
for (i = 0; i < (int)_DirectionalLightCount; ++i)
|
|
{
|
|
if (IsMatchingLightLayer(_DirectionalLightDatas[i].lightLayers, builtinData.renderingLayers))
|
|
{
|
|
if (mainLightIndex != i)
|
|
{
|
|
if (currentIndex < i)
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return -1; // not found
|
|
}
|
|
|
|
int GetUtsMainLightIndex(BuiltinData builtinData)
|
|
{
|
|
int mainLightIndex = -1;
|
|
float3 lightColor = float3(0.0f, 0.0f, 0.0f);
|
|
float lightAttenuation = 0.0f;
|
|
uint i = 0; // Declare once to avoid the D3D11 compiler warning.
|
|
for (i = 0; i < _DirectionalLightCount; ++i)
|
|
{
|
|
if (IsMatchingLightLayer(_DirectionalLightDatas[i].lightLayers, builtinData.renderingLayers))
|
|
{
|
|
float3 currentLightColor = _DirectionalLightDatas[i].color;
|
|
float currentLightAttenuation = GetLightAttenuation(currentLightColor);
|
|
|
|
if (mainLightIndex == -1 || (currentLightAttenuation > lightAttenuation))
|
|
{
|
|
mainLightIndex = i;
|
|
lightAttenuation = currentLightAttenuation;
|
|
|
|
lightColor = currentLightColor;
|
|
}
|
|
}
|
|
}
|
|
|
|
return mainLightIndex;
|
|
}
|
|
|
|
// UTSLightData GetUTSMainPunctualLightData(BuiltinData builtinData, PositionInputs posInput)
|
|
// {
|
|
// UTSLightData mainPunctualLight;
|
|
|
|
// uint lightCount, lightStart;
|
|
|
|
// #ifndef LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
|
|
// GetCountAndStart(posInput, LIGHTCATEGORY_PUNCTUAL, lightStart, lightCount);
|
|
// #else // LIGHTLOOP_DISABLE_TILE_AND_CLUSTER
|
|
// lightCount = _PunctualLightCount;
|
|
// lightStart = 0;
|
|
// #endif
|
|
// bool fastPath = false;
|
|
// #if SCALARIZE_LIGHT_LOOP
|
|
// uint lightStartLane0;
|
|
// fastPath = IsFastPath(lightStart, lightStartLane0);
|
|
|
|
// if (fastPath)
|
|
// {
|
|
// lightStart = lightStartLane0;
|
|
// }
|
|
// #endif
|
|
|
|
// uint v_lightListOffset = 0;
|
|
// uint v_lightIdx = lightStart;
|
|
// float channelAlpha = 0.0f;
|
|
// [loop] // vulkan shader compiler can not unroll.
|
|
// while (v_lightListOffset < lightCount)
|
|
// {
|
|
// v_lightIdx = FetchIndex(lightStart, v_lightListOffset);
|
|
// #if SCALARIZE_LIGHT_LOOP
|
|
// uint s_lightIdx = ScalarizeElementIndex(v_lightIdx, fastPath);
|
|
// #else
|
|
// uint s_lightIdx = v_lightIdx;
|
|
// #endif
|
|
// if (s_lightIdx == -1)
|
|
// break;
|
|
|
|
// LightData s_lightData = FetchLight(s_lightIdx);
|
|
|
|
// // If current scalar and vector light index match, we process the light. The v_lightListOffset for current thread is increased.
|
|
// // Note that the following should really be ==, however, since helper lanes are not considered by WaveActiveMin, such helper lanes could
|
|
// // end up with a unique v_lightIdx value that is smaller than s_lightIdx hence being stuck in a loop. All the active lanes will not have this problem.
|
|
// if (s_lightIdx >= v_lightIdx)
|
|
// {
|
|
// v_lightListOffset++;
|
|
// if (IsMatchingLightLayer(s_lightData.lightLayers, builtinData.renderingLayers))
|
|
// {
|
|
// float3 lightDirection;
|
|
// float4 distances; // {d, d^2, 1/d, d_proj}
|
|
// GetPunctualLightVectors(posInput.positionWS, s_lightData, lightDirection, distances);
|
|
// float4 lightColor = EvaluateLight_Punctual(context, posInput, s_lightData, lightDirection, distances);
|
|
// float3 additionalLightColor = ApplyCurrentExposureMultiplier(lightColor.rgb) * lightColor.a;
|
|
// const float notDirectional = 1.0f;
|
|
|
|
// UTSLightData utsLightData;
|
|
// utsLightData.lightColor = additionalLightColor;
|
|
// utsLightData.lightDirection = lightDirection;
|
|
// utsLightData.diffuseDimmer = s_lightData.diffuseDimmer;
|
|
// utsLightData.specularDimmer = s_lightData.specularDimmer;
|
|
// utsLightData.shadowTint = s_lightData.shadowTint;
|
|
// utsLightData.penumbraTint = s_lightData.penumbraTint;
|
|
|
|
// if(length(additionalLightColor) >= length(mainPunctualLight.lightColor))
|
|
// {
|
|
// mainPunctualLight = utsLightData;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
// return mainPunctualLight;
|
|
// }
|
|
|
|
// Todo: calculate the acutal main lighboth dorectional and punctual)t based on the light attenuation, rather than using the main directional light
|
|
UTSLightData GetCustomMainLightData(BuiltinData builtinData, UTSLightData mainPunctualLight)
|
|
{
|
|
UTSLightData utsLightData;
|
|
int mainLightIndex;
|
|
|
|
mainLightIndex = GetUtsMainLightIndex(builtinData);
|
|
|
|
if (mainLightIndex == -1 || length(_DirectionalLightDatas[mainLightIndex].color) < length(mainPunctualLight.lightColor))
|
|
{
|
|
utsLightData = mainPunctualLight;
|
|
}
|
|
else
|
|
{
|
|
utsLightData.lightColor = ApplyCurrentExposureMultiplier(_DirectionalLightDatas[mainLightIndex].color);
|
|
utsLightData.lightDirection = -_DirectionalLightDatas[mainLightIndex].forward;
|
|
utsLightData.diffuseDimmer = _DirectionalLightDatas[mainLightIndex].diffuseDimmer;
|
|
utsLightData.specularDimmer = _DirectionalLightDatas[mainLightIndex].specularDimmer;
|
|
utsLightData.shadowTint = _DirectionalLightDatas[mainLightIndex].shadowTint;
|
|
utsLightData.penumbraTint = _DirectionalLightDatas[mainLightIndex].penumbraTint;
|
|
}
|
|
|
|
return utsLightData;
|
|
}
|
|
|
|
# include "ShadingOtherLight.hlsl"
|
|
# include "UtsSelfShadowMainLight.hlsl"
|
|
# include "ShadingMainLight.hlsl" |