You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
604 lines
22 KiB
604 lines
22 KiB
#ifndef UNIVERSAL_SPEEDTREE8_PASSES_INCLUDED |
|
#define UNIVERSAL_SPEEDTREE8_PASSES_INCLUDED |
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" |
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/UnityGBuffer.hlsl" |
|
#include "SpeedTreeUtility.hlsl" |
|
|
|
struct SpeedTreeVertexInput |
|
{ |
|
float4 vertex : POSITION; |
|
float3 normal : NORMAL; |
|
float4 tangent : TANGENT; |
|
float4 texcoord : TEXCOORD0; |
|
float4 texcoord1 : TEXCOORD1; |
|
float4 texcoord2 : TEXCOORD2; |
|
float4 texcoord3 : TEXCOORD3; |
|
float4 color : COLOR; |
|
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
}; |
|
|
|
struct SpeedTreeVertexOutput |
|
{ |
|
half2 uv : TEXCOORD0; |
|
half4 color : TEXCOORD1; |
|
|
|
half4 fogFactorAndVertexLight : TEXCOORD2; // x: fogFactor, yzw: vertex light |
|
|
|
#ifdef EFFECT_BUMP |
|
half4 normalWS : TEXCOORD3; // xyz: normal, w: viewDir.x |
|
half4 tangentWS : TEXCOORD4; // xyz: tangent, w: viewDir.y |
|
half4 bitangentWS : TEXCOORD5; // xyz: bitangent, w: viewDir.z |
|
#else |
|
half3 normalWS : TEXCOORD3; |
|
half3 viewDirWS : TEXCOORD4; |
|
#endif |
|
|
|
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) |
|
float4 shadowCoord : TEXCOORD6; |
|
#endif |
|
|
|
float3 positionWS : TEXCOORD7; |
|
DECLARE_LIGHTMAP_OR_SH(lightmapUV, vertexSH, 8); |
|
float4 clipPos : SV_POSITION; |
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
UNITY_VERTEX_OUTPUT_STEREO |
|
}; |
|
|
|
struct SpeedTreeVertexDepthOutput |
|
{ |
|
half2 uv : TEXCOORD0; |
|
half4 color : TEXCOORD1; |
|
half3 viewDirWS : TEXCOORD2; |
|
float4 clipPos : SV_POSITION; |
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
UNITY_VERTEX_OUTPUT_STEREO |
|
}; |
|
|
|
struct SpeedTreeVertexDepthNormalOutput |
|
{ |
|
half2 uv : TEXCOORD0; |
|
half4 color : TEXCOORD1; |
|
|
|
#ifdef EFFECT_BUMP |
|
half4 normalWS : TEXCOORD2; // xyz: normal, w: viewDir.x |
|
half4 tangentWS : TEXCOORD3; // xyz: tangent, w: viewDir.y |
|
half4 bitangentWS : TEXCOORD4; // xyz: bitangent, w: viewDir.z |
|
#else |
|
half3 normalWS : TEXCOORD2; |
|
half3 viewDirWS : TEXCOORD3; |
|
#endif |
|
|
|
float4 clipPos : SV_POSITION; |
|
UNITY_VERTEX_INPUT_INSTANCE_ID |
|
UNITY_VERTEX_OUTPUT_STEREO |
|
}; |
|
|
|
struct SpeedTreeDepthNormalFragmentInput |
|
{ |
|
SpeedTreeVertexDepthNormalOutput interpolated; |
|
#ifdef EFFECT_BACKSIDE_NORMALS |
|
FRONT_FACE_TYPE facing : FRONT_FACE_SEMANTIC; |
|
#endif |
|
}; |
|
|
|
struct SpeedTreeFragmentInput |
|
{ |
|
SpeedTreeVertexOutput interpolated; |
|
#ifdef EFFECT_BACKSIDE_NORMALS |
|
FRONT_FACE_TYPE facing : FRONT_FACE_SEMANTIC; |
|
#endif |
|
}; |
|
|
|
void InitializeData(inout SpeedTreeVertexInput input, float lodValue) |
|
{ |
|
#if defined(LOD_FADE_PERCENTAGE) && (!defined(LOD_FADE_CROSSFADE) && !defined(EFFECT_BILLBOARD)) |
|
input.vertex.xyz = lerp(input.vertex.xyz, input.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 = input.vertex.xyz; |
|
|
|
#ifndef EFFECT_BILLBOARD |
|
// geometry type |
|
float geometryType = (int)(input.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(input.texcoord1.zw, input.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, input.normal, input.texcoord3.x, float3(0,0,0), input.texcoord3.y, input.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, input.normal, input.texcoord.x, input.texcoord.y, input.texcoord3.x, input.texcoord3.y, input.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(input.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); |
|
input.vertex.xyz = windyPosition; |
|
} |
|
#endif |
|
|
|
#if defined(EFFECT_BILLBOARD) |
|
float3 treePos = float3(UNITY_MATRIX_M[0].w, UNITY_MATRIX_M[1].w, UNITY_MATRIX_M[2].w); |
|
// crossfade faces |
|
bool topDown = (input.texcoord.z > 0.5); |
|
float3 viewDir = UNITY_MATRIX_IT_MV[2].xyz; |
|
float3 cameraDir = normalize(mul((float3x3)UNITY_MATRIX_M, _WorldSpaceCameraPos - treePos)); |
|
float viewDot = max(dot(viewDir, input.normal), dot(cameraDir, input.normal)); |
|
viewDot *= viewDot; |
|
viewDot *= viewDot; |
|
viewDot += topDown ? 0.38 : 0.18; // different scales for horz and vert billboards to fix transition zone |
|
|
|
// if invisible, avoid overdraw |
|
if (viewDot < 0.3333) |
|
{ |
|
input.vertex.xyz = float3(0, 0, 0); |
|
} |
|
|
|
input.color = float4(1, 1, 1, clamp(viewDot, 0, 1)); |
|
|
|
// adjust lighting on billboards to prevent seams between the different faces |
|
if (topDown) |
|
{ |
|
input.normal += cameraDir; |
|
} |
|
else |
|
{ |
|
half3 binormal = cross(input.normal, input.tangent.xyz) * input.tangent.w; |
|
float3 right = cross(cameraDir, binormal); |
|
input.normal = cross(binormal, right); |
|
} |
|
input.normal = normalize(input.normal); |
|
#endif |
|
} |
|
|
|
SpeedTreeVertexOutput SpeedTree8Vert(SpeedTreeVertexInput input) |
|
{ |
|
SpeedTreeVertexOutput output = (SpeedTreeVertexOutput)0; |
|
UNITY_SETUP_INSTANCE_ID(input); |
|
UNITY_TRANSFER_INSTANCE_ID(input, output); |
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); |
|
|
|
// handle speedtree wind and lod |
|
InitializeData(input, unity_LODFade.x); |
|
|
|
output.uv = input.texcoord.xy; |
|
output.color = input.color; |
|
|
|
// color already contains (ao, ao, ao, blend) |
|
// put hue variation amount in there |
|
#ifdef EFFECT_HUE_VARIATION |
|
float3 treePos = float3(UNITY_MATRIX_M[0].w, UNITY_MATRIX_M[1].w, UNITY_MATRIX_M[2].w); |
|
float hueVariationAmount = frac(treePos.x + treePos.y + treePos.z); |
|
output.color.g = saturate(hueVariationAmount * _HueVariationColor.a); |
|
#endif |
|
|
|
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.vertex.xyz); |
|
half3 normalWS = TransformObjectToWorldNormal(input.normal); |
|
|
|
half3 vertexLight = VertexLighting(vertexInput.positionWS, normalWS); |
|
half fogFactor = 0.0; |
|
#if !defined(_FOG_FRAGMENT) |
|
fogFactor = ComputeFogFactor(vertexInput.positionCS.z); |
|
#endif |
|
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight); |
|
|
|
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS); |
|
|
|
#ifdef EFFECT_BUMP |
|
real sign = input.tangent.w * GetOddNegativeScale(); |
|
output.normalWS.xyz = normalWS; |
|
output.tangentWS.xyz = TransformObjectToWorldDir(input.tangent.xyz); |
|
output.bitangentWS.xyz = cross(output.normalWS.xyz, output.tangentWS.xyz) * sign; |
|
|
|
// View dir packed in w. |
|
output.normalWS.w = viewDirWS.x; |
|
output.tangentWS.w = viewDirWS.y; |
|
output.bitangentWS.w = viewDirWS.z; |
|
#else |
|
output.normalWS = normalWS; |
|
output.viewDirWS = viewDirWS; |
|
#endif |
|
|
|
output.positionWS = vertexInput.positionWS; |
|
|
|
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) |
|
output.shadowCoord = GetShadowCoord(vertexInput); |
|
#endif |
|
|
|
output.clipPos = vertexInput.positionCS; |
|
|
|
OUTPUT_SH(output.normalWS.xyz, output.vertexSH); |
|
|
|
return output; |
|
} |
|
|
|
SpeedTreeVertexDepthOutput SpeedTree8VertDepth(SpeedTreeVertexInput input) |
|
{ |
|
SpeedTreeVertexDepthOutput output = (SpeedTreeVertexDepthOutput)0; |
|
UNITY_SETUP_INSTANCE_ID(input); |
|
UNITY_TRANSFER_INSTANCE_ID(input, output); |
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); |
|
|
|
// handle speedtree wind and lod |
|
InitializeData(input, unity_LODFade.x); |
|
output.uv = input.texcoord.xy; |
|
output.color = input.color; |
|
|
|
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.vertex.xyz); |
|
|
|
output.viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS); |
|
|
|
#ifdef SHADOW_CASTER |
|
half3 normalWS = TransformObjectToWorldNormal(input.normal); |
|
|
|
#if _CASTING_PUNCTUAL_LIGHT_SHADOW |
|
float3 lightDirectionWS = normalize(_LightPosition - vertexInput.positionWS); |
|
#else |
|
float3 lightDirectionWS = _LightDirection; |
|
#endif |
|
|
|
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, lightDirectionWS)); |
|
output.clipPos = positionCS; |
|
#else |
|
output.clipPos = vertexInput.positionCS; |
|
#endif |
|
|
|
return output; |
|
} |
|
|
|
void InitializeInputData(SpeedTreeFragmentInput input, half3 normalTS, out InputData inputData) |
|
{ |
|
inputData = (InputData)0; |
|
|
|
inputData.positionWS = input.interpolated.positionWS.xyz; |
|
inputData.positionCS = input.interpolated.clipPos; |
|
|
|
#ifdef EFFECT_BUMP |
|
inputData.normalWS = TransformTangentToWorld(normalTS, half3x3(input.interpolated.tangentWS.xyz, input.interpolated.bitangentWS.xyz, input.interpolated.normalWS.xyz)); |
|
inputData.normalWS = NormalizeNormalPerPixel(inputData.normalWS); |
|
inputData.viewDirectionWS = half3(input.interpolated.normalWS.w, input.interpolated.tangentWS.w, input.interpolated.bitangentWS.w); |
|
#else |
|
inputData.normalWS = NormalizeNormalPerPixel(input.interpolated.normalWS); |
|
inputData.viewDirectionWS = input.interpolated.viewDirWS; |
|
#endif |
|
|
|
#if SHADER_HINT_NICE_QUALITY |
|
inputData.viewDirectionWS = SafeNormalize(inputData.viewDirectionWS); |
|
#endif |
|
|
|
#if defined(REQUIRES_VERTEX_SHADOW_COORD_INTERPOLATOR) |
|
inputData.shadowCoord = input.interpolated.shadowCoord; |
|
#elif defined(MAIN_LIGHT_CALCULATE_SHADOWS) |
|
inputData.shadowCoord = TransformWorldToShadowCoord(inputData.positionWS); |
|
#else |
|
inputData.shadowCoord = float4(0, 0, 0, 0); |
|
#endif |
|
|
|
inputData.fogCoord = InitializeInputDataFog(float4(input.interpolated.positionWS, 1.0), input.interpolated.fogFactorAndVertexLight.x); |
|
inputData.vertexLighting = input.interpolated.fogFactorAndVertexLight.yzw; |
|
inputData.bakedGI = SAMPLE_GI(NOT_USED, input.interpolated.vertexSH, inputData.normalWS); |
|
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.interpolated.clipPos); |
|
inputData.shadowMask = half4(1, 1, 1, 1); // No GI currently. |
|
|
|
#if defined(DEBUG_DISPLAY) && !defined(LIGHTMAP_ON) |
|
inputData.vertexSH = input.interpolated.vertexSH; |
|
#endif |
|
|
|
#if defined(_NORMALMAP) |
|
inputData.tangentToWorld = half3x3(input.interpolated.tangentWS.xyz, input.interpolated.bitangentWS.xyz, input.interpolated.normalWS.xyz); |
|
#endif |
|
} |
|
|
|
#ifdef GBUFFER |
|
FragmentOutput SpeedTree8Frag(SpeedTreeFragmentInput input) |
|
#else |
|
half4 SpeedTree8Frag(SpeedTreeFragmentInput input) : SV_Target |
|
#endif |
|
{ |
|
UNITY_SETUP_INSTANCE_ID(input.interpolated); |
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input.interpolated); |
|
|
|
#if !defined(SHADER_QUALITY_LOW) |
|
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group |
|
#ifdef EFFECT_BUMP |
|
half3 viewDirectionWS = half3(input.interpolated.normalWS.w, input.interpolated.tangentWS.w, input.interpolated.bitangentWS.w); |
|
#else |
|
half3 viewDirectionWS = input.interpolated.viewDirWS; |
|
#endif |
|
LODDitheringTransition(ComputeFadeMaskSeed(viewDirectionWS, input.interpolated.clipPos.xy), unity_LODFade.x); |
|
#endif |
|
#endif |
|
|
|
half2 uv = input.interpolated.uv; |
|
half4 diffuse = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_MainTex, sampler_MainTex)) * _Color; |
|
|
|
half alpha = diffuse.a * input.interpolated.color.a; |
|
AlphaDiscard(alpha, 0.3333); |
|
|
|
half3 albedo = diffuse.rgb; |
|
half3 emission = 0; |
|
half metallic = 0; |
|
half smoothness = 0; |
|
half occlusion = 0; |
|
half3 specular = 0; |
|
|
|
// hue variation |
|
#ifdef EFFECT_HUE_VARIATION |
|
half3 shiftedColor = lerp(albedo, _HueVariationColor.rgb, input.interpolated.color.g); |
|
|
|
// preserve vibrance |
|
half maxBase = max(albedo.r, max(albedo.g, albedo.b)); |
|
half newMaxBase = max(shiftedColor.r, max(shiftedColor.g, shiftedColor.b)); |
|
maxBase /= newMaxBase; |
|
maxBase = maxBase * 0.5f + 0.5f; |
|
shiftedColor.rgb *= maxBase; |
|
|
|
albedo = saturate(shiftedColor); |
|
#endif |
|
|
|
// normal |
|
#ifdef EFFECT_BUMP |
|
half3 normalTs = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap)); |
|
#else |
|
half3 normalTs = half3(0, 0, 1); |
|
#endif |
|
|
|
// flip normal on backsides |
|
#ifdef EFFECT_BACKSIDE_NORMALS |
|
normalTs.z = IS_FRONT_VFACE(input.facing, normalTs.z, -normalTs.z); |
|
#endif |
|
|
|
// adjust billboard normals to improve GI and matching |
|
#ifdef EFFECT_BILLBOARD |
|
normalTs.z *= 0.5; |
|
normalTs = normalize(normalTs); |
|
#endif |
|
|
|
// extra |
|
#ifdef EFFECT_EXTRA_TEX |
|
half4 extra = tex2D(_ExtraTex, uv); |
|
smoothness = extra.r; |
|
metallic = extra.g; |
|
occlusion = extra.b * input.interpolated.color.r; |
|
#else |
|
smoothness = _Glossiness; |
|
metallic = _Metallic; |
|
occlusion = input.interpolated.color.r; |
|
#endif |
|
|
|
InputData inputData; |
|
InitializeInputData(input, normalTs, inputData); |
|
SETUP_DEBUG_TEXTURE_DATA(inputData, input.interpolated.uv, _MainTex); |
|
|
|
#if defined(GBUFFER) || defined(EFFECT_SUBSURFACE) |
|
Light mainLight = GetMainLight(inputData.shadowCoord, inputData.positionWS, inputData.shadowMask); |
|
#endif |
|
|
|
// subsurface (hijack emissive) |
|
#ifdef EFFECT_SUBSURFACE |
|
half fSubsurfaceRough = 0.7 - smoothness * 0.5; |
|
half fSubsurface = D_GGX(clamp(-dot(mainLight.direction.xyz, inputData.viewDirectionWS.xyz), 0, 1), fSubsurfaceRough); |
|
|
|
float4 shadowCoord = TransformWorldToShadowCoord(inputData.positionWS); |
|
half realtimeShadow = MainLightRealtimeShadow(shadowCoord); |
|
float3 tintedSubsurface = tex2D(_SubsurfaceTex, uv).rgb * _SubsurfaceColor.rgb; |
|
float3 directSubsurface = tintedSubsurface.rgb * mainLight.color.rgb * fSubsurface * realtimeShadow; |
|
float3 indirectSubsurface = tintedSubsurface.rgb * inputData.bakedGI.rgb * _SubsurfaceIndirect; |
|
emission = directSubsurface + indirectSubsurface; |
|
#endif |
|
|
|
#ifdef GBUFFER |
|
// in LitForwardPass GlobalIllumination (and temporarily LightingPhysicallyBased) are called inside UniversalFragmentPBR |
|
// in Deferred rendering we store the sum of these values (and of emission as well) in the GBuffer |
|
BRDFData brdfData; |
|
InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData); |
|
|
|
MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, inputData.shadowMask); |
|
half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.positionWS, inputData.normalWS, inputData.viewDirectionWS); |
|
|
|
return BRDFDataToGbuffer(brdfData, inputData, smoothness, emission + color, occlusion); |
|
|
|
#else |
|
SurfaceData surfaceData; |
|
|
|
surfaceData.albedo = albedo; |
|
surfaceData.specular = specular; |
|
surfaceData.metallic = metallic; |
|
surfaceData.smoothness = smoothness; |
|
surfaceData.normalTS = normalTs; |
|
surfaceData.emission = emission; |
|
surfaceData.occlusion = occlusion; |
|
surfaceData.alpha = alpha; |
|
surfaceData.clearCoatMask = 0; |
|
surfaceData.clearCoatSmoothness = 1; |
|
|
|
#if defined(DEBUG_DISPLAY) |
|
inputData.uv = uv; |
|
#endif |
|
|
|
half4 color = UniversalFragmentPBR(inputData, surfaceData); |
|
|
|
color.rgb = MixFog(color.rgb, inputData.fogCoord); |
|
color.a = OutputAlpha(color.a, _Surface); |
|
|
|
return color; |
|
|
|
#endif |
|
} |
|
|
|
half4 SpeedTree8FragDepth(SpeedTreeVertexDepthOutput input) : SV_Target |
|
{ |
|
UNITY_SETUP_INSTANCE_ID(input); |
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input); |
|
|
|
#if !defined(SHADER_QUALITY_LOW) |
|
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group |
|
LODDitheringTransition(ComputeFadeMaskSeed(input.viewDirWS, input.clipPos.xy), unity_LODFade.x); |
|
#endif |
|
#endif |
|
|
|
half2 uv = input.uv; |
|
half4 diffuse = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_MainTex, sampler_MainTex)) * _Color; |
|
|
|
half alpha = diffuse.a * input.color.a; |
|
AlphaDiscard(alpha, 0.3333); |
|
|
|
#if defined(SCENESELECTIONPASS) |
|
// We use depth prepass for scene selection in the editor, this code allow to output the outline correctly |
|
return half4(_ObjectId, _PassValue, 1.0, 1.0); |
|
#else |
|
return half4(0, 0, 0, 0); |
|
#endif |
|
} |
|
|
|
SpeedTreeVertexDepthNormalOutput SpeedTree8VertDepthNormal(SpeedTreeVertexInput input) |
|
{ |
|
SpeedTreeVertexDepthNormalOutput output = (SpeedTreeVertexDepthNormalOutput)0; |
|
UNITY_SETUP_INSTANCE_ID(input); |
|
UNITY_TRANSFER_INSTANCE_ID(input, output); |
|
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output); |
|
|
|
// handle speedtree wind and lod |
|
InitializeData(input, unity_LODFade.x); |
|
output.uv = input.texcoord.xy; |
|
output.color = input.color; |
|
|
|
VertexPositionInputs vertexInput = GetVertexPositionInputs(input.vertex.xyz); |
|
half3 normalWS = TransformObjectToWorldNormal(input.normal); |
|
half3 viewDirWS = GetWorldSpaceNormalizeViewDir(vertexInput.positionWS); |
|
#ifdef EFFECT_BUMP |
|
real sign = input.tangent.w * GetOddNegativeScale(); |
|
output.normalWS.xyz = normalWS; |
|
output.tangentWS.xyz = TransformObjectToWorldDir(input.tangent.xyz); |
|
output.bitangentWS.xyz = cross(output.normalWS.xyz, output.tangentWS.xyz) * sign; |
|
|
|
// View dir packed in w. |
|
output.normalWS.w = viewDirWS.x; |
|
output.tangentWS.w = viewDirWS.y; |
|
output.bitangentWS.w = viewDirWS.z; |
|
#else |
|
output.normalWS = normalWS; |
|
output.viewDirWS = viewDirWS; |
|
#endif |
|
|
|
output.clipPos = vertexInput.positionCS; |
|
return output; |
|
} |
|
|
|
half4 SpeedTree8FragDepthNormal(SpeedTreeDepthNormalFragmentInput input) : SV_Target |
|
{ |
|
UNITY_SETUP_INSTANCE_ID(input.interpolated); |
|
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input.interpolated); |
|
|
|
#if !defined(SHADER_QUALITY_LOW) |
|
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group |
|
#ifdef EFFECT_BUMP |
|
half3 viewDirectionWS = half3(input.interpolated.normalWS.w, input.interpolated.tangentWS.w, input.interpolated.bitangentWS.w); |
|
#else |
|
half3 viewDirectionWS = input.interpolated.viewDirWS; |
|
#endif |
|
LODDitheringTransition(ComputeFadeMaskSeed(viewDirectionWS, input.interpolated.clipPos.xy), unity_LODFade.x); |
|
#endif |
|
#endif |
|
|
|
half2 uv = input.interpolated.uv; |
|
half4 diffuse = SampleAlbedoAlpha(uv, TEXTURE2D_ARGS(_MainTex, sampler_MainTex)) * _Color; |
|
|
|
half alpha = diffuse.a * input.interpolated.color.a; |
|
AlphaDiscard(alpha, 0.3333); |
|
|
|
// normal |
|
#if defined(EFFECT_BUMP) |
|
half3 normalTs = SampleNormal(uv, TEXTURE2D_ARGS(_BumpMap, sampler_BumpMap)); |
|
#else |
|
half3 normalTs = half3(0, 0, 1); |
|
#endif |
|
|
|
// flip normal on backsides |
|
#ifdef EFFECT_BACKSIDE_NORMALS |
|
if (input.facing < 0.5) |
|
{ |
|
normalTs.z = -normalTs.z; |
|
} |
|
#endif |
|
|
|
// adjust billboard normals to improve GI and matching |
|
#if defined(EFFECT_BILLBOARD) |
|
normalTs.z *= 0.5; |
|
normalTs = normalize(normalTs); |
|
#endif |
|
|
|
#if defined(EFFECT_BUMP) |
|
float3 normalWS = TransformTangentToWorld(normalTs, half3x3(input.interpolated.tangentWS.xyz, input.interpolated.bitangentWS.xyz, input.interpolated.normalWS.xyz)); |
|
return half4(NormalizeNormalPerPixel(normalWS), 0.0h); |
|
#else |
|
return half4(NormalizeNormalPerPixel(input.interpolated.normalWS), 0.0h); |
|
#endif |
|
} |
|
|
|
#endif
|
|
|