// Physically Based Rendering // Copyright (c) 2017-2018 MichaƂ Siejak // Physically Based shading model: Lambetrtian diffuse BRDF + Cook-Torrance microfacet specular BRDF + IBL for ambient. // This implementation is based on "Real Shading in Unreal Engine 4" SIGGRAPH 2013 course notes by Epic Games. // See: http://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf static const float PI = 3.141592; static const float Epsilon = 0.00001; static const uint NumLights = 3; // Constant normal incidence Fresnel factor for all dielectrics. static const float3 Fdielectric = 0.04; // UNIFORM CONSTANTS float4x4 viewProjectionMatrix; float4x4 sceneRotationMatrix; float3 direction[NumLights]; float3 radiance[NumLights]; float3 eyePosition; int specularTextureLevels; struct VertexShaderInput { float3 position : POSITION; float3 normal : NORMAL; float3 tangent : TANGENT; float3 binormal : BINORMAL; float2 texcoord : TEXCOORD; }; struct PixelShaderInput { float4 pixelPosition : SV_POSITION; float3 position : POSITION1; float2 texcoord : TEXCOORD0; float3 T : TEXCOORD1; float3 B : TEXCOORD2; float3 N : TEXCOORD3; }; sampler albedoTexture : register(s0); sampler normalTexture : register(s1); sampler metalnessTexture : register(s2); sampler roughnessTexture : register(s3); sampler specularTexture : register(s4); sampler irradianceTexture : register(s5); sampler specularBRDF_LUT : register(s6); // GGX/Towbridge-Reitz normal distribution function. // Uses Disney's reparametrization of alpha = roughness^2. float ndfGGX(float cosLh, float roughness) { float alpha = roughness * roughness; float alphaSq = alpha * alpha; float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0; return alphaSq / (PI * denom * denom); } // Single term for separable Schlick-GGX below. float gaSchlickG1(float cosTheta, float k) { return cosTheta / (cosTheta * (1.0 - k) + k); } // Schlick-GGX approximation of geometric attenuation function using Smith's method. float gaSchlickGGX(float cosLi, float cosLo, float roughness) { float r = roughness + 1.0; float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights. return gaSchlickG1(cosLi, k) * gaSchlickG1(cosLo, k); } // Shlick's approximation of the Fresnel factor. float3 fresnelSchlick(float3 F0, float cosTheta) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } // Vertex shader PixelShaderInput main_vs(VertexShaderInput vin) { PixelShaderInput vout; vout.position = mul(sceneRotationMatrix, float4(vin.position, 1.0)).xyz; vout.texcoord = float2(vin.texcoord.x, 1.0-vin.texcoord.y); // Pass tangent space basis vectors (for normal mapping). float3 worldTangent = mul(vin.tangent, (float3x3) sceneRotationMatrix); vout.T = normalize(worldTangent); float3 worldBinormal = mul(vin.binormal, (float3x3) sceneRotationMatrix); vout.B = normalize(worldBinormal); float3 worldNormal = mul(vin.normal, (float3x3) sceneRotationMatrix); vout.N = normalize(worldNormal); float4x4 mvpMatrix = mul(viewProjectionMatrix, sceneRotationMatrix); vout.pixelPosition = mul(mvpMatrix, float4(vin.position, 1.0)); return vout; } // Pixel shader float4 main_ps(PixelShaderInput pin) : SV_Target0 { // Sample input textures to get shading model params. float3 albedo = tex2D(albedoTexture, pin.texcoord).rgb; float metalness = tex2D(metalnessTexture, pin.texcoord).r; float roughness = tex2D(roughnessTexture, pin.texcoord).r; // Outgoing light direction (vector from world-space fragment position to the "eye"). float3 Lo = normalize(eyePosition - pin.position); // tranpose to transform tangent space => world float3x3 TBN = transpose(float3x3(normalize(pin.T), normalize(pin.B), normalize(pin.N))); // Get current fragment's normal and transform to world space. float3 tangentNormal = normalize(2.0 * tex2D(normalTexture, pin.texcoord).rgb - 1.0); // world normal float3 N = normalize(mul(TBN, tangentNormal)); // Angle between surface normal and outgoing light direction. float cosLo = max(0.0, dot(N, Lo)); // Specular reflection vector. float3 Lr = 2.0 * cosLo * N - Lo; // Fresnel reflectance at normal incidence (for metals use albedo color). float3 F0 = lerp(Fdielectric, albedo, metalness); // Direct lighting calculation for analytical lights. float3 directLighting = 0.0; for(uint i=0; i