您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
443 行
16 KiB
443 行
16 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
|
|
}
|
|
|
|
void GetBuiltinData(FragInput input, SurfaceData surfaceData, float alpha, out BuiltinData builtinData)
|
|
{
|
|
// 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
|
|
// If we have a MaskMap, use emissive slot as a mask on baseColor
|
|
#elif defined(_MASKMAP) && !defined(LAYERED_LIT_SHADER) // With layered lit we have no emissive mask option
|
|
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;
|
|
}
|
|
|
|
// Gather all kind of mapping in one struct, allow to improve code readability
|
|
struct LayerUV
|
|
{
|
|
float2 uv;
|
|
// triplanar
|
|
bool isTriplanar;
|
|
float2 uvYZ;
|
|
float2 uvZX;
|
|
float2 uvXY;
|
|
};
|
|
|
|
struct LayerTexCoord
|
|
{
|
|
#ifndef LAYERED_LIT_SHADER
|
|
LayerUV base;
|
|
LayerUV details;
|
|
#else
|
|
// Regular texcoord
|
|
LayerUV base0;
|
|
LayerUV base1;
|
|
LayerUV base2;
|
|
LayerUV base3;
|
|
|
|
LayerUV details0;
|
|
LayerUV details1;
|
|
LayerUV details2;
|
|
LayerUV details3;
|
|
#endif
|
|
|
|
// triplanar weight
|
|
float3 weights;
|
|
};
|
|
|
|
float4 SampleLayer(TEXTURE2D_ARGS(layerTex, layerSampler), LayerUV layerUV, float3 weights)
|
|
{
|
|
if (layerUV.isTriplanar)
|
|
{
|
|
float4 val = float4(0.0, 0.0, 0.0, 0.0);
|
|
|
|
if (weights.x > 0.0)
|
|
val += weights.x * SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvYZ);
|
|
if (weights.y > 0.0)
|
|
val += weights.y * SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvZX);
|
|
if (weights.z > 0.0)
|
|
val += weights.z * SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvXY);
|
|
|
|
return val;
|
|
}
|
|
else
|
|
{
|
|
return SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uv);
|
|
}
|
|
}
|
|
|
|
// TODO: Handle BC5 format, currently this code is for DXT5nm
|
|
// THis function below must call UnpackNormalmapRGorAG
|
|
float3 SampleNormalLayer(TEXTURE2D_ARGS(layerTex, layerSampler), LayerUV layerUV, float3 weights)
|
|
{
|
|
if (layerUV.isTriplanar)
|
|
{
|
|
float3 val = float3(0.0, 0.0, 0.0);
|
|
|
|
if (weights.x > 0.0)
|
|
val += weights.x * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvYZ));
|
|
if (weights.y > 0.0)
|
|
val += weights.y * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvZX));
|
|
if (weights.z > 0.0)
|
|
val += weights.z * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvXY));
|
|
|
|
return normalize(val);
|
|
}
|
|
else
|
|
{
|
|
return UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uv));
|
|
}
|
|
}
|
|
|
|
// This version is for normalmap with AG encoding only (use with details map)
|
|
float3 SampleNormalLayerAG(TEXTURE2D_ARGS(layerTex, layerSampler), LayerUV layerUV, float3 weights)
|
|
{
|
|
if (layerUV.isTriplanar)
|
|
{
|
|
float3 val = float3(0.0, 0.0, 0.0);
|
|
|
|
if (weights.x > 0.0)
|
|
val += weights.x * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvYZ));
|
|
if (weights.y > 0.0)
|
|
val += weights.y * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvZX));
|
|
if (weights.z > 0.0)
|
|
val += weights.z * UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uvXY));
|
|
|
|
return normalize(val);
|
|
}
|
|
else
|
|
{
|
|
return UnpackNormalAG(SAMPLE_TEXTURE2D(layerTex, layerSampler, layerUV.uv));
|
|
}
|
|
}
|
|
|
|
// Macro to improve readibility of surface data
|
|
#define SAMPLE_LAYER_TEXTURE2D(textureName, samplerName, coord) SampleLayer(TEXTURE2D_PARAM(textureName, samplerName), coord, layerTexCoord.weights)
|
|
#define SAMPLE_LAYER_NORMALMAP(textureName, samplerName, coord) SampleNormalLayer(TEXTURE2D_PARAM(textureName, samplerName), coord, layerTexCoord.weights)
|
|
#define SAMPLE_LAYER_NORMALMAP_AG(textureName, samplerName, coord) SampleNormalLayerAG(TEXTURE2D_PARAM(textureName, samplerName), coord, layerTexCoord.weights)
|
|
|
|
// Transforms 2D UV by scale/bias property
|
|
#define TRANSFORM_TEX(tex,name) ((tex.xy) * name##_ST.xy + name##_ST.zw)
|
|
|
|
#ifndef LAYERED_LIT_SHADER
|
|
|
|
#define LAYER_INDEX 0
|
|
#define ADD_IDX(Name) Name
|
|
#define ADD_ZERO_IDX(Name) Name
|
|
#include "LitSurfaceData.hlsl"
|
|
|
|
void GetSurfaceAndBuiltinData(FragInput input, out SurfaceData surfaceData, out BuiltinData builtinData)
|
|
{
|
|
LayerTexCoord layerTexCoord;
|
|
ZERO_INITIALIZE(LayerTexCoord, layerTexCoord);
|
|
|
|
#ifdef _MAPPING_TRIPLANAR
|
|
// one weight for each direction XYZ - Use vertex normal for triplanar
|
|
layerTexCoord.weights = ComputeTriplanarWeights(input.tangentToWorld[2].xyz);
|
|
#endif
|
|
|
|
// Be sure that the compiler is aware that we don't touch UV1 and UV3 for base layer in case of non layer shader
|
|
// so it can remove code
|
|
_UVMappingMask.yz = float2(0.0, 0.0);
|
|
bool isTriplanar = false;
|
|
#ifdef _MAPPING_TRIPLANAR
|
|
isTriplanar = true;
|
|
#endif
|
|
ComputeLayerTexCoord(input, isTriplanar, layerTexCoord);
|
|
ApplyDisplacement(input, layerTexCoord);
|
|
|
|
float alpha = GetSurfaceData(input, layerTexCoord, surfaceData);
|
|
GetBuiltinData(input, surfaceData, alpha, builtinData);
|
|
}
|
|
|
|
#else
|
|
|
|
#define ADD_ZERO_IDX(Name) Name##0
|
|
|
|
// Generate function for all layer
|
|
#define LAYER_INDEX 0
|
|
#define ADD_IDX(Name) Name##0
|
|
#include "LitSurfaceData.hlsl"
|
|
#undef LAYER_INDEX
|
|
#undef ADD_IDX
|
|
|
|
#define LAYER_INDEX 1
|
|
#define ADD_IDX(Name) Name##1
|
|
#include "LitSurfaceData.hlsl"
|
|
#undef LAYER_INDEX
|
|
#undef ADD_IDX
|
|
|
|
#define LAYER_INDEX 2
|
|
#define ADD_IDX(Name) Name##2
|
|
#include "LitSurfaceData.hlsl"
|
|
#undef LAYER_INDEX
|
|
#undef ADD_IDX
|
|
|
|
#define LAYER_INDEX 3
|
|
#define ADD_IDX(Name) Name##3
|
|
#include "LitSurfaceData.hlsl"
|
|
#undef LAYER_INDEX
|
|
#undef ADD_IDX
|
|
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
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 normalize(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;
|
|
}
|
|
|
|
#define SURFACEDATA_BLEND_COLOR(surfaceData, name, mask) BlendLayeredColor(surfaceData##0.##name, surfaceData##1.##name, surfaceData##2.##name, surfaceData##3.##name, mask);
|
|
#define SURFACEDATA_BLEND_NORMAL(surfaceData, name, mask) BlendLayeredNormal(surfaceData##0.##name, surfaceData##1.##name, surfaceData##2.##name, surfaceData##3.##name, mask);
|
|
#define SURFACEDATA_BLEND_SCALAR(surfaceData, name, mask) BlendLayeredScalar(surfaceData##0.##name, surfaceData##1.##name, surfaceData##2.##name, surfaceData##3.##name, mask);
|
|
#define PROP_BLEND_SCALAR(name, mask) BlendLayeredScalar(name##0, name##1, name##2, name##3, mask);
|
|
|
|
void GetSurfaceAndBuiltinData(FragInput input, out SurfaceData surfaceData, out BuiltinData builtinData)
|
|
{
|
|
LayerTexCoord layerTexCoord;
|
|
ZERO_INITIALIZE(LayerTexCoord, layerTexCoord);
|
|
|
|
#if defined(_LAYER_MAPPING_TRIPLANAR_0) || defined(_LAYER_MAPPING_TRIPLANAR_1) || defined(_LAYER_MAPPING_TRIPLANAR_2) || defined(_LAYER_MAPPING_TRIPLANAR_3)
|
|
// one weight for each direction XYZ - Use vertex normal for triplanar
|
|
layerTexCoord.weights = ComputeTriplanarWeights(input.tangentToWorld[2].xyz);
|
|
#endif
|
|
|
|
bool isTriplanar = false;
|
|
#ifdef _LAYER_MAPPING_TRIPLANAR_0
|
|
isTriplanar = true;
|
|
#endif
|
|
ComputeLayerTexCoord0(input, isTriplanar, layerTexCoord);
|
|
isTriplanar = false;
|
|
#ifdef _LAYER_MAPPING_TRIPLANAR_1
|
|
isTriplanar = true;
|
|
#endif
|
|
ComputeLayerTexCoord1(input, isTriplanar, layerTexCoord);
|
|
isTriplanar = false;
|
|
#ifdef _LAYER_MAPPING_TRIPLANAR_2
|
|
isTriplanar = true;
|
|
#endif
|
|
ComputeLayerTexCoord2(input, isTriplanar, layerTexCoord);
|
|
isTriplanar = false;
|
|
#ifdef _LAYER_MAPPING_TRIPLANAR_3
|
|
isTriplanar = true;
|
|
#endif
|
|
ComputeLayerTexCoord3(input, isTriplanar, layerTexCoord);
|
|
ApplyDisplacement0(input, layerTexCoord);
|
|
ApplyDisplacement1(input, layerTexCoord);
|
|
ApplyDisplacement2(input, layerTexCoord);
|
|
ApplyDisplacement3(input, layerTexCoord);
|
|
|
|
SurfaceData surfaceData0;
|
|
SurfaceData surfaceData1;
|
|
SurfaceData surfaceData2;
|
|
SurfaceData surfaceData3;
|
|
float alpha0 = GetSurfaceData0(input, layerTexCoord, surfaceData0);
|
|
float alpha1 = GetSurfaceData1(input, layerTexCoord, surfaceData1);
|
|
float alpha2 = GetSurfaceData2(input, layerTexCoord, surfaceData2);
|
|
float alpha3 = GetSurfaceData3(input, layerTexCoord, surfaceData3);
|
|
|
|
// Mask Values : Layer 1, 2, 3 are r, g, b
|
|
float3 maskValues = SAMPLE_TEXTURE2D(_LayerMaskMap, sampler_LayerMaskMap, input.texCoord0).rgb;
|
|
#if defined(_LAYER_MASK_VERTEX_COLOR)
|
|
maskValues *= input.vertexColor.rgb;
|
|
#endif
|
|
|
|
float weights[_MAX_LAYER];
|
|
ComputeMaskWeights(maskValues, weights);
|
|
|
|
surfaceData.baseColor = SURFACEDATA_BLEND_COLOR(surfaceData, baseColor, weights);
|
|
surfaceData.specularOcclusion = SURFACEDATA_BLEND_SCALAR(surfaceData, specularOcclusion, weights);
|
|
// Note: for normal map (in tangent space) it is possible to have better performance
|
|
// by blending in tangent space then transform to world and apply flip.
|
|
// Sadly this require a specific path (without taking into account that there is detail normal map)
|
|
// mean it add an extra cost of maintenance. We chose to not do this optimization in favor
|
|
// of simpler code and in the future will rely on shader graph to create optimize code.
|
|
surfaceData.normalWS = SURFACEDATA_BLEND_NORMAL(surfaceData, normalWS, weights);
|
|
surfaceData.perceptualSmoothness = SURFACEDATA_BLEND_SCALAR(surfaceData, perceptualSmoothness, weights);
|
|
surfaceData.ambientOcclusion = SURFACEDATA_BLEND_SCALAR(surfaceData, ambientOcclusion, weights);
|
|
surfaceData.metallic = SURFACEDATA_BLEND_SCALAR(surfaceData, metallic, weights);
|
|
|
|
// Init other unused parameter
|
|
surfaceData.materialId = 0;
|
|
surfaceData.tangentWS = input.tangentToWorld[0].xyz;
|
|
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);
|
|
|
|
float alpha = PROP_BLEND_SCALAR(alpha, weights);
|
|
GetBuiltinData(input, surfaceData, alpha, builtinData);
|
|
}
|
|
|
|
#endif // #ifndef LAYERED_LIT_SHADER
|