136 lines
6.7 KiB
HLSL
136 lines
6.7 KiB
HLSL
// SPDX-License-Identifier: (Not available for this version, you are only allowed to use this software if you have express permission from the copyright holder and agreed to the latest NiloToonURP EULA)
|
|
// Copyright (c) 2021 Kuroneko ShaderLab Limited
|
|
|
|
// For more information, visit -> https://github.com/ColinLeung-NiloCat/UnityURPToonLitShaderExample
|
|
|
|
// #pragma once is a safe guard best practice in almost every .hlsl,
|
|
// doing this can make sure your .hlsl's user can include this .hlsl anywhere anytime without producing any multi include conflict
|
|
#pragma once
|
|
|
|
// classic outline's width fov & cam distance control, similar concept to
|
|
// https://docs.google.com/presentation/d/e/2PACX-1vSLQNQyqfGCVsqcEuOJLFqvHpASQZ5UZhjAuWnS5C3tYSGWjpmGYmI9ZOkt36hGGe3mWYXqxJgjCCAz/pub?start=false&loop=false&delayms=3000&fbclid=IwZXh0bgNhZW0CMTAAAR2KgeLNQqQWjE4EHIBhXgnB0xBHOzwhl8oWyfdRM5VU6AKIcjbaTVjvYPI_aem_AYGshQfPSTNeG2PDowtL2m6whirj2ruJvpbkQnTaR4CWiAYRRpACRmf64m91pAfFf5c_S6Na9GavPEVG53mxxuso&slide=id.ga37e29a62e_3_386
|
|
|
|
// possible inner outline concept:
|
|
// - vertex color sdf step = fragment shader inner outline with AA (texture size independent)
|
|
// - can use the same classic outline color logic for inner outline, just like ss outline
|
|
// https://docs.google.com/presentation/d/e/2PACX-1vSLQNQyqfGCVsqcEuOJLFqvHpASQZ5UZhjAuWnS5C3tYSGWjpmGYmI9ZOkt36hGGe3mWYXqxJgjCCAz/pub?start=false&loop=false&delayms=3000&fbclid=IwZXh0bgNhZW0CMTAAAR2KgeLNQqQWjE4EHIBhXgnB0xBHOzwhl8oWyfdRM5VU6AKIcjbaTVjvYPI_aem_AYGshQfPSTNeG2PDowtL2m6whirj2ruJvpbkQnTaR4CWiAYRRpACRmf64m91pAfFf5c_S6Na9GavPEVG53mxxuso&slide=id.ga37e29a62e_3_400
|
|
|
|
// love live mobile(2020)'s outline data:
|
|
// R = outline width
|
|
// g = outline zoffset
|
|
// b = inner outline weight
|
|
float ApplyOutlineFadeOutPerspectiveCamera(float inputMulFix, float cameraFov)
|
|
{
|
|
// make outline "fadeout" if character is too small/far in camera's view
|
|
// imagine this line is the most simple way to do tone mapping that clamp at 60/cameraFov,
|
|
// but here we are not remapping HDR color, we are remapping outline width
|
|
return min(60/cameraFov, inputMulFix); // keep it similar to min(2,inputMulFix) in fov 30 camera
|
|
}
|
|
float ApplyOutlineFadeOutOrthoCamera(float inputMulFix)
|
|
{
|
|
// make outline "fadeout" if character is too small in camera's view
|
|
// imagine this line is the most simple way to do tone mapping that clamp at 2,
|
|
// but here we are not remapping HDR color, we are remapping outline width
|
|
return min(2,inputMulFix);
|
|
}
|
|
|
|
float SmoothClamp(float x, float max, float smoothness)
|
|
{
|
|
// concert min(max,x) to a smooth curve
|
|
|
|
// smoothness > 0, larger = smoother transition
|
|
// when smoothness = 1, basic smooth curve
|
|
// when smoothness = 0.2, reaches max faster
|
|
// when smoothness = 5, reaches max much later
|
|
return max * (1 - exp(-x/(max * smoothness)));
|
|
}
|
|
float SmoothClampB(float x, float cameraFov)
|
|
{
|
|
float delayStart = 60.0 / cameraFov;
|
|
|
|
if (x <= delayStart)
|
|
{
|
|
// Linear part for x between 0 and delayStart
|
|
return x;
|
|
}
|
|
|
|
// Non-linear part for x > delayStart
|
|
// Using a modified logarithmic function for slower decay
|
|
float a = delayStart; // Value at x = delayStart
|
|
float b = 0.5; // Controls how quickly the curve flattens (smaller value for slower decay)
|
|
return a + log(1.0 + (x - delayStart) * b) / b;
|
|
}
|
|
|
|
float GetOutlineCameraFovAndDistanceFixMultiplier(float positionVS_Z, float cameraFOV, float applyPercentage)
|
|
{
|
|
float outlineWidthMulFix;
|
|
if(IsPerspectiveProjection())
|
|
{
|
|
////////////////////////////////
|
|
// Perspective camera case
|
|
////////////////////////////////
|
|
|
|
// keep outline similar width on screen across all camera distance
|
|
outlineWidthMulFix = abs(positionVS_Z);
|
|
|
|
// can replace to a better tonemapping function if a smooth stop is needed
|
|
outlineWidthMulFix = ApplyOutlineFadeOutPerspectiveCamera(outlineWidthMulFix,cameraFOV);
|
|
|
|
// keep outline similar width on screen accoss all camera fov
|
|
outlineWidthMulFix *= cameraFOV;
|
|
}
|
|
else
|
|
{
|
|
////////////////////////////////
|
|
// Orthographic camera case
|
|
////////////////////////////////
|
|
|
|
// There is no need to consider camera distance or field of view (FOV) for an orthographic camera, as they don't need to be taken into account.
|
|
float orthoSize = abs(unity_OrthoParams.y);
|
|
orthoSize = ApplyOutlineFadeOutOrthoCamera(orthoSize);
|
|
outlineWidthMulFix = orthoSize * 50; // 100/2 is a magic number to match perspective camera's outline width
|
|
}
|
|
|
|
// allow user to select applying how much of this auto outline width adjustment(this function)
|
|
outlineWidthMulFix = lerp(60,outlineWidthMulFix, saturate(applyPercentage));
|
|
|
|
//----------------------------------------------------------------
|
|
// Experimental method override
|
|
// The original method maybe weird and old, here is another attempt to improve it
|
|
// TODO: make it optional for user, expose smoothness
|
|
/*
|
|
float x = 1.0/abs(UNITY_MATRIX_P[1][1]) * 120;
|
|
if(IsPerspectiveProjection()) x *= abs(positionVS_Z);
|
|
outlineWidthMulFix = SmoothClamp(x, 120, 1);
|
|
*/
|
|
//----------------------------------------------------------------
|
|
|
|
//----------------------------------------------------------------
|
|
// GGX example code
|
|
// https://game.watch.impress.co.jp/docs/kikaku/1617901.html
|
|
// https://game.watch.impress.co.jp/img/gmw/docs/1617/901/html/11_o.jpg.html
|
|
//outlineWidthMulFix = 1.0 / UNITY_MATRIX_P[0][0] * 60 * abs(positionVS_Z); // const width no matter distance or fov, unclamped. not sure if they want P[0][0] or P[1][1]
|
|
//----------------------------------------------------------------
|
|
|
|
return outlineWidthMulFix * 0.00005; // mul a const to make return result = default normal expand amount WS
|
|
}
|
|
|
|
// [currently not being used in NiloToonURP]
|
|
// If your project has a faster way to get camera fov in shader, you don't need to use this method.
|
|
// For example, you write cmd.SetGlobalFloat("_CurrentCameraFOV",cameraFOV) using a new RendererFeature in C#
|
|
// (NiloToonURP's renderer feature did that already by providing _CurrentCameraFOV).
|
|
float GetCameraFOV()
|
|
{
|
|
// https://answers.unity.com/questions/770838/how-can-i-extract-the-fov-information-from-the-pro.html
|
|
float t = unity_CameraProjection._m11;
|
|
float Rad2Deg = 180 / 3.1415;
|
|
float fov = atan(1.0f / t) * 2.0 * Rad2Deg;
|
|
return fov;
|
|
}
|
|
// [currently not being used in NiloToonURP]
|
|
// slower due to GetCameraFOV(), but don't need to provide cameraFOV from C#
|
|
float GetOutlineCameraFovAndDistanceFixMultiplier(float positionVS_Z, float applyPercentage = 1)
|
|
{
|
|
return GetOutlineCameraFovAndDistanceFixMultiplier(positionVS_Z, GetCameraFOV(), applyPercentage);
|
|
}
|