245 lines
6.9 KiB
HLSL
245 lines
6.9 KiB
HLSL
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include "Common.ush"
|
|
#include "ShadingCommon.ush"
|
|
//#include "HairStrands/HairBsdfSample.ush"
|
|
#include "ShadingModels.ush"
|
|
#include "MonteCarlo.ush"
|
|
#include "DeferredShadingCommon.ush"
|
|
|
|
#ifndef USE_HAIR_COMPLEX_TRANSMITTANCE
|
|
#define USE_HAIR_COMPLEX_TRANSMITTANCE 0
|
|
#endif
|
|
#if USE_HAIR_COMPLEX_TRANSMITTANCE
|
|
#include "HairStrands/HairStrandsCommon.ush"
|
|
#include "HairStrands/HairStrandsDeepTransmittanceCommon.ush"
|
|
#include "HairStrands/HairStrandsDeepTransmittanceDualScattering.ush"
|
|
#endif
|
|
|
|
// Diffuse therm
|
|
#define SHADING_TERM_DIFFUSE 0x01
|
|
|
|
// Specular therm, need to apply EnvBRDF() before apply to scene color.
|
|
#define SHADING_TERM_SPECULAR 0x02
|
|
|
|
// Hair's specular therm
|
|
// TODO: should be same as SHADING_TERM_SPECULAR?
|
|
#define SHADING_TERM_HAIR_R 0x04
|
|
|
|
// Hair's double transmitance
|
|
#define SHADING_TERM_HAIR_TT 0x08
|
|
|
|
// Hair's inner reflection bound.
|
|
#define SHADING_TERM_HAIR_TRT 0x10
|
|
|
|
|
|
struct FBxDFSample
|
|
{
|
|
// World space direction of the sample to do.
|
|
float3 L;
|
|
|
|
// PDF or the ray.
|
|
float PDF;
|
|
|
|
// Weight to apply to the sample's radiance.
|
|
// EvaluateBxDF(L) / PDF
|
|
float3 Weight;
|
|
|
|
// Term that has been selected.
|
|
uint Term;
|
|
};
|
|
|
|
FBxDFSample SampleDiffuseBxDF(float3 N, float4 E)
|
|
{
|
|
float2 DiskE = UniformSampleDiskConcentric(E.xy);
|
|
float3x3 TangentBasis = GetTangentBasis(N);
|
|
FBxDFSample BxDFSample = (FBxDFSample)0;
|
|
const float TangentZ = sqrt(1 - length2(DiskE));
|
|
|
|
BxDFSample.L = mul(float3(DiskE, TangentZ), TangentBasis);
|
|
BxDFSample.PDF = TangentZ * rcp(PI);
|
|
BxDFSample.Weight = 1.0;
|
|
BxDFSample.Term = SHADING_TERM_DIFFUSE;
|
|
return BxDFSample;
|
|
}
|
|
|
|
FBxDFSample SampleDefaultLitBxDF(uint TermMask, float3 WorldNormal, float3x3 TangentBasis, float Anisotropy, float Roughness, float3 V, float4 E)
|
|
{
|
|
TermMask &= SHADING_TERM_DIFFUSE | SHADING_TERM_SPECULAR;
|
|
|
|
float2 DiskE = UniformSampleDiskConcentric(E.xy);
|
|
float3 N = WorldNormal;
|
|
|
|
// TODO: stocastically choose SHADING_TERM_D or SHADING_TERM_DIFFUSE if TermMask == (SHADING_TERM_D | SHADING_TERM_SPECULAR)
|
|
|
|
FBxDFSample BxDFSample = (FBxDFSample)0;
|
|
if (TermMask == SHADING_TERM_DIFFUSE)
|
|
{
|
|
float TangentZ = sqrt(1 - length2(DiskE));
|
|
|
|
BxDFSample.L = mul(float3(DiskE, TangentZ), TangentBasis);
|
|
BxDFSample.PDF = TangentZ * rcp(PI);
|
|
BxDFSample.Weight = 1.0;
|
|
BxDFSample.Term = SHADING_TERM_DIFFUSE;
|
|
}
|
|
else if (TermMask == SHADING_TERM_SPECULAR)
|
|
{
|
|
float2 Alpha = Pow2(Roughness).xx;
|
|
|
|
if (Anisotropy != 0)
|
|
{
|
|
GetAnisotropicRoughness(Alpha.x, Anisotropy, Alpha.x, Alpha.y);
|
|
}
|
|
|
|
float3 TangentV = mul(TangentBasis, V);
|
|
|
|
#if 1
|
|
// PDF = G_SmithV * VoH * D / NoV / (4 * VoH)
|
|
// PDF = G_SmithV * D / (4 * NoV);
|
|
float4 TangentH = ImportanceSampleVisibleGGX(E.xy, Alpha, TangentV);
|
|
#else
|
|
// PDF = D * NoH / (4 * VoH),
|
|
float4 TangentH = ImportanceSampleGGX(E.xy, Alpha * Alpha);
|
|
#endif
|
|
|
|
float HPDF = TangentH.w;
|
|
|
|
float3 H = mul(TangentH.xyz, TangentBasis);
|
|
|
|
float VoH = saturate(dot(V, H));
|
|
|
|
BxDFSample.L = 2 * dot(V, H) * H - V;
|
|
BxDFSample.PDF = RayPDFToReflectionRayPDF(VoH, HPDF);
|
|
|
|
// float D = D_GGX(a2, NoH);
|
|
// float G_SmithL = 2 * NoL / (NoL + sqrt(NoL * (NoL - NoL * a2) + a2));
|
|
// float Vis = Vis_Smith(a2, NoV, NoL);
|
|
// float3 F = F_Schlick(GBuffer.SpecularColor, VoH);
|
|
|
|
// Correct integration applies DGF below but for speed we apply EnvBRDF later when compositing
|
|
// BxDFSample.Weight = F * ( NoL * Vis * (4 * VoH / NoH) ); // for ImportanceSampleGGX
|
|
// BxDFSample.Weight = F * G_SmithL; // for ImportanceSampleVisibleGGX
|
|
BxDFSample.Weight = 1.0;
|
|
|
|
BxDFSample.Term = SHADING_TERM_SPECULAR;
|
|
}
|
|
|
|
return BxDFSample;
|
|
}
|
|
FBxDFSample SampleDefaultLitBxDF(uint TermMask, FGBufferData GBuffer, float3 V, float4 E)
|
|
{
|
|
float3x3 TangentBasis;
|
|
|
|
if (GBuffer.Anisotropy != 0)
|
|
{
|
|
TangentBasis[0] = GBuffer.WorldTangent;
|
|
TangentBasis[1] = cross(GBuffer.WorldNormal, GBuffer.WorldTangent);
|
|
TangentBasis[2] = GBuffer.WorldNormal;
|
|
}
|
|
else
|
|
{
|
|
TangentBasis = GetTangentBasis(GBuffer.WorldNormal);
|
|
}
|
|
return SampleDefaultLitBxDF(TermMask, GBuffer.WorldNormal, TangentBasis, GBuffer.Anisotropy, GBuffer.Roughness, V, E);
|
|
}
|
|
|
|
FBxDFSample SampleHairBxDF(uint TermMask, FGBufferData GBuffer, float3 V, float4 E)
|
|
#if 1
|
|
{
|
|
float4 L = UniformSampleSphere(E.xy);
|
|
|
|
FDirectLighting Lighting;
|
|
const float OpaqueVisibility = 1; // No occlusion...
|
|
const float Area = 0; // This is used for faking area light sources by increasing the roughness of the surface. Disabled = 0.
|
|
const float Backlit = 1; // This is used for suppressing the R & TT terms when when the lighting direction comes from behind. Disabled = 1.
|
|
Lighting.Diffuse = 1; // Integrate over the entire sphere, and will reweight this with HairShading() during the composition. This is not correct, but give plausible result.
|
|
Lighting.Transmission = 0;
|
|
Lighting.Specular = 0;
|
|
|
|
FBxDFSample BxDFSample;
|
|
BxDFSample.L = L.xyz;
|
|
BxDFSample.PDF = L.w;
|
|
BxDFSample.Weight = 1;
|
|
BxDFSample.Term = TermMask;
|
|
|
|
return BxDFSample;
|
|
}
|
|
#else
|
|
{
|
|
TermMask &= SHADING_TERM_HAIR_R | SHADING_TERM_HAIR_TT | SHADING_TERM_HAIR_TRT;
|
|
|
|
float Backlit = GBuffer.CustomData.z;
|
|
|
|
float3 T = GBuffer.WorldNormal;
|
|
|
|
// TODO: should have a unique term bit mask instead?
|
|
uint HairComponents = 0;
|
|
HairComponents |= (TermMask & SHADING_TERM_HAIR_R) ? HAIR_COMPONENT_R : 0;
|
|
HairComponents |= (TermMask & SHADING_TERM_HAIR_TT) ? HAIR_COMPONENT_TT : 0;
|
|
HairComponents |= (TermMask & SHADING_TERM_HAIR_TRT) ? HAIR_COMPONENT_TRT : 0;
|
|
|
|
FBxDFSample BxDFSample = (FBxDFSample)0;
|
|
SampleHair(
|
|
GBuffer.Roughness,
|
|
GBuffer.BaseColor,
|
|
GBuffer.Specular,
|
|
Backlit,
|
|
V,
|
|
T,
|
|
E.xy,
|
|
E.zw,
|
|
HairComponents,
|
|
/* out */ BxDFSample.L,
|
|
/* out */ BxDFSample.Weight);
|
|
|
|
BxDFSample.PDF = 1.0; // TODO Guillaume: not used.
|
|
BxDFSample.Term = TermMask; // TODO
|
|
|
|
return BxDFSample;
|
|
}
|
|
#endif
|
|
|
|
// TODO: Oh god this feels duplicated with path tracer that is using ray tracing specific data structures :'(
|
|
FBxDFSample SampleBxDF(const uint TermMask, FGBufferData GBuffer, float3 V, float4 E)
|
|
{
|
|
switch( GBuffer.ShadingModelID )
|
|
{
|
|
case SHADINGMODELID_DEFAULT_LIT:
|
|
case SHADINGMODELID_SINGLELAYERWATER:
|
|
case SHADINGMODELID_SUBSURFACE:
|
|
case SHADINGMODELID_SUBSURFACE_PROFILE:
|
|
case SHADINGMODELID_PREINTEGRATED_SKIN:
|
|
case SHADINGMODELID_CLEAR_COAT:
|
|
case SHADINGMODELID_TWOSIDED_FOLIAGE:
|
|
case SHADINGMODELID_CLOTH:
|
|
case SHADINGMODELID_EYE:
|
|
return SampleDefaultLitBxDF(TermMask, GBuffer, V, E);
|
|
case SHADINGMODELID_HAIR:
|
|
return SampleHairBxDF(TermMask, GBuffer, V, E);
|
|
default:
|
|
return (FBxDFSample)0;
|
|
}
|
|
}
|
|
|
|
bool SupportsSampleBxDF(uint ShadingModelID)
|
|
{
|
|
switch (ShadingModelID)
|
|
{
|
|
case SHADINGMODELID_DEFAULT_LIT:
|
|
case SHADINGMODELID_SINGLELAYERWATER:
|
|
case SHADINGMODELID_SUBSURFACE:
|
|
case SHADINGMODELID_SUBSURFACE_PROFILE:
|
|
case SHADINGMODELID_PREINTEGRATED_SKIN:
|
|
case SHADINGMODELID_CLEAR_COAT:
|
|
case SHADINGMODELID_TWOSIDED_FOLIAGE:
|
|
case SHADINGMODELID_CLOTH:
|
|
case SHADINGMODELID_EYE:
|
|
case SHADINGMODELID_HAIR:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|