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 c6d92be..bcc23fa 100644 Binary files a/Effects/PBREffect.fxb and b/Effects/PBREffect.fxb differ diff --git a/Effects/ReferencePBREffect.fx b/Effects/ReferencePBREffect.fx new file mode 100644 index 0000000..61609b9 --- /dev/null +++ b/Effects/ReferencePBREffect.fx @@ -0,0 +1,410 @@ + +#include "Macros.fxh" + +#define NORMALS +#define UV +#define HAS_BASECOLORMAP + +// A constant buffer that stores the three basic column-major matrices for composing geometry. +cbuffer ModelViewProjectionConstantBuffer : register(b0) +{ + matrix model; + matrix view; + matrix projection; +}; + +// 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 +}; + +// Per-pixel color data passed through the pixel shader. +struct PixelShaderInput +{ + float4 position : SV_POSITION; + float4 positionWS : TEXCOORD1; + float3 normalWS : TEXCOORD2; + +#ifdef NORMALS + float3 normal : NORMAL; +#endif + + float2 texcoord : TEXCOORD0; +}; + +PixelShaderInput main_vs(VertexShaderInput input) +{ + PixelShaderInput output; + + // Transform the vertex position into projected space. + float4 pos = mul(input.position, model); + +#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; + + 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 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 +} + +// 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) +{ + // 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); + +#ifdef HAS_NORMALS + float3 ng = normalize(normal); +#else + float3 ng = cross(pos_dx, pos_dy); +#endif + + 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; +} + +#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 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; + + float3 diffuseLight = SRGBtoLINEAR(SAMPLE_CUBEMAP(envDiffuseTexture, n)).rgb; + +#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; +} + +// 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) +{ + return pbrInputs.reflectance0 + (pbrInputs.reflectance90 - pbrInputs.reflectance0) * pow(clamp(1.0 - pbrInputs.VdotH, 0.0, 1.0), 5.0); +} + +// 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) +{ + float NdotL = pbrInputs.NdotL; + float NdotV = pbrInputs.NdotV; + float r = pbrInputs.alphaRoughness; + + 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; +} + +// 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); +} + +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; + +#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); + + // Had to reverse the order of the channels here - TODO: investigate.. + perceptualRoughness = mrSample.g * perceptualRoughness; + metallic = mrSample.b * metallic; +#endif + + perceptualRoughness = clamp(perceptualRoughness, c_MinRoughness, 1.0); + metallic = clamp(metallic, 0.0, 1.0); + + // Roughness is authored as perceptual roughness; as is convention, + // convert to material roughness by squaring the perceptual roughness [2]. + float alphaRoughness = perceptualRoughness * perceptualRoughness; + + // The albedo may be defined from a base texture or a flat color + +#ifdef HAS_BASECOLORMAP + float4 baseColor = SRGBtoLINEAR(SAMPLE_TEXTURE(baseColourTexture, input.texcoord)) * baseColorFactor; +#else + float4 baseColor = baseColorFactor; +#endif + + float3 f0 = float3(0.04, 0.04, 0.04); + float3 diffuseColor = baseColor.rgb * (float3(1.0, 1.0, 1.0) - f0); + + 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); + + //return float4(baseColor.xyz, 1.0); + return float4(color, 1.0); +} + +Technique PBR +{ + Pass pass1 + { + VertexShader = compile vs_3_0 main_vs(); + PixelShader = compile ps_3_0 main_ps(); + } +} diff --git a/Importer.cs b/Importer.cs index ce4b783..7f1b75c 100644 --- a/Importer.cs +++ b/Importer.cs @@ -141,98 +141,113 @@ namespace Smuggler if (primitive.Material != null) { - var normalChannel = primitive.Material.FindChannel("Normal"); - if (normalChannel.HasValue) + //var normalChannel = primitive.Material.FindChannel("Normal"); + //if (normalChannel.HasValue) + //{ + // if (normalChannel.Value.Texture != null) + // { + // effect.NormalTexture = Texture2D.FromStream( + // graphicsDevice, + // normalChannel.Value.Texture.PrimaryImage.Content.Open() + // ); + // } + + // effect.NormalScale = normalChannel.Value.Parameter.X; + //} + + //var occlusionChannel = primitive.Material.FindChannel("Occlusion"); + //if (occlusionChannel.HasValue) + //{ + // if (occlusionChannel.Value.Texture != null) + // { + // effect.OcclusionTexture = Texture2D.FromStream( + // graphicsDevice, + // occlusionChannel.Value.Texture.PrimaryImage.Content.Open() + // ); + // } + + // effect.OcclusionStrength = occlusionChannel.Value.Parameter.X; + //} + + //var emissiveChannel = primitive.Material.FindChannel("Emissive"); + //if (emissiveChannel.HasValue) + //{ + // if (emissiveChannel.Value.Texture != null) + // { + // effect.EmissionTexture = Texture2D.FromStream( + // graphicsDevice, + // emissiveChannel.Value.Texture.PrimaryImage.Content.Open() + // ); + // } + + // var parameter = emissiveChannel.Value.Parameter; + + // effect.EmissiveFactor = new Vector3( + // parameter.X, + // parameter.Y, + // parameter.Z + // ); + //} + + var albedoChannel = primitive.Material.FindChannel("BaseColor"); + if (albedoChannel.HasValue) { - if (normalChannel.Value.Texture != null) - { - effect.NormalTexture = Texture2D.FromStream( - graphicsDevice, - normalChannel.Value.Texture.PrimaryImage.Content.Open() - ); - } + //if (albedoChannel.Value.Texture != null) + //{ + // effect.BaseColourTexture = Texture2D.FromStream( + // graphicsDevice, + // albedoChannel.Value.Texture.PrimaryImage.Content.Open() + // ); + //} - effect.NormalScale = normalChannel.Value.Parameter.X; - } + var parameter = albedoChannel.Value.Parameter; - var occlusionChannel = primitive.Material.FindChannel("Occlusion"); - if (occlusionChannel.HasValue) - { - if (occlusionChannel.Value.Texture != null) - { - effect.OcclusionTexture = Texture2D.FromStream( - graphicsDevice, - occlusionChannel.Value.Texture.PrimaryImage.Content.Open() - ); - } - - effect.OcclusionStrength = occlusionChannel.Value.Parameter.X; - } - - var emissiveChannel = primitive.Material.FindChannel("Emissive"); - if (emissiveChannel.HasValue) - { - if (emissiveChannel.Value.Texture != null) - { - effect.EmissionTexture = Texture2D.FromStream( - graphicsDevice, - emissiveChannel.Value.Texture.PrimaryImage.Content.Open() - ); - } - - var parameter = emissiveChannel.Value.Parameter; - - effect.EmissiveFactor = new Vector3( + effect.Albedo = new Vector3( parameter.X, parameter.Y, parameter.Z ); } - var baseColorChannel = primitive.Material.FindChannel("BaseColor"); - if (baseColorChannel.HasValue) - { - if (baseColorChannel.Value.Texture != null) - { - effect.BaseColourTexture = Texture2D.FromStream( - graphicsDevice, - baseColorChannel.Value.Texture.PrimaryImage.Content.Open() - ); - } - - var parameter = baseColorChannel.Value.Parameter; - - effect.BaseColorFactor = new Vector4( - parameter.X, - parameter.Y, - parameter.Z, - parameter.W - ); - } - var metallicRoughnessChannel = primitive.Material.FindChannel("MetallicRoughness"); if (metallicRoughnessChannel.HasValue) { - if (metallicRoughnessChannel.Value.Texture != null) - { - effect.MetallicRoughnessTexture = Texture2D.FromStream( - graphicsDevice, - metallicRoughnessChannel.Value.Texture.PrimaryImage.Content.Open() - ); - } + //if (metallicRoughnessChannel.Value.Texture != null) + //{ + // effect.MetallicRoughnessTexture = Texture2D.FromStream( + // graphicsDevice, + // metallicRoughnessChannel.Value.Texture.PrimaryImage.Content.Open() + // ); + //} var parameter = metallicRoughnessChannel.Value.Parameter; - effect.MetallicRoughnessValue = new Vector2( - parameter.X, - parameter.Y - ); + effect.Metallic = parameter.X; + effect.Roughness = parameter.Y; } } - effect.Light = new PBRLight( - new Vector3(0.5f, 0.5f, -0.5f), - new Vector3(10f, 10f, 10f) + effect.Albedo = new Vector3(0.5f, 0, 0); + effect.AO = 1f; + + effect.Lights[0] = new PBRLight( + new Vector3(-10f, 10f, 10f), + new Vector3(300f, 300f, 300f) + ); + + effect.Lights[1] = new PBRLight( + new Vector3(10f, 10f, 10f), + new Vector3(300f, 300f, 300f) + ); + + effect.Lights[2] = new PBRLight( + new Vector3(-10f, -10f, 10f), + new Vector3(300f, 300f, 300f) + ); + + effect.Lights[3] = new PBRLight( + new Vector3(10f, -10f, 10f), + new Vector3(300f, 300f, 300f) ); /* FIXME: how to load cube maps from GLTF? */ diff --git a/Smuggler.csproj b/Smuggler.csproj index 59eef25..e5628ef 100644 --- a/Smuggler.csproj +++ b/Smuggler.csproj @@ -8,6 +8,7 @@ Cassandra Lugo and Evan Hemsley 2020 true Smuggler + AnyCPU;x86 @@ -15,7 +16,7 @@ - +