584 lines
21 KiB
GLSL
584 lines
21 KiB
GLSL
Shader "XC Character Toon"
|
|
{
|
|
Properties
|
|
{
|
|
_BaseMap ("Base Map", 2D) = "white" [Semantic(BaseColorTexture)]
|
|
_BaseColor ("Base Color", Color) = (1,1,1,1) [Semantic(BaseColor)]
|
|
_Cutoff ("Alpha Cutoff", Range) = 0.5 [Semantic(AlphaCutoff)]
|
|
_IsDay ("Is Day", Float) = 1
|
|
_DoubleSided ("Double Sided", Float) = 0
|
|
_LightMap ("Light Map", 2D) = "white"
|
|
_LightDirectionMultiplier ("Light Direction Multiplier", Vector) = (1,0.5,1,0)
|
|
_ShadowOffset ("Shadow Offset", Float) = 0.1
|
|
_ShadowSmoothness ("Shadow Smoothness", Float) = 0.4
|
|
_ShadowColor ("Shadow Color", Color) = (1.1,1.1,1.1,1)
|
|
_ShadowRamp ("Shadow Ramp", 2D) = "white"
|
|
_UseCustomMaterialType ("Use Custom Material Type", Float) = 0
|
|
_CustomMaterialType ("Custom Material Type", Float) = 1
|
|
_UseEmission ("Use Emission", Float) = 0
|
|
_EmissionIntensity ("Emission Intensity", Float) = 0.2
|
|
_UseNormalMap ("Use Normal Map", Float) = 0
|
|
_NormalMap ("Normal Map", 2D) = "bump"
|
|
_IsFace ("Is Face", Float) = 0
|
|
_FaceDirection ("Face Direction", Vector) = (0,0,1,0)
|
|
_FaceShadowOffset ("Face Shadow Offset", Float) = 0
|
|
_FaceBlushColor ("Face Blush Color", Color) = (1,0.72156864,0.69803923,1)
|
|
_FaceBlushStrength ("Face Blush Strength", Float) = 0
|
|
_FaceLightMap ("Face Light Map", 2D) = "white"
|
|
_FaceShadow ("Face Shadow", 2D) = "white"
|
|
_UseSpecular ("Use Specular", Float) = 0
|
|
_SpecularSmoothness ("Specular Smoothness", Float) = 5
|
|
_NonmetallicIntensity ("Nonmetallic Intensity", Float) = 0.3
|
|
_MetallicIntensity ("Metallic Intensity", Float) = 8
|
|
_MetalMap ("Metal Map", 2D) = "white"
|
|
_UseRim ("Use Rim", Float) = 0
|
|
_RimOffset ("Rim Offset", Float) = 5
|
|
_RimThreshold ("Rim Threshold", Float) = 0.5
|
|
_RimIntensity ("Rim Intensity", Float) = 0.5
|
|
_UseSmoothNormal ("Use Smooth Normal", Float) = 0
|
|
_OutlineWidth ("Outline Width", Float) = 1.6
|
|
_OutlineWidthParams ("Outline Width Params", Vector) = (0,6,0.1,0.6)
|
|
_OutlineZOffset ("Outline Z Offset", Float) = 0.1
|
|
_ScreenOffset ("Screen Offset", Vector) = (0,0,0,0)
|
|
_OutlineColor ("Outline Color", Color) = (0.5176471,0.35686275,0.34117648,1)
|
|
_OutlineColor2 ("Outline Color 2", Color) = (0.3529412,0.3529412,0.3529412,1)
|
|
_OutlineColor3 ("Outline Color 3", Color) = (0.47058824,0.47058824,0.5647059,1)
|
|
_OutlineColor4 ("Outline Color 4", Color) = (0.5176471,0.35686275,0.34117648,1)
|
|
_OutlineColor5 ("Outline Color 5", Color) = (0.35,0.35,0.35,1)
|
|
}
|
|
HLSLINCLUDE
|
|
cbuffer PerObjectConstants
|
|
{
|
|
float4x4 gProjectionMatrix;
|
|
float4x4 gViewMatrix;
|
|
float4x4 gModelMatrix;
|
|
float4x4 gNormalMatrix;
|
|
};
|
|
|
|
static const int XC_MAX_ADDITIONAL_LIGHTS = 8;
|
|
|
|
struct AdditionalLightData
|
|
{
|
|
float4 colorAndIntensity;
|
|
float4 positionAndRange;
|
|
float4 directionAndType;
|
|
float4 spotAnglesAndFlags;
|
|
};
|
|
|
|
cbuffer LightingConstants
|
|
{
|
|
float4 gMainLightDirectionAndIntensity;
|
|
float4 gMainLightColorAndFlags;
|
|
float4 gLightingParams;
|
|
AdditionalLightData gAdditionalLights[XC_MAX_ADDITIONAL_LIGHTS];
|
|
};
|
|
|
|
cbuffer MaterialConstants
|
|
{
|
|
float4 _BaseColor;
|
|
float4 _Cutoff;
|
|
float4 _IsDay;
|
|
float4 _DoubleSided;
|
|
float4 _LightDirectionMultiplier;
|
|
float4 _ShadowOffset;
|
|
float4 _ShadowSmoothness;
|
|
float4 _ShadowColor;
|
|
float4 _UseCustomMaterialType;
|
|
float4 _CustomMaterialType;
|
|
float4 _UseEmission;
|
|
float4 _EmissionIntensity;
|
|
float4 _UseNormalMap;
|
|
float4 _IsFace;
|
|
float4 _FaceDirection;
|
|
float4 _FaceShadowOffset;
|
|
float4 _FaceBlushColor;
|
|
float4 _FaceBlushStrength;
|
|
float4 _UseSpecular;
|
|
float4 _SpecularSmoothness;
|
|
float4 _NonmetallicIntensity;
|
|
float4 _MetallicIntensity;
|
|
float4 _UseRim;
|
|
float4 _RimOffset;
|
|
float4 _RimThreshold;
|
|
float4 _RimIntensity;
|
|
float4 _UseSmoothNormal;
|
|
float4 _OutlineWidth;
|
|
float4 _OutlineWidthParams;
|
|
float4 _OutlineZOffset;
|
|
float4 _ScreenOffset;
|
|
float4 _OutlineColor;
|
|
float4 _OutlineColor2;
|
|
float4 _OutlineColor3;
|
|
float4 _OutlineColor4;
|
|
float4 _OutlineColor5;
|
|
};
|
|
|
|
cbuffer ShadowReceiverConstants
|
|
{
|
|
float4x4 gWorldToShadowMatrix;
|
|
float4 gShadowBiasAndTexelSize;
|
|
float4 gShadowOptions;
|
|
};
|
|
|
|
Texture2D BaseColorTexture;
|
|
Texture2D _LightMap;
|
|
Texture2D _ShadowRamp;
|
|
Texture2D _NormalMap;
|
|
Texture2D _FaceLightMap;
|
|
Texture2D _FaceShadow;
|
|
Texture2D _MetalMap;
|
|
SamplerState LinearClampSampler;
|
|
Texture2D ShadowMapTexture;
|
|
SamplerState ShadowMapSampler;
|
|
|
|
struct VSInput
|
|
{
|
|
float3 position : POSITION;
|
|
float3 normal : NORMAL;
|
|
float4 color : COLOR;
|
|
float2 texcoord : TEXCOORD0;
|
|
float2 backTexcoord : TEXCOORD1;
|
|
float3 tangent : TEXCOORD2;
|
|
float3 bitangent : TEXCOORD3;
|
|
};
|
|
|
|
struct PSInput
|
|
{
|
|
float4 position : SV_POSITION;
|
|
float2 texcoord : TEXCOORD0;
|
|
float2 backTexcoord : TEXCOORD1;
|
|
float3 positionWS : TEXCOORD2;
|
|
float3 positionVS : TEXCOORD3;
|
|
float3 normalWS : TEXCOORD4;
|
|
float3 tangentWS : TEXCOORD5;
|
|
float3 bitangentWS : TEXCOORD6;
|
|
float4 color : COLOR;
|
|
};
|
|
|
|
float3 NormalizeSafe(float3 value, float3 fallbackValue)
|
|
{
|
|
const float lengthSq = dot(value, value);
|
|
if (lengthSq <= 1e-6f) {
|
|
return fallbackValue;
|
|
}
|
|
|
|
return value * rsqrt(lengthSq);
|
|
}
|
|
|
|
bool UseEmissionFeature()
|
|
{
|
|
#ifdef _EMISSION
|
|
return true;
|
|
#else
|
|
return _UseEmission.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
bool UseNormalMapFeature()
|
|
{
|
|
#ifdef _NORMAL_MAP
|
|
return true;
|
|
#else
|
|
return _UseNormalMap.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
bool UseFaceFeature()
|
|
{
|
|
#ifdef _IS_FACE
|
|
return true;
|
|
#else
|
|
return _IsFace.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
bool UseSpecularFeature()
|
|
{
|
|
#ifdef _SPECULAR
|
|
return true;
|
|
#else
|
|
return _UseSpecular.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
bool UseRimFeature()
|
|
{
|
|
#ifdef _RIM
|
|
return true;
|
|
#else
|
|
return _UseRim.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
bool UseDoubleSidedFeature()
|
|
{
|
|
#ifdef _DOUBLE_SIDED
|
|
return true;
|
|
#else
|
|
return _DoubleSided.x > 0.5f;
|
|
#endif
|
|
}
|
|
|
|
float2 ResolveMaterialTexcoord(PSInput input, bool isFrontFace)
|
|
{
|
|
if (!UseDoubleSidedFeature()) {
|
|
return input.texcoord;
|
|
}
|
|
|
|
return lerp(input.texcoord, input.backTexcoord, isFrontFace ? 0.0f : 1.0f);
|
|
}
|
|
|
|
PSInput MainVS(VSInput input)
|
|
{
|
|
PSInput output;
|
|
const float4 positionWS = mul(gModelMatrix, float4(input.position, 1.0f));
|
|
const float4 positionVS = mul(gViewMatrix, positionWS);
|
|
output.position = mul(gProjectionMatrix, positionVS);
|
|
output.texcoord = input.texcoord;
|
|
output.backTexcoord = input.backTexcoord;
|
|
output.positionWS = positionWS.xyz;
|
|
output.positionVS = positionVS.xyz;
|
|
output.normalWS = normalize(mul((float3x3)gNormalMatrix, input.normal));
|
|
output.tangentWS = normalize(mul((float3x3)gNormalMatrix, input.tangent));
|
|
output.bitangentWS = normalize(mul((float3x3)gNormalMatrix, input.bitangent));
|
|
output.color = input.color;
|
|
return output;
|
|
}
|
|
|
|
float ComputeShadowAttenuation(float3 positionWS, float3 normalWS, float3 lightDirectionWS)
|
|
{
|
|
#ifndef XC_MAIN_LIGHT_SHADOWS
|
|
return 1.0f;
|
|
#else
|
|
if (gShadowOptions.x < 0.5f) {
|
|
return 1.0f;
|
|
}
|
|
|
|
const float3 resolvedNormalWS = NormalizeSafe(normalWS, float3(0.0f, 1.0f, 0.0f));
|
|
const float3 resolvedLightDirectionWS =
|
|
NormalizeSafe(lightDirectionWS, float3(0.0f, -1.0f, 0.0f));
|
|
const float nDotL = saturate(dot(resolvedNormalWS, resolvedLightDirectionWS));
|
|
const float normalBiasWorld = gShadowOptions.y * gShadowOptions.z * (1.0f - nDotL);
|
|
const float3 shadowPositionWS = positionWS + resolvedNormalWS * normalBiasWorld;
|
|
const float4 shadowClip = mul(gWorldToShadowMatrix, float4(shadowPositionWS, 1.0f));
|
|
if (shadowClip.w <= 0.0f) {
|
|
return 1.0f;
|
|
}
|
|
|
|
const float3 shadowNdc = shadowClip.xyz / shadowClip.w;
|
|
#if UNITY_UV_STARTS_AT_TOP
|
|
const float shadowUvY = shadowNdc.y * -0.5f + 0.5f;
|
|
#else
|
|
const float shadowUvY = shadowNdc.y * 0.5f + 0.5f;
|
|
#endif
|
|
const float2 shadowUv = float2(shadowNdc.x * 0.5f + 0.5f, shadowUvY);
|
|
#if UNITY_NEAR_CLIP_VALUE < 0
|
|
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
|
|
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
|
|
shadowNdc.z < -1.0f || shadowNdc.z > 1.0f) {
|
|
return 1.0f;
|
|
}
|
|
|
|
const float receiverDepth = shadowNdc.z * 0.5f + 0.5f - gShadowBiasAndTexelSize.x;
|
|
#else
|
|
if (shadowUv.x < 0.0f || shadowUv.x > 1.0f ||
|
|
shadowUv.y < 0.0f || shadowUv.y > 1.0f ||
|
|
shadowNdc.z < 0.0f || shadowNdc.z > 1.0f) {
|
|
return 1.0f;
|
|
}
|
|
|
|
const float receiverDepth = shadowNdc.z - gShadowBiasAndTexelSize.x;
|
|
#endif
|
|
const float2 shadowTexelSize = gShadowBiasAndTexelSize.yz;
|
|
float visibility = 0.0f;
|
|
[unroll]
|
|
for (int offsetY = -1; offsetY <= 1; ++offsetY) {
|
|
[unroll]
|
|
for (int offsetX = -1; offsetX <= 1; ++offsetX) {
|
|
const float2 sampleUv =
|
|
shadowUv + float2((float)offsetX, (float)offsetY) * shadowTexelSize;
|
|
if (sampleUv.x < 0.0f || sampleUv.x > 1.0f ||
|
|
sampleUv.y < 0.0f || sampleUv.y > 1.0f) {
|
|
visibility += 1.0f;
|
|
continue;
|
|
}
|
|
|
|
const float shadowDepth = ShadowMapTexture.Sample(ShadowMapSampler, sampleUv).r;
|
|
visibility += receiverDepth <= shadowDepth ? 1.0f : 0.0f;
|
|
}
|
|
}
|
|
|
|
visibility *= (1.0f / 9.0f);
|
|
const float shadowStrength = saturate(gShadowBiasAndTexelSize.w);
|
|
return lerp(1.0f - shadowStrength, 1.0f, visibility);
|
|
#endif
|
|
}
|
|
|
|
float ComputeRangeAttenuation(float distanceSq, float range)
|
|
{
|
|
if (range <= 0.0f) {
|
|
return 0.0f;
|
|
}
|
|
|
|
const float clampedRange = max(range, 0.0001f);
|
|
const float rangeSq = clampedRange * clampedRange;
|
|
if (distanceSq >= rangeSq) {
|
|
return 0.0f;
|
|
}
|
|
|
|
const float distance = sqrt(max(distanceSq, 0.0f));
|
|
const float normalized = saturate(1.0f - distance / clampedRange);
|
|
return normalized * normalized;
|
|
}
|
|
|
|
float ComputeSpotAttenuation(AdditionalLightData light, float3 directionToLightWS)
|
|
{
|
|
const float cosOuter = light.spotAnglesAndFlags.x;
|
|
const float cosInner = light.spotAnglesAndFlags.y;
|
|
const float3 spotAxisToLightWS = NormalizeSafe(light.directionAndType.xyz, float3(0.0f, -1.0f, 0.0f));
|
|
const float cosTheta = dot(spotAxisToLightWS, directionToLightWS);
|
|
return saturate((cosTheta - cosOuter) / max(cosInner - cosOuter, 1e-4f));
|
|
}
|
|
|
|
float3 EvaluateAdditionalLight(AdditionalLightData light, float3 normalWS, float3 positionWS)
|
|
{
|
|
const float lightType = light.directionAndType.w;
|
|
const float3 lightColor = light.colorAndIntensity.rgb;
|
|
const float lightIntensity = light.colorAndIntensity.w;
|
|
|
|
float3 directionToLightWS = float3(0.0f, 0.0f, 0.0f);
|
|
float attenuation = 1.0f;
|
|
|
|
if (lightType < 0.5f) {
|
|
directionToLightWS = NormalizeSafe(light.directionAndType.xyz, float3(0.0f, -1.0f, 0.0f));
|
|
} else {
|
|
const float3 lightVectorWS = light.positionAndRange.xyz - positionWS;
|
|
const float distanceSq = dot(lightVectorWS, lightVectorWS);
|
|
if (distanceSq <= 1e-6f) {
|
|
return float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
directionToLightWS = lightVectorWS * rsqrt(distanceSq);
|
|
attenuation = ComputeRangeAttenuation(distanceSq, light.positionAndRange.w);
|
|
if (attenuation <= 0.0f) {
|
|
return float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
if (lightType > 1.5f) {
|
|
attenuation *= ComputeSpotAttenuation(light, directionToLightWS);
|
|
if (attenuation <= 0.0f) {
|
|
return float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
}
|
|
}
|
|
|
|
const float diffuse = saturate(dot(normalWS, directionToLightWS));
|
|
if (diffuse <= 0.0f) {
|
|
return float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
|
|
return lightColor * (diffuse * lightIntensity * attenuation);
|
|
}
|
|
|
|
float3 ComputeSurfaceNormalWS(PSInput input, float2 texcoord)
|
|
{
|
|
float3 normalWS = NormalizeSafe(input.normalWS, float3(0.0f, 1.0f, 0.0f));
|
|
if (!UseNormalMapFeature()) {
|
|
return normalWS;
|
|
}
|
|
|
|
const float3 tangentWS = NormalizeSafe(input.tangentWS, float3(1.0f, 0.0f, 0.0f));
|
|
const float3 bitangentWS = NormalizeSafe(input.bitangentWS, float3(0.0f, 0.0f, 1.0f));
|
|
const float3 normalTS = _NormalMap.Sample(LinearClampSampler, texcoord).xyz * 2.0f - 1.0f;
|
|
const float3 mappedNormalWS =
|
|
tangentWS * normalTS.x +
|
|
bitangentWS * normalTS.y +
|
|
normalWS * normalTS.z;
|
|
return NormalizeSafe(mappedNormalWS, normalWS);
|
|
}
|
|
|
|
float3 GetAdjustedMainLightDirection()
|
|
{
|
|
const float3 scaledDirection =
|
|
gMainLightDirectionAndIntensity.xyz * _LightDirectionMultiplier.xyz;
|
|
return NormalizeSafe(
|
|
scaledDirection,
|
|
NormalizeSafe(gMainLightDirectionAndIntensity.xyz, float3(0.0f, -1.0f, 0.0f)));
|
|
}
|
|
|
|
float ResolveMaterialSelector(float lightMapAlpha)
|
|
{
|
|
const float useCustomMaterialType = _UseCustomMaterialType.x > 0.5f ? 1.0f : 0.0f;
|
|
return lerp(lightMapAlpha, _CustomMaterialType.x, useCustomMaterialType);
|
|
}
|
|
|
|
float GetGenericShadow(float3 normalWS, float3 lightDirectionWS, float aoFactor)
|
|
{
|
|
const float ndotl = dot(normalWS, lightDirectionWS);
|
|
const float halfLambert = ndotl * 0.5f + 0.5f;
|
|
const float shadow = saturate(2.0f * halfLambert * aoFactor);
|
|
return lerp(shadow, 1.0f, step(0.9f, aoFactor));
|
|
}
|
|
|
|
float GetFaceShadow(PSInput input, float2 texcoord, float3 lightDirectionWS)
|
|
{
|
|
const float3 faceDirection = NormalizeSafe(
|
|
float3(_FaceDirection.x, 0.0f, _FaceDirection.z),
|
|
float3(0.0f, 0.0f, 1.0f));
|
|
const float3 flatLightDirection = NormalizeSafe(
|
|
float3(lightDirectionWS.x, 0.0f, lightDirectionWS.z),
|
|
float3(0.0f, 0.0f, 1.0f));
|
|
const float faceDotLight = dot(faceDirection, flatLightDirection);
|
|
const float faceCrossLight = cross(faceDirection, flatLightDirection).y;
|
|
|
|
float2 shadowUv = texcoord;
|
|
shadowUv.x = lerp(shadowUv.x, 1.0f - shadowUv.x, step(0.0f, faceCrossLight));
|
|
|
|
const float faceShadowMap = _FaceLightMap.Sample(LinearClampSampler, shadowUv).r;
|
|
const float faceShadow = step(-0.5f * faceDotLight + 0.5f + _FaceShadowOffset.x, faceShadowMap);
|
|
const float faceMask = _FaceShadow.Sample(LinearClampSampler, texcoord).a;
|
|
return lerp(faceShadow, 1.0f, faceMask);
|
|
}
|
|
|
|
float3 GetShadowColor(float shadow, float materialSelector)
|
|
{
|
|
float rampIndex = 4.0f;
|
|
rampIndex = lerp(rampIndex, 1.0f, step(0.2f, materialSelector));
|
|
rampIndex = lerp(rampIndex, 2.0f, step(0.4f, materialSelector));
|
|
rampIndex = lerp(rampIndex, 0.0f, step(0.6f, materialSelector));
|
|
rampIndex = lerp(rampIndex, 3.0f, step(0.8f, materialSelector));
|
|
|
|
const float rangeMin = 0.5f + _ShadowOffset.x - _ShadowSmoothness.x;
|
|
const float rangeMax = 0.5f + _ShadowOffset.x;
|
|
const float2 rampUv = float2(
|
|
smoothstep(rangeMin, rangeMax, shadow),
|
|
rampIndex / 10.0f + 0.5f * saturate(_IsDay.x) + 0.05f);
|
|
const float3 shadowRamp = _ShadowRamp.Sample(LinearClampSampler, rampUv).rgb;
|
|
|
|
float3 shadowColor =
|
|
shadowRamp * lerp(_ShadowColor.rgb, float3(1.0f, 1.0f, 1.0f), smoothstep(0.9f, 1.0f, rampUv.x));
|
|
shadowColor = lerp(shadowColor, float3(1.0f, 1.0f, 1.0f), step(rangeMax, shadow));
|
|
return shadowColor;
|
|
}
|
|
|
|
float3 GetSpecular(
|
|
PSInput input,
|
|
float3 normalWS,
|
|
float3 lightDirectionWS,
|
|
float3 albedo,
|
|
float3 lightMap)
|
|
{
|
|
const float3 viewDirectionVS = NormalizeSafe(-input.positionVS, float3(0.0f, 0.0f, 1.0f));
|
|
const float3 lightDirectionVS = NormalizeSafe(
|
|
mul((float3x3)gViewMatrix, lightDirectionWS),
|
|
float3(0.0f, 0.0f, 1.0f));
|
|
const float3 normalVS = NormalizeSafe(mul((float3x3)gViewMatrix, normalWS), float3(0.0f, 0.0f, 1.0f));
|
|
const float3 halfDirectionVS = NormalizeSafe(lightDirectionVS + viewDirectionVS, lightDirectionVS);
|
|
const float nDotH = dot(normalVS, halfDirectionVS);
|
|
const float blinnPhong = pow(saturate(nDotH), max(_SpecularSmoothness.x, 1.0f));
|
|
|
|
const float2 matcapUv = normalVS.xy * 0.5f + 0.5f;
|
|
const float3 metalMap = _MetalMap.Sample(LinearClampSampler, matcapUv).rgb;
|
|
|
|
const float3 nonMetallic =
|
|
step(1.1f, lightMap.b + blinnPhong) * lightMap.r * _NonmetallicIntensity.x;
|
|
const float3 metallic =
|
|
blinnPhong * lightMap.b * albedo * metalMap * _MetallicIntensity.x;
|
|
return lerp(nonMetallic, metallic, step(0.9f, lightMap.r));
|
|
}
|
|
|
|
float GetRim(PSInput input, float3 normalWS)
|
|
{
|
|
const float3 viewDirectionVS = NormalizeSafe(-input.positionVS, float3(0.0f, 0.0f, 1.0f));
|
|
const float3 normalVS = NormalizeSafe(mul((float3x3)gViewMatrix, normalWS), float3(0.0f, 0.0f, 1.0f));
|
|
const float nDotV = dot(normalVS, viewDirectionVS);
|
|
const float fresnel = pow(saturate(1.0f - nDotV), max(_RimOffset.x, 0.001f));
|
|
return smoothstep(0.0f, max(_RimThreshold.x, 0.001f), fresnel) * _RimIntensity.x;
|
|
}
|
|
|
|
float4 MainPS(PSInput input, bool isFrontFace : SV_IsFrontFace) : SV_TARGET
|
|
{
|
|
const float2 texcoord = ResolveMaterialTexcoord(input, isFrontFace);
|
|
float4 baseMap = BaseColorTexture.Sample(LinearClampSampler, texcoord);
|
|
float3 albedo = baseMap.rgb * _BaseColor.rgb;
|
|
const float alphaMask = baseMap.a;
|
|
|
|
if (UseFaceFeature()) {
|
|
albedo = lerp(albedo, _FaceBlushColor.rgb, _FaceBlushStrength.x * alphaMask);
|
|
}
|
|
|
|
const float3 normalWS = ComputeSurfaceNormalWS(input, texcoord);
|
|
|
|
float3 color = albedo;
|
|
if (gMainLightColorAndFlags.a >= 0.5f) {
|
|
const float3 lightDirectionWS = GetAdjustedMainLightDirection();
|
|
const float4 lightMap = _LightMap.Sample(LinearClampSampler, texcoord);
|
|
const float materialSelector = ResolveMaterialSelector(lightMap.a);
|
|
float shadow = UseFaceFeature()
|
|
? GetFaceShadow(input, texcoord, lightDirectionWS)
|
|
: GetGenericShadow(normalWS, lightDirectionWS, lightMap.g * input.color.x);
|
|
shadow *= ComputeShadowAttenuation(input.positionWS, normalWS, lightDirectionWS);
|
|
|
|
const float3 shadowColor = GetShadowColor(shadow, materialSelector);
|
|
|
|
float3 specular = float3(0.0f, 0.0f, 0.0f);
|
|
if (UseSpecularFeature()) {
|
|
specular = GetSpecular(input, normalWS, lightDirectionWS, albedo, lightMap.rgb);
|
|
}
|
|
|
|
float3 emission = float3(0.0f, 0.0f, 0.0f);
|
|
if (UseEmissionFeature()) {
|
|
emission = albedo * _EmissionIntensity.x * alphaMask;
|
|
}
|
|
|
|
float3 rim = float3(0.0f, 0.0f, 0.0f);
|
|
if (UseRimFeature()) {
|
|
rim = albedo * GetRim(input, normalWS);
|
|
}
|
|
|
|
color = albedo * shadowColor + specular + emission + rim;
|
|
}
|
|
|
|
const int additionalLightCount = min((int)gLightingParams.x, XC_MAX_ADDITIONAL_LIGHTS);
|
|
[unroll]
|
|
for (int lightIndex = 0; lightIndex < XC_MAX_ADDITIONAL_LIGHTS; ++lightIndex) {
|
|
if (lightIndex >= additionalLightCount) {
|
|
break;
|
|
}
|
|
|
|
color +=
|
|
albedo *
|
|
EvaluateAdditionalLight(gAdditionalLights[lightIndex], normalWS, input.positionWS) *
|
|
0.15f;
|
|
}
|
|
|
|
return float4(saturate(color), 1.0f);
|
|
}
|
|
ENDHLSL
|
|
SubShader
|
|
{
|
|
Cull Back
|
|
ZWrite On
|
|
ZTest LEqual
|
|
Pass
|
|
{
|
|
Name "ForwardLit"
|
|
Tags { "LightMode" = "ForwardLit" }
|
|
HLSLPROGRAM
|
|
#pragma target 4.5
|
|
#pragma vertex MainVS
|
|
#pragma fragment MainPS
|
|
#pragma multi_compile _ XC_MAIN_LIGHT_SHADOWS
|
|
#pragma shader_feature_local _ XC_ALPHA_TEST
|
|
#pragma shader_feature_local _ _EMISSION
|
|
#pragma shader_feature_local _ _NORMAL_MAP
|
|
#pragma shader_feature_local _ _IS_FACE
|
|
#pragma shader_feature_local _ _SPECULAR
|
|
#pragma shader_feature_local _ _RIM
|
|
ENDHLSL
|
|
}
|
|
UsePass "Builtin Depth Only/DepthOnly"
|
|
UsePass "Builtin Shadow Caster/ShadowCaster"
|
|
}
|
|
}
|