- Markers now become entirely vertical when dropped by non-VR players. - Created more music loops.
774 lines
35 KiB
GLSL
774 lines
35 KiB
GLSL
Shader "Carmen/Maps/SDF Map Marker Spokes"
|
|
{
|
|
Properties
|
|
{
|
|
_MainTex("Albedo(RGB)", 2D) = "white" {}
|
|
_Color("Color", Color) = (1,1,1,1)
|
|
_SDFCutoff("SDF Cutoff", Range(0.0, 1.0)) = 1.0
|
|
|
|
[NoScaleOffset] _MetallicGlossMap("Metallic(R) Smoothness(A) Map", 2D) = "white" {}
|
|
[Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 1.0
|
|
_Glossiness("Smoothness", Range(0.0, 1.0)) = 1.0
|
|
|
|
_BumpScale("Scale", Float) = 1.0
|
|
[NoScaleOffset] _BumpMap("Normal Map", 2D) = "bump" {}
|
|
|
|
[NoScaleOffset] _OcclusionMap("Occlusion(G)", 2D) = "white" {}
|
|
_OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
|
|
|
|
[NoScaleOffset] _EmissionMap("Emission(RGB)", 2D) = "white" {}
|
|
_EmissionColor("Emission Color", Color) = (0, 0, 0)
|
|
|
|
[Enum(UV0,0,UV1,1)] _UVSec ("UV Set for secondary textures", Float) = 0
|
|
[NoScaleOffset] _DetailMask("Detail Mask(A)", 2D) = "white" {}
|
|
|
|
_DetailAlbedoMap("Detail Albedo x2(RGB)", 2D) = "grey" {}
|
|
_DetailNormalMapScale("Scale", Float) = 1.0
|
|
[NoScaleOffset] _DetailNormalMap("Detail Normal Map", 2D) = "bump" {}
|
|
|
|
[ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 0
|
|
[ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 0
|
|
|
|
[Toggle(_ENABLE_GEOMETRIC_SPECULAR_AA)] _EnableGeometricSpecularAA("EnableGeometricSpecularAA", Float) = 1.0
|
|
_SpecularAAScreenSpaceVariance("SpecularAAScreenSpaceVariance", Range(0.0, 1.0)) = 0.1
|
|
_SpecularAAThreshold("SpecularAAThreshold", Range(0.0, 1.0)) = 0.2
|
|
|
|
[Enum(Default,0,MonoSH,1,MonoSH (no highlights),2)] _LightmapType ("Lightmap Type", Float) = 0
|
|
|
|
// TODO: This has questionable performance impact on Mobile but very little discernable impact on PC. Should
|
|
// make a toggle once we have properly branched compilation between those platforms, that's PC-only
|
|
[Toggle(_BICUBIC)] _Bicubic ("Enable Bicubic Sampling", Float) = 0
|
|
}
|
|
|
|
SubShader
|
|
{
|
|
Tags { "RenderType"="Opaque" }
|
|
LOD 200
|
|
|
|
CGPROGRAM
|
|
|
|
//#define _DEBUG_VRC
|
|
#ifdef _DEBUG_VRC
|
|
#define DEBUG_COL(rgb) debugCol = half4(rgb, 1)
|
|
#define DEBUG_VAL(val) debugCol = half4(val, val, val, 1)
|
|
half4 debugCol = half4(0,0,0,1);
|
|
#else
|
|
#define DEBUG_COL(rgb)
|
|
#define DEBUG_VAL(val)
|
|
#endif
|
|
|
|
#pragma target 3.0
|
|
#pragma multi_compile_fragment _ _EMISSION
|
|
#pragma multi_compile _ _DETAIL
|
|
#pragma multi_compile_fragment _ _SPECULARHIGHLIGHTS_OFF
|
|
#pragma multi_compile_fragment _ _GLOSSYREFLECTIONS_OFF
|
|
#pragma multi_compile_fragment _ _MONOSH_SPECULAR _MONOSH_NOSPECULAR
|
|
#pragma multi_compile_fragment _ _ENABLE_GEOMETRIC_SPECULAR_AA
|
|
#pragma dynamic_branch_local_fragment _ DISABLE_VERTEX_COLORING
|
|
//SDK-SYNC-IGNORE-LINE - unused variants in SDK projects - #pragma multi_compile_fragment _ FORCE_UNITY_DLDR_LIGHTMAP_ENCODING FORCE_UNITY_RGBM_LIGHTMAP_ENCODING FORCE_UNITY_LIGHTMAP_FULL_HDR_ENCODING UNITY_LIGHTMAP_NONE
|
|
//#pragma multi_compile _ _BICUBIC
|
|
|
|
#if defined(LIGHTMAP_ON)
|
|
#if defined(_MONOSH_SPECULAR) || defined(_MONOSH_NOSPECULAR)
|
|
#define _MONOSH
|
|
#if defined(_MONOSH_SPECULAR)
|
|
#define _LMSPEC
|
|
#endif
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef VRCHAT_INCLUDED
|
|
#define VRCHAT_INCLUDED
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "UnityPBSLighting.cginc"
|
|
|
|
#if defined(UNITY_SHOULD_SAMPLE_SH) || defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
|
|
#define UNITY_LIGHT_FUNCTION_APPLY_INDIRECT
|
|
#endif
|
|
|
|
#if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(UNITY_COMPILER_HLSLCC) || defined(SHADER_API_PSSL) || (defined(SHADER_TARGET_SURFACE_ANALYSIS) && !defined(SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER))
|
|
#define SAMPLE_TEXTURE2D(tex,samplertex,coord) tex.Sample (samplertex,coord)
|
|
#define SAMPLE_TEXTURE2D_GRAD(tex,samplertex,coord,ddx,ddy) tex.SampleGrad(samplertex, coord, ddx, ddy)
|
|
#define TEXTURE2D_PARAM(textureName, samplerName) Texture2D textureName, SamplerState samplerName
|
|
#define TEXTURE2D_ARGS(textureName, samplerName) textureName, samplerName
|
|
#else
|
|
#define SAMPLE_TEXTURE2D(tex,samplertex,coord) tex2D (tex,coord)
|
|
#define SAMPLE_TEXTURE2D_GRAD(tex,samplertex,coord,ddx,ddy) tex2Dgrad(tex, coord, ddx, ddy)
|
|
#define TEXTURE2D_PARAM(textureName, samplerName) sampler2D textureName
|
|
#define TEXTURE2D_ARGS(textureName, samplerName) textureName
|
|
#endif
|
|
|
|
// Use our squeezed BRDF on mobile
|
|
// In general we want FLOAT_MIN to be the smallest value such that (1.0f + FLOAT_MIN) != FLOAT_MIN
|
|
#if defined(SHADER_API_MOBILE)
|
|
#define VRC_BRDF_PBS BRDF2_VRC_PBS
|
|
#define FLOAT_MIN 1e-4
|
|
#else
|
|
#define VRC_BRDF_PBS UNITY_BRDF_PBS
|
|
#define FLOAT_MIN 1e-6
|
|
#endif
|
|
|
|
#if defined(_BICUBIC)
|
|
float BakeryBicubic_w0(float a)
|
|
{
|
|
return (1.0f/6.0f)*(a*(a*(-a + 3.0f) - 3.0f) + 1.0f);
|
|
}
|
|
|
|
float BakeryBicubic_w1(float a)
|
|
{
|
|
return (1.0f/6.0f)*(a*a*(3.0f*a - 6.0f) + 4.0f);
|
|
}
|
|
|
|
float BakeryBicubic_w2(float a)
|
|
{
|
|
return (1.0f/6.0f)*(a*(a*(-3.0f*a + 3.0f) + 3.0f) + 1.0f);
|
|
}
|
|
|
|
float BakeryBicubic_w3(float a)
|
|
{
|
|
return (1.0f/6.0f)*(a*a*a);
|
|
}
|
|
|
|
float BakeryBicubic_g0(float a)
|
|
{
|
|
return BakeryBicubic_w0(a) + BakeryBicubic_w1(a);
|
|
}
|
|
|
|
float BakeryBicubic_g1(float a)
|
|
{
|
|
return BakeryBicubic_w2(a) + BakeryBicubic_w3(a);
|
|
}
|
|
|
|
float BakeryBicubic_h0(float a)
|
|
{
|
|
return -1.0f + BakeryBicubic_w1(a) / (BakeryBicubic_w0(a) + BakeryBicubic_w1(a)) + 0.5f;
|
|
}
|
|
|
|
float BakeryBicubic_h1(float a)
|
|
{
|
|
return 1.0f + BakeryBicubic_w3(a) / (BakeryBicubic_w2(a) + BakeryBicubic_w3(a)) + 0.5f;
|
|
}
|
|
|
|
// Bicubic
|
|
float4 SampleTexture2DBicubicFilter(TEXTURE2D_PARAM(tex, smp), float2 coord)
|
|
{
|
|
#if defined(SHADER_API_D3D11) || defined(SHADER_API_XBOXONE) || defined(UNITY_COMPILER_HLSLCC) || defined(SHADER_API_PSSL) || (defined(SHADER_TARGET_SURFACE_ANALYSIS) && !defined(SHADER_TARGET_SURFACE_ANALYSIS_MOJOSHADER))
|
|
float width, height;
|
|
tex.GetDimensions(width, height);
|
|
float4 texelSize = float4(width, height, 1/width, 1/height);
|
|
#else
|
|
float2 theSize = textureSize(tex, 0);
|
|
float4 texelSize = float4(
|
|
float(theSize.x),
|
|
float(theSize.y),
|
|
1/float(theSize.x),
|
|
1/float(theSize.y));;
|
|
#endif
|
|
|
|
float x = coord.x * texelSize.z;
|
|
float y = coord.y * texelSize.z;
|
|
|
|
x -= 0.5f;
|
|
y -= 0.5f;
|
|
|
|
float px = floor(x);
|
|
float py = floor(y);
|
|
|
|
float fx = x - px;
|
|
float fy = y - py;
|
|
|
|
float g0x = BakeryBicubic_g0(fx);
|
|
float g1x = BakeryBicubic_g1(fx);
|
|
float h0x = BakeryBicubic_h0(fx);
|
|
float h1x = BakeryBicubic_h1(fx);
|
|
float h0y = BakeryBicubic_h0(fy);
|
|
float h1y = BakeryBicubic_h1(fy);
|
|
|
|
return BakeryBicubic_g0(fy) * ( g0x * SAMPLE_TEXTURE2D(tex, smp, (float2(px + h0x, py + h0y) * texelSize.x)) +
|
|
g1x * SAMPLE_TEXTURE2D(tex, smp, (float2(px + h1x, py + h0y) * texelSize.x))) +
|
|
BakeryBicubic_g1(fy) * ( g0x * SAMPLE_TEXTURE2D(tex, smp, (float2(px + h0x, py + h1y) * texelSize.x)) +
|
|
g1x * SAMPLE_TEXTURE2D(tex, smp, (float2(px + h1x, py + h1y) * texelSize.x)));
|
|
}
|
|
#define MAYBE_BICUBIC_SAMPLE(texture, smp, uv) SampleTexture2DBicubicFilter(TEXTURE2D_ARGS(texture, smp), uv)
|
|
#else
|
|
#define MAYBE_BICUBIC_SAMPLE(texture, smp, uv) SAMPLE_TEXTURE2D(texture, smp, uv)
|
|
#endif
|
|
|
|
inline half3 VRC_SafeNormalize(half3 value)
|
|
{
|
|
float lenSqr = max((float)dot(value, value), FLOAT_MIN);
|
|
return value * (half) rsqrt(lenSqr);
|
|
}
|
|
|
|
inline half shEvaluateDiffuseL1Geomerics(half L0, half3 L1, half3 n)
|
|
{
|
|
// avg direction of incoming light
|
|
half3 R1 = 0.5f * L1;
|
|
|
|
// directional brightness
|
|
half lenR1 = length(R1);
|
|
|
|
// linear angle between normal and direction 0-1, saturate fix from filamented
|
|
half q = dot(VRC_SafeNormalize(R1), n) * 0.5 + 0.5;
|
|
q = isnan(q) ? 1 : q;
|
|
q = saturate(q);
|
|
|
|
// power for q
|
|
// lerps from 1 (linear) to 3 (cubic) based on directionality
|
|
//half p = 1.0f + 2.0f * lenR1 / L0;
|
|
|
|
// dynamic range constant
|
|
// should vary between 4 (highly directional) and 0 (ambient)
|
|
//half a = (1.0f - lenR1 / L0) / (1.0f + lenR1 / L0);
|
|
|
|
// negative ambient fix, if L0 <= 0, return 0
|
|
//return (L0 <= 0.f) ? 0.f : (L0 * (a + (1.0f - a) * (p + 1.0f) * pow(q, p)));
|
|
|
|
// optimized reordering. thanks wolfram
|
|
return (L0 <= 0.f) ? 0.f : ( 4. * lenR1 * pow(q, (2 * lenR1) / L0 + 1) + ( L0 * (L0 - lenR1) )/(L0 + lenR1));
|
|
}
|
|
|
|
inline half shEvaluateDiffuseL1Normalized(half L0, half3 L1, half3 n)
|
|
{
|
|
return shEvaluateDiffuseL1Geomerics(1, L1 / L0, n);
|
|
}
|
|
|
|
#if defined(_ENABLE_GEOMETRIC_SPECULAR_AA)
|
|
float PerceptualSmoothnessToRoughness(float perceptualSmoothness)
|
|
{
|
|
float perceptualRoughness = SmoothnessToPerceptualRoughness(perceptualSmoothness);
|
|
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
return roughness;
|
|
}
|
|
|
|
float RoughnessToPerceptualSmoothness(float roughness)
|
|
{
|
|
float perceptualRoughness = RoughnessToPerceptualRoughness(roughness);
|
|
return 1.0 - perceptualRoughness;
|
|
}
|
|
|
|
float ProjectedSpaceNormalFiltering(half perceptualSmoothness, float variance, float threshold)
|
|
{
|
|
float roughness = PerceptualSmoothnessToRoughness(perceptualSmoothness);
|
|
// Ref: Stable Geometric Specular Antialiasing with Projected-Space NDF Filtering - https://yusuketokuyoshi.com/papers/2021/Tokuyoshi2021SAA.pdf
|
|
float squaredRoughness = roughness * roughness;
|
|
float projRoughness2 = squaredRoughness / (1.0 - squaredRoughness);
|
|
float filteredProjRoughness2 = saturate(projRoughness2 + min(2.0 * variance, threshold * threshold));
|
|
squaredRoughness = filteredProjRoughness2 / (filteredProjRoughness2 + 1.0f);
|
|
|
|
return RoughnessToPerceptualSmoothness(sqrt(squaredRoughness));
|
|
}
|
|
|
|
// Reference: Error Reduction and Simplification for Shading Anti-Aliasing
|
|
// Specular antialiasing for geometry-induced normal (and NDF) variations: Tokuyoshi / Kaplanyan et al.'s method.
|
|
// This is the deferred approximation, which works reasonably well so we keep it for forward too for now.
|
|
// screenSpaceVariance should be at most 0.5^2 = 0.25, as that corresponds to considering
|
|
// a gaussian pixel reconstruction kernel with a standard deviation of 0.5 of a pixel, thus 2 sigma covering the whole pixel.
|
|
float GeometricNormalVariance(float3 geometricNormalWS, float screenSpaceVariance)
|
|
{
|
|
float3 deltaU = ddx(geometricNormalWS);
|
|
float3 deltaV = ddy(geometricNormalWS);
|
|
|
|
return screenSpaceVariance * (dot(deltaU, deltaU) + dot(deltaV, deltaV));
|
|
}
|
|
|
|
float ProjectedSpaceGeometricNormalFiltering(float perceptualSmoothness, float3 geometricNormalWS, float screenSpaceVariance, float threshold)
|
|
{
|
|
float variance = GeometricNormalVariance(geometricNormalWS, screenSpaceVariance);
|
|
return ProjectedSpaceNormalFiltering(perceptualSmoothness, variance, threshold);
|
|
}
|
|
#endif
|
|
|
|
//#define OLD_GGX_TERM
|
|
#if !defined (OLD_GGX_TERM)
|
|
inline half ComputeSpecularGGX(half3 nL1, half3 viewDir, half3 normalWorld, half smoothness)
|
|
{
|
|
nL1 = VRC_SafeNormalize(nL1);
|
|
|
|
half3 halfDir = VRC_SafeNormalize(nL1 - viewDir);
|
|
half nh = saturate(dot(normalWorld, halfDir));
|
|
|
|
half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness);//* sqrt(focus));
|
|
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
|
|
half lh = saturate(dot(nL1, halfDir));
|
|
// ------------------------
|
|
// Specular term
|
|
// GGX Distribution multiplied by combined approximation of Visibility and Fresnel
|
|
// See "Optimizing PBR for Mobile" from Siggraph 2015 moving mobile graphics course
|
|
// https://community.arm.com/events/1155
|
|
|
|
half a = roughness;
|
|
half a2 = a*a;
|
|
|
|
half d = nh * nh * (a2 - 1.f) + 1.00001f;
|
|
half specularTerm = a2 / (max(0.1f, lh*lh) * (a + 0.5f) * (d * d) * 4);
|
|
|
|
#if defined (SHADER_API_MOBILE)
|
|
// on mobiles (where half actually means something) denominator have risk of overflow
|
|
// clamp below was added specifically to "fix" that, but dx compiler (we convert bytecode to metal/gles)
|
|
// sees that specularTerm have only non-negative terms, so it skips max(0,..) in clamp (leaving only min(100,...))
|
|
specularTerm = specularTerm - FLOAT_MIN;
|
|
specularTerm = clamp(specularTerm, 0.0, 100.0); // Prevent FP16 overflow on mobiles
|
|
#endif
|
|
|
|
return specularTerm;
|
|
}
|
|
#else
|
|
inline half ComputeSpecularGGX(half3 nL1, half3 viewDir, half3 normalWorld, half smoothness)
|
|
{
|
|
nL1 = VRC_SafeNormalize(nL1);
|
|
half3 halfDir = VRC_SafeNormalize(nL1 - viewDir);
|
|
half nh = saturate(dot(normalWorld, halfDir));
|
|
half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness );//* sqrt(focus));
|
|
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
return GGXTerm(nh, roughness);
|
|
}
|
|
#endif
|
|
|
|
#if defined(_MONOSH)
|
|
// MonoSH by Bakery Lightmapper https://assetstore.unity.com/packages/tools/level-design/bakery-gpu-lightmapper-122218
|
|
inline void BakeryMonoSH(out half3 diffuseColor, out half3 specularContrib, float2 lmUV, half3 normalWorld, half3 viewDir, half smoothness, half occlusion)
|
|
{
|
|
half3 dominantDir = MAYBE_BICUBIC_SAMPLE(unity_LightmapInd, samplerunity_Lightmap, lmUV).xyz;;
|
|
half3 L0 = DecodeLightmap(MAYBE_BICUBIC_SAMPLE(unity_Lightmap, samplerunity_Lightmap, lmUV));
|
|
|
|
half3 nL1 = dominantDir * 2 - 1;
|
|
half3x3 L1 = half3x3(nL1.x * L0, nL1.y * L0, nL1.z * L0) * 2;
|
|
|
|
half lumaL0 = dot(L0, 1);
|
|
half3 lumaL1 = mul(L1, half3(1, 1, 1));
|
|
half lumaSH = shEvaluateDiffuseL1Geomerics(lumaL0, lumaL1, normalWorld);
|
|
|
|
half3 sh = L0 + mul(normalWorld, L1);
|
|
half regularLumaSH = dot(sh, 1);
|
|
sh *= lerp(1, lumaSH / regularLumaSH, saturate(regularLumaSH*16));
|
|
|
|
diffuseColor = max(sh, 0.0);
|
|
|
|
#if defined(_LMSPEC)
|
|
half L1len = length(mul(L1, half3(1, 1, 1)));
|
|
half focus = L1len / (length(L0) + L1len);
|
|
half specularTerm = ComputeSpecularGGX(nL1, viewDir, normalWorld, smoothness * focus);
|
|
|
|
sh = L0 + mul(nL1, L1);
|
|
|
|
specularContrib = max(specularTerm * sh, 0.0);
|
|
|
|
// Reflection Probes use occlusion, direct lights don't. MonoSH and Specular Hack are both somewhere in between,
|
|
// so we use focus to split the difference - 1.0 is direct, 0.0 is reflection probe, so we invert.
|
|
specularContrib *= LerpOneTo(occlusion, 1 - focus);
|
|
#else
|
|
specularContrib = 0;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
inline UnityGI UnityGI_BaseVRC(UnityGIInput data, half occlusion, half3 normalWorld, half3 eyeVec, half smoothness, half hasReflProbe)
|
|
{
|
|
UnityGI o_gi;
|
|
|
|
// Base pass with Lightmap support is responsible for handling ShadowMask / blending here for performance reason
|
|
#if defined(HANDLE_SHADOWS_BLENDING_IN_GI)
|
|
half bakedAtten = UnitySampleBakedOcclusion(data.lightmapUV.xy, data.worldPos);
|
|
float zDist = dot(_WorldSpaceCameraPos - data.worldPos, UNITY_MATRIX_V[2].xyz);
|
|
float fadeDist = UnityComputeShadowFadeDistance(data.worldPos, zDist);
|
|
data.atten = UnityMixRealtimeAndBakedShadows(data.atten, bakedAtten, UnityComputeShadowFade(fadeDist));
|
|
#endif
|
|
|
|
o_gi.light = data.light;
|
|
o_gi.light.color *= data.atten;
|
|
|
|
#if defined(LIGHTMAP_ON)
|
|
#if defined(_MONOSH)
|
|
BakeryMonoSH(o_gi.indirect.diffuse, o_gi.indirect.specular, data.lightmapUV.xy, normalWorld, eyeVec, smoothness, occlusion);
|
|
#else
|
|
// Baked lightmaps
|
|
|
|
half3 bakedColor = half3(1.0, 1.0, 1.0);
|
|
half4 bakedColorTex = UNITY_SAMPLE_TEX2D(unity_Lightmap, data.lightmapUV.xy);
|
|
#if defined(FORCE_UNITY_DLDR_LIGHTMAP_ENCODING)
|
|
bakedColor = DecodeLightmapDoubleLDR(bakedColorTex, unity_Lightmap_HDR);
|
|
#elif defined(FORCE_UNITY_RGBM_LIGHTMAP_ENCODING)
|
|
bakedColor = DecodeLightmapRGBM(bakedColorTex, unity_Lightmap_HDR);
|
|
#elif defined(FORCE_UNITY_LIGHTMAP_FULL_HDR_ENCODING)
|
|
bakedColor = bakedColorTex;
|
|
#else
|
|
bakedColor = DecodeLightmap(bakedColorTex);
|
|
#endif
|
|
|
|
// Can be set if the renderer has a valid lightmap but the shader doesn't use it
|
|
#if !defined(UNITY_LIGHTMAP_NONE)
|
|
#if defined(DIRLIGHTMAP_COMBINED)
|
|
fixed4 bakedDirTex = UNITY_SAMPLE_TEX2D_SAMPLER(unity_LightmapInd, unity_Lightmap, data.lightmapUV.xy);
|
|
o_gi.indirect.diffuse = DecodeDirectionalLightmap(bakedColor, bakedDirTex, normalWorld);
|
|
#else // not directional lightmap
|
|
o_gi.indirect.diffuse = bakedColor;
|
|
#endif
|
|
#else
|
|
o_gi.indirect.diffuse = 1;
|
|
#endif
|
|
|
|
o_gi.indirect.specular = 0;
|
|
#endif
|
|
o_gi.indirect.diffuse *= occlusion;
|
|
#elif defined(UNITY_SHOULD_SAMPLE_SH)
|
|
o_gi.indirect.diffuse.r = shEvaluateDiffuseL1Geomerics(unity_SHAr.w, unity_SHAr.xyz, normalWorld);
|
|
o_gi.indirect.diffuse.g = shEvaluateDiffuseL1Geomerics(unity_SHAg.w, unity_SHAg.xyz, normalWorld);
|
|
o_gi.indirect.diffuse.b = shEvaluateDiffuseL1Geomerics(unity_SHAb.w, unity_SHAb.xyz, normalWorld);
|
|
|
|
#if !defined(_SPECULARHIGHLIGHTS_OFF)
|
|
UNITY_BRANCH
|
|
#if !defined(_GLOSSYREFLECTIONS_OFF)
|
|
if(!any(o_gi.light.color) && !hasReflProbe)
|
|
#else
|
|
if(!any(o_gi.light.color))
|
|
#endif
|
|
{
|
|
half3 L0rgb = half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
|
|
half3x3 L1rgb = half3x3(unity_SHAr.x, unity_SHAg.x, unity_SHAb.x,
|
|
unity_SHAr.y, unity_SHAg.y, unity_SHAb.y,
|
|
unity_SHAr.z, unity_SHAg.z, unity_SHAb.z);
|
|
half3 L1 = unity_SHAr.xyz + unity_SHAg.xyz + unity_SHAb.xyz;
|
|
|
|
half3 dominantDir = VRC_SafeNormalize(L1);
|
|
|
|
// Light can be anywhere from 'fully sparse' to 'completely focused' based on how much of it is L0 or L1rgb.
|
|
half L1len = length(L1);
|
|
half focus = L1len / (length(L0rgb) + L1len);
|
|
half specularTerm = ComputeSpecularGGX(dominantDir, eyeVec, normalWorld, smoothness * focus);
|
|
|
|
// L0 + L1, the total light energy expected, is the same over the whole mesh. This is a problem with specular highlights
|
|
// as they have a second peak in the negative direction - normally hidden by the fact that light energy there is normally zero.
|
|
// Multiplying by non-linear diffuse gives satisfactory results, though isn't particularly physically accurate.
|
|
// The brightness vs ground truth (a reflection probe) is too low though... closest we can get appears to be
|
|
// a dimensionless version, shEvaluateDiffuseL1Geometrics but applied to just the ratio.
|
|
half energyFactor = shEvaluateDiffuseL1Normalized(dot(L0rgb, 1), L1, normalWorld);
|
|
half3 sh = (L0rgb + mul(dominantDir, L1rgb)) * energyFactor;
|
|
|
|
o_gi.indirect.specular = max(specularTerm * sh, 0.0);
|
|
|
|
// Reflection Probes use occlusion, direct lights don't. MonoSH and Specular Hack are both somewhere in between,
|
|
// so we use focus to split the difference - 1.0 is direct, 0.0 is reflection probe, so we invert.
|
|
o_gi.indirect.specular *= LerpOneTo(occlusion, 1 - focus);
|
|
}
|
|
else
|
|
{
|
|
o_gi.indirect.specular = 0;
|
|
}
|
|
#else
|
|
o_gi.indirect.specular = 0;
|
|
#endif
|
|
o_gi.indirect.diffuse += data.ambient;
|
|
o_gi.indirect.diffuse *= occlusion;
|
|
#else
|
|
o_gi.indirect.specular = 0;
|
|
o_gi.indirect.diffuse = 0;
|
|
#endif
|
|
|
|
return o_gi;
|
|
}
|
|
|
|
struct SurfaceOutputStandardVRC
|
|
{
|
|
fixed3 Albedo; // base (diffuse or specular) color
|
|
float3 Normal; // tangent space normal, if written
|
|
half3 Emission;
|
|
half Metallic; // 0=non-metal, 1=metal
|
|
// Smoothness is the user facing name, it should be perceptual smoothness but user should not have to deal with it.
|
|
// Everywhere in the code you meet smoothness it is perceptual smoothness
|
|
half Smoothness; // 0=rough, 1=smooth
|
|
half Occlusion;
|
|
#if defined(_ENABLE_GEOMETRIC_SPECULAR_AA)
|
|
half SpecularAAVariance;
|
|
half SpecularAAThreshold;
|
|
#endif
|
|
fixed Alpha; // alpha for transparencies
|
|
half MinimumBrightness; // minimum brightness regardless of lighting
|
|
};
|
|
|
|
struct SurfaceOutputVRC
|
|
{
|
|
fixed3 Albedo;
|
|
fixed3 Normal;
|
|
fixed3 Emission;
|
|
half Specular;
|
|
fixed Gloss;
|
|
fixed Alpha;
|
|
};
|
|
|
|
// Based on BRDF2_Unity_PBS
|
|
// Modified here to re-use calculations for MonoSH and squeeze out all use cases not covered by VRC
|
|
half4 BRDF2_VRC_PBS (half3 diffColor, half3 specColor, half oneMinusReflectivity, half smoothness,
|
|
float3 normal, float3 viewDir,
|
|
UnityLight light, UnityIndirect gi)
|
|
{
|
|
half3 color = gi.diffuse * diffColor;
|
|
#if !defined(_SPECULARHIGHLIGHTS_OFF) || !defined(_GLOSSYREFLECTIONS_OFF)
|
|
half nv = saturate(dot(normal, viewDir));
|
|
half grazingTerm = saturate(smoothness + (1-oneMinusReflectivity));
|
|
// surfaceReduction = Int D(NdotH) * NdotH * Id(NdotL>0) dH = 1/(realRoughness^2+1)
|
|
half perceptualRoughness = SmoothnessToPerceptualRoughness(smoothness );//* sqrt(focus));
|
|
half roughness = PerceptualRoughnessToRoughness(perceptualRoughness);
|
|
|
|
// 1-0.28*x^3 as approximation for (1/(x^4+1))^(1/2.2) on the domain [0;1]
|
|
// 1-x^3*(0.6-0.08*x) approximation for 1/(x^4+1)
|
|
half surfaceReduction = (0.6-0.08*perceptualRoughness);
|
|
|
|
surfaceReduction = 1.0 - roughness*perceptualRoughness*surfaceReduction;
|
|
|
|
color += surfaceReduction * gi.specular * FresnelLerpFast (specColor, grazingTerm, nv);
|
|
#endif
|
|
|
|
// The DIRECTIONAL static branch exists, but in practice it seems like unity never switches back to the non-DIRECTIONAL variant once it switches
|
|
// Not worth branching for a single dotproduct, i.e. if no specularity
|
|
#if !defined(_SPECULARHIGHLIGHTS_OFF)
|
|
UNITY_BRANCH
|
|
if(any(light.color))
|
|
#else
|
|
if (true)
|
|
#endif
|
|
{
|
|
half nl = saturate(dot(normal, light.dir));
|
|
half3 mergedContrib = diffColor * nl;
|
|
#if !defined(_SPECULARHIGHLIGHTS_OFF)
|
|
half specularTerm = ComputeSpecularGGX(light.dir, -viewDir, normal, smoothness);
|
|
mergedContrib += max(specularTerm * specColor, 0.0);
|
|
#endif
|
|
|
|
color += light.color * mergedContrib;
|
|
}
|
|
// Original BRDF's color function.
|
|
// Interestingly, it doesn't appear as though fresnel is applied to specular highlights caused by lights?
|
|
// half3 color = (diffColor + specularTerm * specColor) * light.color * nl
|
|
// + gi.diffuse * diffColor
|
|
// + surfaceReduction * gi.specular * FresnelLerpFast (specColor, grazingTerm, nv);
|
|
|
|
return half4(color, 1);
|
|
}
|
|
|
|
// executed second
|
|
inline half4 LightingStandardVRC(SurfaceOutputStandardVRC s, float3 viewDir, UnityGI gi)
|
|
{
|
|
s.Normal = normalize(s.Normal);
|
|
#if defined(_ENABLE_GEOMETRIC_SPECULAR_AA)
|
|
s.Smoothness = ProjectedSpaceGeometricNormalFiltering(s.Smoothness, s.Normal, s.SpecularAAVariance, s.SpecularAAThreshold);
|
|
#endif
|
|
half3 specularColor;
|
|
half oneMinusReflectivity;
|
|
s.Albedo = DiffuseAndSpecularFromMetallic(s.Albedo, s.Metallic, /*out*/ specularColor, /*out*/ oneMinusReflectivity);
|
|
|
|
// shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
|
|
// this is necessary to handle transparency in physically correct way - only diffuse component gets affected by alpha
|
|
half outputAlpha;
|
|
s.Albedo = PreMultiplyAlpha (s.Albedo, s.Alpha, oneMinusReflectivity, /*out*/ outputAlpha);
|
|
|
|
half4 c = VRC_BRDF_PBS(s.Albedo, specularColor, oneMinusReflectivity, s.Smoothness, s.Normal, viewDir, gi.light, gi.indirect);
|
|
c.a = outputAlpha;
|
|
|
|
#ifndef _DEBUG_VRC
|
|
return c;
|
|
#else
|
|
return debugCol;
|
|
#endif
|
|
}
|
|
|
|
half3 VRC_GlossyEnvironment (UNITY_ARGS_TEXCUBE(tex), half4 hdr, Unity_GlossyEnvironmentData glossIn, out half hasReflProbe)
|
|
{
|
|
half perceptualRoughness = glossIn.roughness /* perceptualRoughness */ ;
|
|
|
|
// TODO: CAUTION: remap from Morten may work only with offline convolution, see impact with runtime convolution!
|
|
// For now disabled
|
|
#if 0
|
|
float m = PerceptualRoughnessToRoughness(perceptualRoughness); // m is the real roughness parameter
|
|
const float fEps = 1.192092896e-07F; // smallest such that 1.0+FLT_EPSILON != 1.0 (+1e-4h is NOT good here. is visibly very wrong)
|
|
float n = (2.0/max(fEps, m*m))-2.0; // remap to spec power. See eq. 21 in --> https://dl.dropboxusercontent.com/u/55891920/papers/mm_brdf.pdf
|
|
|
|
n /= 4; // remap from n_dot_h formulatino to n_dot_r. See section "Pre-convolved Cube Maps vs Path Tracers" --> https://s3.amazonaws.com/docs.knaldtech.com/knald/1.0.0/lys_power_drops.html
|
|
|
|
perceptualRoughness = pow( 2/(n+2), 0.25); // remap back to square root of real roughness (0.25 include both the sqrt root of the conversion and sqrt for going from roughness to perceptualRoughness)
|
|
#else
|
|
// MM: came up with a surprisingly close approximation to what the #if 0'ed out code above does.
|
|
perceptualRoughness = perceptualRoughness*(1.7 - 0.7*perceptualRoughness);
|
|
#endif
|
|
|
|
half mip = perceptualRoughnessToMipmapLevel(perceptualRoughness);
|
|
half3 R = glossIn.reflUVW;
|
|
half4 rgbm = UNITY_SAMPLE_TEXCUBE_LOD(tex, R, mip);
|
|
hasReflProbe = rgbm.a;
|
|
|
|
return DecodeHDR(rgbm, hdr);
|
|
}
|
|
|
|
inline half3 UnityGI_IndirectSpecularVRC(UnityGIInput data, half occlusion, Unity_GlossyEnvironmentData glossIn, out half hasReflProbe)
|
|
{
|
|
half3 specular;
|
|
|
|
#if defined(_GLOSSYREFLECTIONS_OFF)
|
|
specular = unity_IndirectSpecColor.rgb;
|
|
hasReflProbe = 0;
|
|
#else
|
|
#if defined(UNITY_SPECCUBE_BOX_PROJECTION)
|
|
// we will tweak reflUVW in glossIn directly (as we pass it to Unity_GlossyEnvironment twice for probe0 and probe1), so keep original to pass into BoxProjectedCubemapDirection
|
|
half3 originalReflUVW = glossIn.reflUVW;
|
|
glossIn.reflUVW = BoxProjectedCubemapDirection (originalReflUVW, data.worldPos, data.probePosition[0], data.boxMin[0], data.boxMax[0]);
|
|
#endif
|
|
|
|
half3 env0 = VRC_GlossyEnvironment (UNITY_PASS_TEXCUBE(unity_SpecCube0), data.probeHDR[0], glossIn, hasReflProbe);
|
|
#if defined(UNITY_SPECCUBE_BLENDING)
|
|
const float kBlendFactor = 0.99999;
|
|
float blendLerp = data.boxMin[0].w;
|
|
UNITY_BRANCH
|
|
if (blendLerp < kBlendFactor)
|
|
{
|
|
half secondReflProbe = 0;
|
|
half3 env1 = VRC_GlossyEnvironment (UNITY_PASS_TEXCUBE_SAMPLER(unity_SpecCube1,unity_SpecCube0), data.probeHDR[1], glossIn, secondReflProbe);
|
|
hasReflProbe += secondReflProbe;
|
|
specular = lerp(env1, env0, blendLerp);
|
|
}
|
|
else
|
|
{
|
|
specular = env0;
|
|
}
|
|
#else
|
|
specular = env0;
|
|
#endif
|
|
#endif
|
|
return specular * occlusion;
|
|
}
|
|
|
|
inline void VRC_ApplyMinBrightness(inout UnityGI gi, half minBright)
|
|
{
|
|
gi.indirect.diffuse = max(gi.indirect.diffuse, minBright);
|
|
}
|
|
|
|
// executed first
|
|
inline void LightingStandardVRC_GI(SurfaceOutputStandardVRC s, UnityGIInput data, inout UnityGI gi)
|
|
{
|
|
Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.Smoothness, data.worldViewDir, s.Normal, lerp(unity_ColorSpaceDielectricSpec.rgb, s.Albedo, s.Metallic));
|
|
half hasReflProbe = 0;
|
|
half3 indirectSpecular = UnityGI_IndirectSpecularVRC(data, s.Occlusion, g, /* out */ hasReflProbe);
|
|
gi = UnityGI_BaseVRC(data, s.Occlusion, s.Normal, -data.worldViewDir, s.Smoothness, hasReflProbe);
|
|
VRC_ApplyMinBrightness(gi, s.MinimumBrightness);
|
|
gi.indirect.specular += indirectSpecular;
|
|
}
|
|
|
|
inline fixed4 UnityLambertVRCLight (SurfaceOutputVRC s, UnityLight light)
|
|
{
|
|
fixed diff = max (0, dot (s.Normal, light.dir));
|
|
|
|
fixed4 c;
|
|
c.rgb = s.Albedo * light.color * diff;
|
|
c.a = s.Alpha;
|
|
return c;
|
|
}
|
|
|
|
inline fixed4 LightingLambertVRC (SurfaceOutputVRC s, UnityGI gi)
|
|
{
|
|
fixed4 c;
|
|
c = UnityLambertVRCLight (s, gi.light);
|
|
|
|
#if defined(UNITY_LIGHT_FUNCTION_APPLY_INDIRECT)
|
|
c.rgb += s.Albedo * gi.indirect.diffuse;
|
|
#endif
|
|
|
|
return c;
|
|
}
|
|
|
|
inline void LightingLambertVRC_GI (
|
|
SurfaceOutputVRC s,
|
|
UnityGIInput data,
|
|
inout UnityGI gi)
|
|
{
|
|
gi = UnityGI_BaseVRC(data, 1.0, s.Normal, half3(0, 0, 0), half(0), 0);
|
|
}
|
|
|
|
|
|
#endif
|
|
|
|
|
|
#pragma surface surf StandardVRC vertex:vert exclude_path:prepass exclude_path:deferred noforwardadd noshadow nodynlightmap nolppv noshadowmask
|
|
#pragma skip_variants LIGHTMAP_SHADOW_MIXING
|
|
|
|
// -------------------------------------
|
|
|
|
struct Input
|
|
{
|
|
float2 texcoord0;
|
|
#ifdef _DETAIL
|
|
float2 texcoord1;
|
|
#endif
|
|
fixed4 color : COLOR;
|
|
};
|
|
|
|
UNITY_DECLARE_TEX2D(_MainTex);
|
|
float4 _MainTex_ST;
|
|
half4 _Color;
|
|
uniform half _SDFCutoff;
|
|
|
|
UNITY_DECLARE_TEX2D(_MetallicGlossMap);
|
|
uniform half _Glossiness;
|
|
uniform half _Metallic;
|
|
|
|
UNITY_DECLARE_TEX2D(_BumpMap);
|
|
uniform half _BumpScale;
|
|
|
|
UNITY_DECLARE_TEX2D(_OcclusionMap);
|
|
uniform half _OcclusionStrength;
|
|
|
|
uniform half _SpecularAAScreenSpaceVariance;
|
|
uniform half _SpecularAAThreshold;
|
|
|
|
#ifdef _EMISSION
|
|
UNITY_DECLARE_TEX2D(_EmissionMap);
|
|
half4 _EmissionColor;
|
|
#endif
|
|
|
|
#ifdef _DETAIL
|
|
uniform half _UVSec;
|
|
float4 _DetailAlbedoMap_ST;
|
|
UNITY_DECLARE_TEX2D(_DetailMask);
|
|
UNITY_DECLARE_TEX2D(_DetailAlbedoMap);
|
|
UNITY_DECLARE_TEX2D(_DetailNormalMap);
|
|
uniform half _DetailNormalMapScale;
|
|
#endif
|
|
|
|
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
|
|
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
|
|
// #pragma instancing_options assumeuniformscaling
|
|
UNITY_INSTANCING_BUFFER_START(Props)
|
|
// put more per-instance properties here
|
|
UNITY_INSTANCING_BUFFER_END(Props)
|
|
|
|
// -------------------------------------
|
|
void vert(inout appdata_full v, out Input o)
|
|
{
|
|
UNITY_INITIALIZE_OUTPUT(Input,o);
|
|
o.texcoord0 = TRANSFORM_TEX(v.texcoord.xy, _MainTex); // Always source from uv0
|
|
#ifdef _DETAIL
|
|
o.texcoord1 = TRANSFORM_TEX(((_UVSec == 0) ? v.texcoord.xy : v.texcoord1.xy), _DetailAlbedoMap);
|
|
#endif
|
|
}
|
|
|
|
void surf(Input IN, inout SurfaceOutputStandardVRC o)
|
|
{
|
|
half alphastep = step(UNITY_SAMPLE_TEX2D(_MainTex, IN.texcoord0), 0.0020f);
|
|
if (alphastep < 0.5f) discard;
|
|
o.Albedo = _Color;
|
|
o.Alpha = alphastep;
|
|
|
|
o.Metallic = 0.0f;
|
|
o.Smoothness = _Glossiness;
|
|
o.Normal = half3(0, 0, 1);
|
|
|
|
#ifdef _EMISSION
|
|
o.Emission = UNITY_SAMPLE_TEX2D(_EmissionMap, IN.texcoord0) * _EmissionColor;
|
|
#endif
|
|
}
|
|
ENDCG
|
|
}
|
|
|
|
FallBack "VRChat/Mobile/Diffuse"
|
|
CustomEditor "StandardLiteShaderGUI"
|
|
} |