public static const float PI = 3.14159265f; float TrowbridgeReitzGGX(float3 normal, float3 halfway, float roughness) { float alpha = roughness * roughness; float alpha2 = alpha * alpha; float nDotH = max(dot(normal, halfway), 0.0f); float nDotH2 = nDotH * nDotH; float numerator = alpha2; float denominator = nDotH2 * (alpha2 - 1.0f) + 1.0f; denominator = PI * denominator * denominator; return numerator / denominator; } float GeometrySchlickGGX(float nDotV, float roughness) { float r = roughness + 1.0f; float k = (r * r) / 8.0f; float numerator = nDotV; float denominator = nDotV * (1.0f - k) + k; return numerator / denominator; } float GeometrySmith(float nDotV, float nDotL, float roughness) { float ggx1 = GeometrySchlickGGX(nDotV, roughness); float ggx2 = GeometrySchlickGGX(nDotL, roughness); return ggx1 * ggx2; } // https://en.wikipedia.org/wiki/Schlick%27s_approximation float3 FresnelSchlick(float cosine, float3 f_0) { return f_0 + (1.0f - f_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. } // Sebastian Lagarde float3 FresnelSchlickRoughness(float cosine, float3 f_0, float roughness) { return f_0 + (max((1.0f - roughness).xxx, f_0) - f_0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts. } float3 GetPBRContrib( float3 albedo, float3 radiance, float3 viewDir, float3 normal, float3 lightDir, float2 metalRough, float3 f_0) { float3 halfway = normalize(viewDir + lightDir); float cosineFactor = max(dot(halfway, viewDir), 0.0f); float nDotV = max(dot(normal, viewDir), 0.0f); float nDotL = max(dot(normal, lightDir), 0.0f); float normalDistribution = TrowbridgeReitzGGX(normal, halfway, metalRough.y); float geometry = GeometrySmith(nDotV, nDotL, metalRough.y); float3 fresnel = FresnelSchlickRoughness(cosineFactor, f_0, metalRough.y); float3 numerator = (normalDistribution * geometry) * fresnel; float denominator = 4.0f * nDotV * nDotL; float3 specular = numerator / (denominator + 0.00001f); float3 kSpecular = fresnel; float3 kDiffuse = 1.0f - kSpecular; kDiffuse *= 1.0f - metalRough.x; return nDotL * radiance * (kDiffuse * albedo / PI + specular); } public struct PointLight { public float3 position; public float range; public float3 color; float attenuation; public float3 getInfluence(float3 albedo, float2 metalRough, float3 viewDir, float3 worldPosition, float3 normal, float3 f_0) { float3 lightDir = float3(position) - worldPosition; float lightDistance = length(lightDir); if (lightDistance > range) return 0.0f.xxx; lightDir /= lightDistance; // Normalization // Color Unpack //float R = (Light.Color & 0xFF000000) >> 24; //float G = (Light.Color & 0x00FF0000) >> 16; //float B = (Light.Color & 0x0000FF00) >> 8; //float3 LightColor = Light.Intensity * float3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255 let attenuation_ = attenuation / (lightDistance * lightDistance); let radiance = color * attenuation_; return GetPBRContrib(albedo, radiance, viewDir, normal, lightDir, metalRough, f_0); } };