318 lines
10 KiB
HLSL
318 lines
10 KiB
HLSL
// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// SpeedTree8Common.cginc
|
|
|
|
#ifndef SPEEDTREE8_COMMON_INCLUDED
|
|
#define SPEEDTREE8_COMMON_INCLUDED
|
|
|
|
#include "UnityCG.cginc"
|
|
#include "UnityPBSLighting.cginc"
|
|
|
|
#if defined(ENABLE_WIND) && !defined(_WINDQUALITY_NONE)
|
|
#define SPEEDTREE_Y_UP
|
|
#include "SpeedTreeWind.cginc"
|
|
float _WindEnabled;
|
|
UNITY_INSTANCING_BUFFER_START(STWind)
|
|
UNITY_DEFINE_INSTANCED_PROP(float, _GlobalWindTime)
|
|
UNITY_INSTANCING_BUFFER_END(STWind)
|
|
#endif
|
|
|
|
struct Input
|
|
{
|
|
half2 uv_MainTex : TEXCOORD0;
|
|
fixed4 color : COLOR;
|
|
|
|
#ifdef EFFECT_BACKSIDE_NORMALS
|
|
fixed facing : VFACE;
|
|
#endif
|
|
};
|
|
|
|
sampler2D _MainTex;
|
|
fixed4 _Color;
|
|
int _TwoSided;
|
|
|
|
#ifdef EFFECT_BUMP
|
|
sampler2D _BumpMap;
|
|
#endif
|
|
|
|
#ifdef EFFECT_EXTRA_TEX
|
|
sampler2D _ExtraTex;
|
|
#else
|
|
half _Glossiness;
|
|
half _Metallic;
|
|
#endif
|
|
|
|
#ifdef EFFECT_HUE_VARIATION
|
|
half4 _HueVariationColor;
|
|
#endif
|
|
|
|
#ifdef EFFECT_BILLBOARD
|
|
half _BillboardShadowFade;
|
|
#endif
|
|
|
|
#ifdef EFFECT_SUBSURFACE
|
|
sampler2D _SubsurfaceTex;
|
|
fixed4 _SubsurfaceColor;
|
|
half _SubsurfaceIndirect;
|
|
#endif
|
|
|
|
#define GEOM_TYPE_BRANCH 0
|
|
#define GEOM_TYPE_FROND 1
|
|
#define GEOM_TYPE_LEAF 2
|
|
#define GEOM_TYPE_FACINGLEAF 3
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// OffsetSpeedTreeVertex
|
|
|
|
void OffsetSpeedTreeVertex(inout appdata_full data, float lodValue)
|
|
{
|
|
// smooth LOD
|
|
#if defined(LOD_FADE_PERCENTAGE) && !defined(EFFECT_BILLBOARD)
|
|
data.vertex.xyz = lerp(data.vertex.xyz, data.texcoord2.xyz, lodValue);
|
|
#endif
|
|
|
|
// wind
|
|
#if defined(ENABLE_WIND) && !defined(_WINDQUALITY_NONE)
|
|
if (_WindEnabled > 0)
|
|
{
|
|
float3 rotatedWindVector = mul(_ST_WindVector.xyz, (float3x3)unity_ObjectToWorld);
|
|
float windLength = length(rotatedWindVector);
|
|
if (windLength < 1e-5)
|
|
{
|
|
// sanity check that wind data is available
|
|
return;
|
|
}
|
|
rotatedWindVector /= windLength;
|
|
|
|
float3 treePos = float3(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w);
|
|
float3 windyPosition = data.vertex.xyz;
|
|
|
|
#ifndef EFFECT_BILLBOARD
|
|
// geometry type
|
|
float geometryType = (int)(data.texcoord3.w + 0.25);
|
|
bool leafTwo = false;
|
|
if (geometryType > GEOM_TYPE_FACINGLEAF)
|
|
{
|
|
geometryType -= 2;
|
|
leafTwo = true;
|
|
}
|
|
|
|
// leaves
|
|
if (geometryType > GEOM_TYPE_FROND)
|
|
{
|
|
// remove anchor position
|
|
float3 anchor = float3(data.texcoord1.zw, data.texcoord2.w);
|
|
windyPosition -= anchor;
|
|
|
|
if (geometryType == GEOM_TYPE_FACINGLEAF)
|
|
{
|
|
// face camera-facing leaf to camera
|
|
float offsetLen = length(windyPosition);
|
|
windyPosition = mul(windyPosition.xyz, (float3x3)UNITY_MATRIX_IT_MV); // inv(MV) * windyPosition
|
|
windyPosition = normalize(windyPosition) * offsetLen; // make sure the offset vector is still scaled
|
|
}
|
|
|
|
// leaf wind
|
|
#if defined(_WINDQUALITY_FAST) || defined(_WINDQUALITY_BETTER) || defined(_WINDQUALITY_BEST)
|
|
#ifdef _WINDQUALITY_BEST
|
|
bool bBestWind = true;
|
|
#else
|
|
bool bBestWind = false;
|
|
#endif
|
|
float leafWindTrigOffset = anchor.x + anchor.y;
|
|
windyPosition = LeafWind(bBestWind, leafTwo, windyPosition, data.normal, data.texcoord3.x, float3(0,0,0), data.texcoord3.y, data.texcoord3.z, leafWindTrigOffset, rotatedWindVector);
|
|
#endif
|
|
|
|
// move back out to anchor
|
|
windyPosition += anchor;
|
|
}
|
|
|
|
// frond wind
|
|
bool bPalmWind = false;
|
|
#ifdef _WINDQUALITY_PALM
|
|
bPalmWind = true;
|
|
if (geometryType == GEOM_TYPE_FROND)
|
|
{
|
|
windyPosition = RippleFrond(windyPosition, data.normal, data.texcoord.x, data.texcoord.y, data.texcoord3.x, data.texcoord3.y, data.texcoord3.z);
|
|
}
|
|
#endif
|
|
|
|
// branch wind (applies to all 3D geometry)
|
|
#if defined(_WINDQUALITY_BETTER) || defined(_WINDQUALITY_BEST) || defined(_WINDQUALITY_PALM)
|
|
float3 rotatedBranchAnchor = normalize(mul(_ST_WindBranchAnchor.xyz, (float3x3)unity_ObjectToWorld)) * _ST_WindBranchAnchor.w;
|
|
windyPosition = BranchWind(bPalmWind, windyPosition, treePos, float4(data.texcoord.zw, 0, 0), rotatedWindVector, rotatedBranchAnchor);
|
|
#endif
|
|
|
|
#endif // !EFFECT_BILLBOARD
|
|
|
|
// global wind
|
|
float globalWindTime = _ST_WindGlobal.x;
|
|
#if defined(EFFECT_BILLBOARD) && defined(UNITY_INSTANCING_ENABLED)
|
|
globalWindTime += UNITY_ACCESS_INSTANCED_PROP(STWind, _GlobalWindTime);
|
|
#endif
|
|
windyPosition = GlobalWind(windyPosition, treePos, true, rotatedWindVector, globalWindTime);
|
|
data.vertex.xyz = windyPosition;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// vertex program
|
|
|
|
void SpeedTreeVert(inout appdata_full v)
|
|
{
|
|
// handle speedtree wind and lod
|
|
OffsetSpeedTreeVertex(v, unity_LODFade.x);
|
|
|
|
float3 treePos = float3(unity_ObjectToWorld[0].w, unity_ObjectToWorld[1].w, unity_ObjectToWorld[2].w);
|
|
|
|
#if defined(EFFECT_BILLBOARD)
|
|
|
|
// crossfade faces
|
|
bool topDown = (v.texcoord.z > 0.5);
|
|
float3 viewDir = UNITY_MATRIX_IT_MV[2].xyz;
|
|
float3 cameraDir = normalize(mul((float3x3)unity_WorldToObject, _WorldSpaceCameraPos - treePos));
|
|
float viewDot = max(dot(viewDir, v.normal), dot(cameraDir, v.normal));
|
|
viewDot *= viewDot;
|
|
viewDot *= viewDot;
|
|
viewDot += topDown ? 0.38 : 0.18; // different scales for horz and vert billboards to fix transition zone
|
|
v.color = float4(1, 1, 1, clamp(viewDot, 0, 1));
|
|
|
|
// if invisible, avoid overdraw
|
|
if (viewDot < 0.3333)
|
|
{
|
|
v.vertex.xyz = float3(0,0,0);
|
|
}
|
|
|
|
// adjust lighting on billboards to prevent seams between the different faces
|
|
if (topDown)
|
|
{
|
|
v.normal += cameraDir;
|
|
}
|
|
else
|
|
{
|
|
half3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w;
|
|
float3 right = cross(cameraDir, binormal);
|
|
v.normal = cross(binormal, right);
|
|
}
|
|
v.normal = normalize(v.normal);
|
|
|
|
#endif
|
|
|
|
// color already contains (ao, ao, ao, blend)
|
|
// put hue variation amount in there
|
|
#ifdef EFFECT_HUE_VARIATION
|
|
float hueVariationAmount = frac(treePos.x + treePos.y + treePos.z);
|
|
v.color.g = saturate(hueVariationAmount * _HueVariationColor.a);
|
|
#endif
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// lighting function to add subsurface
|
|
|
|
half4 LightingSpeedTreeSubsurface(inout SurfaceOutputStandard s, half3 viewDir, UnityGI gi)
|
|
{
|
|
#ifdef EFFECT_SUBSURFACE
|
|
half fSubsurfaceRough = 0.7 - s.Smoothness * 0.5;
|
|
half fSubsurface = GGXTerm(clamp(-dot(gi.light.dir, viewDir), 0, 1), fSubsurfaceRough);
|
|
|
|
// put modulated subsurface back into emission
|
|
s.Emission *= (gi.indirect.diffuse * _SubsurfaceIndirect + gi.light.color * fSubsurface);
|
|
#endif
|
|
|
|
return LightingStandard(s, viewDir, gi);
|
|
}
|
|
|
|
void LightingSpeedTreeSubsurface_GI(inout SurfaceOutputStandard s, UnityGIInput data, inout UnityGI gi)
|
|
{
|
|
#ifdef EFFECT_BILLBOARD
|
|
// fade off the shadows on billboards to avoid artifacts
|
|
data.atten = lerp(data.atten, 1.0, _BillboardShadowFade);
|
|
#endif
|
|
|
|
LightingStandard_GI(s, data, gi);
|
|
}
|
|
|
|
half4 LightingSpeedTreeSubsurface_Deferred(SurfaceOutputStandard s, half3 viewDir, UnityGI gi, out half4 outGBuffer0, out half4 outGBuffer1, out half4 outGBuffer2)
|
|
{
|
|
// no light/shadow info in deferred, so stop subsurface
|
|
s.Emission = half3(0,0,0);
|
|
|
|
return LightingStandard_Deferred(s, viewDir, gi, outGBuffer0, outGBuffer1, outGBuffer2);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////
|
|
// surface shader
|
|
|
|
void SpeedTreeSurf(Input IN, inout SurfaceOutputStandard OUT)
|
|
{
|
|
fixed4 color = tex2D(_MainTex, IN.uv_MainTex) * _Color;
|
|
|
|
// transparency
|
|
OUT.Alpha = color.a * IN.color.a;
|
|
clip(OUT.Alpha - 0.3333);
|
|
|
|
// color
|
|
OUT.Albedo = color.rgb;
|
|
|
|
// hue variation
|
|
#ifdef EFFECT_HUE_VARIATION
|
|
half3 shiftedColor = lerp(OUT.Albedo, _HueVariationColor.rgb, IN.color.g);
|
|
|
|
// preserve vibrance
|
|
half maxBase = max(OUT.Albedo.r, max(OUT.Albedo.g, OUT.Albedo.b));
|
|
half newMaxBase = max(shiftedColor.r, max(shiftedColor.g, shiftedColor.b));
|
|
maxBase /= newMaxBase;
|
|
maxBase = maxBase * 0.5f + 0.5f;
|
|
shiftedColor.rgb *= maxBase;
|
|
|
|
OUT.Albedo = saturate(shiftedColor);
|
|
#endif
|
|
|
|
// normal
|
|
#ifdef EFFECT_BUMP
|
|
OUT.Normal = UnpackNormal(tex2D(_BumpMap, IN.uv_MainTex));
|
|
#elif defined(EFFECT_BACKSIDE_NORMALS) || defined(EFFECT_BILLBOARD)
|
|
OUT.Normal = float3(0, 0, 1);
|
|
#endif
|
|
|
|
// flip normal on backsides
|
|
#ifdef EFFECT_BACKSIDE_NORMALS
|
|
if (IN.facing < 0.5)
|
|
{
|
|
OUT.Normal.z = -OUT.Normal.z;
|
|
}
|
|
#endif
|
|
|
|
// adjust billboard normals to improve GI and matching
|
|
#ifdef EFFECT_BILLBOARD
|
|
OUT.Normal.z *= 0.5;
|
|
OUT.Normal = normalize(OUT.Normal);
|
|
#endif
|
|
|
|
// extra
|
|
#ifdef EFFECT_EXTRA_TEX
|
|
fixed4 extra = tex2D(_ExtraTex, IN.uv_MainTex);
|
|
OUT.Smoothness = extra.r;
|
|
OUT.Metallic = extra.g;
|
|
OUT.Occlusion = extra.b * IN.color.r;
|
|
#else
|
|
OUT.Smoothness = _Glossiness;
|
|
OUT.Metallic = _Metallic;
|
|
OUT.Occlusion = IN.color.r;
|
|
#endif
|
|
|
|
// subsurface (hijack emissive)
|
|
#ifdef EFFECT_SUBSURFACE
|
|
OUT.Emission = tex2D(_SubsurfaceTex, IN.uv_MainTex) * _SubsurfaceColor;
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif // SPEEDTREE8_COMMON_INCLUDED
|