Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,51 @@ uint FastLog2(uint x)
// Note: https://msdn.microsoft.com/en-us/library/windows/desktop/bb509636(v=vs.85).aspx pow(0, >0) == 0
TEMPLATE_2_REAL(PositivePow, base, power, return pow(abs(base), power))

// SafePositivePow: Same as pow(x,y) but considers x always positive and never exactly 0 such that
// SafePositivePow(0,y) will numerically converge to 1 as y -> 0, including SafePositivePow(0,0) returning 1.
//
// First, like PositivePow, SafePositivePow removes this warning for when you know the x value is positive or 0 and you know
// you avoid a NaN:
// ie you know that x == 0 and y > 0, such that pow(x,y) == pow(0, >0) == 0
// SafePositivePow(0, y) will however return close to 1 as y -> 0, see below.
//
// Also, pow(x,y) is most probably approximated as exp2(log2(x) * y), so pow(0,0) will give exp2(-inf * 0) == exp2(NaN) == NaN.
//
// SafePositivePow avoids NaN in allowing SafePositivePow(x,y) where (x,y) == (0,y) for any y including 0 by clamping x to a
// minimum of FLT_EPS. The consequences are:
//
// -As a replacement for pow(0,y) where y >= 1, the result of SafePositivePow(x,y) should be close enough to 0.
// -For cases where we substitute for pow(0,y) where 0 < y < 1, SafePositivePow(x,y) will quickly reach 1 as y -> 0, while
// normally pow(0,y) would give 0 instead of 1 for all 0 < y.
// eg: if we #define FLT_EPS 5.960464478e-8 (for fp32),
// SafePositivePow(0, 0.1) = 0.1894646
// SafePositivePow(0, 0.01) = 0.8467453
// SafePositivePow(0, 0.001) = 0.9835021
//
// Depending on the intended usage of pow(), this difference in behavior might be a moot point since:
// 1) by leaving "y" free to get to 0, we get a NaNs
// 2) the behavior of SafePositivePow() has more continuity when both x and y get closer together to 0, since
// when x is assured to be positive non-zero, pow(x,x) -> 1 as x -> 0.
//
// TL;DR: SafePositivePow(x,y) avoids NaN and is safe for positive (x,y) including (x,y) == (0,0),
// but SafePositivePow(0, y) will return close to 1 as y -> 0, instead of 0, so watch out
// for behavior depending on pow(0, y) giving always 0, especially for 0 < y < 1.
//
// Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/bb509636(v=vs.85).aspx
TEMPLATE_2_REAL(SafePositivePow, base, power, return pow(max(abs(base), real(REAL_EPS)), power))

// Helpers for making shadergraph functions consider precision spec through the same $precision token used for variable types
TEMPLATE_2_FLT(SafePositivePow_float, base, power, return pow(max(abs(base), float(FLT_EPS)), power))
TEMPLATE_2_HALF(SafePositivePow_half, base, power, return pow(max(abs(base), half(HALF_EPS)), power))

float Eps_float() { return FLT_EPS; }
float Min_float() { return FLT_MIN; }
float Max_float() { return FLT_MAX; }
half Eps_half() { return HALF_EPS; }
half Min_half() { return HALF_MIN; }
half Max_half() { return HALF_MAX; }


// Composes a floating point value with the magnitude of 'x' and the sign of 's'.
// See the comment about FastSign() below.
float CopySign(float x, float s, bool ignoreNegZero = true)
Expand Down
1 change: 1 addition & 0 deletions com.unity.shadergraph/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Optimized loading a large Shader Graph. [1209047](https://issuetracker.unity3d.com/issues/shader-graph-unresponsive-editor-when-using-large-graphs)
- New deleted asset dialogue fixes a bug where deleted assets would throw a missing file exception in the console. [1232246](https://issuetracker.unity3d.com/product/unity/issues/guid/1232246/)
- Fixed a bug where `Scene Depth` nodes would stop working after adding a keyword on the blackboard. [1203333](https://issuetracker.unity3d.com/product/unity/issues/guid/1203333/)
- Fixed NaN issue in triplanar SG node when blend goes to 0.

## [8.0.1] - 2020-05-25

Expand Down
2 changes: 1 addition & 1 deletion com.unity.shadergraph/Documentation~/Triplanar-Node.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Description

Triplanar is a method of generating UVs and sampling a texture by projecting in world space. The input **Texture** is sampled 3 times, once in each of the world x, y and z axises, and the resulting information is planar projected onto the model, blended by the normal, or surface angle. The generated UVs can be scaled with the input **Tile** and the final blending strength can be controlled with the input **Blend**. The projection can be modified by overriding the inputs **Position** and **Normal**. This is commonly used to texture large models such as terrain, where hand authoring UV coordinates would be problematic or not performant.
Triplanar is a method of generating UVs and sampling a texture by projecting in world space. The input **Texture** is sampled 3 times, once in each of the world x, y and z axes, and the resulting information is planar projected onto the model, blended by the normal, or surface angle. The generated UVs can be scaled with the input **Tile** and the final blending strength can be controlled with the input **Blend**. **Blend** controls the way the normal affects the blending of each plane sample and should be greater or equal to 0. The larger **blend** is, the more contribution will be given to the sample from the plane towards which the normal is most oriented. (The maximum blend exponent is between 17 and 158 depending on platform and the precision of the node.) A blend of 0 makes each plane get equal weight regardless of normal orientation. The projection can be modified by overriding the inputs **Position** and **Normal**. This is commonly used to texture large models such as terrain, where hand authoring UV coordinates would be problematic or not performant.

The expected type of the input **Texture** can be switched with the dropdown **Type**. If set to **Normal** the normals will be converted into world space so new tangents can be constructed then converted back to tangent space before output.

Expand Down
14 changes: 12 additions & 2 deletions com.unity.shadergraph/Editor/Data/Nodes/UV/TriplanarNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode gene
// Whiteout blend method
// https://medium.com/@bgolus/normal-mapping-for-a-triplanar-shader-10bf39dca05a
case TextureType.Normal:
sb.AppendLine("$precision3 {0}_Blend = max(pow(abs({1}), {2}), 0);"
// See comment for default case.
sb.AppendLine("$precision3 {0}_Blend = SafePositivePow_$precision({1}, min({2}, floor(log2(Min_$precision())/log2(1/sqrt(3)))) );"
, GetVariableNameForNode()
, GetSlotValue(NormalInputId, generationMode)
, GetSlotValue(BlendInputId, generationMode));
Expand Down Expand Up @@ -134,7 +135,16 @@ public virtual void GenerateNodeCode(ShaderStringBuilder sb, GenerationMode gene
, GetVariableNameForNode());
break;
default:
sb.AppendLine("$precision3 {0}_Blend = pow(abs({1}), {2});"
// We want the sum of the 3 blend weights (by which we normalize them) to be > 0.
// Max safe exponent is log2(REAL_MIN)/log2(1/sqrt(3)):
// Take the set of all possible normalized vectors, make a set from selecting the maximum component of each 3-vectors from the previous set,
// the minimum (:= min_of_max) of that new set is 1/sqrt(3) (by the fact vectors are normalized).
// We then want a maximum exponent such that
// precision_min < min_of_max^exponent_max
// where exponent_max is blend,
// log(precision_min) < log(min_of_max) * exponent_max
// log(precision_min) / log(min_of_max) > exponent_max
sb.AppendLine("$precision3 {0}_Blend = SafePositivePow_$precision({1}, min({2}, floor(log2(Min_$precision())/log2(1/sqrt(3)))) );"
, GetVariableNameForNode()
, GetSlotValue(NormalInputId, generationMode)
, GetSlotValue(BlendInputId, generationMode));
Expand Down