您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
407 行
14 KiB
407 行
14 KiB
#ifndef UNIVERSAL_SPEEDTREE8_PASSES_INCLUDED
|
|
#define UNIVERSAL_SPEEDTREE8_PASSES_INCLUDED
|
|
|
|
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.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
|
|
|
|
#ifdef _MAIN_LIGHT_SHADOWS
|
|
float4 shadowCoord : TEXCOORD6;
|
|
#endif
|
|
|
|
float3 positionWS : TEXCOORD7;
|
|
float4 clipPos : SV_POSITION;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
struct SpeedTreeVertexDepthOutput
|
|
{
|
|
half2 uv : TEXCOORD0;
|
|
half4 color : TEXCOORD1;
|
|
float4 clipPos : SV_POSITION;
|
|
UNITY_VERTEX_INPUT_INSTANCE_ID
|
|
UNITY_VERTEX_OUTPUT_STEREO
|
|
};
|
|
|
|
struct SpeedTreeFragmentInput
|
|
{
|
|
SpeedTreeVertexOutput interpolated;
|
|
#ifdef EFFECT_BACKSIDE_NORMALS
|
|
half facing : VFACE;
|
|
#endif
|
|
};
|
|
|
|
void InitializeData(inout SpeedTreeVertexInput input, float lodValue)
|
|
{
|
|
// smooth LOD
|
|
#if defined(LOD_FADE_PERCENTAGE) && !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 = ComputeFogFactor(vertexInput.positionCS.z);
|
|
output.fogFactorAndVertexLight = half4(fogFactor, vertexLight);
|
|
|
|
half3 viewDirWS = GetCameraPositionWS() - 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
|
|
|
|
#ifdef _MAIN_LIGHT_SHADOWS
|
|
output.shadowCoord = GetShadowCoord(vertexInput);
|
|
#endif
|
|
|
|
output.positionWS = vertexInput.positionWS;
|
|
output.clipPos = vertexInput.positionCS;
|
|
|
|
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);
|
|
|
|
#ifdef SHADOW_CASTER
|
|
half3 normalWS = TransformObjectToWorldNormal(input.normal);
|
|
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(vertexInput.positionWS, normalWS, _LightDirection));
|
|
output.clipPos = positionCS;
|
|
#else
|
|
output.clipPos = vertexInput.positionCS;
|
|
#endif
|
|
|
|
return output;
|
|
}
|
|
|
|
void InitializeInputData(SpeedTreeFragmentInput input, half3 normalTS, out InputData inputData)
|
|
{
|
|
inputData.positionWS = input.interpolated.positionWS.xyz;
|
|
|
|
#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 = input.interpolated.normalWS;
|
|
inputData.viewDirectionWS = input.interpolated.viewDirWS;
|
|
#endif
|
|
|
|
#if SHADER_HINT_NICE_QUALITY
|
|
inputData.viewDirectionWS = SafeNormalize(inputData.viewDirectionWS);
|
|
#endif
|
|
|
|
#ifdef _MAIN_LIGHT_SHADOWS
|
|
inputData.shadowCoord = input.interpolated.shadowCoord;
|
|
#else
|
|
inputData.shadowCoord = float4(0, 0, 0, 0);
|
|
#endif
|
|
|
|
inputData.fogCoord = input.interpolated.fogFactorAndVertexLight.x;
|
|
inputData.vertexLighting = input.interpolated.fogFactorAndVertexLight.yzw;
|
|
inputData.bakedGI = half3(0, 0, 0); // No GI currently.
|
|
}
|
|
|
|
half4 SpeedTree8Frag(SpeedTreeFragmentInput input) : SV_Target
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(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_BILLBOARD
|
|
LODDitheringTransition(input.interpolated.clipPos.xyz, unity_LODFade.x);
|
|
#endif
|
|
#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, 0.0);
|
|
|
|
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
|
|
if (input.facing < 0.5)
|
|
{
|
|
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
|
|
|
|
// subsurface (hijack emissive)
|
|
#ifdef EFFECT_SUBSURFACE
|
|
emission = tex2D(_SubsurfaceTex, uv).rgb * _SubsurfaceColor.rgb;
|
|
#endif
|
|
|
|
InputData inputData;
|
|
InitializeInputData(input, normalTs, inputData);
|
|
|
|
half4 color = UniversalFragmentPBR(inputData, albedo, metallic, specular, smoothness, occlusion, emission, alpha);
|
|
color.rgb = MixFog(color.rgb, inputData.fogCoord);
|
|
return color;
|
|
}
|
|
|
|
half4 SpeedTree8FragDepth(SpeedTreeVertexDepthOutput input) : SV_Target
|
|
{
|
|
UNITY_SETUP_INSTANCE_ID(input);
|
|
|
|
#if !defined(SHADER_QUALITY_LOW)
|
|
#ifdef LOD_FADE_CROSSFADE // enable dithering LOD transition if user select CrossFade transition in LOD group
|
|
#ifdef EFFECT_BILLBOARD
|
|
LODDitheringTransition(input.clipPos.xyz, unity_LODFade.x);
|
|
#endif
|
|
#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, 0.0);
|
|
|
|
#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
|
|
}
|
|
|
|
#endif
|