浏览代码

Merge pull request #250 from EvgeniiG/material_id_update

Swap material IDs for a simpler stencil test configuration
/Branch_Batching2
GitHub 7 年前
当前提交
78d7833b
共有 17 个文件被更改,包括 224 次插入190 次删除
  1. 40
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Editor/HDRenderPipelineMenuItems.cs
  2. 36
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs
  3. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLit.shader
  4. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader
  5. 26
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs
  6. 31
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/LitUI.cs
  7. 14
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.cs
  8. 8
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.cs.hlsl
  9. 93
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl
  10. 4
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.shader
  11. 2
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitData.hlsl
  12. 4
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitDataInternal.hlsl
  13. 4
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitTessellation.shader
  14. 96
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs
  15. 8
      Assets/ScriptableRenderPipeline/HDRenderPipeline/Utilities.cs
  16. 20
      Assets/ScriptableRenderPipeline/ShaderLibrary/AreaLighting.hlsl
  17. 24
      Assets/ScriptableRenderPipeline/ShaderLibrary/CommonLighting.hlsl

40
Assets/ScriptableRenderPipeline/HDRenderPipeline/Editor/HDRenderPipelineMenuItems.cs


}
}
[MenuItem("HDRenderPipeline/Swap standard and SSS material IDs")]
static void SwapStandardAndSssMaterialIds()
{
try
{
Object[] materials = Resources.FindObjectsOfTypeAll<Material>();
for (int i = 0, length = materials.Length; i < length; i++)
{
Material mat = materials[i] as Material;
EditorUtility.DisplayProgressBar(
"Updating materials...",
string.Format("{0} / {1}", i, length),
i / (float)(length - 1));
if (mat.shader.name == "HDRenderPipeline/Lit" || mat.shader.name == "HDRenderPipeline/LitTessellation")
{
int matID = (int)mat.GetFloat("_MaterialID");
if (matID == 0)
{
matID = 1;
mat.SetInt("_MaterialID", matID);
EditorUtility.SetDirty(mat);
}
else if (matID == 1)
{
matID = 0;
mat.SetInt("_MaterialID", matID);
EditorUtility.SetDirty(mat);
}
}
}
}
finally
{
EditorUtility.ClearProgressBar();
}
}
[MenuItem("HDRenderPipeline/Debug/Remove tessellation materials (not reversible)")]
static void RemoveTessellationMaterials()
{

36
Assets/ScriptableRenderPipeline/HDRenderPipeline/Lighting/TilePass/TilePass.cs


Utilities.SelectKeyword(m_DeferredDirectMaterialSRT, tileKeywords, 0);
m_DeferredDirectMaterialSRT.EnableKeyword("LIGHTLOOP_TILE_PASS");
m_DeferredDirectMaterialSRT.DisableKeyword("OUTPUT_SPLIT_LIGHTING");
m_DeferredDirectMaterialSRT.SetInt("_StencilRef", (int)StencilBits.Standard);
m_DeferredDirectMaterialSRT.SetInt("_StencilCmp", 4 /* LEqual */);
m_DeferredDirectMaterialSRT.SetInt("_StencilRef", (int)StencilBits.SSS);
m_DeferredDirectMaterialSRT.SetInt("_StencilCmp", 2 /* Less */); // Shade if stencil is not 0 and not SSS
m_DeferredDirectMaterialSRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_DeferredDirectMaterialSRT.SetInt("_DstBlend", (int)BlendMode.Zero);

Utilities.SelectKeyword(m_DeferredIndirectMaterialSRT, tileKeywords, 1);
m_DeferredIndirectMaterialSRT.EnableKeyword("LIGHTLOOP_TILE_PASS");
m_DeferredIndirectMaterialSRT.DisableKeyword("OUTPUT_SPLIT_LIGHTING");
m_DeferredIndirectMaterialSRT.SetInt("_StencilRef", (int)StencilBits.Standard);
m_DeferredIndirectMaterialSRT.SetInt("_StencilCmp", 4 /* LEqual */);
m_DeferredIndirectMaterialSRT.SetInt("_StencilRef", (int)StencilBits.SSS);
m_DeferredIndirectMaterialSRT.SetInt("_StencilCmp", 2 /* Less */); // Shade if stencil is not 0 and not SSS
m_DeferredIndirectMaterialSRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_DeferredIndirectMaterialSRT.SetInt("_DstBlend", (int)BlendMode.One); // Additive color & alpha source

Utilities.SelectKeyword(m_DeferredAllMaterialSRT, tileKeywords, 2);
m_DeferredAllMaterialSRT.EnableKeyword("LIGHTLOOP_TILE_PASS");
m_DeferredAllMaterialSRT.DisableKeyword("OUTPUT_SPLIT_LIGHTING");
m_DeferredAllMaterialSRT.SetInt("_StencilRef", (int)StencilBits.Standard);
m_DeferredAllMaterialSRT.SetInt("_StencilCmp", 4 /* LEqual */);
m_DeferredAllMaterialSRT.SetInt("_StencilRef", (int)StencilBits.SSS);
m_DeferredAllMaterialSRT.SetInt("_StencilCmp", 2 /* Less */); // Shade if stencil is not 0 and not SSS
m_DeferredAllMaterialSRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_DeferredAllMaterialSRT.SetInt("_DstBlend", (int)BlendMode.Zero);

m_SingleDeferredMaterialSRT = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/Deferred");
m_SingleDeferredMaterialSRT.EnableKeyword("LIGHTLOOP_SINGLE_PASS");
m_SingleDeferredMaterialSRT.DisableKeyword("OUTPUT_SPLIT_LIGHTING");
m_SingleDeferredMaterialSRT.SetInt("_StencilRef", (int)StencilBits.Standard);
m_SingleDeferredMaterialSRT.SetInt("_StencilCmp", 4 /* LEqual */);
m_SingleDeferredMaterialSRT.SetInt("_StencilRef", (int)StencilBits.SSS);
m_SingleDeferredMaterialSRT.SetInt("_StencilCmp", 2 /* Less */); // Shade if stencil is not 0 and not SSS
m_SingleDeferredMaterialSRT.SetInt("_SrcBlend", (int)BlendMode.One);
m_SingleDeferredMaterialSRT.SetInt("_DstBlend", (int)BlendMode.Zero);

}
else
{
// Note: in the enum StencilBits, Standard is before SSS and the stencil is setup to greater equal. So the code below is draw all stencil bit except SSS
m_SingleDeferredMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.Standard : StencilBits.SSS));
// The stencil test uses a LESS comparison mode.
// If asked to disable SSS, we set the material ID of SSS materials to Standard, and shade all pixels with non-zero stencil values.
m_SingleDeferredMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.SSS : 0));
Utilities.DrawFullScreen(cmd, m_SingleDeferredMaterialSRT, hdCamera, colorBuffers[0], depthStencilBuffer);
}
}

}
else
{
// Note: in the enum StencilBits, Standard is before SSS and the stencil is setup to greater equal. So the code below is draw all stencil bit except SSS
m_DeferredDirectMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.Standard : StencilBits.SSS));
// The stencil test uses a LESS comparison mode.
// If asked to disable SSS, we set the material ID of SSS materials to Standard, and shade all pixels with non-zero stencil values.
m_DeferredDirectMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.SSS : 0));
m_DeferredIndirectMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.SSS : 0));
m_DeferredIndirectMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.Standard : StencilBits.SSS));
Utilities.SelectKeyword(m_DeferredIndirectMaterialSRT, "USE_CLUSTERED_LIGHTLIST", "USE_FPTL_LIGHTLIST", bUseClusteredForDeferred);
Utilities.DrawFullScreen(cmd, m_DeferredIndirectMaterialSRT, hdCamera, colorBuffers[0], depthStencilBuffer);
}

}
else
{
// Note: in the enum StencilBits, Standard is before SSS and the stencil is setup to greater equal. So the code below is draw all stencil bit except SSS
m_DeferredAllMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.Standard : StencilBits.SSS));
// The stencil test uses a LESS comparison mode.
// If asked to disable SSS, we set the material ID of SSS materials to Standard, and shade all pixels with non-zero stencil values.
m_DeferredAllMaterialSRT.SetInt("_StencilRef", (int)(debugDisplaySettings.renderingDebugSettings.enableSSS ? StencilBits.NonSSS : StencilBits.SSS));
Utilities.SelectKeyword(m_DeferredAllMaterialSRT, "USE_CLUSTERED_LIGHTLIST", "USE_FPTL_LIGHTLIST", bUseClusteredForDeferred);
Utilities.DrawFullScreen(cmd, m_DeferredAllMaterialSRT, hdCamera, colorBuffers[0], depthStencilBuffer);
}

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLit.shader


_HorizonFade("Horizon fade", Range(0.0, 5.0)) = 1.0
// Stencil state
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.Standard
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.NonSSS (fixed at compile time)
// Blending state
[HideInInspector] _SurfaceType("__surfacetype", Float) = 0.0

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/LayeredLit/LayeredLitTessellation.shader


_HorizonFade("Horizon fade", Range(0.0, 5.0)) = 1.0
// Stencil state
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.Standard
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.NonSSS (fixed at compile time)
// Blending state
[HideInInspector] _SurfaceType("__surfacetype", Float) = 0.0

26
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/BaseLitUI.cs


using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering.HDPipeline;
namespace UnityEditor.Experimental.Rendering.HDPipeline
{

// Properties
// Material ID
protected MaterialProperty materialID = null;
protected const string kMaterialID = "_MaterialID";
protected MaterialProperty materialID = null;
protected const string kMaterialID = "_MaterialID";
protected const string kStencilRef = "_StencilRef";
// Wind
protected MaterialProperty windEnable = null;

bool depthOffsetEnable = material.GetFloat(kDepthOffsetEnable) > 0.0f;
SetKeyword(material, "_DEPTHOFFSET_ON", depthOffsetEnable);
int stencilRef = (int)UnityEngine.Experimental.Rendering.HDPipeline.StencilBits.Standard; // See 'StencilBits'.
// Set the reference value for the stencil test.
int stencilRef = (int)StencilBits.NonSSS;
int materialID = (int)material.GetFloat(kMaterialID);
switch (materialID)
if ((int)material.GetFloat(kMaterialID) == (int)UnityEngine.Experimental.Rendering.HDPipeline.Lit.MaterialId.LitSSS)
case (int)UnityEngine.Experimental.Rendering.HDPipeline.Lit.MaterialId.LitSSS:
stencilRef = (int)UnityEngine.Experimental.Rendering.HDPipeline.StencilBits.SSS;
break;
case (int)UnityEngine.Experimental.Rendering.HDPipeline.Lit.MaterialId.LitStandard:
stencilRef = (int)UnityEngine.Experimental.Rendering.HDPipeline.StencilBits.Standard;
break;
default:
stencilRef = 1 + materialID;
break;
stencilRef = (int)StencilBits.SSS;
material.SetInt("_StencilRef", stencilRef);
material.SetInt(kStencilRef, stencilRef);
bool enablePerPixelDisplacement = material.GetFloat(kEnablePerPixelDisplacement) > 0.0f;
SetKeyword(material, "_PER_PIXEL_DISPLACEMENT", enablePerPixelDisplacement);

31
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Editor/LitUI.cs


using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Experimental.Rendering.HDPipeline;
using UnityEngine.Experimental.Rendering.HDPipeline.Lit;
namespace UnityEditor.Experimental.Rendering.HDPipeline
{

{
UseEmissiveColor,
UseEmissiveMask,
}
public enum MaterialIDType
{
Standard = 0,
SubsurfaceScattering = 1,
SpecularColor = 2
}
protected MaterialProperty UVBase = null;

EditorGUI.indentLevel--;
}
if ((MaterialIDType)materialID.floatValue == MaterialIDType.Standard)
switch ((MaterialId)materialID.floatValue)
ShaderStandardInputGUI();
}
else if ((MaterialIDType)materialID.floatValue == MaterialIDType.SubsurfaceScattering)
{
ShaderSSSInputGUI(material);
}
else if ((MaterialIDType)materialID.floatValue == MaterialIDType.SpecularColor)
{
m_MaterialEditor.TexturePropertySingleLine(Styles.specularColorText, specularColorMap, specularColor);
case MaterialId.LitSSS:
ShaderSSSInputGUI(material);
break;
case MaterialId.LitStandard:
ShaderStandardInputGUI();
break;
case MaterialId.LitSpecular:
m_MaterialEditor.TexturePropertySingleLine(Styles.specularColorText, specularColorMap, specularColor);
break;
default:
Debug.Assert(false, "Encountered an unsupported MaterialID.");
break;
}
EditorGUILayout.Space();

14
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.cs


[GenerateHLSL(PackingRules.Exact)]
public enum MaterialId
{
LitStandard = 0,
LitSSS = 1,
LitSSS = 0,
LitStandard = 1,
LitUnused = 3,
LitAniso = 4 // Should be the last as it is not setup by the users but generated based on anisotropy property
LitUnused = 3,
LitAniso = 4 // Should be the last as it is not setup by the users but generated based on anisotropy property
public static uint FEATURE_FLAG_MATERIAL_LIT_STANDARD = 1 << 12;
public static uint FEATURE_FLAG_MATERIAL_LIT_SSS = 1 << 13;
public static uint FEATURE_FLAG_MATERIAL_LIT_SSS = 1 << 12;
public static uint FEATURE_FLAG_MATERIAL_LIT_STANDARD = 1 << 13;
public static uint FEATURE_FLAG_MATERIAL_LIT_ANISO = 1 << 15;
public static uint FEATURE_FLAG_MATERIAL_LIT_ANISO = 1 << 15;
}
//-----------------------------------------------------------------------------

8
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.cs.hlsl


//
// UnityEngine.Experimental.Rendering.HDPipeline.Lit.MaterialId: static fields
//
#define MATERIALID_LIT_STANDARD (0)
#define MATERIALID_LIT_SSS (1)
#define MATERIALID_LIT_SSS (0)
#define MATERIALID_LIT_STANDARD (1)
#define MATERIALID_LIT_SPECULAR (2)
#define MATERIALID_LIT_UNUSED (3)
#define MATERIALID_LIT_ANISO (4)

//
#define FEATURE_FLAG_MATERIAL_LIT_STANDARD (4096)
#define FEATURE_FLAG_MATERIAL_LIT_SSS (8192)
#define FEATURE_FLAG_MATERIAL_LIT_SSS (4096)
#define FEATURE_FLAG_MATERIAL_LIT_STANDARD (8192)
#define FEATURE_FLAG_MATERIAL_LIT_SPECULAR (16384)
#define FEATURE_FLAG_MATERIAL_LIT_ANISO (32768)

93
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.hlsl


#define LTC_LUT_SCALE ((LTC_LUT_SIZE - 1) * rcp(LTC_LUT_SIZE))
#define LTC_LUT_OFFSET (0.5 * rcp(LTC_LUT_SIZE))
#define MIN_N_DOT_V 0.0001 // The minimum value of 'NdotV'
#define SSS_UNIT_CONVERSION (1.0 / 300.0) // From 1/3 centimeters to meters
#define CENTIMETERS_TO_METERS (1.0 / 100.0)
#define CENTIMETERS_TO_METERS 0.01
uint _EnableSSS; // Globally toggles subsurface scattering on/off
uint _TransmissionFlags; // 1 bit/profile; 0 = inf. thick, 1 = supports transmission

// TODO take from subsurfaceProfile
bsdfData.fresnel0 = 0.04; // Should be 0.028 for the skin
bsdfData.subsurfaceProfile = subsurfaceProfile;
// Make the Std. Dev. of 1 correspond to the effective radius of 1 cm (three-sigma rule).
bsdfData.subsurfaceRadius = SSS_UNIT_CONVERSION * subsurfaceRadius + 0.0001;
bsdfData.thickness = CENTIMETERS_TO_METERS * (_ThicknessRemaps[subsurfaceProfile][0] +
_ThicknessRemaps[subsurfaceProfile][1] * thickness);
bsdfData.subsurfaceRadius = CENTIMETERS_TO_METERS * subsurfaceRadius + 0.0001;
bsdfData.thickness = CENTIMETERS_TO_METERS * (_ThicknessRemaps[subsurfaceProfile][0] +
_ThicknessRemaps[subsurfaceProfile][1] * thickness);
bsdfData.enableTransmission = IsBitSet(_TransmissionFlags, subsurfaceProfile);
if (bsdfData.enableTransmission)

struct PreLightData
{
// General
float NdotV; // Between 0.0001 and 1
float unNdotV; // Between -1 and 1
float NdotV; // Geometric version (not clamped)
// GGX iso
float ggxLambdaV;

float anisoGGXLambdaV;
// IBL
float3 iblDirWS; // Dominant specular direction, used for IBL in EvaluateBSDF_Env()
float3 iblDirWS; // Dominant specular direction, used for IBL in EvaluateBSDF_Env()
float3 specularFGD; // Store preconvole BRDF for both specular and diffuse
float3 specularFGD; // Store preconvoled BRDF for both specular and diffuse
float diffuseFGD;
// area light

{
PreLightData preLightData;
// General
float3 iblNormalWS = bsdfData.normalWS;
float NdotV = dot(bsdfData.normalWS, V);
float3 iblNormalWS = GetViewShiftedNormal(bsdfData.normalWS, V, NdotV, MIN_N_DOT_V);
preLightData.NdotV = NdotV; // Store the unaltered (geometric) version
NdotV = max(NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
preLightData.unNdotV = dot(bsdfData.normalWS, V);
// GetShiftedNdotV return a positive NdotV
// In case a material use negative normal for double sided lighting like Speedtree they need to do a new calculation
preLightData.NdotV = GetShiftedNdotV(iblNormalWS, V, preLightData.unNdotV);
float3 iblR = reflect(-V, iblNormalWS);
preLightData.ggxLambdaV = GetSmithJointGGXLambdaV(preLightData.NdotV, bsdfData.roughness);
preLightData.ggxLambdaV = GetSmithJointGGXLambdaV(NdotV, bsdfData.roughness);
// GGX aniso
if (bsdfData.materialId == MATERIALID_LIT_ANISO)

preLightData.anisoGGXLambdaV = GetSmithJointGGXAnisoLambdaV(preLightData.TdotV, preLightData.BdotV, preLightData.NdotV, bsdfData.roughnessT, bsdfData.roughnessB);
preLightData.anisoGGXLambdaV = GetSmithJointGGXAnisoLambdaV(preLightData.TdotV, preLightData.BdotV, NdotV, bsdfData.roughnessT, bsdfData.roughnessB);
iblNormalWS = GetAnisotropicModifiedNormal(bsdfData.bitangentWS, iblNormalWS, V, bsdfData.anisotropy);
float3 anisoIblNormalWS = GetAnisotropicModifiedNormal(bsdfData.bitangentWS, iblNormalWS, V, bsdfData.anisotropy);
iblR = reflect(-V, anisoIblNormalWS);
GetPreIntegratedFGD(preLightData.NdotV, bsdfData.perceptualRoughness, bsdfData.fresnel0, preLightData.specularFGD, preLightData.diffuseFGD);
GetPreIntegratedFGD(NdotV, bsdfData.perceptualRoughness, bsdfData.fresnel0, preLightData.specularFGD, preLightData.diffuseFGD);
// We need to take into account the modified normal for faking anisotropic here.
float3 iblR = reflect(-V, iblNormalWS);
preLightData.iblDirWS = GetSpecularDominantDir(bsdfData.normalWS, iblR, bsdfData.roughness, preLightData.NdotV);
preLightData.iblDirWS = GetSpecularDominantDir(iblNormalWS, iblR, bsdfData.roughness, NdotV);
float theta = FastACos(preLightData.NdotV);
float theta = FastACos(NdotV);
float2 uv = LTC_LUT_OFFSET + LTC_LUT_SCALE * float2(bsdfData.perceptualRoughness, theta * INV_HALF_PI);
// Get the inverse LTC matrix for GGX

out float3 diffuseLighting,
out float3 specularLighting)
{
float NdotL = saturate(dot(bsdfData.normalWS, L));
float NdotV = preLightData.unNdotV; // This value must not be clamped
// Optimized math. Ref: PBR Diffuse Lighting for GGX + Smith Microsurfaces (slide 114).
float NdotL = saturate(dot(bsdfData.normalWS, L)); // Must have the same value without the clamp
float NdotV = preLightData.NdotV; // Get the unaltered (geometric) version
// GCN Optimization: reference PBR Diffuse Lighting for GGX + Smith Microsurfaces
float invLenLV = rsqrt(abs(2.0 * LdotV + 2.0)); // invLenLV = rcp(length(L + V))
float invLenLV = rsqrt(abs(2 * LdotV + 2)); // invLenLV = rcp(length(L + V))
NdotV = max(NdotV, MIN_N_DOT_V); // Use the modified (clamped) version
float3 F = F_Schlick(bsdfData.fresnel0, LdotH);

bsdfData.roughnessB = ClampRoughnessForAnalyticalLights(bsdfData.roughnessB);
#ifdef LIT_USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGXAnisoLambdaV(preLightData.TdotV, preLightData.BdotV, preLightData.NdotV, TdotL, BdotL, NdotL,
Vis = V_SmithJointGGXAnisoLambdaV(preLightData.TdotV, preLightData.BdotV, NdotV, TdotL, BdotL, NdotL,
Vis = V_SmithJointGGXAniso(preLightData.TdotV, preLightData.BdotV, preLightData.NdotV, TdotL, BdotL, NdotL,
Vis = V_SmithJointGGXAniso(preLightData.TdotV, preLightData.BdotV, NdotV, TdotL, BdotL, NdotL,
bsdfData.roughnessT, bsdfData.roughnessB);
#endif

bsdfData.roughness = ClampRoughnessForAnalyticalLights(bsdfData.roughness);
#ifdef LIT_USE_BSDF_PRE_LAMBDAV
Vis = V_SmithJointGGX(NdotL, preLightData.NdotV, bsdfData.roughness, preLightData.ggxLambdaV);
Vis = V_SmithJointGGX(NdotL, NdotV, bsdfData.roughness, preLightData.ggxLambdaV);
Vis = V_SmithJointGGX(NdotL, preLightData.NdotV, bsdfData.roughness);
Vis = V_SmithJointGGX(NdotL, NdotV, bsdfData.roughness);
#endif
D = D_GGX(NdotH, bsdfData.roughness);
}

float diffuseTerm = Lambert();
#elif LIT_DIFFUSE_GGX_BRDF
float3 diffuseTerm = DiffuseGGX(bsdfData.diffuseColor, preLightData.NdotV, NdotL, NdotH, LdotV, bsdfData.perceptualRoughness);
float3 diffuseTerm = DiffuseGGX(bsdfData.diffuseColor, NdotV, NdotL, NdotH, LdotV, bsdfData.perceptualRoughness);
float diffuseTerm = DisneyDiffuse(preLightData.NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
float diffuseTerm = DisneyDiffuse(NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
#endif
diffuseLighting = bsdfData.diffuseColor * diffuseTerm;

float sinLT = length(cross(L, T));
float NdotL = saturate(dot(bsdfData.normalWS, L));
float3 lightDiff, lightSpec;
if (NdotL > 0)
{
float3 lightDiff, lightSpec;
BSDF(V, L, positionWS, preLightData, bsdfData, lightDiff, lightSpec);
BSDF(V, L, positionWS, preLightData, bsdfData, lightDiff, lightSpec);
// The value of the specular BSDF could be infinite.
// Summing up infinities leads to NaNs.
lightSpec = min(lightSpec, FLT_MAX);
diffuseLighting += lightDiff * (sinLT / dist2 * NdotL);
specularLighting += lightSpec * (sinLT / dist2 * NdotL);
diffuseLighting += lightDiff * (sinLT / dist2 * NdotL);
specularLighting += lightSpec * (sinLT / dist2 * NdotL);
}
}
// The factor of 2 is due to the fact: Integral{0, 2 PI}{max(0, cos(x))dx} = 2.

uint sampleCount = 4096)
{
float3x3 localToWorld = float3x3(bsdfData.tangentWS, bsdfData.bitangentWS, bsdfData.normalWS);
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d

float LdotH = dot(L, H);
// Note: we call DisneyDiffuse that require to multiply by Albedo / PI. Divide by PI is already taken into account
// in weightOverPdf of ImportanceSampleLambert call.
float disneyDiffuse = DisneyDiffuse(preLightData.NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
float disneyDiffuse = DisneyDiffuse(NdotV, NdotL, LdotH, bsdfData.perceptualRoughness);
// diffuse Albedo is apply here as describe in ImportanceSampleLambert function
float4 val = SampleEnv(lightLoopContext, lightData.envIndex, L, 0);

uint sampleCount = 4096)
{
float3x3 localToWorld = float3x3(bsdfData.tangentWS, bsdfData.bitangentWS, bsdfData.normalWS);
float NdotV = max(preLightData.NdotV, MIN_N_DOT_V);
float3 acc = float3(0.0, 0.0, 0.0);
// Add some jittering on Hammersley2d

// GGX BRDF
if (bsdfData.materialId == MATERIALID_LIT_ANISO)
{
ImportanceSampleAnisoGGX(u, V, localToWorld, bsdfData.roughnessT, bsdfData.roughnessB, preLightData.NdotV, L, VdotH, NdotL, weightOverPdf);
ImportanceSampleAnisoGGX(u, V, localToWorld, bsdfData.roughnessT, bsdfData.roughnessB, NdotV, L, VdotH, NdotL, weightOverPdf);
ImportanceSampleGGX(u, V, localToWorld, bsdfData.roughness, preLightData.NdotV, L, VdotH, NdotL, weightOverPdf);
ImportanceSampleGGX(u, V, localToWorld, bsdfData.roughness, NdotV, L, VdotH, NdotL, weightOverPdf);
}

4
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/Lit.shader


_HorizonFade("Horizon fade", Range(0.0, 5.0)) = 1.0
// Stencil state
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.Standard
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.NonSSS
// Blending state
[HideInInspector] _SurfaceType("__surfacetype", Float) = 0.0

[HideInInspector] _UVMappingMask("_UVMappingMask", Color) = (1, 0, 0, 0)
[Enum(TangentSpace, 0, ObjectSpace, 1)] _NormalMapSpace("NormalMap space", Float) = 0
[Enum(Standard, 0, Subsurface Scattering, 1, Specular Color, 2)] _MaterialID("MaterialId", Int) = 0
[Enum(Subsurface Scattering, 0, Standard, 1, Specular Color, 2)] _MaterialID("MaterialId", Int) = 1 // MaterialId.LitStandard
[ToggleOff] _EnablePerPixelDisplacement("Enable per pixel displacement", Float) = 0.0
_PPDMinSamples("Min sample for POM", Range(1.0, 64.0)) = 5

2
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitData.hlsl


surfaceData.tangentWS = input.worldToTangent[0].xyz;
#endif
// Init other parameters
surfaceData.materialId = 0;
surfaceData.materialId = 1; // MaterialId.LitStandard
surfaceData.anisotropy = 0;
surfaceData.specular = 0.04;
surfaceData.subsurfaceRadius = 1.0;

4
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitDataInternal.hlsl


// Mandatory to setup value to keep compiler quiet
// Layered shader only support materialId 0
surfaceData.materialId = 0;
// Layered shader only supports the standard material
surfaceData.materialId = 1; // MaterialId.LitStandard
// All these parameters are ignore as they are re-setup outside of the layers function
surfaceData.tangentWS = float3(0.0, 0.0, 0.0);

4
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/LitTessellation.shader


_HorizonFade("Horizon fade", Range(0.0, 5.0)) = 1.0
// Stencil state
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.Standard
[HideInInspector] _StencilRef("_StencilRef", Int) = 2 // StencilBits.NonSSS
// Blending state
[HideInInspector] _SurfaceType("__surfacetype", Float) = 0.0

[HideInInspector] _UVMappingMask("_UVMappingMask", Color) = (1, 0, 0, 0)
[Enum(TangentSpace, 0, ObjectSpace, 1)] _NormalMapSpace("NormalMap space", Float) = 0
[Enum(Standard, 0, Subsurface Scattering, 1, Specular Color, 2)] _MaterialID("MaterialId", Int) = 0
[Enum(Subsurface Scattering, 0, Standard, 1, Specular Color, 2)] _MaterialID("MaterialId", Int) = 1 // MaterialId.LitStandard
[ToggleOff] _EnablePerPixelDisplacement("Enable per pixel displacement", Float) = 0.0
_PPDMinSamples("Min sample for POM", Range(1.0, 64.0)) = 5

96
Assets/ScriptableRenderPipeline/HDRenderPipeline/Material/Lit/SubsurfaceScatteringProfile.cs


public const int numSamples = 11; // Must be an odd number
[ColorUsage(false, true, 0.05f, 2.0f, 1.0f, 1.0f)]
public Color stdDev1;
public Color scatterDistance1;
public Color stdDev2;
public Color scatterDistance2;
public float lerpWeight;
public TexturingMode texturingMode;
public bool enableTransmission;

public SubsurfaceScatteringProfile()
{
stdDev1 = new Color(0.3f, 0.3f, 0.3f, 0.0f);
stdDev2 = new Color(0.6f, 0.6f, 0.6f, 0.0f);
scatterDistance1 = new Color(0.3f, 0.3f, 0.3f, 0.0f);
scatterDistance2 = new Color(0.6f, 0.6f, 0.6f, 0.0f);
lerpWeight = 0.5f;
texturingMode = TexturingMode.PreAndPostScatter;
enableTransmission = false;

{
m_HalfRcpVariances = new Vector3[2];
}
// Apply the three-sigma rule.
Color stdDev1 = scatterDistance1 * (1.0f / 3.0f);
Color stdDev2 = scatterDistance2 * (1.0f / 3.0f);
// Our goal is to blur the image using a filter which is represented
// as a product of a linear combination of two normalized 1D Gaussians

public int numProfiles;
public SubsurfaceScatteringProfile[] profiles;
// Below is the cache filled during OnValidate().
[NonSerialized] public int texturingModeFlags; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
[NonSerialized] public int transmissionFlags; // 1 bit/profile; 0 = inf. thick, 1 = supports transmission
[NonSerialized] public Vector4[] tintColors; // For transmission; alpha is unused
[NonSerialized] public float[] thicknessRemaps;
[NonSerialized] public int texturingModeFlags; // 1 bit/profile; 0 = PreAndPostScatter, 1 = PostScatter
[NonSerialized] public int transmissionFlags; // 1 bit/profile; 0 = inf. thick, 1 = supports transmission
[NonSerialized] public Vector4[] tintColors; // For transmission; alpha is unused
[NonSerialized] public float[] thicknessRemaps; // Remap: 0 = start, 1 = end - start
[NonSerialized] public Vector4[] halfRcpVariancesAndLerpWeights;
[NonSerialized] public Vector4[] halfRcpWeightedVariances;
[NonSerialized] public Vector4[] filterKernels;

// Skip unassigned profiles.
if (profiles[i] == null) continue;
c.r = Mathf.Clamp(profiles[i].stdDev1.r, 0.05f, 2.0f);
c.g = Mathf.Clamp(profiles[i].stdDev1.g, 0.05f, 2.0f);
c.b = Mathf.Clamp(profiles[i].stdDev1.b, 0.05f, 2.0f);
c.r = Mathf.Clamp(profiles[i].scatterDistance1.r, 0.05f, 2.0f);
c.g = Mathf.Clamp(profiles[i].scatterDistance1.g, 0.05f, 2.0f);
c.b = Mathf.Clamp(profiles[i].scatterDistance1.b, 0.05f, 2.0f);
profiles[i].stdDev1 = c;
profiles[i].scatterDistance1 = c;
c.r = Mathf.Clamp(profiles[i].stdDev2.r, 0.05f, 2.0f);
c.g = Mathf.Clamp(profiles[i].stdDev2.g, 0.05f, 2.0f);
c.b = Mathf.Clamp(profiles[i].stdDev2.b, 0.05f, 2.0f);
c.r = Mathf.Clamp(profiles[i].scatterDistance2.r, 0.05f, 2.0f);
c.g = Mathf.Clamp(profiles[i].scatterDistance2.g, 0.05f, 2.0f);
c.b = Mathf.Clamp(profiles[i].scatterDistance2.b, 0.05f, 2.0f);
profiles[i].stdDev2 = c;
profiles[i].scatterDistance2 = c;
profiles[i].lerpWeight = Mathf.Clamp01(profiles[i].lerpWeight);

{
private class Styles
{
public readonly GUIContent sssProfilePreview0 = new GUIContent("Profile Preview");
public readonly GUIContent sssProfilePreview1 = new GUIContent("Shows the fraction of light scattered from the source as the radius increases to 1.");
public readonly GUIContent sssProfilePreview2 = new GUIContent("Note that the intensity of the region in the center may be clamped.");
public readonly GUIContent sssTransmittancePreview0 = new GUIContent("Transmittance Preview");
public readonly GUIContent sssTransmittancePreview1 = new GUIContent("Shows the fraction of light passing through the object for thickness values from the remap.");
public readonly GUIContent sssTransmittancePreview2 = new GUIContent("Can be thought of as a cross section of a slab of material illuminated by a white light from the left.");
public readonly GUIContent sssProfileStdDev1 = new GUIContent("Standard Deviation #1", "Determines the shape of the 1st Gaussian filter. Increases the strength and the radius of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileStdDev2 = new GUIContent("Standard Deviation #2", "Determines the shape of the 2nd Gaussian filter. Increases the strength and the radius of the blur of the corresponding color channel.");
public readonly GUIContent sssProfileLerpWeight = new GUIContent("Filter Interpolation", "Controls linear interpolation between the two Gaussian filters.");
public readonly GUIContent sssTexturingMode = new GUIContent("Texturing Mode", "Specifies when the diffuse texture should be applied.");
public readonly GUIContent[] sssTexturingModeOptions = new GUIContent[2]
public readonly GUIContent sssProfilePreview0 = new GUIContent("Profile Preview");
public readonly GUIContent sssProfilePreview1 = new GUIContent("Shows the fraction of light scattered from the source as the radius increases to 1.");
public readonly GUIContent sssProfilePreview2 = new GUIContent("Note that the intensity of the region in the center may be clamped.");
public readonly GUIContent sssTransmittancePreview0 = new GUIContent("Transmittance Preview");
public readonly GUIContent sssTransmittancePreview1 = new GUIContent("Shows the fraction of light passing through the object for thickness values from the remap.");
public readonly GUIContent sssTransmittancePreview2 = new GUIContent("Can be thought of as a cross section of a slab of material illuminated by a white light from the left.");
public readonly GUIContent sssProfileScatterDistance1 = new GUIContent("Scatter Distance #1", "The radius (in centimeters) of the 1st Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness.");
public readonly GUIContent sssProfileScatterDistance2 = new GUIContent("Scatter Distance #2", "The radius (in centimeters) of the 2nd Gaussian filter, one per color channel. Alpha is ignored. The blur is energy-preserving, so a wide filter results in a large area with small contributions of individual samples. Smaller values increase the sharpness.");
public readonly GUIContent sssProfileLerpWeight = new GUIContent("Filter Interpolation", "Controls linear interpolation between the two Gaussian filters.");
public readonly GUIContent sssTexturingMode = new GUIContent("Texturing Mode", "Specifies when the diffuse texture should be applied.");
public readonly GUIContent[] sssTexturingModeOptions = new GUIContent[2]
{
new GUIContent("Pre- and post-scatter", "Texturing is performed during both the lighting and the SSS passes. Slightly blurs the diffuse texture. Choose this mode if your diffuse texture contains little to no SSS lighting."),
new GUIContent("Post-scatter", "Texturing is performed only during the SSS pass. Effectively preserves the sharpness of the diffuse texture. Choose this mode if your diffuse texture already contains SSS lighting (e.g. a photo of skin).")

private RenderTexture m_ProfileImage, m_TransmittanceImage;
private Material m_ProfileMaterial, m_TransmittanceMaterial;
private SerializedProperty m_StdDev1, m_StdDev2, m_LerpWeight, m_TintColor,
private SerializedProperty m_ScatterDistance1, m_ScatterDistance2, m_LerpWeight, m_TintColor,
m_StdDev1 = serializedObject.FindProperty("stdDev1");
m_StdDev2 = serializedObject.FindProperty("stdDev2");
m_LerpWeight = serializedObject.FindProperty("lerpWeight");
m_TexturingMode = serializedObject.FindProperty("texturingMode");
m_Transmission = serializedObject.FindProperty("enableTransmission");
m_TintColor = serializedObject.FindProperty("tintColor");
m_ThicknessRemap = serializedObject.FindProperty("thicknessRemap");
m_ScatterDistance1 = serializedObject.FindProperty("scatterDistance1");
m_ScatterDistance2 = serializedObject.FindProperty("scatterDistance2");
m_LerpWeight = serializedObject.FindProperty("lerpWeight");
m_TexturingMode = serializedObject.FindProperty("texturingMode");
m_Transmission = serializedObject.FindProperty("enableTransmission");
m_TintColor = serializedObject.FindProperty("tintColor");
m_ThicknessRemap = serializedObject.FindProperty("thicknessRemap");
m_ProfileMaterial = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/DrawGaussianProfile");
m_TransmittanceMaterial = Utilities.CreateEngineMaterial("Hidden/HDRenderPipeline/DrawTransmittanceGraph");

EditorGUI.BeginChangeCheck();
{
EditorGUILayout.PropertyField(m_StdDev1, styles.sssProfileStdDev1);
EditorGUILayout.PropertyField(m_StdDev2, styles.sssProfileStdDev2);
EditorGUILayout.PropertyField(m_LerpWeight, styles.sssProfileLerpWeight);
EditorGUILayout.PropertyField(m_ScatterDistance1, styles.sssProfileScatterDistance1);
EditorGUILayout.PropertyField(m_ScatterDistance2, styles.sssProfileScatterDistance2);
EditorGUILayout.PropertyField(m_LerpWeight, styles.sssProfileLerpWeight);
EditorGUILayout.PropertyField(m_Transmission, styles.sssProfileTransmission);
EditorGUILayout.PropertyField(m_TintColor, styles.sssProfileTintColor);
EditorGUILayout.PropertyField(m_Transmission, styles.sssProfileTransmission);
EditorGUILayout.PropertyField(m_TintColor, styles.sssProfileTintColor);
EditorGUILayout.PropertyField(m_ThicknessRemap, styles.sssProfileMinMaxThickness);
Vector2 thicknessRemap = m_ThicknessRemap.vector2Value;

EditorGUILayout.Space();
}
// Apply the three-sigma rule.
Color stdDev1 = m_ScatterDistance1.colorValue * (1.0f / 3.0f);
Color stdDev2 = m_ScatterDistance2.colorValue * (1.0f / 3.0f);
m_ProfileMaterial.SetColor("_StdDev1", m_StdDev1.colorValue);
m_ProfileMaterial.SetColor("_StdDev2", m_StdDev2.colorValue);
m_ProfileMaterial.SetColor("_StdDev1", stdDev1);
m_ProfileMaterial.SetColor("_StdDev2", stdDev2);
m_ProfileMaterial.SetFloat("_LerpWeight", m_LerpWeight.floatValue);
EditorGUI.DrawPreviewTexture(GUILayoutUtility.GetRect(256, 256), m_ProfileImage, m_ProfileMaterial, ScaleMode.ScaleToFit, 1.0f);

EditorGUILayout.Space();
// Draw the transmittance graph.
m_TransmittanceMaterial.SetColor("_StdDev1", m_StdDev1.colorValue);
m_TransmittanceMaterial.SetColor("_StdDev2", m_StdDev2.colorValue);
m_TransmittanceMaterial.SetColor("_StdDev1", stdDev1);
m_TransmittanceMaterial.SetColor("_StdDev2", stdDev2);
m_TransmittanceMaterial.SetFloat("_LerpWeight", m_LerpWeight.floatValue);
m_TransmittanceMaterial.SetVector("_ThicknessRemap", m_ThicknessRemap.vector2Value);
m_TransmittanceMaterial.SetVector("_TintColor", m_TintColor.colorValue);

8
Assets/ScriptableRenderPipeline/HDRenderPipeline/Utilities.cs


[Flags]
public enum StencilBits
{
None = 0,
SSS = 0 + Lit.MaterialId.LitSSS, // 1
Standard = 2 + Lit.MaterialId.LitStandard, // 2
All = 255 // 0xff
None = 0, // 0
SSS = 1 + Lit.MaterialId.LitSSS, // 1
NonSSS = 2 + Lit.MaterialId.LitSSS, // 2
All = 255 // 0xFF
}
public class Utilities

20
Assets/ScriptableRenderPipeline/ShaderLibrary/AreaLighting.hlsl


float x = sinSqSigma;
float y = cosOmega;
#if 1
// Use a numerical fit found in Mathematica.
// For most of the domain, the absolute error is fairly low, under 0.005.
// You can use the following Mathematica code to reproduce our results:
// t = Flatten[Table[{x, y, f[x, y]}, {x, 0, 0.999999, 0.001}, {y, -0.999999, 0.999999, 0.002}], 1]
// m = NonlinearModelFit[t, x * (y + e) * (0.5 + (y - e) * (a + b * x + c * x^2 + d * x^3)), {a, b, c, d, e}, {x, y}]
return saturate(x * (0.9245867471551246 + y) * (0.5 + (-0.9245867471551246 + y) * (0.5359050373687144 + x * (-1.0054221851257754 + x * (1.8199061187417047 - x * 1.3172081704209504)))));
#else
// Another fit found with Mathematica. The absolute error is larger (around 0.02 on average), but the function is very smooth.
// You can use the following Mathematica code to reproduce our results:
// t = Flatten[Table[{x, y, f[x, y]}, {x, 0, 0.999999, 0.001}, {y, -0.999999, 0.999999, 0.002}], 1]
// m = NonlinearModelFit[t, 1 - (1 - x)^(a * (y + 1) + b * (y + 1)^2 + c * (y + 1)^3 + d * (y + 1)^4)}, {a, b, c, d}, {x, y}]
float p = saturate(0.14506085844485772 + y * (0.2858221675641456 + y * (0.23405929637528905 + y * (0.20682928702038633 + y * 0.1135312997643852))));
return saturate(1 - pow(1 - x, p));
#endif
// Use a numerical fit found in Mathematica. Mean absolute error: 0.00476944.
// You can use the following Mathematica code to reproduce our results:
// t = Flatten[Table[{x, y, f[x, y]}, {x, 0, 0.999999, 0.001}, {y, -0.999999, 0.999999, 0.002}], 1]
// m = NonlinearModelFit[t, x * (y + e) * (0.5 + (y - e) * (a + b * x + c * x^2 + d * x^3)), {a, b, c, d, e}, {x, y}]
return saturate(x * (0.9245867471551246 + y) * (0.5 + (-0.9245867471551246 + y) * (0.5359050373687144 + x * (-1.0054221851257754 + x * (1.8199061187417047 - x * 1.3172081704209504)))));
#else
#if 0 // Ref: Area Light Sources for Real-Time Graphics, page 4 (1996).
float sinSqOmega = saturate(1 - cosOmega * cosOmega);

24
Assets/ScriptableRenderPipeline/ShaderLibrary/CommonLighting.hlsl


// Helper functions
//-----------------------------------------------------------------------------
// NdotV can be negative for visible pixels due to the perspective projection, the normal mapping and decals.
// This can produce visible artifacts with direct specular lighting (white point, black point) and indirect specular (artifact with cubemap fetch)
// A way to reduce artifact is to limit NdotV value to not be negative and calculate reflection vector for cubemap with a shifted normal (i.e what depends on the view)
// This is what provide this function
// Note: NdotV return by this function is always positive, no need for saturate
float GetShiftedNdotV(inout float3 N, float3 V, float NdotV)
// 'NdotV' can become negative for visible pixels due to the perspective projection, normal mapping and decals.
// This can produce visible artifacts under specular lighting, both direct (overly dark/bright pixels) and indirect (incorrect cubemap direction).
// One way of avoiding these artifacts is to limit the value of 'NdotV' to a small positive number,
// and calculate the reflection vector for the cubemap fetch using a normal shifted into view.
float3 GetViewShiftedNormal(float3 N, float3 V, float NdotV, float minNdotV)
const float limit = 0.0001; // Epsilon value that avoid divide by 0 (several BSDF divide by NdotV)
if (NdotV < limit)
if (NdotV < minNdotV)
// We do not renormalize the normal because { abs(length(N) - 1.0) < limit } + It is use for cubemap
N += (-NdotV + limit) * V;
NdotV = limit;
// We do not renormalize the normal to save a few clock cycles.
// The magnitude difference is typically negligible, and the normal is only used to compute
// the reflection vector for the IBL cube map fetch (which does not depend on the magnitude).
N += (-NdotV + minNdotV) * V;
return NdotV;
return N;
}
// Generates an orthonormal basis from a unit vector.

正在加载...
取消
保存