// Caution: We require sun light here as some sky use the sun light to render, mean UpdateSkyEnvironment
// must be call after BuildGPULightLists.
// TODO: Try to arrange code so we can trigger this call earlier and use async compute here to run sky convolution during other passes (once we move convolution shader to compute).
UpdateSkyEnvironment(hdCamera,renderContext);
// Caution: We require sun light here as some sky use the sun light to render, mean UpdateSkyEnvironment
// must be call after BuildGPULightLists.
// TODO: Try to arrange code so we can trigger this call earlier and use async compute here to run sky convolution during other passes (once we move convolution shader to compute).
// For opaque forward we have split rendering in two categories
// Material that are always forward and material that can be deferred or forward depends on render pipeline options (like switch to rendering forward only mode)
// Material that are always forward are unlit and complex (Like Hair) and don't require sorting, so it is ok to split them.
RenderForward(cullResults,camera,renderContext,true);// Render deferred or forward opaque
// For opaque forward we have split rendering in two categories
// Material that are always forward and material that can be deferred or forward depends on render pipeline options (like switch to rendering forward only mode)
// Material that are always forward are unlit and complex (Like Hair) and don't require sorting, so it is ok to split them.
RenderForward(cullResults,camera,renderContext,true);// Render deferred or forward opaque
RenderVelocity(cullResults,camera,renderContext);// Note we may have to render velocity earlier if we do temporalAO, temporal volumetric etc... Mean we will not take into account forward opaque in case of deferred rendering ?
// Planar and real time cubemap doesn't need post process and render in FP16
RenderVelocity(cullResults,camera,renderContext);// Note we may have to render velocity earlier if we do temporalAO, temporal volumetric etc... Mean we will not take into account forward opaque in case of deferred rendering ?
// TODO: Check with VFX team.
// Rendering distortion here have off course lot of artifact.
// But resolving at each objects that write in distortion is not possible (need to sort transparent, render those that do not distort, then resolve, then etc...)
// Instead we chose to apply distortion at the end after we cumulate distortion vector and desired blurriness. This
// Rendering distortion here have off course lot of artifact.
// But resolving at each objects that write in distortion is not possible (need to sort transparent, render those that do not distort, then resolve, then etc...)
// Instead we chose to apply distortion at the end after we cumulate distortion vector and desired blurriness. This
layerUVMappingMask[layerIndex].colorValue=(layerIndex==0)?newColor(1.0f,0.0f,0.0f,0.0f):newColor(X,Y,Z,W);// Special case for Main Layer and Blend Mask, only UV0. As Layer0 is share by both here, need to force X to 1.0 in all case
layerUVMappingPlanar[layerIndex].floatValue=(layerUVBaseMapping==LayerUVBaseMapping.Planar)?1.0f:0.0f;// Planar have priority on UV0
float flipSign = dot(dPdy, cross(vertexNormalWS, dPdx)) < 0.0 ? -1.0 : 1.0; // gives same as the commented out line above
// TODO: Optimize! The compiler will not be able to remove the tangent space that are not use because it can't know due to our UVMapping constant we use for both base and details
// To solve this we should track which UVSet is use for normal mapping... Maybe not as simple as it sounds
// to be able to combine object space normal with detail map we transform it to tangent space (object space normal composition is complex operation).
// to be able to combine object space normal with detail map or to apply a "scale" we transform it to tangent space (object space normal composition is complex operation).
// Note: There is no such a thing like triplanar with object space normal, so we call directly 2D function
// TODO : Test if GetOddNegativeScale() is necessary here in case of normal map, as GetOddNegativeScale is take into account in CreateTangentToWorld();
// Flipping or mirroring a normal can be done directly on the tangent space. This has the benefit to apply to the whole process either in surface gradient or not.
// This function will modify FragInputs and this is not propagate outside of GetSurfaceAndBuiltinData(). This is ok as tangent space is not use outside of GetSurfaceAndBuiltinData().
// _DoubleSidedConstants is float3(-1, -1, -1) in flip mode and float3(1, 1, -1) in mirror mode
// To get a flipped normal with the tangent space, we must flip bitangent (because it is construct from the normal) and normal
// To get a mirror normal with the tangent space, we only need to flip the normal and not the tangent
float2 flipSign = input.isFrontFace ? float2(1.0, 1.0) : _DoubleSidedConstants.yz; // TOCHECK : GetOddNegativeScale() is not necessary here as it is apply for tangent space creation.
publicstaticGUIContentdistortionEnableText=newGUIContent("Distortion","Enable distortion on this shader");
publicstaticGUIContentdistortionOnlyText=newGUIContent("Distortion Only","This shader will only be use to render distortion");
publicstaticGUIContentdistortionDepthTestText=newGUIContent("Distortion Depth Test","Enable the depth test for distortion");
publicstaticGUIContentemissiveWarning=newGUIContent("Emissive value is animated but the material has not been configured to support emissive. Please make sure the material itself has some amount of emissive.");
publicstaticGUIContentemissiveColorWarning=newGUIContent("Ensure emissive color is non-black for emission to have effect.");
float3 tangentToWorld[3]; // These 3 vectors are normalized (no need for the material to normalize) and these are only for UVSet 0
// TODO: confirm with Morten following statement
// Our TBN is orthogonal but is maybe not orthonormal in order to be compliant with external bakers (Like xnormal that use mikktspace).
// (xnormal for example take into account the interpolation when baking the normal and normalizing the tangent basis could cause distortion).
// When using worldToTangent with surface gradient, it doesn't normalize the tangent/bitangent vector (We instead use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// this mean that any usage of worldToTangent[1] or worldToTangent[2] outside of the context of normal map (like for POM) must normalize the TBN (TCHECK if this make any difference ?)
// When not using surface gradient, each vector of worldToTangent are normalize (TODO: Maybe they should not even in case of no surface gradient ? Ask Morten)
float3x3 worldToTangent;
// For two sided lighting
bool isFrontFace;
FragInputs output;
ZERO_INITIALIZE(FragInputs, output);
output.tangentToWorld[0] = float3(0.0, 0.0, 1.0);
output.tangentToWorld[2] = float3(0.0, 0.0, 1.0);
// Init to some default value to make the computer quiet (else it output "divide by zero" warning even if value is not used).
output.worldToTangent[0] = float3(0.0, 0.0, 1.0);
output.worldToTangent[2] = float3(0.0, 0.0, 1.0);
return output;
}
result = float3(input.texCoord3, 0.0);
break;
case DEBUGVIEWVARYING_VERTEX_TANGENT_WS:
result = input.tangentToWorld[0].xyz * 0.5 + 0.5;
result = input.worldToTangent[0].xyz * 0.5 + 0.5;
result = input.tangentToWorld[1].xyz * 0.5 + 0.5;
result = input.worldToTangent[1].xyz * 0.5 + 0.5;
result = input.tangentToWorld[2].xyz * 0.5 + 0.5;
result = input.worldToTangent[2].xyz * 0.5 + 0.5;
break;
case DEBUGVIEWVARYING_VERTEX_COLOR:
result = input.color.rgb; needLinearToSRGB = true;
// TODO: We should be able to not make distinction between the two path, but it mean material need to be aware to normalize the TBN when required, like for example for POM.
// For now do some test by keeping code consistent with previous visual.
#ifdef SURFACE_GRADIENT
// Normalize normalWS vector but keep the renormFactor to apply it to bitangent and tangent
// prepare for surfgrad formulation without breaking compliance (use exact same scale as applied to interpolated vertex normal to avoid breaking compliance).
// NdotV should not be negative for visible pixels, but it can happen due to the
// perspective projection and the normal mapping + decals. In that case, the normal
// should be modified to become valid (i.e facing the camera) to avoid weird artifacts.
// Note: certain applications (e.g. SpeedTree) require to still have negative normal to perform their own two sided lighting
// This will potentially reduce the length of the normal at edges of geometry.
float GetShiftedNdotV(inout float3 N, float3 V, bool twoSided)
// Note: certain applications (e.g. SpeedTree) require to still have negative normal to perform their own two sided lighting, they can use wantNegativeNormal
// This will potentially reduce the length of the normal at edges of geometry.
float GetShiftedNdotV(inout float3 N, float3 V, bool wantNegativeNormal)
if (!twoSided && NdotV < limit)
if (!wantNegativeNormal && NdotV < limit)
{
// We do not renormalize the normal because { abs(length(N) - 1.0) < limit }.
// surface gradient from an on the fly TBN (deriv obtained using tspaceNormalToDerivative()) or from conventional vertex level TBN (mikktspace compliant and deriv obtained using tspaceNormalToDerivative())
// used to produce a surface gradient from the gradient of a volume bump function such as a volume of perlin noise.
// equation 2. in "bump mapping unparametrized surfaces on the GPU".
// Observe the difference in figure 2. between using the gradient vs. the surface gradient to do bump mapping (the original method is proved wrong in the paper!).
// The 128 means the derivative will come out no greater than 128 numerically (where 1 is 45 degrees so 128 is very steap). You can increase it if u like of course
// Basically tan(angle) limited to 128
// So a max angle of 89.55 degrees ;) id argue thats close enough to the vertical limit at 90 degrees
// vT is channels.xy of a tangent space normal in[-1; 1]