analytic ::begin parameters color Diffuse 1 0 0 color Specular 1 1 1 float DiffuseScale 0 1 0.5 float SpecularScale 0 1 .028 float Roughness 0.00005 2 0.2 float Roughness2 0.00005 2 0.2 float Retroreflective 0 1 0 bool FullFresnel 0 1 0 bool isotropic 1 ::end parameters ::begin shader //Thanks to Chris_F for starting ideas. // Cook-Torrance, GGX distribution (bastardised with ashikhman shirley), Smith geometry, qualitative Oren-Nayar diffuse. float saturate(float x) { return clamp(x,0,1); } vec3 Fresnel(float CosTheta, vec3 Ks) { CosTheta = min(CosTheta, 0.99999999999999999); vec3 n2 = (1.0 + sqrt(Ks)) / (1.0 - sqrt(Ks)); vec3 SinTheta = vec3(sqrt(1 - CosTheta * CosTheta)); vec3 SinThetaT = SinTheta / n2; vec3 CosThetaT = sqrt(1 - SinThetaT * SinThetaT); vec3 n2CosThetaT = n2 * CosThetaT; vec3 n2CosTheta = n2 * CosTheta; vec3 RsSqrt = (CosTheta - n2CosThetaT) / (CosTheta + n2CosThetaT); vec3 Rs = RsSqrt * RsSqrt; vec3 RpSqrt = (n2CosTheta - CosThetaT) / (n2CosTheta + CosThetaT); vec3 Rp = RpSqrt * RpSqrt; return (Rs + Rp) / 2; } vec3 BRDF( vec3 L, vec3 V, vec3 N, vec3 X, vec3 Y ) { //vec3 temp = L; L = V; V = temp; float PI = 3.14159265358979323846; vec3 Kd = Diffuse * DiffuseScale; vec3 Ks = Specular * SpecularScale; vec3 H = normalize(L + V); float NdotL = clamp(dot(N, L), 0, 1); float NdotV = dot(N, V); float NdotH = dot(N, H); float LdotV = dot(L, V); float VdotH = dot(V, H); float LdotH = dot(L, H); float HdotX = dot(H,X); float HdotY = dot(H,Y); float m1 = 1/Roughness; float m2 = 1/Roughness2; float M = isotropic ? Roughness : (1-(NdotH*NdotH)) / ( m1*HdotX*HdotX + m2*HdotY*HdotY ); float M_2 = isotropic ? M * M : M* min(Roughness, Roughness2); NdotH = mix( mix( NdotH, max(NdotH,LdotV), saturate(Retroreflective*2) ), LdotV, saturate(Retroreflective*2-1) ); float NdotL_2 = NdotL * NdotL; float NdotV_2 = NdotV * NdotV; float NdotH_2 = NdotH * NdotH; float OneMinusNdotL_2 = 1.0 - NdotL_2; float OneMinusNdotV_2 = 1.0 - NdotV_2; vec3 Fd = 1.0 - Ks; float gamma = clamp(dot(V - N * NdotV, L - N * NdotL), 0, 1); float A = 1.0 - 0.5 * (M_2 / (M_2 + 0.33)); float B = 0.45 * (M_2 / (M_2 + 0.09)); float C = sqrt(OneMinusNdotL_2 * OneMinusNdotV_2) / max(NdotL, NdotV); vec3 Rd = Kd * Fd * (A + B * gamma * C) * 1/PI; float ggxDenom = NdotH_2 * (M_2 + (1-NdotH_2)/NdotH_2); float D = M/ggxDenom; D = D*D/PI; D *= isotropic ? 1 : min(m1,m2)/max(m1,m2); vec3 Fs; if( FullFresnel ) Fs = Fresnel(NdotV, Ks); else Fs = Ks + Fd * pow(1-LdotH, 5); float G1_1 = 1.0 + sqrt(1.0 + M_2 * (OneMinusNdotL_2 / NdotL_2)); float G1_2 = 1.0 + sqrt(1.0 + M_2 * (OneMinusNdotV_2 / NdotV_2)); float G = ((2/G1_1) * (2/G1_2))/(4 * NdotV * NdotL); vec3 Rs = Fs * D * G; return vec3(Rd + Rs); } ::end shader