diff --git a/Effects/PBREffect.fx b/Effects/PBREffect.fx index ca66512..1ea26fa 100644 --- a/Effects/PBREffect.fx +++ b/Effects/PBREffect.fx @@ -57,7 +57,7 @@ PixelShaderInput main_vs(VertexShaderInput input) output.TexCoord = input.TexCoord; output.PositionWS = mul(input.Position, World).xyz; - output.NormalWS = mul(input.Normal, WorldInverseTranspose); + output.NormalWS = mul(input.Normal, (float3x3)WorldInverseTranspose).xyz; output.Position = mul(input.Position, WorldViewProjection); return output; @@ -103,6 +103,24 @@ float GeometrySmith(float3 N, float3 V, float3 L, float roughness) return ggx1 * ggx2; } +// Easy trick to get tangent-normals to world-space to keep PBR code simplified. +float3 GetNormalFromMap(float3 worldPos, float2 texCoords, float3 normal) +{ + float3 tangentNormal = SAMPLE_TEXTURE(NormalTexture, texCoords).xyz * 2.0 - 1.0; + + float3 Q1 = ddx(worldPos); + float3 Q2 = ddy(worldPos); + float2 st1 = ddx(texCoords); + float2 st2 = ddy(texCoords); + + float3 N = normalize(normal); + float3 T = normalize(Q1*st2.y - Q2*st1.y); + float3 B = -normalize(cross(N, T)); + float3x3 TBN = float3x3(T, B, N); + + return normalize(mul(tangentNormal, TBN)); +} + // The case where we have no texture maps for any PBR data float4 None(PixelShaderInput input) : SV_TARGET0 { @@ -258,11 +276,63 @@ float4 AlbedoMetallicRoughnessMapPS(PixelShaderInput input) : SV_TARGET return float4(color, 1.0); } +float4 AlbedoMetallicRoughnessNormalMapPS(PixelShaderInput input) : SV_TARGET +{ + float3 albedo = pow(SAMPLE_TEXTURE(AlbedoTexture, input.TexCoord), 2.2).rgb; + float2 metallicRoughness = SAMPLE_TEXTURE(MetallicRoughnessTexture, input.TexCoord).rg; + float metallic = metallicRoughness.r; + float roughness = metallicRoughness.g; + + float3 N = GetNormalFromMap(input.PositionWS, input.TexCoord, input.NormalWS); + float3 V = normalize(EyePosition - input.PositionWS); + + float3 F0 = float3(0.04, 0.04, 0.04); + F0 = lerp(F0, albedo, metallic); + + float3 Lo = float3(0.0, 0.0, 0.0); + + for (int i = 0; i < 4; i++) + { + float3 lightDir = LightPositions[i] - input.PositionWS; + float3 L = normalize(lightDir); + float3 H = normalize(V + L); + + float distance = length(lightDir); + float attenuation = 1.0 / (distance * distance); + float3 radiance = LightColors[i] * attenuation; + + float NDF = DistributionGGX(N, H, roughness); + float G = GeometrySmith(N, V, L, roughness); + float3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + + 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); + + float3 kS = F; + float3 kD = float3(1.0, 1.0, 1.0) - kS; + + kD *= 1.0 - metallic; + + float NdotL = max(dot(N, L), 0.0); + Lo += (kD * albedo / PI + specular) * radiance * NdotL; + } + + float3 ambient = float3(0.03, 0.03, 0.03) * albedo * AO; + float3 color = ambient + Lo; + + 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 AlbedoMetallicRoughnessMapPS(); + PixelShader = compile ps_3_0 AlbedoMetallicRoughnessNormalMapPS(); } } diff --git a/Effects/PBREffect.fxb b/Effects/PBREffect.fxb index 217a521..f901f3e 100644 Binary files a/Effects/PBREffect.fxb and b/Effects/PBREffect.fxb differ diff --git a/Importer.cs b/Importer.cs index 97f8a03..adc5dfb 100644 --- a/Importer.cs +++ b/Importer.cs @@ -141,19 +141,17 @@ namespace Smuggler if (primitive.Material != null) { - //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 normalChannel = primitive.Material.FindChannel("Normal"); + if (normalChannel.HasValue) + { + if (normalChannel.Value.Texture != null) + { + effect.NormalTexture = Texture2D.FromStream( + graphicsDevice, + normalChannel.Value.Texture.PrimaryImage.Content.Open() + ); + } + } //var occlusionChannel = primitive.Material.FindChannel("Occlusion"); //if (occlusionChannel.HasValue) @@ -218,7 +216,6 @@ namespace Smuggler graphicsDevice, metallicRoughnessChannel.Value.Texture.PrimaryImage.Content.Open() ); - System.Console.WriteLine(effect.MetallicRoughnessTexture.Width); } var parameter = metallicRoughnessChannel.Value.Parameter; @@ -416,7 +413,6 @@ namespace Smuggler uint[] indices, Vector3[] positions ) { - System.Console.WriteLine("normals"); var normals = Normals(primitive); var texcoords = TexCoords(primitive);