From 4b36860b6288b1cd27b78c210699fa54c66df50c Mon Sep 17 00:00:00 2001 From: Evan Hemsley <2342303+ehemsley@users.noreply.github.com> Date: Sat, 1 Aug 2020 03:23:20 -0700 Subject: [PATCH] trying stuff but the shading still seems to be model-relative --- Effects/PBREffect.cs | 260 ++++++++++--------- Effects/PBREffect.fx | 457 ++++++++-------------------------- Effects/PBREffect.fxb | Bin 7552 -> 8852 bytes Effects/ReferencePBREffect.fx | 410 ++++++++++++++++++++++++++++++ Importer.cs | 163 ++++++------ Smuggler.csproj | 3 +- 6 files changed, 743 insertions(+), 550 deletions(-) create mode 100644 Effects/ReferencePBREffect.fx diff --git a/Effects/PBREffect.cs b/Effects/PBREffect.cs index 4472bd7..ba73003 100644 --- a/Effects/PBREffect.cs +++ b/Effects/PBREffect.cs @@ -5,48 +5,81 @@ namespace Smuggler { public struct PBRLight { - public Vector3 direction; - public Vector3 colour; + public Vector3 position; + public Vector3 color; - public PBRLight(Vector3 direction, Vector3 colour) + public PBRLight(Vector3 position, Vector3 colour) { - this.direction = direction; - this.colour = colour; + this.position = position; + this.color = colour; + } + } + + public class PBRLightCollection + { + private readonly Vector3[] positions = new Vector3[4]; + private readonly Vector3[] colors = new Vector3[4]; + + readonly EffectParameter lightPositionsParam; + readonly EffectParameter lightColorsParam; + + public PBRLightCollection(EffectParameter lightPositionsParam, EffectParameter lightColorsParam) + { + this.lightPositionsParam = lightPositionsParam; + this.lightColorsParam = lightColorsParam; + } + + public PBRLight this[int i] + { + get { return new PBRLight(positions[i], colors[i]); } + set + { + positions[i] = value.position; + colors[i] = value.color; + lightPositionsParam.SetValue(positions); + lightColorsParam.SetValue(colors); + } } } public class PBREffect : Effect { - readonly EffectParameter modelParam; - readonly EffectParameter viewParam; - readonly EffectParameter projectionParam; - readonly EffectParameter lightDirParam; - readonly EffectParameter lightColourParam; - readonly EffectParameter normalScaleParam; - readonly EffectParameter emissiveFactorParam; - readonly EffectParameter occlusionStrengthParam; - readonly EffectParameter metallicRoughnessValuesParam; - readonly EffectParameter baseColorFactorParam; - readonly EffectParameter cameraLookParam; + EffectParameter worldParam; + EffectParameter viewParam; + EffectParameter projectionParam; + EffectParameter worldViewProjectionParam; + EffectParameter worldInverseTransposeParam; - readonly EffectParameter baseColourTextureParam; - readonly EffectParameter normalTextureParam; - readonly EffectParameter emissionTextureParam; - readonly EffectParameter occlusionTextureParam; - readonly EffectParameter metallicRoughnessTextureParam; - readonly EffectParameter envDiffuseTextureParam; - readonly EffectParameter brdfLutTextureParam; - readonly EffectParameter envSpecularTextureParam; + EffectParameter baseColorTextureParam; + EffectParameter normalTextureParam; + EffectParameter emissionTextureParam; + EffectParameter occlusionTextureParam; + EffectParameter metallicRoughnessTextureParam; + EffectParameter envDiffuseTextureParam; + EffectParameter brdfLutTextureParam; + EffectParameter envSpecularTextureParam; + + EffectParameter lightPositionsParam; + EffectParameter lightColorsParam; + + EffectParameter albedoParam; + EffectParameter metallicParam; + EffectParameter roughnessParam; + EffectParameter aoParam; + + EffectParameter eyePositionParam; Matrix world = Matrix.Identity; Matrix view = Matrix.Identity; Matrix projection = Matrix.Identity; - PBRLight light = new PBRLight(); - float normalScale = 1; - Vector3 emissiveFactor; - float occlusionStrength; - Vector2 metallicRoughnessValue; - Vector4 baseColorFactor; + PBRLightCollection pbrLightCollection; + + Vector3 albedo; + float metallic; + float roughness; + float ao; + + // FIXME: lazily set properties for performance public Matrix World { @@ -54,7 +87,9 @@ namespace Smuggler set { world = value; - modelParam.SetValue(world); + worldParam.SetValue(world); + worldViewProjectionParam.SetValue(world * view * projection); + worldInverseTransposeParam.SetValue(Matrix.Transpose(Matrix.Invert(world))); } } @@ -65,11 +100,8 @@ namespace Smuggler { view = value; viewParam.SetValue(view); - cameraLookParam.SetValue(-new Vector3( - view.M13, - view.M23, - view.M33 - )); + worldViewProjectionParam.SetValue(world * view * projection); + eyePositionParam.SetValue(Matrix.Invert(view).Translation); } } @@ -79,75 +111,61 @@ namespace Smuggler set { projection = value; - projectionParam.SetValue(value); + projectionParam.SetValue(projection); + worldViewProjectionParam.SetValue(world * view * projection); } } - public PBRLight Light + public PBRLightCollection Lights { - get { return light; } - set - { - light = value; - lightDirParam.SetValue(light.direction); - lightColourParam.SetValue(light.colour); - } + get { return pbrLightCollection; } + internal set { pbrLightCollection = value; } } - public float NormalScale + public Vector3 Albedo { - get { return normalScale; } + get { return albedo; } set { - normalScale = value; - normalScaleParam.SetValue(normalScale); + albedo = value; + albedoParam.SetValue(albedo); } } - public Vector3 EmissiveFactor + public float Metallic { - get { return emissiveFactor; } - set - { - emissiveFactor = value; - emissiveFactorParam.SetValue(emissiveFactor); - } - } - - public float OcclusionStrength - { - get { return occlusionStrength; } + get { return metallic; } set { - occlusionStrength = value; - occlusionStrengthParam.SetValue(occlusionStrength); + metallic = value; + metallicParam.SetValue(metallic); } } - public Vector2 MetallicRoughnessValue + public float Roughness { - get { return metallicRoughnessValue; } + get { return roughness; } set { - metallicRoughnessValue = value; - metallicRoughnessValuesParam.SetValue(metallicRoughnessValue); + roughness = value; + roughnessParam.SetValue(roughness); } } - public Vector4 BaseColorFactor + public float AO { - get { return baseColorFactor; } + get { return ao; } set { - baseColorFactor = value; - baseColorFactorParam.SetValue(baseColorFactor); + ao = value; + aoParam.SetValue(ao); } } public Texture2D BaseColourTexture { - get { return baseColourTextureParam.GetValueTexture2D(); } - set { baseColourTextureParam.SetValue(value); } + get { return baseColorTextureParam.GetValueTexture2D(); } + set { baseColorTextureParam.SetValue(value); } } public Texture2D NormalTexture @@ -194,66 +212,31 @@ namespace Smuggler public PBREffect(GraphicsDevice graphicsDevice) : base(graphicsDevice, Resources.PBREffect) { - modelParam = Parameters["model"]; - viewParam = Parameters["view"]; - projectionParam = Parameters["projection"]; + CacheEffectParameters(null); - lightDirParam = Parameters["lightDir"]; - lightColourParam = Parameters["lightColour"]; - - normalScaleParam = Parameters["normalScale"]; - emissiveFactorParam = Parameters["emissiveFactor"]; - occlusionStrengthParam = Parameters["occlusionStrength"]; - metallicRoughnessValuesParam = Parameters["metallicRoughnessValues"]; - baseColorFactorParam = Parameters["baseColorFactor"]; - cameraLookParam = Parameters["camera"]; - - baseColourTextureParam = Parameters["baseColourTexture"]; - normalTextureParam = Parameters["normalTexture"]; - emissionTextureParam = Parameters["emissionTexture"]; - occlusionTextureParam = Parameters["occlusionTexture"]; - metallicRoughnessTextureParam = Parameters["metallicRoughnessTexture"]; - envDiffuseTextureParam = Parameters["envDiffuseTexture"]; - brdfLutTextureParam = Parameters["brdfLutTexture"]; - envSpecularTextureParam = Parameters["envSpecularTexture"]; + pbrLightCollection = new PBRLightCollection( + Parameters["LightPositions"], + Parameters["LightColors"] + ); } protected PBREffect(PBREffect cloneSource) : base(cloneSource) { - modelParam = Parameters["model"]; - viewParam = Parameters["view"]; - projectionParam = Parameters["param"]; - - lightDirParam = Parameters["lightDir"]; - lightColourParam = Parameters["lightColour"]; - - normalScaleParam = Parameters["normalScale"]; - emissiveFactorParam = Parameters["emissiveFactor"]; - occlusionStrengthParam = Parameters["occlusionStrength"]; - metallicRoughnessValuesParam = Parameters["metallicRoughnessValues"]; - baseColorFactorParam = Parameters["baseColorFactor"]; - cameraLookParam = Parameters["camera"]; - - baseColourTextureParam = Parameters["baseColourTexture"]; - normalTextureParam = Parameters["normalTexture"]; - emissionTextureParam = Parameters["emissionTexture"]; - occlusionTextureParam = Parameters["occlusionTexture"]; - metallicRoughnessTextureParam = Parameters["metallicRoughnessTexture"]; - envDiffuseTextureParam = Parameters["envDiffuseTexture"]; - brdfLutTextureParam = Parameters["brdfLutTexture"]; - envSpecularTextureParam = Parameters["envSpecularTexture"]; + CacheEffectParameters(cloneSource); World = cloneSource.World; View = cloneSource.View; Projection = cloneSource.Projection; - Light = cloneSource.Light; + Lights = new PBRLightCollection( + Parameters["LightPositions"], + Parameters["LightColors"] + ); - NormalScale = cloneSource.normalScale; - EmissiveFactor = cloneSource.EmissiveFactor; - OcclusionStrength = cloneSource.OcclusionStrength; - MetallicRoughnessValue = cloneSource.MetallicRoughnessValue; - BaseColorFactor = cloneSource.BaseColorFactor; + for (int i = 0; i < 4; i++) + { + Lights[i] = cloneSource.Lights[i]; + } BaseColourTexture = cloneSource.BaseColourTexture; NormalTexture = cloneSource.NormalTexture; @@ -263,6 +246,11 @@ namespace Smuggler EnvDiffuseTexture = cloneSource.EnvDiffuseTexture; BRDFLutTexture = cloneSource.BRDFLutTexture; EnvSpecularTexture = cloneSource.EnvSpecularTexture; + + Albedo = cloneSource.Albedo; + Metallic = cloneSource.Metallic; + Roughness = cloneSource.Roughness; + AO = cloneSource.AO; } public override Effect Clone() @@ -275,5 +263,33 @@ namespace Smuggler { base.OnApply(); } + + void CacheEffectParameters(PBREffect cloneSource) + { + worldParam = Parameters["World"]; + viewParam = Parameters["View"]; + projectionParam = Parameters["Projection"]; + worldViewProjectionParam = Parameters["WorldViewProjection"]; + worldInverseTransposeParam = Parameters["WorldInverseTranspose"]; + + baseColorTextureParam = Parameters["BaseColorTexture"]; + normalTextureParam = Parameters["NormalTexture"]; + emissionTextureParam = Parameters["EmissionTexture"]; + occlusionTextureParam = Parameters["OcclusionTexture"]; + metallicRoughnessTextureParam = Parameters["MetallicRoughnessTexture"]; + envDiffuseTextureParam = Parameters["EnvDiffuseTexture"]; + brdfLutTextureParam = Parameters["BrdfLutTexture"]; + envSpecularTextureParam = Parameters["EnvSpecularTexture"]; + + lightPositionsParam = Parameters["LightPositions"]; + lightColorsParam = Parameters["LightColors"]; + + albedoParam = Parameters["Albedo"]; + metallicParam = Parameters["Metallic"]; + roughnessParam = Parameters["Roughness"]; + aoParam = Parameters["AO"]; + + eyePositionParam = Parameters["EyePosition"]; + } } } diff --git a/Effects/PBREffect.fx b/Effects/PBREffect.fx index 9d49a36..e26bd49 100644 --- a/Effects/PBREffect.fx +++ b/Effects/PBREffect.fx @@ -1,408 +1,159 @@ +#include "Macros.fxh" //from FNA -#include "Macros.fxh" +static const float PI = 3.141592653589793; -#define NORMALS -#define UV +// Transformation Matrices -// A constant buffer that stores the three basic column-major matrices for composing geometry. -cbuffer ModelViewProjectionConstantBuffer : register(b0) -{ - matrix model; - matrix view; - matrix projection; -}; +float4x4 World; +float4x4 View; +float4x4 Projection; + +float4x4 WorldViewProjection; +float4x3 WorldInverseTranspose; + +// Samplers + +DECLARE_TEXTURE(BaseColorTexture, 0); +DECLARE_TEXTURE(NormalTexture, 1); +DECLARE_TEXTURE(EmissionTexture, 2); +DECLARE_TEXTURE(OcclusionTexture, 3); +DECLARE_TEXTURE(MetallicRoughnessTexture, 4); +DECLARE_CUBEMAP(EnvDiffuseTexture, 8); +DECLARE_TEXTURE(BrdfLutTexture, 9); +DECLARE_CUBEMAP(EnvSpecularTexture, 10); + +// Light Info +float3 LightPositions[4]; +float3 LightColors[4]; + +// PBR Values +float3 Albedo; +float Metallic; +float Roughness; +float AO; + +float3 EyePosition; -// Per-vertex data used as input to the vertex shader. struct VertexShaderInput { - float4 position : POSITION; -#ifdef NORMALS - float3 normal : NORMAL; -#endif -#ifdef UV - float2 texcoord : TEXCOORD0; -#endif + float4 Position : POSITION; + float3 Normal : NORMAL; + float2 TexCoord : TEXCOORD0; }; -// Per-pixel color data passed through the pixel shader. struct PixelShaderInput { - float4 position : SV_POSITION; - float3 poswithoutw : POSITION1; - -#ifdef NORMALS - float3 normal : NORMAL; -#endif - - float2 texcoord : TEXCOORD0; + float4 Position : SV_POSITION; + float2 TexCoord : TEXCOORD0; + float3 PositionWS : TEXCOORD1; + float3 NormalWS : TEXCOORD2; }; PixelShaderInput main_vs(VertexShaderInput input) { PixelShaderInput output; - // Transform the vertex position into projected space. - float4 pos = mul(input.position, model); - output.poswithoutw = float3(pos.xyz) / pos.w; - -#ifdef NORMALS - // If we have normals... - output.normal = normalize(mul(float4(input.normal.xyz, 0.0), model)); -#endif - -#ifdef UV - output.texcoord = input.texcoord; -#else - output.texcoord = float2(0.0f, 0.0f); -#endif - -#ifdef HAS_NORMALS -#ifdef HAS_TANGENTS - vec3 normalW = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0))); - vec3 tangentW = normalize(vec3(u_ModelMatrix * vec4(a_Tangent.xyz, 0.0))); - vec3 bitangentW = cross(normalW, tangentW) * a_Tangent.w; - v_TBN = mat3(tangentW, bitangentW, normalW); -#else // HAS_TANGENTS != 1 - v_Normal = normalize(vec3(u_ModelMatrix * vec4(a_Normal.xyz, 0.0))); -#endif -#endif - - // Transform the vertex position into projected space. - pos = mul(pos, view); - pos = mul(pos, projection); - output.position = pos; + output.PositionWS = mul(input.Position, World).xyz; + output.TexCoord = input.TexCoord; + output.NormalWS = normalize(mul(WorldInverseTranspose, input.Normal)); + output.Position = mul(input.Position, WorldViewProjection); return output; } -// -// This fragment shader defines a reference implementation for Physically Based Shading of -// a microfacet surface material defined by a glTF model. -// -// References: -// [1] Real Shading in Unreal Engine 4 -// http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf -// [2] Physically Based Shading at Disney -// http://blog.selfshadow.com/publications/s2012-shading-course/burley/s2012_pbs_disney_brdf_notes_v3.pdf -// [3] README.md - Environment Maps -// https://github.com/KhronosGroup/glTF-WebGL-PBR/#environment-maps -// [4] "An Inexpensive BRDF Model for Physically based Rendering" by Christophe Schlick -// https://www.cs.virginia.edu/~jdl/bib/appearance/analytic%20models/schlick94b.pdf - -#define NORMALS -#define UV -#define HAS_NORMALS -// #define USE_IBL -#define USE_TEX_LOD - -DECLARE_TEXTURE(baseColourTexture, 0); -DECLARE_TEXTURE(normalTexture, 1); -DECLARE_TEXTURE(emissionTexture, 2); -DECLARE_TEXTURE(occlusionTexture, 3); -DECLARE_TEXTURE(metallicRoughnessTexture, 4); -DECLARE_CUBEMAP(envDiffuseTexture, 8); -DECLARE_TEXTURE(brdfLutTexture, 9); -DECLARE_CUBEMAP(envSpecularTexture, 10); - -cbuffer cbPerFrame : register(b0) +float3 FresnelSchlick(float cosTheta, float3 F0) { - float3 lightDir; - float3 lightColour; -}; - -cbuffer cbPerObject : register(b1) -{ - float normalScale; - float3 emissiveFactor; - float occlusionStrength; - float2 metallicRoughnessValues; - float4 baseColorFactor; - float3 camera; - - // debugging flags used for shader output of intermediate PBR variables - float4 scaleDiffBaseMR; - float4 scaleFGDSpec; - float4 scaleIBLAmbient; -}; - -#ifdef HAS_NORMALS -#ifdef HAS_TANGENTS -varying mat3 v_TBN; -#else -#endif -#endif - -// Encapsulate the various inputs used by the various functions in the shading equation -// We store values in this struct to simplify the integration of alternative implementations -// of the shading terms, outlined in the Readme.MD Appendix. -struct PBRInfo -{ - float NdotL; // cos angle between normal and light direction - float NdotV; // cos angle between normal and view direction - float NdotH; // cos angle between normal and half vector - float LdotH; // cos angle between light direction and half vector - float VdotH; // cos angle between view direction and half vector - float perceptualRoughness; // roughness value, as authored by the model creator (input to shader) - float metalness; // metallic value at the surface - float3 reflectance0; // full reflectance color (normal incidence angle) - float3 reflectance90; // reflectance color at grazing angle - float alphaRoughness; // roughness mapped to a more linear change in the roughness (proposed by [2]) - float3 diffuseColor; // color contribution from diffuse lighting - float3 specularColor; // color contribution from specular lighting -}; - -static const float M_PI = 3.141592653589793; -static const float c_MinRoughness = 0.04; - -float4 SRGBtoLINEAR(float4 srgbIn) -{ -#ifdef MANUAL_SRGB -#ifdef SRGB_FAST_APPROXIMATION - float3 linOut = pow(srgbIn.xyz,float3(2.2, 2.2, 2.2)); -#else //SRGB_FAST_APPROXIMATION - float3 bLess = step(float3(0.04045, 0.04045, 0.04045), srgbIn.xyz); - float3 linOut = lerp(srgbIn.xyz / float3(12.92, 12.92, 12.92), pow((srgbIn.xyz + float3(0.055, 0.055, 0.055)) / float3(1.055, 1.055, 1.055), float3(2.4, 2.4, 2.4)), bLess); -#endif //SRGB_FAST_APPROXIMATION - return float4(linOut,srgbIn.w);; -#else //MANUAL_SRGB - return srgbIn; -#endif //MANUAL_SRGB + return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } -// Find the normal for this fragment, pulling either from a predefined normal map -// or from the interpolated mesh normal and tangent attributes. -float3 getNormal(float3 position, float3 normal, float2 uv) +float DistributionGGX(float3 N, float3 H, float roughness) { - // Retrieve the tangent space matrix -#ifndef HAS_TANGENTS - float3 pos_dx = ddx(position); - float3 pos_dy = ddy(position); - float3 tex_dx = ddx(float3(uv, 0.0)); - float3 tex_dy = ddy(float3(uv, 0.0)); - float3 t = (tex_dy.y * pos_dx - tex_dx.y * pos_dy) / (tex_dx.x * tex_dy.y - tex_dy.x * tex_dx.y); + float a = roughness * roughness; + float a2 = a * a; + float NdotH = max(dot(N, H), 0.0); + float NdotH2 = NdotH * NdotH; -#ifdef HAS_NORMALS - float3 ng = normalize(normal); -#else - float3 ng = cross(pos_dx, pos_dy); -#endif + float num = a2; + float denom = (NdotH2 * (a2 - 1.0) + 1.0); + denom = PI * denom * denom; - t = normalize(t - ng * dot(ng, t)); - float3 b = normalize(cross(ng, t)); - row_major float3x3 tbn = float3x3(t, b, ng); - -#else // HAS_TANGENTS - mat3 tbn = v_TBN; -#endif - -#ifdef HAS_NORMALMAP - float3 n = SAMPLE_TEXTURE(normalTexture, uv).rgb; - - // Need to check the multiplication is equivalent.. - n = normalize(mul(((2.0 * n - 1.0) * float3(normalScale, normalScale, 1.0)), tbn)); -#else - float3 n = tbn[2].xyz; -#endif - - return n; + return num / denom; } -#ifdef USE_IBL -// Calculation of the lighting contribution from an optional Image Based Light source. -// Precomputed Environment Maps are required uniform inputs and are computed as outlined in [1]. -// See our README.md on Environment Maps [3] for additional discussion. -float3 getIBLContribution(PBRInfo pbrInputs, float3 n, float3 reflection) +float GeometrySchlickGGX(float NdotV, float roughness) { - float mipCount = 9.0; // resolution of 512x512 - float lod = (pbrInputs.perceptualRoughness * mipCount); - - // retrieve a scale and bias to F0. See [1], Figure 3 - float2 val = float2(pbrInputs.NdotV, 1.0 - pbrInputs.perceptualRoughness); - float3 brdf = SRGBtoLINEAR(SAMPLE_TEXTURE(brdfLutTexture, val)).rgb; + float r = (roughness + 1.0); + float k = (r * r) / 8.0; - float3 diffuseLight = SRGBtoLINEAR(SAMPLE_CUBEMAP(envDiffuseTexture, n)).rgb; + float num = NdotV; + float denom = NdotV * (1.0 - k) + k; -#ifdef USE_TEX_LOD - float4 reflectionWithLOD = float4(reflection, 0); - float3 specularLight = SRGBtoLINEAR(SAMPLE_CUBEMAP_LOD(envSpecularTexture, reflectionWithLOD)).rgb; -#else - float3 specularLight = SRGBtoLINEAR(SAMPLE_CUBEMAP(envSpecularTexture, reflection)).rgb; -#endif - - float3 diffuse = diffuseLight * pbrInputs.diffuseColor; - float3 specular = specularLight * (pbrInputs.specularColor * brdf.x + brdf.y); - - // For presentation, this allows us to disable IBL terms - diffuse *= scaleIBLAmbient.x; - specular *= scaleIBLAmbient.y; - - return diffuse + specular; -} -#endif - -// Basic Lambertian diffuse -// Implementation from Lambert's Photometria https://archive.org/details/lambertsphotome00lambgoog -// See also [1], Equation 1 -float3 diffuse(PBRInfo pbrInputs) -{ - return pbrInputs.diffuseColor / M_PI; + return num / denom; } -// The following equation models the Fresnel reflectance term of the spec equation (aka F()) -// Implementation of fresnel from [4], Equation 15 -float3 specularReflection(PBRInfo pbrInputs) +float GeometrySmith(float3 N, float3 V, float3 L, float roughness) { - return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); + float NdotV = max(dot(N, V), 0.0); + float NdotL = max(dot(N, L), 0.0); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + + return ggx1 * ggx2; } -// This calculates the specular geometric attenuation (aka G()), -// where rougher material will reflect less light back to the viewer. -// This implementation is based on [1] Equation 4, and we adopt their modifications to -// alphaRoughness as input as originally proposed in [2]. -float geometricOcclusion(PBRInfo pbrInputs) +// The case where we have no texture maps for any PBR data +float4 None(PixelShaderInput input) : SV_TARGET { - float NdotL = pbrInputs.NdotL; - float NdotV = pbrInputs.NdotV; - float r = pbrInputs.alphaRoughness; + float3 N = normalize(input.NormalWS); + float3 V = normalize(EyePosition - input.PositionWS); - float attenuationL = 2.0 * NdotL / (NdotL + sqrt(r * r + (1.0 - r * r) * (NdotL * NdotL))); - float attenuationV = 2.0 * NdotV / (NdotV + sqrt(r * r + (1.0 - r * r) * (NdotV * NdotV))); - return attenuationL * attenuationV; -} + float3 Lo = float3(0.0, 0.0, 0.0); -// The following equation(s) model the distribution of microfacet normals across the area being drawn (aka D()) -// Implementation from "Average Irregularity Representation of a Roughened Surface for Ray Reflection" by T. S. Trowbridge, and K. P. Reitz -// Follows the distribution function recommended in the SIGGRAPH 2013 course notes from EPIC Games [1], Equation 3. -float microfacetDistribution(PBRInfo pbrInputs) -{ - float roughnessSq = pbrInputs.alphaRoughness * pbrInputs.alphaRoughness; - float f = (pbrInputs.NdotH * roughnessSq - pbrInputs.NdotH) * pbrInputs.NdotH + 1.0; - return roughnessSq / (M_PI * f * f); -} + for (int i = 0; i < 4; i++) + { + float3 lightDir = LightPositions[i] - input.PositionWS; + float3 L = normalize(lightDir); + float3 H = normalize(V + L); -float4 main_ps(PixelShaderInput input) : SV_TARGET -{ - // Metallic and Roughness material properties are packed together - // In glTF, these factors can be specified by fixed scalar values - // or from a metallic-roughness map - float perceptualRoughness = metallicRoughnessValues.y; - float metallic = metallicRoughnessValues.x; + float distance = length(lightDir); + float attenuation = 1.0 / (distance * distance); + float3 radiance = LightColors[i] * attenuation; -#ifdef HAS_METALROUGHNESSMAP - // Roughness is stored in the 'g' channel, metallic is stored in the 'b' channel. - // This layout intentionally reserves the 'r' channel for (optional) occlusion map data - float4 mrSample = SAMPLE_TEXTURE(metallicRoughnessTexture, input.texcoord); + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, Albedo, Metallic); + float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); - // Had to reverse the order of the channels here - TODO: investigate.. - perceptualRoughness = mrSample.g * perceptualRoughness; - metallic = mrSample.b * metallic; -#endif + float NDF = DistributionGGX(N, H, Roughness); + float G = GeometrySmith(N, V, L, Roughness); - perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); - metallic = clamp(metallic, 0.0, 1.0); + float3 numerator = NDF * G * F; + float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); + float3 specular = numerator / max(denominator, 0.001); - // Roughness is authored as perceptual roughness; as is convention, - // convert to material roughness by squaring the perceptual roughness [2]. - float alphaRoughness = perceptualRoughness * perceptualRoughness; + float3 kS = F; + float3 kD = float3(1.0, 1.0, 1.0) - kS; - // The albedo may be defined from a base texture or a flat color + kD *= 1.0 - Metallic; -#ifdef HAS_BASECOLORMAP - float4 baseColor = SRGBtoLINEAR(SAMPLE_TEXTURE(baseColourTexture, input.texcoord)) * baseColorFactor; -#else - float4 baseColor = baseColorFactor; -#endif + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * Albedo / PI + specular) * radiance * NdotL; + } - float3 f0 = float3(0.04, 0.04, 0.04); - float3 diffuseColor = baseColor.rgb * (float3(1.0, 1.0, 1.0) - f0); + float3 ambient = float3(0.03, 0.03, 0.03) * Albedo * AO; + float3 color = ambient + Lo; - diffuseColor *= 1.0 - metallic; - - float3 specularColor = lerp(f0, baseColor.rgb, metallic); - - // Compute reflectance. - float reflectance = max(max(specularColor.r, specularColor.g), specularColor.b); - - // For typical incident reflectance range (between 4% to 100%) set the grazing reflectance to 100% for typical fresnel effect. - // For very low reflectance range on highly diffuse objects (below 4%), incrementally reduce grazing reflecance to 0%. - float reflectance90 = clamp(reflectance * 25.0, 0.0, 1.0); - float3 specularEnvironmentR0 = specularColor.rgb; - float3 specularEnvironmentR90 = float3(1.0, 1.0, 1.0) * reflectance90; - - float3 n = getNormal(input.poswithoutw, input.normal, input.texcoord); // normal at surface point - float3 v = normalize(camera - input.poswithoutw); // Vector from surface point to camera - - float3 l = normalize(lightDir); // Vector from surface point to light - float3 h = normalize(l + v); // Half vector between both l and v - float3 reflection = -normalize(reflect(v, n)); - - float NdotL = clamp(dot(n, l), 0.001, 1.0); - float NdotV = abs(dot(n, v)) + 0.001; - float NdotH = clamp(dot(n, h), 0.0, 1.0); - float LdotH = clamp(dot(l, h), 0.0, 1.0); - float VdotH = clamp(dot(v, h), 0.0, 1.0); - - PBRInfo pbrInputs; - pbrInputs.NdotL = NdotL; - pbrInputs.NdotV = NdotV; - pbrInputs.NdotH = NdotH; - pbrInputs.LdotH = LdotH; - pbrInputs.VdotH = VdotH; - pbrInputs.perceptualRoughness = perceptualRoughness; - pbrInputs.metalness = metallic; - pbrInputs.reflectance0 = specularEnvironmentR0; - pbrInputs.reflectance90 = specularEnvironmentR90; - pbrInputs.alphaRoughness = alphaRoughness; - pbrInputs.diffuseColor = diffuseColor; - pbrInputs.specularColor = specularColor; - - // Calculate the shading terms for the microfacet specular shading model - float3 F = specularReflection(pbrInputs); - - float G = geometricOcclusion(pbrInputs); - float D = microfacetDistribution(pbrInputs); - - // Calculation of analytical lighting contribution - float3 diffuseContrib = (1.0 - F) * diffuse(pbrInputs); - float3 specContrib = F * G * D / (4.0 * NdotL * NdotV); - float3 color = NdotL * lightColour * (diffuseContrib + specContrib); - - - // Calculate lighting contribution from image based lighting source (IBL) -#ifdef USE_IBL - color += getIBLContribution(pbrInputs, n, reflection); -#endif - - // Apply optional PBR terms for additional (optional) shading -#ifdef HAS_OCCLUSIONMAP - float ao = SAMPLE_TEXTURE(occlusionTexture, input.texcoord).r; - color = lerp(color, color * ao, occlusionStrength); -#endif - -#ifdef HAS_EMISSIVEMAP - float3 emissive = SRGBtoLINEAR(SAMPLE_TEXTURE(emissionTexture, input.texcoord)).rgb * emissiveFactor; - color += emissive; -#endif - - // This section uses lerp to override final color for reference app visualization - // of various parameters in the lighting equation. - color = lerp(color, F, scaleFGDSpec.x); - color = lerp(color, float3(G, G, G), scaleFGDSpec.y); - color = lerp(color, float3(D, D, D), scaleFGDSpec.z); - color = lerp(color, specContrib, scaleFGDSpec.w); - color = lerp(color, diffuseContrib, scaleDiffBaseMR.x); - color = lerp(color, baseColor.rgb, scaleDiffBaseMR.y); - color = lerp(color, float3(metallic, metallic, metallic), scaleDiffBaseMR.z); - color = lerp(color, float3(perceptualRoughness, perceptualRoughness, perceptualRoughness), scaleDiffBaseMR.w); + color = color / (color + float3(1.0, 1.0, 1.0)); + float exposureConstant = 1.0 / 2.2; + color = pow(color, float3(exposureConstant, exposureConstant, exposureConstant)); return float4(color, 1.0); } Technique PBR { - Pass pass1 - { - VertexShader = compile vs_3_0 main_vs(); - PixelShader = compile ps_3_0 main_ps(); - } + Pass Pass1 + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 None(); + } } diff --git a/Effects/PBREffect.fxb b/Effects/PBREffect.fxb index c6d92be11767fad03ad71c8ec1136836565fdd26..bcc23fa9549d9cd0d2f7e549485601743026db86 100644 GIT binary patch literal 8852 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 literal 7552 zcmcIpPiP$H75`>;6Unh-$*J19C8T>$!Rf)+Negx{m2A0A4GL`Zr|1^1rLnbPrCoMc zu4r=@6Bi8aVcX`ALk>gHp-7?VDB3`KFf@libI2jk9#oivp%7fi!G@yg@ArM*n|(8q z;|rbHMBMZaCFyw$?gx z;d*ba6GHY3kowz7pS2f1ATyxK200LSfp>u40K3ocY<;EO3_Da6Klf?uf%P~JTy1q$ z>dpPRTwnqon49*_PeNYwE6IqD zJIePF=yOe1!d|`EY%I>S)^0AZhTX0d2V;^`qUm+Iuj_15LajT>v-aFCSbw664zL09~E7xtoaszHAm$T{!xE6HF?=C-Z;KMVN? z@Oo$I#`Ib*OGa|3u)#dA-^GW>c-6W59lk#Sehu=x&p{$GpL5DLbNC7n`Omh)#kFRA zu)!slzS3vUiyx2~=vo`^2%j4EOU|k z#{Dw@Wsi-2_0sgkmFtagwP(*y)`B(~xJF#tqMS?9u6Mg<{aEeNjClbxYs_!o9e8Miepm4yfld_vZ_uaIeP97}Q}K5|Z!3Nm^k<46MbR#> za2}eVBgJok{z&n|n9%d85a&QIDgGVM?2+zr6>tuL$gC0{e!*vyLF4r6jTxss*fG3J_V1H;Qta?xoXa zYVP8t=fOUt7G(589#H)k{zSkMqSCexjDg$05>7$Nc7QSP17HamYZ-gDgv_;VVr9p3 zvf|kiV5bJy8MHeH{CTqF^r-7VpW(%`BX<_GyRX0e`Wf?TW3kifwr=#Qr)FNP{%Cr3 zx;ndDUkW?bsn$xnfv3yr`O`0*KYjjtFP(n*?AfzsYinyS-3w|nKb#Fp3s?5ii&_2t ziWDeg{cZgMQ~eP10q)4>&!S zAHtg6`jeMsFFj^zGqYD|FTiI40a+g{+e^P=rlzmXjfv9#@w)TE&;R+_&ZiSD#QDi$ zsGp7f{*{0HbmLEX`GW~BpV&*I;L5`E)VHBfHnmueN6w4S0tCvqX9=<=12dZGa$Jr% z+VNSOKwb6>WSOqWI8I&OUkH@b2mFpwP7s;U&e!A@^2NQb=R;qV5l9O#W)%qEHJQ6y zsiSGJ3rstgBlQRpIZ{V#zqKCxOL2l~}rt)a8A)=!-f8juVUH(yttI z{hH4OVhbGSIy?Z30MgW$*RV_@W+ZMs_tqbP-!~?j_}j?UNy6(I>4WA z*&`~5F!MfX%p(dwk6ZZ==%kMvARdNJ5|vHD{wd=r0DKzZ*)K-^5S_F+T@#vW8 z$9PKo73YL!9*jM*$e83y(4}qJaPE&R?{Yj2KCyfmKE|P6@jCPsz}D9%a{L%;6;*() zekbfWdFBFh_}jcaVm4#PSHREzV#G+j=x@`ai(m3JvUBk@qL2B=)yUCqp3e5*u}bEb z^F;ZOE3aeh+zisC&gOwSc+ZUOe1@qbIVBN$$_x+N_2t?qU7qLAv1e*v?=y+@?ao~0q)Tc|D!+?tVGsd1C_j zbNk)T)$&KO@rvbn?z-_|%X5#ov$4F3y!-52_OCoo8UV%*{oR|oqg3)hUH)E^|6Ser zCassandra Lugo and Evan Hemsley 2020 true Smuggler + AnyCPU;x86 @@ -15,7 +16,7 @@ - +