您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
593 行
23 KiB
593 行
23 KiB
|
|
//-------------------------------------------------------------------------------------
|
|
// Fill SurfaceData/Builtin data function
|
|
//-------------------------------------------------------------------------------------
|
|
|
|
// In unity we can have a mix of fully baked lightmap (static lightmap) + enlighten realtime lightmap (dynamic lightmap)
|
|
// for each case we can have directional lightmap or not.
|
|
// Else we have lightprobe for dynamic/moving entity. Either SH9 per object lightprobe or SH4 per pixel per object volume probe
|
|
float3 SampleBakedGI(float3 positionWS, float3 normalWS, float2 uvStaticLightmap, float2 uvDynamicLightmap)
|
|
{
|
|
// If there is no lightmap, it assume lightprobe
|
|
#if !defined(LIGHTMAP_ON) && !defined(DYNAMICLIGHTMAP_ON)
|
|
|
|
// TODO: Confirm with Ionut but it seems that UNITY_LIGHT_PROBE_PROXY_VOLUME is always define for high end and
|
|
// unity_ProbeVolumeParams always bind.
|
|
if (unity_ProbeVolumeParams.x == 0.0)
|
|
{
|
|
// TODO: pass a tab of coefficient instead!
|
|
float4 SHCoefficients[7];
|
|
SHCoefficients[0] = unity_SHAr;
|
|
SHCoefficients[1] = unity_SHAg;
|
|
SHCoefficients[2] = unity_SHAb;
|
|
SHCoefficients[3] = unity_SHBr;
|
|
SHCoefficients[4] = unity_SHBg;
|
|
SHCoefficients[5] = unity_SHBb;
|
|
SHCoefficients[6] = unity_SHC;
|
|
|
|
return SampleSH9(SHCoefficients, normalWS);
|
|
}
|
|
else
|
|
{
|
|
// TODO: Move all this to C++!
|
|
float4x4 identity = 0;
|
|
identity._m00_m11_m22_m33 = 1.0;
|
|
float4x4 WorldToTexture = (unity_ProbeVolumeParams.y == 1.0f) ? unity_ProbeVolumeWorldToObject : identity;
|
|
|
|
float4x4 translation = identity;
|
|
translation._m30_m31_m32 = -unity_ProbeVolumeMin.xyz;
|
|
|
|
float4x4 scale = 0;
|
|
scale._m00_m11_m22_m33 = float4(unity_ProbeVolumeSizeInv.xyz, 1.0);
|
|
|
|
WorldToTexture = mul(mul(scale, translation), WorldToTexture);
|
|
|
|
return SampleProbeVolumeSH4(TEXTURE3D_PARAM(unity_ProbeVolumeSH, samplerunity_ProbeVolumeSH), positionWS, normalWS, WorldToTexture, unity_ProbeVolumeParams.z);
|
|
}
|
|
|
|
#else
|
|
|
|
float3 bakeDiffuseLighting = float3(0.0, 0.0, 0.0);
|
|
|
|
#ifdef LIGHTMAP_ON
|
|
#ifdef DIRLIGHTMAP_COMBINED
|
|
bakeDiffuseLighting += SampleDirectionalLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap),
|
|
TEXTURE2D_PARAM(unity_LightmapInd, samplerunity_Lightmap),
|
|
uvStaticLightmap, unity_LightmapST, normalWS);
|
|
#else
|
|
bakeDiffuseLighting += SampleSingleLightmap(TEXTURE2D_PARAM(unity_Lightmap, samplerunity_Lightmap), uvStaticLightmap, unity_LightmapST);
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef DYNAMICLIGHTMAP_ON
|
|
#ifdef DIRLIGHTMAP_COMBINED
|
|
bakeDiffuseLighting += SampleDirectionalLightmap(TEXTURE2D_PARAM(unity_DynamicLightmap, samplerunity_DynamicLightmap),
|
|
TEXTURE2D_PARAM(unity_DynamicDirectionality, samplerunity_DynamicLightmap),
|
|
uvDynamicLightmap, unity_DynamicLightmapST, normalWS);
|
|
#else
|
|
bakeDiffuseLighting += SampleSingleLightmap(TEXTURE2D_PARAM(unity_DynamicLightmap, samplerunity_DynamicLightmap), uvDynamicLightmap, unity_DynamicLightmapST);
|
|
#endif
|
|
#endif
|
|
|
|
return bakeDiffuseLighting;
|
|
|
|
#endif
|
|
}
|
|
|
|
float2 CalculateVelocity(float4 positionCS, float4 previousPositionCS)
|
|
{
|
|
// This test on define is required to remove warning of divide by 0 when initializing empty struct
|
|
// TODO: Add forward opaque MRT case...
|
|
#if (SHADERPASS == SHADERPASS_VELOCITY) || (SHADERPASS == SHADERPASS_GBUFFER && SHADEROPTIONS_VELOCITY_IN_GBUFFER)
|
|
// Encode velocity
|
|
positionCS.xy = positionCS.xy / positionCS.w;
|
|
previousPositionCS.xy = previousPositionCS.xy / previousPositionCS.w;
|
|
|
|
return (positionCS.xy - previousPositionCS.xy) * _ForceNoMotion;
|
|
#else
|
|
return float2(0.0, 0.0);
|
|
#endif
|
|
}
|
|
|
|
float3 LerpWhiteTo(float3 b, float t)
|
|
{
|
|
float oneMinusT = 1.0 - t;
|
|
return float3(oneMinusT, oneMinusT, oneMinusT) + b * t;
|
|
}
|
|
|
|
// Transforms 2D UV by scale/bias property
|
|
#define TRANSFORM_TEX(tex,name) ((tex.xy) * name##_ST.xy + name##_ST.zw)
|
|
|
|
|
|
#if !defined(LAYERED_LIT_SHADER)
|
|
|
|
void GetSurfaceAndBuiltinData(FragInput input, out SurfaceData surfaceData, out BuiltinData builtinData)
|
|
{
|
|
#ifdef _HEIGHTMAP
|
|
// TODO: in case of shader graph, a node like parallax must be nullify if use to generate code for Meta pass
|
|
#ifndef _HEIGHTMAP_AS_DISPLACEMENT
|
|
float3 V = GetWorldSpaceNormalizeViewDir(input.positionWS); // This should be remove by the compiler as we usually cal it before.
|
|
float height = SAMPLE_TEXTURE2D(_HeightMap, sampler_HeightMap, input.texCoord0).r * _HeightScale + _HeightBias;
|
|
// Transform view vector in tangent space
|
|
float3 viewDirTS = TransformWorldToTangent(V, input.tangentToWorld);
|
|
float2 offset = ParallaxOffset(viewDirTS, height);
|
|
input.texCoord0 += offset;
|
|
input.texCoord1 += offset;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef _DETAIL_MAP
|
|
float2 texCoordDetail = TRANSFORM_TEX(_UVDetail ? input.texCoord1 : input.texCoord0, _DetailMap);
|
|
float detailMask = SAMPLE_TEXTURE2D(_DetailMask, sampler_DetailMask, input.texCoord0).b;
|
|
float4 detail = SAMPLE_TEXTURE2D(_DetailMap, sampler_DetailMap, texCoordDetail);
|
|
float detailAlbedo = detail.r;
|
|
float detailSmoothness = detail.b;
|
|
#ifdef _DETAIL_MAP_WITH_NORMAL
|
|
float3 detailNormalTS = UnpackNormalAG(detail, _DetailNormalScale);
|
|
//float detailAO = 0.0;
|
|
#else
|
|
// TODO: Use heightmap as a derivative with Morten Mikklesen approach
|
|
// Or reconstruct
|
|
float U = SAMPLE_TEXTURE2D(_DetailMap, sampler_DetailMap, texCoordDetail + float2(0.005, 0)).a;
|
|
float V = SAMPLE_TEXTURE2D(_DetailMap, sampler_DetailMap, texCoordDetail + float2(0, 0.005)).a;
|
|
float dHdU = U - detail.a; //create bump map U offset
|
|
float dHdV = V - detail.a; //create bump map V offset
|
|
//float3 detailNormal = 1 - float3(dHdU, dHdV, 0.05); //create the tangent space normal
|
|
float3 detailNormalTS = float3(0.0, 0.0, 1.0);
|
|
//float3 detailNormal = UnpackNormalAG(unifiedDetail.r).a;
|
|
//float detailAO = detail.b;
|
|
#endif
|
|
#endif
|
|
|
|
surfaceData.baseColor = SAMPLE_TEXTURE2D(_BaseColorMap, sampler_BaseColorMap, input.texCoord0).rgb * _BaseColor.rgb;
|
|
#ifdef _DETAIL_MAP
|
|
surfaceData.baseColor *= LerpWhiteTo(2.0 * saturate(detailAlbedo * _DetailAlbedoScale), detailMask);
|
|
#endif
|
|
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
float alpha = _BaseColor.a;
|
|
#else
|
|
float alpha = SAMPLE_TEXTURE2D(_BaseColorMap, sampler_BaseColorMap, input.texCoord0).a * _BaseColor.a;
|
|
#endif
|
|
|
|
#ifdef _ALPHATEST_ON
|
|
clip(alpha - _AlphaCutoff);
|
|
#endif
|
|
|
|
#ifdef _SPECULAROCCLUSIONMAP
|
|
// TODO: Do something. For now just take alpha channel
|
|
surfaceData.specularOcclusion = SAMPLE_TEXTURE2D(_SpecularOcclusionMap, sampler_SpecularOcclusionMap, input.texCoord0).a;
|
|
#else
|
|
// Horizon Occlusion for Normal Mapped Reflections: http://marmosetco.tumblr.com/post/81245981087
|
|
//surfaceData.specularOcclusion = saturate(1.0 + horizonFade * dot(r, input.tangentToWorld[2].xyz);
|
|
// smooth it
|
|
//surfaceData.specularOcclusion *= surfaceData.specularOcclusion;
|
|
surfaceData.specularOcclusion = 1.0;
|
|
#endif
|
|
|
|
// TODO: think about using BC5
|
|
float3 vertexNormalWS = input.tangentToWorld[2].xyz;
|
|
|
|
#ifdef _NORMALMAP
|
|
#ifdef _NORMALMAP_TANGENT_SPACE
|
|
float3 normalTS = UnpackNormalAG(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.texCoord0));
|
|
#ifdef _DETAIL_MAP
|
|
normalTS = lerp(normalTS, blendNormal(normalTS, detailNormalTS), detailMask);
|
|
#endif
|
|
surfaceData.normalWS = TransformTangentToWorld(normalTS, input.tangentToWorld);
|
|
#else // Object space
|
|
float3 normalOS = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.texCoord0).rgb;
|
|
surfaceData.normalWS = TransformObjectToWorldDir(normalOS);
|
|
#ifdef _DETAIL_MAP
|
|
float3 detailNormalWS = TransformTangentToWorld(detailNormalTS, input.tangentToWorld);
|
|
surfaceData.normalWS = lerp(surfaceData.normalWS, blendNormal(surfaceData.normalWS, detailNormalWS), detailMask);
|
|
#endif
|
|
#endif
|
|
#else
|
|
surfaceData.normalWS = vertexNormalWS;
|
|
#endif
|
|
|
|
#if defined(_DOUBLESIDED_LIGHTING_FLIP) || defined(_DOUBLESIDED_LIGHTING_MIRROR)
|
|
#ifdef _DOUBLESIDED_LIGHTING_FLIP
|
|
float3 oppositeNormalWS = -surfaceData.normalWS;
|
|
#else
|
|
// Mirror the normal with the plane define by vertex normal
|
|
float3 oppositeNormalWS = reflect(surfaceData.normalWS, vertexNormalWS);
|
|
#endif
|
|
// TODO : Test if GetOdddNegativeScale() is necessary here in case of normal map, as GetOdddNegativeScale is take into account in CreateTangentToWorld();
|
|
surfaceData.normalWS = input.isFrontFace ?
|
|
(GetOdddNegativeScale() >= 0.0 ? surfaceData.normalWS : oppositeNormalWS) :
|
|
(-GetOdddNegativeScale() >= 0.0 ? surfaceData.normalWS : oppositeNormalWS);
|
|
#endif
|
|
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
surfaceData.perceptualSmoothness = SAMPLE_TEXTURE2D(_BaseColorMap, sampler_BaseColorMap, input.texCoord0).a;
|
|
#elif defined(_MASKMAP)
|
|
surfaceData.perceptualSmoothness = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, input.texCoord0).a;
|
|
#else
|
|
surfaceData.perceptualSmoothness = 1.0;
|
|
#endif
|
|
surfaceData.perceptualSmoothness *= _Smoothness;
|
|
#ifdef _DETAIL_MAP
|
|
surfaceData.perceptualSmoothness *= LerpWhiteTo(2.0 * saturate(detailSmoothness * _DetailSmoothnessScale), detailMask);
|
|
#endif
|
|
|
|
surfaceData.materialId = 0;
|
|
|
|
// MaskMap is Metallic, Ambient Occlusion, (Optional) - emissive Mask, Optional - Smoothness (in alpha)
|
|
#ifdef _MASKMAP
|
|
surfaceData.metallic = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, input.texCoord0).r;
|
|
surfaceData.ambientOcclusion = SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, input.texCoord0).g;
|
|
#else
|
|
surfaceData.metallic = 1.0;
|
|
surfaceData.ambientOcclusion = 1.0;
|
|
#endif
|
|
surfaceData.metallic *= _Metallic;
|
|
|
|
// TODO: think about using BC5
|
|
#ifdef _TANGENTMAP
|
|
#ifdef _NORMALMAP_TANGENT_SPACE // Normal and tangent use same space
|
|
float3 tangentTS = UnpackNormalAG(SAMPLE_TEXTURE2D(_TangentMap, sampler_TangentMap, input.texCoord0));
|
|
surfaceData.tangentWS = TransformTangentToWorld(tangentTS, input.tangentToWorld);
|
|
#else // Object space (TODO: We need to apply the world rotation here! - Require to pass world transform)
|
|
surfaceData.tangentWS = SAMPLE_TEXTURE2D(_TangentMap, sampler_TangentMap, input.texCoord0).rgb;
|
|
#endif
|
|
#else
|
|
surfaceData.tangentWS = input.tangentToWorld[0].xyz;
|
|
#endif
|
|
// TODO: Is there anything todo regarding flip normal but for the tangent ?
|
|
|
|
#ifdef _ANISOTROPYMAP
|
|
surfaceData.anisotropy = SAMPLE_TEXTURE2D(_AnisotropyMap, sampler_AnisotropyMap, input.texCoord0).g;
|
|
#else
|
|
surfaceData.anisotropy = 1.0;
|
|
#endif
|
|
surfaceData.anisotropy *= _Anisotropy;
|
|
|
|
surfaceData.specular = 0.04;
|
|
|
|
surfaceData.subSurfaceRadius = 1.0;
|
|
surfaceData.thickness = 0.0;
|
|
surfaceData.subSurfaceProfile = 0;
|
|
|
|
surfaceData.coatNormalWS = float3(1.0, 0.0, 0.0);
|
|
surfaceData.coatPerceptualSmoothness = 1.0;
|
|
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
|
|
|
|
|
|
// Builtin Data
|
|
builtinData.opacity = alpha;
|
|
|
|
// TODO: Sample lightmap/lightprobe/volume proxy
|
|
// This should also handle projective lightmap
|
|
// Note that data input above can be use to sample into lightmap (like normal)
|
|
builtinData.bakeDiffuseLighting = SampleBakedGI(input.positionWS, surfaceData.normalWS, input.texCoord1, input.texCoord2);
|
|
|
|
// Emissive Intensity is only use here, but is part of BuiltinData to enforce UI parameters as we want the users to fill one color and one intensity
|
|
builtinData.emissiveIntensity = _EmissiveIntensity; // We still store intensity here so we can reuse it with debug code
|
|
|
|
// If we chose an emissive color, we have a dedicated texture for it and don't use MaskMap
|
|
#ifdef _EMISSIVE_COLOR
|
|
#ifdef _EMISSIVE_COLOR_MAP
|
|
builtinData.emissiveColor = SAMPLE_TEXTURE2D(_EmissiveColorMap, sampler_EmissiveColorMap, input.texCoord0).rgb * _EmissiveColor * builtinData.emissiveIntensity;
|
|
#else
|
|
builtinData.emissiveColor = _EmissiveColor * builtinData.emissiveIntensity;
|
|
#endif
|
|
#elif defined(_MASKMAP) // If we have a MaskMap, use emissive slot as a mask on baseColor
|
|
builtinData.emissiveColor = surfaceData.baseColor * (SAMPLE_TEXTURE2D(_MaskMap, sampler_MaskMap, input.texCoord0).b * builtinData.emissiveIntensity).xxx;
|
|
#else
|
|
builtinData.emissiveColor = float3(0.0, 0.0, 0.0);
|
|
#endif
|
|
|
|
builtinData.velocity = CalculateVelocity(input.positionCS, input.previousPositionCS);
|
|
|
|
builtinData.distortion = float2(0.0, 0.0);
|
|
builtinData.distortionBlur = 0.0;
|
|
}
|
|
|
|
#else
|
|
|
|
|
|
float3 BlendLayeredColor(float3 rgb0, float3 rgb1, float3 rgb2, float3 rgb3, float weight[4])
|
|
{
|
|
float3 result = float3(0.0, 0.0, 0.0);
|
|
|
|
result = rgb0 * weight[0] + rgb1 * weight[1];
|
|
#if _LAYER_COUNT >= 3
|
|
result += (rgb2 * weight[2]);
|
|
#endif
|
|
#if _LAYER_COUNT >= 4
|
|
result += rgb3 * weight[3];
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
float3 BlendLayeredNormal(float3 normal0, float3 normal1, float3 normal2, float3 normal3, float weight[4])
|
|
{
|
|
float3 result = float3(0.0, 0.0, 0.0);
|
|
|
|
// TODO : real normal map blending function
|
|
result = normal0 * weight[0] + normal1 * weight[1];
|
|
#if _LAYER_COUNT >= 3
|
|
result += normal2 * weight[2];
|
|
#endif
|
|
#if _LAYER_COUNT >= 4
|
|
result += normal3 * weight[3];
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
float BlendLayeredScalar(float x0, float x1, float x2, float x3, float weight[4])
|
|
{
|
|
float result = 0.0;
|
|
|
|
result = x0 * weight[0] + x1 * weight[1];
|
|
#if _LAYER_COUNT >= 3
|
|
result += x2 * weight[2];
|
|
#endif
|
|
#if _LAYER_COUNT >= 4
|
|
result += x3 * weight[3];
|
|
#endif
|
|
|
|
return result;
|
|
}
|
|
|
|
void ComputeMaskWeights(float3 inputMasks, out float outWeights[_MAX_LAYER])
|
|
{
|
|
float masks[_MAX_LAYER];
|
|
masks[0] = 1.0f; // Layer 0 is always full
|
|
masks[1] = inputMasks.r;
|
|
masks[2] = inputMasks.g;
|
|
masks[3] = inputMasks.b;
|
|
|
|
// calculate weight of each layers
|
|
float left = 1.0f;
|
|
|
|
[unroll]
|
|
for (int i = _LAYER_COUNT - 1; i > 0; --i)
|
|
{
|
|
outWeights[i] = masks[i] * left;
|
|
left -= outWeights[i];
|
|
}
|
|
outWeights[0] = left;
|
|
}
|
|
|
|
float2 ComputePlanarXZCoord(float3 worldPos, float layerSize)
|
|
{
|
|
return frac(worldPos.xz / layerSize);
|
|
}
|
|
|
|
void ComputeLayerCoordinates(out LayerCoordinates outCoord, FragInput input)
|
|
{
|
|
#if defined(_LAYER_MAPPING_UV1_0)
|
|
outCoord.texcoord[0] = input.texCoord1;
|
|
outCoord.isTriplanar[0] = false;
|
|
#elif defined(_LAYER_MAPPING_PLANAR_0)
|
|
outCoord.texcoord[0] = ComputePlanarXZCoord(input.positionWS, _LayerSize0);
|
|
outCoord.isTriplanar[0] = false;
|
|
#elif defined(_LAYER_MAPPING_TRIPLANAR_0)
|
|
outCoord.texcoord[0] = input.texCoord0;
|
|
outCoord.isTriplanar[0] = true;
|
|
#else
|
|
outCoord.texcoord[0] = input.texCoord0;
|
|
outCoord.isTriplanar[0] = false;
|
|
#endif
|
|
|
|
#if defined(_LAYER_MAPPING_UV1_1)
|
|
outCoord.texcoord[1] = input.texCoord1;
|
|
outCoord.isTriplanar[1] = false;
|
|
#elif defined(_LAYER_MAPPING_PLANAR_1)
|
|
outCoord.texcoord[1] = ComputePlanarXZCoord(input.positionWS, _LayerSize1);
|
|
outCoord.isTriplanar[1] = false;
|
|
#elif defined(_LAYER_MAPPING_TRIPLANAR_1)
|
|
outCoord.texcoord[1] = input.texCoord0;
|
|
outCoord.isTriplanar[1] = true;
|
|
#else
|
|
outCoord.texcoord[1] = input.texCoord0;
|
|
outCoord.isTriplanar[1] = false;
|
|
#endif
|
|
|
|
#if defined(_LAYER_MAPPING_UV1_2)
|
|
outCoord.texcoord[2] = input.texCoord1;
|
|
outCoord.isTriplanar[2] = false;
|
|
#elif defined(_LAYER_MAPPING_PLANAR_2)
|
|
outCoord.texcoord[2] = ComputePlanarXZCoord(input.positionWS, _LayerSize2);
|
|
outCoord.isTriplanar[2] = false;
|
|
#elif defined(_LAYER_MAPPING_TRIPLANAR_2)
|
|
outCoord.texcoord[2] = input.texCoord0;
|
|
outCoord.isTriplanar[2] = true;
|
|
#else
|
|
outCoord.texcoord[2] = input.texCoord0;
|
|
outCoord.isTriplanar[2] = false;
|
|
#endif
|
|
|
|
#if defined(_LAYER_MAPPING_UV1_3)
|
|
outCoord.texcoord[3] = input.texCoord1;
|
|
outCoord.isTriplanar[3] = false;
|
|
#elif defined(_LAYER_MAPPING_PLANAR_3)
|
|
outCoord.texcoord[3] = ComputePlanarXZCoord(input.positionWS, _LayerSize3);
|
|
outCoord.isTriplanar[3] = false;
|
|
#elif defined(_LAYER_MAPPING_TRIPLANAR_3)
|
|
outCoord.texcoord[3] = input.texCoord0;
|
|
outCoord.isTriplanar[3] = true;
|
|
#else
|
|
outCoord.texcoord[3] = input.texCoord0;
|
|
outCoord.isTriplanar[3] = false;
|
|
#endif
|
|
}
|
|
|
|
void GetSurfaceAndBuiltinData(FragInput input, out SurfaceData surfaceData, out BuiltinData builtinData)
|
|
{
|
|
LayerCoordinates layerCoord;
|
|
ComputeLayerCoordinates(layerCoord, input);
|
|
|
|
// Mask Values : Layer 1, 2, 3 are r, g, b
|
|
float3 maskValues = float3(0.0, 0.0, 0.0);
|
|
|
|
#if defined(_LAYER_MASK_MAP)
|
|
maskValues = SAMPLE_TEXTURE2D(_LayerMaskMap, sampler_LayerMaskMap, input.texCoord0).rgb;
|
|
#endif
|
|
|
|
#if defined(_LAYER_MASK_VERTEX_COLOR)
|
|
maskValues = input.vertexColor.rgb;
|
|
#endif
|
|
|
|
#if defined(_LAYER_MASK_MAP) && defined(_LAYER_MASK_VERTEX_COLOR)
|
|
maskValues = input.vertexColor.rgb * SAMPLE_TEXTURE2D(_LayerMaskMap, sampler_LayerMaskMap, input.texCoord0).rgb;
|
|
#endif
|
|
|
|
float weights[_MAX_LAYER];
|
|
ComputeMaskWeights(maskValues, weights);
|
|
|
|
PROP_DECL(float3, baseColor);
|
|
PROP_SAMPLE(baseColor, _BaseColorMap, layerCoord, rgb);
|
|
PROP_MUL(baseColor, _BaseColor, rgb);
|
|
PROP_BLEND_COLOR(baseColor, weights);
|
|
|
|
surfaceData.baseColor = baseColor;
|
|
|
|
PROP_DECL(float, alpha);
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
PROP_ASSIGN(alpha, _BaseColor, a);
|
|
#else
|
|
PROP_SAMPLE(alpha, _BaseColorMap, layerCoord, a);
|
|
PROP_MUL(alpha, _BaseColor, a);
|
|
#endif
|
|
PROP_BLEND_SCALAR(alpha, weights);
|
|
|
|
#ifdef _ALPHATEST_ON
|
|
clip(alpha - _AlphaCutoff);
|
|
#endif
|
|
|
|
builtinData.opacity = alpha;
|
|
|
|
PROP_DECL(float, specularOcclusion);
|
|
#ifdef _SPECULAROCCLUSIONMAP
|
|
// TODO: Do something. For now just take alpha channel
|
|
PROP_SAMPLE(specularOcclusion, _SpecularOcclusionMap, layerCoord, a);
|
|
#else
|
|
// Horizon Occlusion for Normal Mapped Reflections: http://marmosetco.tumblr.com/post/81245981087
|
|
//surfaceData.specularOcclusion = saturate(1.0 + horizonFade * dot(r, input.tangentToWorld[2].xyz);
|
|
// smooth it
|
|
//surfaceData.specularOcclusion *= surfaceData.specularOcclusion;
|
|
PROP_ASSIGN_VALUE(specularOcclusion, 1.0);
|
|
#endif
|
|
PROP_BLEND_SCALAR(specularOcclusion, weights);
|
|
surfaceData.specularOcclusion = specularOcclusion;
|
|
|
|
// TODO: think about using BC5
|
|
float3 vertexNormalWS = input.tangentToWorld[2].xyz;
|
|
|
|
#ifdef _NORMALMAP
|
|
#ifdef _NORMALMAP_TANGENT_SPACE
|
|
float3 normalTS0 = UnpackNormalAG(SampleLayer(TEXTURE2D_PARAM(_NormalMap0, sampler_NormalMap0), layerCoord, 0));
|
|
float3 normalTS1 = UnpackNormalAG(SampleLayer(TEXTURE2D_PARAM(_NormalMap1, sampler_NormalMap0), layerCoord, 1));
|
|
float3 normalTS2 = UnpackNormalAG(SampleLayer(TEXTURE2D_PARAM(_NormalMap2, sampler_NormalMap0), layerCoord, 2));
|
|
float3 normalTS3 = UnpackNormalAG(SampleLayer(TEXTURE2D_PARAM(_NormalMap3, sampler_NormalMap0), layerCoord, 3));
|
|
|
|
float3 normalTS = BlendLayeredNormal(normalTS0, normalTS1, normalTS2, normalTS3, weights);
|
|
|
|
surfaceData.normalWS = TransformTangentToWorld(normalTS, input.tangentToWorld);
|
|
#else // Object space (TODO: We need to apply the world rotation here!)
|
|
surfaceData.normalWS = SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.texCoord0).rgb;
|
|
#endif
|
|
#else
|
|
surfaceData.normalWS = vertexNormalWS;
|
|
#endif
|
|
|
|
#if defined(_DOUBLESIDED_LIGHTING_FLIP) || defined(_DOUBLESIDED_LIGHTING_MIRROR)
|
|
#ifdef _DOUBLESIDED_LIGHTING_FLIP
|
|
float3 oppositeNormalWS = -surfaceData.normalWS;
|
|
#else
|
|
// Mirror the normal with the plane define by vertex normal
|
|
float3 oppositeNormalWS = reflect(surfaceData.normalWS, vertexNormalWS);
|
|
#endif
|
|
// TODO : Test if GetOdddNegativeScale() is necessary here in case of normal map, as GetOdddNegativeScale is take into account in CreateTangentToWorld();
|
|
surfaceData.normalWS = IS_FRONT_VFACE(input.cullFace, GetOdddNegativeScale() >= 0.0 ? surfaceData.normalWS : oppositeNormalWS, -GetOdddNegativeScale() >= 0.0 ? surfaceData.normalWS : oppositeNormalWS);
|
|
#endif
|
|
|
|
|
|
PROP_DECL(float, perceptualSmoothness);
|
|
#ifdef _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
|
|
PROP_SAMPLE(perceptualSmoothness, _BaseColorMap, layerCoord, a);
|
|
#elif defined(_MASKMAP)
|
|
PROP_SAMPLE(perceptualSmoothness, _MaskMap, layerCoord, a);
|
|
#else
|
|
PROP_ASSIGN_VALUE(perceptualSmoothness, 1.0);
|
|
#endif
|
|
PROP_MUL(perceptualSmoothness, _Smoothness, r);
|
|
PROP_BLEND_SCALAR(perceptualSmoothness, weights);
|
|
|
|
surfaceData.perceptualSmoothness = perceptualSmoothness;
|
|
|
|
surfaceData.materialId = 0;
|
|
|
|
// MaskMap is Metallic, Ambient Occlusion, (Optional) - emissive Mask, Optional - Smoothness (in alpha)
|
|
PROP_DECL(float, metallic);
|
|
PROP_DECL(float, ambientOcclusion);
|
|
#ifdef _MASKMAP
|
|
PROP_SAMPLE(metallic, _MaskMap, layerCoord, a);
|
|
PROP_SAMPLE(ambientOcclusion, _MaskMap, layerCoord, g);
|
|
#else
|
|
PROP_ASSIGN_VALUE(metallic, 1.0);
|
|
PROP_ASSIGN_VALUE(ambientOcclusion, 1.0);
|
|
#endif
|
|
PROP_MUL(metallic, _Metallic, r);
|
|
|
|
PROP_BLEND_SCALAR(metallic, weights);
|
|
PROP_BLEND_SCALAR(ambientOcclusion, weights);
|
|
|
|
surfaceData.metallic = metallic;
|
|
surfaceData.ambientOcclusion = ambientOcclusion;
|
|
|
|
surfaceData.tangentWS = float3(1.0, 0.0, 0.0);
|
|
surfaceData.anisotropy = 0;
|
|
surfaceData.specular = 0.04;
|
|
|
|
surfaceData.subSurfaceRadius = 1.0;
|
|
surfaceData.thickness = 0.0;
|
|
surfaceData.subSurfaceProfile = 0;
|
|
|
|
surfaceData.coatNormalWS = float3(1.0, 0.0, 0.0);
|
|
surfaceData.coatPerceptualSmoothness = 1.0;
|
|
surfaceData.specularColor = float3(0.0, 0.0, 0.0);
|
|
|
|
// Builtin Data
|
|
|
|
// TODO: Sample lightmap/lightprobe/volume proxy
|
|
// This should also handle projective lightmap
|
|
// Note that data input above can be use to sample into lightmap (like normal)
|
|
builtinData.bakeDiffuseLighting = SampleBakedGI(input.positionWS, surfaceData.normalWS, input.texCoord1, input.texCoord2);
|
|
|
|
// Emissive Intensity is only use here, but is part of BuiltinData to enforce UI parameters as we want the users to fill one color and one intensity
|
|
PROP_DECL(float, emissiveIntensity);
|
|
PROP_ASSIGN(emissiveIntensity, _EmissiveIntensity, r);
|
|
PROP_BLEND_SCALAR(emissiveIntensity, weights);
|
|
builtinData.emissiveIntensity = emissiveIntensity; // We still store intensity here so we can reuse it with debug code
|
|
|
|
// If we chose an emissive color, we have a dedicated texture for it and don't use MaskMap
|
|
PROP_DECL(float3, emissiveColor);
|
|
#ifdef _EMISSIVE_COLOR
|
|
#ifdef _EMISSIVE_COLOR_MAP
|
|
PROP_SAMPLE(emissiveColor, _EmissiveColorMap, layerCoord, rgb);
|
|
#else
|
|
PROP_ASSIGN(emissiveColor, _EmissiveColor, rgb);
|
|
#endif
|
|
#elif defined(_MASKMAP) // If we have a MaskMap, use emissive slot as a mask on baseColor
|
|
PROP_SAMPLE(emissiveColor, _MaskMap, layerCoord, bbb);
|
|
PROP_MUL(emissiveColor, baseColor, rgb);
|
|
#else
|
|
PROP_ASSIGN_VALUE(emissiveColor, float3(0.0, 0.0, 0.0));
|
|
#endif
|
|
PROP_BLEND_COLOR(emissiveColor, weights);
|
|
builtinData.emissiveColor = emissiveColor * builtinData.emissiveIntensity;
|
|
|
|
builtinData.velocity = CalculateVelocity(input.positionCS, input.previousPositionCS);
|
|
|
|
builtinData.distortion = float2(0.0, 0.0);
|
|
builtinData.distortionBlur = 0.0;
|
|
}
|
|
|
|
#endif
|