793 lines
27 KiB
HLSL
793 lines
27 KiB
HLSL
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
|
|
|
#ifndef VIDEO_STANDARD_CORE_INCLUDED
|
|
#define VIDEO_STANDARD_CORE_INCLUDED
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "UnityShaderVariables.cginc"
|
|
#include "UnityStandardConfig.cginc"
|
|
#include "UnityStandardInput.cginc"
|
|
#include "UnityPBSLighting.cginc"
|
|
#include "UnityStandardUtils.cginc"
|
|
#include "UnityGBuffer.cginc"
|
|
#include "UnityStandardBRDF.cginc"
|
|
|
|
#include "AutoLight.cginc"
|
|
|
|
int _IsAVProInput;
|
|
float _TargetAspectRatio;
|
|
float4 _EmissionMap_TexelSize;
|
|
|
|
half3 VideoEmission(float2 uv)
|
|
{
|
|
#ifndef _EMISSION
|
|
return 0;
|
|
#else
|
|
float2 emissionRes = _EmissionMap_TexelSize.zw;
|
|
|
|
float currentAspectRatio = emissionRes.x / emissionRes.y;
|
|
|
|
float visibility = 1.0;
|
|
|
|
// If the aspect ratio does not match the target ratio, then we fit the UVs to maintain the aspect ratio while fitting the range 0-1
|
|
if (abs(currentAspectRatio - _TargetAspectRatio) > 0.001)
|
|
{
|
|
float2 normalizedVideoRes = float2(emissionRes.x / _TargetAspectRatio, emissionRes.y);
|
|
float2 correctiveScale;
|
|
|
|
// Find which axis is greater, we will clamp to that
|
|
if (normalizedVideoRes.x > normalizedVideoRes.y)
|
|
correctiveScale = float2(1, normalizedVideoRes.y / normalizedVideoRes.x);
|
|
else
|
|
correctiveScale = float2(normalizedVideoRes.x / normalizedVideoRes.y, 1);
|
|
|
|
uv = ((uv - 0.5) / correctiveScale) + 0.5;
|
|
|
|
// Antialiasing on UV clipping
|
|
float2 uvPadding = (1 / emissionRes) * 0.1;
|
|
float2 uvfwidth = fwidth(uv.xy);
|
|
float2 maxFactor = smoothstep(uvfwidth + uvPadding + 1, uvPadding + 1, uv.xy);
|
|
float2 minFactor = smoothstep(-uvfwidth - uvPadding, -uvPadding, uv.xy);
|
|
|
|
visibility = maxFactor.x * maxFactor.y * minFactor.x * minFactor.y;
|
|
|
|
//if (any(uv <= 0) || any(uv >= 1))
|
|
// return float3(0, 0, 0);
|
|
}
|
|
|
|
float3 texColor = tex2D(_EmissionMap, _IsAVProInput ? float2(uv.x, 1 - uv.y) : uv).rgb;
|
|
|
|
if (_IsAVProInput)
|
|
texColor = pow(texColor, 2.2f);
|
|
|
|
return texColor * _EmissionColor.rgb * visibility;
|
|
#endif
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// counterpart for NormalizePerPixelNormal
|
|
// skips normalization per-vertex and expects normalization to happen per-pixel
|
|
half3 NormalizePerVertexNormal (float3 n) // takes float to avoid overflow
|
|
{
|
|
#if (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
|
|
return normalize(n);
|
|
#else
|
|
return n; // will normalize per-pixel instead
|
|
#endif
|
|
}
|
|
|
|
float3 NormalizePerPixelNormal (float3 n)
|
|
{
|
|
#if (SHADER_TARGET < 30) || UNITY_STANDARD_SIMPLE
|
|
return n;
|
|
#else
|
|
return normalize((float3)n); // takes float to avoid overflow
|
|
#endif
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
UnityLight MainLight ()
|
|
{
|
|
UnityLight l;
|
|
|
|
l.color = _LightColor0.rgb;
|
|
l.dir = _WorldSpaceLightPos0.xyz;
|
|
return l;
|
|
}
|
|
|
|
UnityLight AdditiveLight (half3 lightDir, half atten)
|
|
{
|
|
UnityLight l;
|
|
|
|
l.color = _LightColor0.rgb;
|
|
l.dir = lightDir;
|
|
#ifndef USING_DIRECTIONAL_LIGHT
|
|
l.dir = NormalizePerPixelNormal(l.dir);
|
|
#endif
|
|
|
|
// shadow the light
|
|
l.color *= atten;
|
|
return l;
|
|
}
|
|
|
|
UnityLight DummyLight ()
|
|
{
|
|
UnityLight l;
|
|
l.color = 0;
|
|
l.dir = half3 (0,1,0);
|
|
return l;
|
|
}
|
|
|
|
UnityIndirect ZeroIndirect ()
|
|
{
|
|
UnityIndirect ind;
|
|
ind.diffuse = 0;
|
|
ind.specular = 0;
|
|
return ind;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
// Common fragment setup
|
|
|
|
// deprecated
|
|
half3 WorldNormal(half4 tan2world[3])
|
|
{
|
|
return normalize(tan2world[2].xyz);
|
|
}
|
|
|
|
// deprecated
|
|
#ifdef _TANGENT_TO_WORLD
|
|
half3x3 ExtractTangentToWorldPerPixel(half4 tan2world[3])
|
|
{
|
|
half3 t = tan2world[0].xyz;
|
|
half3 b = tan2world[1].xyz;
|
|
half3 n = tan2world[2].xyz;
|
|
|
|
#if UNITY_TANGENT_ORTHONORMALIZE
|
|
n = NormalizePerPixelNormal(n);
|
|
|
|
// ortho-normalize Tangent
|
|
t = normalize (t - n * dot(t, n));
|
|
|
|
// recalculate Binormal
|
|
half3 newB = cross(n, t);
|
|
b = newB * sign (dot (newB, b));
|
|
#endif
|
|
|
|
return half3x3(t, b, n);
|
|
}
|
|
#else
|
|
half3x3 ExtractTangentToWorldPerPixel(half4 tan2world[3])
|
|
{
|
|
return half3x3(0,0,0,0,0,0,0,0,0);
|
|
}
|
|
#endif
|
|
|
|
float3 PerPixelWorldNormal(float4 i_tex, float4 tangentToWorld[3])
|
|
{
|
|
#ifdef _NORMALMAP
|
|
half3 tangent = tangentToWorld[0].xyz;
|
|
half3 binormal = tangentToWorld[1].xyz;
|
|
half3 normal = tangentToWorld[2].xyz;
|
|
|
|
#if UNITY_TANGENT_ORTHONORMALIZE
|
|
normal = NormalizePerPixelNormal(normal);
|
|
|
|
// ortho-normalize Tangent
|
|
tangent = normalize (tangent - normal * dot(tangent, normal));
|
|
|
|
// recalculate Binormal
|
|
half3 newB = cross(normal, tangent);
|
|
binormal = newB * sign (dot (newB, binormal));
|
|
#endif
|
|
|
|
half3 normalTangent = NormalInTangentSpace(i_tex);
|
|
float3 normalWorld = NormalizePerPixelNormal(tangent * normalTangent.x + binormal * normalTangent.y + normal * normalTangent.z); // @TODO: see if we can squeeze this normalize on SM2.0 as well
|
|
#else
|
|
float3 normalWorld = normalize(tangentToWorld[2].xyz);
|
|
#endif
|
|
return normalWorld;
|
|
}
|
|
|
|
#ifdef _PARALLAXMAP
|
|
#define IN_VIEWDIR4PARALLAX(i) NormalizePerPixelNormal(half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w))
|
|
#define IN_VIEWDIR4PARALLAX_FWDADD(i) NormalizePerPixelNormal(i.viewDirForParallax.xyz)
|
|
#else
|
|
#define IN_VIEWDIR4PARALLAX(i) half3(0,0,0)
|
|
#define IN_VIEWDIR4PARALLAX_FWDADD(i) half3(0,0,0)
|
|
#endif
|
|
|
|
#if UNITY_REQUIRE_FRAG_WORLDPOS
|
|
#if UNITY_PACK_WORLDPOS_WITH_TANGENT
|
|
#define IN_WORLDPOS(i) half3(i.tangentToWorldAndPackedData[0].w,i.tangentToWorldAndPackedData[1].w,i.tangentToWorldAndPackedData[2].w)
|
|
#else
|
|
#define IN_WORLDPOS(i) i.posWorld
|
|
#endif
|
|
#define IN_WORLDPOS_FWDADD(i) i.posWorld
|
|
#else
|
|
#define IN_WORLDPOS(i) half3(0,0,0)
|
|
#define IN_WORLDPOS_FWDADD(i) half3(0,0,0)
|
|
#endif
|
|
|
|
#define IN_LIGHTDIR_FWDADD(i) half3(i.tangentToWorldAndLightDir[0].w, i.tangentToWorldAndLightDir[1].w, i.tangentToWorldAndLightDir[2].w)
|
|
|
|
#define FRAGMENT_SETUP(x) FragmentCommonData x = \
|
|
FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX(i), i.tangentToWorldAndPackedData, IN_WORLDPOS(i));
|
|
|
|
#define FRAGMENT_SETUP_FWDADD(x) FragmentCommonData x = \
|
|
FragmentSetup(i.tex, i.eyeVec.xyz, IN_VIEWDIR4PARALLAX_FWDADD(i), i.tangentToWorldAndLightDir, IN_WORLDPOS_FWDADD(i));
|
|
|
|
struct FragmentCommonData
|
|
{
|
|
half3 diffColor, specColor;
|
|
// Note: smoothness & oneMinusReflectivity for optimization purposes, mostly for DX9 SM2.0 level.
|
|
// Most of the math is being done on these (1-x) values, and that saves a few precious ALU slots.
|
|
half oneMinusReflectivity, smoothness;
|
|
float3 normalWorld;
|
|
float3 eyeVec;
|
|
half alpha;
|
|
float3 posWorld;
|
|
|
|
#if UNITY_STANDARD_SIMPLE
|
|
half3 reflUVW;
|
|
#endif
|
|
|
|
#if UNITY_STANDARD_SIMPLE
|
|
half3 tangentSpaceNormal;
|
|
#endif
|
|
};
|
|
|
|
#ifndef UNITY_SETUP_BRDF_INPUT
|
|
#define UNITY_SETUP_BRDF_INPUT SpecularSetup
|
|
#endif
|
|
|
|
inline FragmentCommonData SpecularSetup (float4 i_tex)
|
|
{
|
|
half4 specGloss = SpecularGloss(i_tex.xy);
|
|
half3 specColor = specGloss.rgb;
|
|
half smoothness = specGloss.a;
|
|
|
|
half oneMinusReflectivity;
|
|
half3 diffColor = EnergyConservationBetweenDiffuseAndSpecular (Albedo(i_tex), specColor, /*out*/ oneMinusReflectivity);
|
|
|
|
FragmentCommonData o = (FragmentCommonData)0;
|
|
o.diffColor = diffColor;
|
|
o.specColor = specColor;
|
|
o.oneMinusReflectivity = oneMinusReflectivity;
|
|
o.smoothness = smoothness;
|
|
return o;
|
|
}
|
|
|
|
inline FragmentCommonData RoughnessSetup(float4 i_tex)
|
|
{
|
|
half2 metallicGloss = MetallicRough(i_tex.xy);
|
|
half metallic = metallicGloss.x;
|
|
half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
|
|
|
|
half oneMinusReflectivity;
|
|
half3 specColor;
|
|
half3 diffColor = DiffuseAndSpecularFromMetallic(Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
|
|
|
|
FragmentCommonData o = (FragmentCommonData)0;
|
|
o.diffColor = diffColor;
|
|
o.specColor = specColor;
|
|
o.oneMinusReflectivity = oneMinusReflectivity;
|
|
o.smoothness = smoothness;
|
|
return o;
|
|
}
|
|
|
|
inline FragmentCommonData MetallicSetup (float4 i_tex)
|
|
{
|
|
half2 metallicGloss = MetallicGloss(i_tex.xy);
|
|
half metallic = metallicGloss.x;
|
|
half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.
|
|
|
|
half oneMinusReflectivity;
|
|
half3 specColor;
|
|
half3 diffColor = DiffuseAndSpecularFromMetallic (Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);
|
|
|
|
FragmentCommonData o = (FragmentCommonData)0;
|
|
o.diffColor = diffColor;
|
|
o.specColor = specColor;
|
|
o.oneMinusReflectivity = oneMinusReflectivity;
|
|
o.smoothness = smoothness;
|
|
return o;
|
|
}
|
|
|
|
// parallax transformed texcoord is used to sample occlusion
|
|
inline FragmentCommonData FragmentSetup (inout float4 i_tex, float3 i_eyeVec, half3 i_viewDirForParallax, float4 tangentToWorld[3], float3 i_posWorld)
|
|
{
|
|
i_tex = Parallax(i_tex, i_viewDirForParallax);
|
|
|
|
half alpha = Alpha(i_tex.xy);
|
|
#if defined(_ALPHATEST_ON)
|
|
clip (alpha - _Cutoff);
|
|
#endif
|
|
|
|
FragmentCommonData o = UNITY_SETUP_BRDF_INPUT (i_tex);
|
|
o.normalWorld = PerPixelWorldNormal(i_tex, tangentToWorld);
|
|
o.eyeVec = NormalizePerPixelNormal(i_eyeVec);
|
|
o.posWorld = i_posWorld;
|
|
|
|
// NOTE: shader relies on pre-multiply alpha-blend (_SrcBlend = One, _DstBlend = OneMinusSrcAlpha)
|
|
o.diffColor = PreMultiplyAlpha (o.diffColor, alpha, o.oneMinusReflectivity, /*out*/ o.alpha);
|
|
return o;
|
|
}
|
|
|
|
inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light, bool reflections)
|
|
{
|
|
UnityGIInput d;
|
|
d.light = light;
|
|
d.worldPos = s.posWorld;
|
|
d.worldViewDir = -s.eyeVec;
|
|
d.atten = atten;
|
|
#if defined(LIGHTMAP_ON) || defined(DYNAMICLIGHTMAP_ON)
|
|
d.ambient = 0;
|
|
d.lightmapUV = i_ambientOrLightmapUV;
|
|
#else
|
|
d.ambient = i_ambientOrLightmapUV.rgb;
|
|
d.lightmapUV = 0;
|
|
#endif
|
|
|
|
d.probeHDR[0] = unity_SpecCube0_HDR;
|
|
d.probeHDR[1] = unity_SpecCube1_HDR;
|
|
#if defined(UNITY_SPECCUBE_BLENDING) || defined(UNITY_SPECCUBE_BOX_PROJECTION)
|
|
d.boxMin[0] = unity_SpecCube0_BoxMin; // .w holds lerp value for blending
|
|
#endif
|
|
#ifdef UNITY_SPECCUBE_BOX_PROJECTION
|
|
d.boxMax[0] = unity_SpecCube0_BoxMax;
|
|
d.probePosition[0] = unity_SpecCube0_ProbePosition;
|
|
d.boxMax[1] = unity_SpecCube1_BoxMax;
|
|
d.boxMin[1] = unity_SpecCube1_BoxMin;
|
|
d.probePosition[1] = unity_SpecCube1_ProbePosition;
|
|
#endif
|
|
|
|
if(reflections)
|
|
{
|
|
Unity_GlossyEnvironmentData g = UnityGlossyEnvironmentSetup(s.smoothness, -s.eyeVec, s.normalWorld, s.specColor);
|
|
// Replace the reflUVW if it has been compute in Vertex shader. Note: the compiler will optimize the calcul in UnityGlossyEnvironmentSetup itself
|
|
#if UNITY_STANDARD_SIMPLE
|
|
g.reflUVW = s.reflUVW;
|
|
#endif
|
|
|
|
return UnityGlobalIllumination (d, occlusion, s.normalWorld, g);
|
|
}
|
|
else
|
|
{
|
|
return UnityGlobalIllumination (d, occlusion, s.normalWorld);
|
|
}
|
|
}
|
|
|
|
inline UnityGI FragmentGI (FragmentCommonData s, half occlusion, half4 i_ambientOrLightmapUV, half atten, UnityLight light)
|
|
{
|
|
return FragmentGI(s, occlusion, i_ambientOrLightmapUV, atten, light, true);
|
|
}
|
|
|
|
|
|
//-------------------------------------------------------------------------------------
|
|
half4 OutputForward (half4 output, half alphaFromSurface)
|
|
{
|
|
#if defined(_ALPHABLEND_ON) || defined(_ALPHAPREMULTIPLY_ON)
|
|
output.a = alphaFromSurface;
|
|
#else
|
|
UNITY_OPAQUE_ALPHA(output.a);
|
|
#endif
|
|
return output;
|
|
}
|
|
|
|
inline half4 VertexGIForward(VertexInput v, float3 posWorld, half3 normalWorld)
|
|
{
|
|
half4 ambientOrLightmapUV = 0;
|
|
// Static lightmaps
|
|
#ifdef LIGHTMAP_ON
|
|
ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
|
|
ambientOrLightmapUV.zw = 0;
|
|
// Sample light probe for Dynamic objects only (no static or dynamic lightmaps)
|
|
#elif UNITY_SHOULD_SAMPLE_SH
|
|
#ifdef VERTEXLIGHT_ON
|
|
// Approximated illumination from non-important point lights
|
|
ambientOrLightmapUV.rgb = Shade4PointLights (
|
|
unity_4LightPosX0, unity_4LightPosY0, unity_4LightPosZ0,
|
|
unity_LightColor[0].rgb, unity_LightColor[1].rgb, unity_LightColor[2].rgb, unity_LightColor[3].rgb,
|
|
unity_4LightAtten0, posWorld, normalWorld);
|
|
#endif
|
|
|
|
ambientOrLightmapUV.rgb = ShadeSHPerVertex (normalWorld, ambientOrLightmapUV.rgb);
|
|
#endif
|
|
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
|
|
#endif
|
|
|
|
return ambientOrLightmapUV;
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Base forward pass (directional light, emission, lightmaps, ...)
|
|
|
|
struct VertexOutputForwardBase
|
|
{
|
|
UNITY_POSITION(pos);
|
|
float4 tex : TEXCOORD0;
|
|
float4 eyeVec : TEXCOORD1; // eyeVec.xyz | fogCoord
|
|
float4 tangentToWorldAndPackedData[3] : TEXCOORD2; // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
|
|
half4 ambientOrLightmapUV : TEXCOORD5; // SH or Lightmap UV
|
|
UNITY_LIGHTING_COORDS(6,7)
|
|
|
|
// next ones would not fit into SM2.0 limits, but they are always for SM3.0+
|
|
#if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
|
|
float3 posWorld : TEXCOORD8;
|
|
#endif
|
|
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
VertexOutputForwardBase vertForwardBase (VertexInput v)
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
VertexOutputForwardBase o;
|
|
UNITY_INITIALIZE_OUTPUT(VertexOutputForwardBase, o);
|
|
UNITY_TRANSFER_INSTANCE_ID(v, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
|
|
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
|
|
#if UNITY_REQUIRE_FRAG_WORLDPOS
|
|
#if UNITY_PACK_WORLDPOS_WITH_TANGENT
|
|
o.tangentToWorldAndPackedData[0].w = posWorld.x;
|
|
o.tangentToWorldAndPackedData[1].w = posWorld.y;
|
|
o.tangentToWorldAndPackedData[2].w = posWorld.z;
|
|
#else
|
|
o.posWorld = posWorld.xyz;
|
|
#endif
|
|
#endif
|
|
o.pos = UnityObjectToClipPos(v.vertex);
|
|
|
|
o.tex = TexCoords(v);
|
|
o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
|
|
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
|
|
#ifdef _TANGENT_TO_WORLD
|
|
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
|
|
|
|
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
|
|
o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
|
|
o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
|
|
o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
|
|
#else
|
|
o.tangentToWorldAndPackedData[0].xyz = 0;
|
|
o.tangentToWorldAndPackedData[1].xyz = 0;
|
|
o.tangentToWorldAndPackedData[2].xyz = normalWorld;
|
|
#endif
|
|
|
|
//We need this for shadow receving
|
|
UNITY_TRANSFER_LIGHTING(o, v.uv1);
|
|
|
|
o.ambientOrLightmapUV = VertexGIForward(v, posWorld, normalWorld);
|
|
|
|
#ifdef _PARALLAXMAP
|
|
TANGENT_SPACE_ROTATION;
|
|
half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));
|
|
o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
|
|
o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
|
|
o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
|
|
#endif
|
|
|
|
UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o,o.pos);
|
|
return o;
|
|
}
|
|
|
|
half4 fragForwardBaseInternal (VertexOutputForwardBase i)
|
|
{
|
|
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
|
|
|
|
FRAGMENT_SETUP(s)
|
|
|
|
UNITY_SETUP_INSTANCE_ID(i);
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
|
|
|
UnityLight mainLight = MainLight ();
|
|
UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld);
|
|
|
|
half occlusion = Occlusion(i.tex.xy);
|
|
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, mainLight);
|
|
|
|
half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect);
|
|
c.rgb += VideoEmission(i.tex.xy);
|
|
|
|
UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
|
|
UNITY_APPLY_FOG(_unity_fogCoord, c.rgb);
|
|
return OutputForward (c, s.alpha);
|
|
}
|
|
|
|
half4 fragForwardBase (VertexOutputForwardBase i) : SV_Target // backward compatibility (this used to be the fragment entry function)
|
|
{
|
|
return fragForwardBaseInternal(i);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Additive forward pass (one light per pass)
|
|
|
|
struct VertexOutputForwardAdd
|
|
{
|
|
UNITY_POSITION(pos);
|
|
float4 tex : TEXCOORD0;
|
|
float4 eyeVec : TEXCOORD1; // eyeVec.xyz | fogCoord
|
|
float4 tangentToWorldAndLightDir[3] : TEXCOORD2; // [3x3:tangentToWorld | 1x3:lightDir]
|
|
float3 posWorld : TEXCOORD5;
|
|
UNITY_LIGHTING_COORDS(6, 7)
|
|
|
|
// next ones would not fit into SM2.0 limits, but they are always for SM3.0+
|
|
#if defined(_PARALLAXMAP)
|
|
half3 viewDirForParallax : TEXCOORD8;
|
|
#endif
|
|
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
VertexOutputForwardAdd vertForwardAdd (VertexInput v)
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
VertexOutputForwardAdd o;
|
|
UNITY_INITIALIZE_OUTPUT(VertexOutputForwardAdd, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
|
|
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
|
|
o.pos = UnityObjectToClipPos(v.vertex);
|
|
|
|
o.tex = TexCoords(v);
|
|
o.eyeVec.xyz = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
|
|
o.posWorld = posWorld.xyz;
|
|
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
|
|
#ifdef _TANGENT_TO_WORLD
|
|
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
|
|
|
|
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
|
|
o.tangentToWorldAndLightDir[0].xyz = tangentToWorld[0];
|
|
o.tangentToWorldAndLightDir[1].xyz = tangentToWorld[1];
|
|
o.tangentToWorldAndLightDir[2].xyz = tangentToWorld[2];
|
|
#else
|
|
o.tangentToWorldAndLightDir[0].xyz = 0;
|
|
o.tangentToWorldAndLightDir[1].xyz = 0;
|
|
o.tangentToWorldAndLightDir[2].xyz = normalWorld;
|
|
#endif
|
|
//We need this for shadow receiving and lighting
|
|
UNITY_TRANSFER_LIGHTING(o, v.uv1);
|
|
|
|
float3 lightDir = _WorldSpaceLightPos0.xyz - posWorld.xyz * _WorldSpaceLightPos0.w;
|
|
#ifndef USING_DIRECTIONAL_LIGHT
|
|
lightDir = NormalizePerVertexNormal(lightDir);
|
|
#endif
|
|
o.tangentToWorldAndLightDir[0].w = lightDir.x;
|
|
o.tangentToWorldAndLightDir[1].w = lightDir.y;
|
|
o.tangentToWorldAndLightDir[2].w = lightDir.z;
|
|
|
|
#ifdef _PARALLAXMAP
|
|
TANGENT_SPACE_ROTATION;
|
|
o.viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));
|
|
#endif
|
|
|
|
UNITY_TRANSFER_FOG_COMBINED_WITH_EYE_VEC(o, o.pos);
|
|
return o;
|
|
}
|
|
|
|
half4 fragForwardAddInternal (VertexOutputForwardAdd i)
|
|
{
|
|
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
|
|
|
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
|
|
|
|
FRAGMENT_SETUP_FWDADD(s)
|
|
|
|
UNITY_LIGHT_ATTENUATION(atten, i, s.posWorld)
|
|
UnityLight light = AdditiveLight (IN_LIGHTDIR_FWDADD(i), atten);
|
|
UnityIndirect noIndirect = ZeroIndirect ();
|
|
|
|
half4 c = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, light, noIndirect);
|
|
|
|
UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);
|
|
UNITY_APPLY_FOG_COLOR(_unity_fogCoord, c.rgb, half4(0,0,0,0)); // fog towards black in additive pass
|
|
return OutputForward (c, s.alpha);
|
|
}
|
|
|
|
half4 fragForwardAdd (VertexOutputForwardAdd i) : SV_Target // backward compatibility (this used to be the fragment entry function)
|
|
{
|
|
return fragForwardAddInternal(i);
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Deferred pass
|
|
|
|
struct VertexOutputDeferred
|
|
{
|
|
UNITY_POSITION(pos);
|
|
float4 tex : TEXCOORD0;
|
|
float3 eyeVec : TEXCOORD1;
|
|
float4 tangentToWorldAndPackedData[3] : TEXCOORD2; // [3x3:tangentToWorld | 1x3:viewDirForParallax or worldPos]
|
|
half4 ambientOrLightmapUV : TEXCOORD5; // SH or Lightmap UVs
|
|
|
|
#if UNITY_REQUIRE_FRAG_WORLDPOS && !UNITY_PACK_WORLDPOS_WITH_TANGENT
|
|
float3 posWorld : TEXCOORD6;
|
|
#endif
|
|
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
|
|
VertexOutputDeferred vertDeferred (VertexInput v)
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(v);
|
|
VertexOutputDeferred o;
|
|
UNITY_INITIALIZE_OUTPUT(VertexOutputDeferred, o);
|
|
UNITY_TRANSFER_INSTANCE_ID(v, o);
|
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
|
|
|
|
float4 posWorld = mul(unity_ObjectToWorld, v.vertex);
|
|
#if UNITY_REQUIRE_FRAG_WORLDPOS
|
|
#if UNITY_PACK_WORLDPOS_WITH_TANGENT
|
|
o.tangentToWorldAndPackedData[0].w = posWorld.x;
|
|
o.tangentToWorldAndPackedData[1].w = posWorld.y;
|
|
o.tangentToWorldAndPackedData[2].w = posWorld.z;
|
|
#else
|
|
o.posWorld = posWorld.xyz;
|
|
#endif
|
|
#endif
|
|
o.pos = UnityObjectToClipPos(v.vertex);
|
|
|
|
o.tex = TexCoords(v);
|
|
o.eyeVec = NormalizePerVertexNormal(posWorld.xyz - _WorldSpaceCameraPos);
|
|
float3 normalWorld = UnityObjectToWorldNormal(v.normal);
|
|
#ifdef _TANGENT_TO_WORLD
|
|
float4 tangentWorld = float4(UnityObjectToWorldDir(v.tangent.xyz), v.tangent.w);
|
|
|
|
float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld.xyz, tangentWorld.w);
|
|
o.tangentToWorldAndPackedData[0].xyz = tangentToWorld[0];
|
|
o.tangentToWorldAndPackedData[1].xyz = tangentToWorld[1];
|
|
o.tangentToWorldAndPackedData[2].xyz = tangentToWorld[2];
|
|
#else
|
|
o.tangentToWorldAndPackedData[0].xyz = 0;
|
|
o.tangentToWorldAndPackedData[1].xyz = 0;
|
|
o.tangentToWorldAndPackedData[2].xyz = normalWorld;
|
|
#endif
|
|
|
|
o.ambientOrLightmapUV = 0;
|
|
#ifdef LIGHTMAP_ON
|
|
o.ambientOrLightmapUV.xy = v.uv1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
|
|
#elif UNITY_SHOULD_SAMPLE_SH
|
|
o.ambientOrLightmapUV.rgb = ShadeSHPerVertex (normalWorld, o.ambientOrLightmapUV.rgb);
|
|
#endif
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
o.ambientOrLightmapUV.zw = v.uv2.xy * unity_DynamicLightmapST.xy + unity_DynamicLightmapST.zw;
|
|
#endif
|
|
|
|
#ifdef _PARALLAXMAP
|
|
TANGENT_SPACE_ROTATION;
|
|
half3 viewDirForParallax = mul (rotation, ObjSpaceViewDir(v.vertex));
|
|
o.tangentToWorldAndPackedData[0].w = viewDirForParallax.x;
|
|
o.tangentToWorldAndPackedData[1].w = viewDirForParallax.y;
|
|
o.tangentToWorldAndPackedData[2].w = viewDirForParallax.z;
|
|
#endif
|
|
|
|
return o;
|
|
}
|
|
|
|
void fragDeferred (
|
|
VertexOutputDeferred i,
|
|
out half4 outGBuffer0 : SV_Target0,
|
|
out half4 outGBuffer1 : SV_Target1,
|
|
out half4 outGBuffer2 : SV_Target2,
|
|
out half4 outEmission : SV_Target3 // RT3: emission (rgb), --unused-- (a)
|
|
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
|
|
,out half4 outShadowMask : SV_Target4 // RT4: shadowmask (rgba)
|
|
#endif
|
|
)
|
|
{
|
|
#if (SHADER_TARGET < 30)
|
|
outGBuffer0 = 1;
|
|
outGBuffer1 = 1;
|
|
outGBuffer2 = 0;
|
|
outEmission = 0;
|
|
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
|
|
outShadowMask = 1;
|
|
#endif
|
|
return;
|
|
#endif
|
|
|
|
UNITY_APPLY_DITHER_CROSSFADE(i.pos.xy);
|
|
|
|
FRAGMENT_SETUP(s)
|
|
UNITY_SETUP_INSTANCE_ID(i);
|
|
|
|
// no analytic lights in this pass
|
|
UnityLight dummyLight = DummyLight ();
|
|
half atten = 1;
|
|
|
|
// only GI
|
|
half occlusion = Occlusion(i.tex.xy);
|
|
#if UNITY_ENABLE_REFLECTION_BUFFERS
|
|
bool sampleReflectionsInDeferred = false;
|
|
#else
|
|
bool sampleReflectionsInDeferred = true;
|
|
#endif
|
|
|
|
UnityGI gi = FragmentGI (s, occlusion, i.ambientOrLightmapUV, atten, dummyLight, sampleReflectionsInDeferred);
|
|
|
|
half3 emissiveColor = UNITY_BRDF_PBS (s.diffColor, s.specColor, s.oneMinusReflectivity, s.smoothness, s.normalWorld, -s.eyeVec, gi.light, gi.indirect).rgb;
|
|
|
|
#ifdef _EMISSION
|
|
emissiveColor += VideoEmission (i.tex.xy);
|
|
#endif
|
|
|
|
#ifndef UNITY_HDR_ON
|
|
emissiveColor.rgb = exp2(-emissiveColor.rgb);
|
|
#endif
|
|
|
|
UnityStandardData data;
|
|
data.diffuseColor = s.diffColor;
|
|
data.occlusion = occlusion;
|
|
data.specularColor = s.specColor;
|
|
data.smoothness = s.smoothness;
|
|
data.normalWorld = s.normalWorld;
|
|
|
|
UnityStandardDataToGbuffer(data, outGBuffer0, outGBuffer1, outGBuffer2);
|
|
|
|
// Emissive lighting buffer
|
|
outEmission = half4(emissiveColor, 1);
|
|
|
|
// Baked direct lighting occlusion if any
|
|
#if defined(SHADOWS_SHADOWMASK) && (UNITY_ALLOWED_MRT_COUNT > 4)
|
|
outShadowMask = UnityGetRawBakedOcclusions(i.ambientOrLightmapUV.xy, IN_WORLDPOS(i));
|
|
#endif
|
|
}
|
|
|
|
|
|
//
|
|
// Old FragmentGI signature. Kept only for backward compatibility and will be removed soon
|
|
//
|
|
|
|
inline UnityGI FragmentGI(
|
|
float3 posWorld,
|
|
half occlusion, half4 i_ambientOrLightmapUV, half atten, half smoothness, half3 normalWorld, half3 eyeVec,
|
|
UnityLight light,
|
|
bool reflections)
|
|
{
|
|
// we init only fields actually used
|
|
FragmentCommonData s = (FragmentCommonData)0;
|
|
s.smoothness = smoothness;
|
|
s.normalWorld = normalWorld;
|
|
s.eyeVec = eyeVec;
|
|
s.posWorld = posWorld;
|
|
return FragmentGI(s, occlusion, i_ambientOrLightmapUV, atten, light, reflections);
|
|
}
|
|
inline UnityGI FragmentGI (
|
|
float3 posWorld,
|
|
half occlusion, half4 i_ambientOrLightmapUV, half atten, half smoothness, half3 normalWorld, half3 eyeVec,
|
|
UnityLight light)
|
|
{
|
|
return FragmentGI (posWorld, occlusion, i_ambientOrLightmapUV, atten, smoothness, normalWorld, eyeVec, light, true);
|
|
}
|
|
|
|
#endif // UNITY_STANDARD_CORE_INCLUDED
|
|
|
|
#ifndef VIDEO_STANDARD_CORE_FORWARD_INCLUDED
|
|
#define VIDEO_STANDARD_CORE_FORWARD_INCLUDED
|
|
|
|
#if defined(UNITY_NO_FULL_STANDARD_SHADER)
|
|
# define UNITY_STANDARD_SIMPLE 1
|
|
#endif
|
|
|
|
#include "UnityStandardConfig.cginc"
|
|
|
|
#if UNITY_STANDARD_SIMPLE
|
|
VertexOutputBaseSimple vertBase(VertexInput v) { return vertForwardBaseSimple(v); }
|
|
VertexOutputForwardAddSimple vertAdd(VertexInput v) { return vertForwardAddSimple(v); }
|
|
half4 fragBase(VertexOutputBaseSimple i) : SV_Target{ return fragForwardBaseSimpleInternal(i); }
|
|
half4 fragAdd(VertexOutputForwardAddSimple i) : SV_Target{ return fragForwardAddSimpleInternal(i); }
|
|
#else
|
|
VertexOutputForwardBase vertBase(VertexInput v) { return vertForwardBase(v); }
|
|
VertexOutputForwardAdd vertAdd(VertexInput v) { return vertForwardAdd(v); }
|
|
half4 fragBase(VertexOutputForwardBase i) : SV_Target{ return fragForwardBaseInternal(i); }
|
|
half4 fragAdd(VertexOutputForwardAdd i) : SV_Target{ return fragForwardAddInternal(i); }
|
|
#endif
|
|
|
|
#endif // UNITY_STANDARD_CORE_FORWARD_INCLUDED |