Oren-Nayar BRDF with Maps

Oren-Nayard diffuse, SchlickGGX specular

Shader code
shader_type spatial;
render_mode blend_mix, depth_draw_opaque, cull_back, specular_schlick_ggx;


group_uniforms transparancy;
uniform int cull_mode = 0; // 0 = Disabled | 1 = Back | 2 = Front

uniform int transparency_mode = 0; // 0 = Disabled | 1 = Alpha | 2 = Alpha Scissor | 3 = Alpha Hash | 4 = Depth Pre-pass
uniform float alpha_scissor_threshold :hint_range(0.0, 1.0) = 0.5;
uniform float alpha_hash_scale :hint_range(0.1, 10.0) = 1.0;

group_uniforms albedo;
uniform vec4 baseColor : source_color = vec4(1.0, 1.0, 1.0, 1.0);
//uniform float baseColor_strength : hint_range(0.0, 1.0) = 1.0;

uniform sampler2D albedo_texture :source_color, filter_linear_mipmap_anisotropic, repeat_enable;

group_uniforms roughness;
uniform float roughness : hint_range(0.0, 8.0) = 1.0;
uniform sampler2D roughness_texture :hint_roughness_r, filter_linear_mipmap_anisotropic, repeat_enable;
uniform int roughness_texture_channel : hint_range(0, 4) = 0; // 0 = Red | 1 = Green | 2 = Blue | 3 = Alpha | 4 = Gray

group_uniforms metallic;
uniform float metallic : hint_range(0.0, 8.0) = 0.0;
uniform sampler2D metallic_texture :hint_default_white, filter_linear_mipmap_anisotropic, repeat_enable;
uniform int metallic_texture_channel : hint_range(0, 4) = 0; // 0 = Red | 1 = Green | 2 = Blue | 3 = Alpha | 4 = Gray

group_uniforms specular;
uniform float specular : hint_range(0.0, 8.0) = 0.5;
uniform sampler2D specular_texture :hint_default_white, filter_linear_mipmap_anisotropic, repeat_enable;
uniform float specularTint : hint_range(0.0, 8.0) = 0.0;
uniform int specular_texture_channel : hint_range(0, 4) = 0; // 0 = Red | 1 = Green | 2 = Blue | 3 = Alpha | 4 = Gray

group_uniforms normal_map;
uniform sampler2D normal_texture : hint_roughness_normal, filter_linear_mipmap_anisotropic, repeat_enable;
uniform float normal_scale :hint_range(-8.0, 8.0) = 1.0;

group_uniforms ambient_occlusion;
uniform sampler2D ao_texture :hint_default_white, filter_linear_mipmap_anisotropic, repeat_enable;
uniform int ao_texture_channel : hint_range(0, 4) = 0; // 0 = Red | 1 = Green | 2 = Blue | 3 = Alpha | 4 = Gray
uniform float ao_light_affect : hint_range(0.0, 1.0, 0.01);

group_uniforms emission;
uniform float emission_energy :hint_range(0.0, 2048.0) = 0.0;
uniform vec3 emission_color : source_color = vec3(0.0);
uniform sampler2D emission_texture :source_color, hint_default_black, filter_linear_mipmap_anisotropic, repeat_enable;

//group_uniforms subsurface;
//uniform float subsurface : hint_range(0.0, 8.0) = 0.0;

group_uniforms anisotropic;
uniform float anisotropic : hint_range(0.0, 8.0) = 0.0;

group_uniforms clearcoat;
uniform float clearcoat : hint_range(0.0, 8.0) = 0.0;
uniform float clearcoatGloss : hint_range(0.0, 8.0) = 1.0;

group_uniforms UV1;
uniform bool uv1_triplanar = false;
varying vec3 uv1_triplanar_pos;

uniform float uv1_blend_sharpness : hint_range(0.0, 150.0, 0.001) = 1.0;
varying vec3 uv1_power_normal;

uniform vec3 uv1_offset = vec3(0.0);
uniform vec3 uv1_scale = vec3(1.0);

group_uniforms UV2;
uniform vec3 uv2_offset = vec3(0.0);
uniform vec3 uv2_scale = vec3(1.0);

group_uniforms transform;
uniform bool use_z_clip_scale = false;
uniform float z_clip_scale : hint_range(0.01, 1.0) = 1.0;
uniform bool use_fov_override = false;
uniform float fov_override : hint_range(1.0, 179.0, 0.1) = 75.0;

varying vec3 v_albedo;
varying float v_metallic;
varying float v_roughness;
varying float v_specular;

varying vec3 v_tangent;
varying vec3 v_binormal;

group_uniforms EXPERIMENTAL;
/** You can use this F0 instead of specular approach if you set dielectric_f0_scale = 1.0 and specular = 1.0. F0 = ((IOR - 1.0) / (IOR + 1.0))^2. IOR - Index of Refraction. IOR Tables: https://pixelandpoly.com/ior.html */ 
uniform float dielectric_f0 = 1.0;
uniform float dielectric_f0_scale = 0.08;
//uniform float subsurface_scale = 1.25;
/** F0 = ((IOR - 1.0) / (IOR + 1.0))^2. IOR - Index of Refraction. IOR Tables: https://pixelandpoly.com/ior.html */
uniform float clearcoat_f0 = 0.04;
uniform float clearcoat_distribution = 0.25;
uniform float mon2lin_factor = 0.5;
uniform float NdotH_factor = 0.000;
uniform float _Sigma : hint_range(0.0, 90.0, 1e-3) = 30.0;


const float PI2 = 9.86960440108935799230;
const float INV_PI = 0.31830988618379067154;

//////////////////////////////////////////////////////


void vertex() {
	UV = UV * uv1_scale.xy + uv1_offset.xy;
	
	
	// UV1 Triplanar: Enabled
	uv1_power_normal = pow(abs(NORMAL), vec3(uv1_blend_sharpness));
	uv1_triplanar_pos = VERTEX * uv1_scale + uv1_offset;
	uv1_power_normal /= dot(uv1_power_normal, vec3(1.0));
	uv1_triplanar_pos *= vec3(1.0, -1.0, 1.0);
	
	
	if (use_z_clip_scale) {
		Z_CLIP_SCALE = z_clip_scale;
	}
	
	if (use_fov_override) {
		if (!IN_SHADOW_PASS)
		{
			float flip_y = sign(PROJECTION_MATRIX[1][1]);
			float aspect = PROJECTION_MATRIX[1][1] / PROJECTION_MATRIX[0][0];
			float f = flip_y / tan(fov_override * PI / 360.0);
			PROJECTION_MATRIX[0][0] = f / aspect;
			PROJECTION_MATRIX[1][1] = f;
		}
	}
}


float sqr(float x)
{
	return x * x;
}


float SchlickFresnel(float u) 
{
	float m = clamp(1.0 - u, 0.0, 1.0);
	float m2 = m * m;
	
	return m2 * m2 * m;
}


float GTR1(float NdotH, float a)
{
	if (a >= 1.0) { return 1.0 / PI; }
	
	float a2 = a * a;
	float t = 1.0 + (a2 - 1.0) * NdotH * NdotH;
	
	return (a2 - 1.0) / (PI * log(a2) * t);
}


float GTR2_aniso(float NdotH, float HdotX, float HdotY, float ax, float ay)
{
	return 1.0 / (PI * ax * ay * sqr(sqr(HdotX / ax) + sqr(HdotY / ay) + NdotH * NdotH));
}


float smithG_GGX(float NdotV, float alphaG)
{
	float a = alphaG * alphaG;
	float b = NdotV * NdotV;
	
	return 1.0 / (NdotV + sqrt(a + b - a * b));
}


float smithG_GGX_aniso(float NdotV, float VdotX, float VdotY, float ax, float ay)
{
	return 1.0 / (NdotV + sqrt(sqr(VdotX * ax) + sqr(VdotY * ay) + sqr(NdotV)));
}


vec3 mon2lin(vec3 x)
{
	return vec3(pow(x[0], mon2lin_factor), pow(x[1], mon2lin_factor), pow(x[2], mon2lin_factor));//x;//pow(x, vec3(0.5));
}


float hash(vec2 p)
{
	p = fract(p * vec2(123.34, 345.45));
	p += dot(p, p + 34.345);
	
	return fract(p.x * p.y);
}


float get_texture_channel(vec4 tex, int channel)
{
	if (channel == 0) { return tex.r; }
	else if (channel == 1) { return tex.g; }
	else if (channel == 2) { return tex.b; }
	else if (channel == 3) { return tex.a; }
	return dot(tex.rgb, vec3(0.299, 0.587, 0.114)); // Gray
}


vec4 triplanar_texture(sampler2D p_sampler, vec3 p_weights, vec3 p_triplanar_pos)
{
	vec4 samp = vec4(0.0);
	samp += texture(p_sampler, p_triplanar_pos.xy) * p_weights.z;
	samp += texture(p_sampler, p_triplanar_pos.xz) * p_weights.y;
	samp += texture(p_sampler, p_triplanar_pos.zy * vec2(-1.0, 1.0)) * p_weights.x;
	return samp;
}


void fragment() {
	// CULL MODE
	
	if (cull_mode == 1 && !FRONT_FACING) { discard; }
	if (cull_mode == 2 && FRONT_FACING) { discard; }
	
	// TEXTURES
	
	vec4 albedo_tex;
	float roughness_tex;
	float metallic_tex;
	float ao_tex;
	float specular_tex;
	vec3 emission_tex;
	
	if (uv1_triplanar)
	{
		albedo_tex = triplanar_texture(albedo_texture, uv1_power_normal, uv1_triplanar_pos);
		roughness_tex = get_texture_channel(triplanar_texture(roughness_texture, uv1_power_normal, uv1_triplanar_pos), roughness_texture_channel);
		metallic_tex = get_texture_channel(triplanar_texture(metallic_texture, uv1_power_normal, uv1_triplanar_pos), metallic_texture_channel);
		ao_tex = get_texture_channel(triplanar_texture(ao_texture, uv1_power_normal, uv1_triplanar_pos), ao_texture_channel);
		specular_tex = get_texture_channel(triplanar_texture(specular_texture, uv1_power_normal, uv1_triplanar_pos), specular_texture_channel);
		emission_tex = triplanar_texture(emission_texture, uv1_power_normal, uv1_triplanar_pos).rgb;
		NORMAL_MAP = triplanar_texture(normal_texture, uv1_power_normal, uv1_triplanar_pos).rgb;
	}
	else
	{
		albedo_tex = texture(albedo_texture, UV);
		roughness_tex = get_texture_channel(texture(roughness_texture, UV), roughness_texture_channel);
		metallic_tex = get_texture_channel(texture(metallic_texture, UV), metallic_texture_channel);
		ao_tex = get_texture_channel(texture(ao_texture, UV), ao_texture_channel);
		specular_tex = texture(specular_texture, UV).r;
		emission_tex = texture(emission_texture, UV).rgb;
		NORMAL_MAP = texture(normal_texture, UV).rgb;
	}
	
	NORMAL_MAP_DEPTH = normal_scale;
	
	// FINAL VALUES
	
	vec4 final_base = baseColor * albedo_tex;
	
	//vec3 albedo = texture(albedo_texture, UV).rgb;
	//vec3 final_rgb = baseColor.rgb * albedo;
	//vec4 final_base = vec4(final_rgb, texture(albedo_texture, UV).a * baseColor.a);
	
	float final_roughness = roughness * roughness_tex;
	float final_metallic = metallic * metallic_tex;
	float final_specular = specular * specular_tex;

	v_albedo = final_base.rgb;
	v_metallic = final_metallic;
	v_roughness = final_roughness;
	v_specular = final_specular;
	v_tangent = TANGENT;
	v_binormal = BINORMAL;
	
	
	// TRANSPARENCY
	
	float alpha = final_base.a;
	
	if (transparency_mode == 0) { ALPHA = 1.0; } // Disabled
	
	else if (transparency_mode == 1){ ALPHA = alpha; } // Alpha
	
	else if (transparency_mode == 2) // Alpha Scissor
	{
		ALPHA_SCISSOR_THRESHOLD = alpha_scissor_threshold;
		ALPHA = alpha;
	}
	
	else if (transparency_mode == 3) // Hash
	{
		float h = hash(FRAGCOORD.xy * alpha_hash_scale);
		
		if (alpha < h) { discard; }
		ALPHA = 1.0;
	}
	else if (transparency_mode == 4) { // Depth Pre-pass
		ALPHA_HASH_SCALE = alpha_hash_scale;
		ALPHA = alpha;
	}
	
	// OUTPUT
	
	ALBEDO = final_base.rgb;
	METALLIC = final_metallic;
	ROUGHNESS = final_roughness;
	SPECULAR = final_specular;
	ANISOTROPY = anisotropic;
	CLEARCOAT = clearcoat;
	CLEARCOAT_ROUGHNESS = 1.0 - clearcoatGloss;
	AO = ao_tex;
	EMISSION = emission_tex * emission_color * emission_energy;
	AO_LIGHT_AFFECT = ao_light_affect;
}

vec3 diffuse_full_oren_nayar(
    in vec3 n,
    in vec3 l,
    in vec3 v,
    in vec3 rho,
    in float sigma
)
{
    float NdotL = dot(n, l); // cos(theta_l) == cos(theta_i).

    if(NdotL < 0.0)
    {
        return vec3(0.0);
    }

    float NdotV = min(max(dot(n, v), 1e-3), 1.0); // cos(theta_v) == cos(theta_r).

    sigma = sigma * PI / 180.0;
    float sigma2 = sigma * sigma;

    float theta_i = acos(NdotL);
    float theta_r = acos(NdotV);

    vec3 l_proj = normalize(l - n * NdotL);
    vec3 v_proj = normalize(v - n * NdotV);

    float cos_phi = dot(v_proj, l_proj);

    float alpha = max(theta_i, theta_r);
    float beta = min(theta_i, theta_r);

    float C1 = 1.0 - 0.5 * sigma2 / (sigma2 + 0.33);

    float C2 = 0.45 * sigma2 / (sigma2 + 0.09);
    if(cos_phi >= 0.0)
    {
        C2 *= sin(alpha);
    }
    else
    {
        C2 *= sin(alpha) - pow( 2.0 * beta / PI, 3.0);
    }

    float C3 = 0.125 * sigma2 / (sigma2 + 0.09) * pow((4.0 * alpha * beta) / PI2, 2.0);

    vec3 L1 = vec3(INV_PI * (C1 + cos_phi * C2 * tan(beta) + (1.0 - abs(cos_phi)) * C3 * tan((alpha + beta) / 2.0)));
    vec3 L2 = 0.17 * rho * INV_PI * sigma2 / (sigma2 + 0.13) * (1.0 - cos_phi * (4.0 * beta * beta) / PI2);

    return (L1 + L2) * NdotL;
}


void light() {
	vec3 N = normalize(NORMAL);
	vec3 V = normalize(VIEW);
	//vec3 up = abs(N.y) < 0.999 ? vec3(0.0, 1.0, 0.0) : vec3(1.0, 0.0, 0.0);
	vec3 X = normalize(v_tangent); //normalize(cross(up, N)));
	vec3 Y = normalize(v_binormal); //normalize(cross(N, X)));
	
	vec3 Cdlin = mon2lin(v_albedo);
	float Cdlum = 0.3 * Cdlin.r + 0.6 * Cdlin.g + 0.1 * Cdlin.b; // luminance approx.
	
	vec3 Ctint = Cdlum > 0.0 ? Cdlin / Cdlum : vec3(1.0); // normalize lum. to isolate hue+sat
	vec3 Cspec0 = mix(v_specular * dielectric_f0_scale * dielectric_f0 * mix(vec3(1.0), Ctint, specularTint), Cdlin, v_metallic);
	
	float NdotV = max(dot(N, V), 0.0001);
	vec3 L = normalize(LIGHT);//normalize(vec3(0.4, 1.0, 0.2));
	float NdotL = max(dot(N, L), 0.0000);
	vec3 H = normalize(L + V);
	float NdotH = max(dot(N, H), NdotH_factor);//max(dot(N, H), 0.0000);
	float LdotH = max(dot(L, H), 0.0000);
	
	//// Diffuse fresnel - go from 1 at normal incidence to .5 at grazing
	//// and mix in diffuse retro-reflection based on roughness
	//float FL = SchlickFresnel(NdotL);
	//float FV = SchlickFresnel(NdotV);
	//
	//// Based on Hanrahan-Krueger brdf approximation of isotropic bssrdf
	//// 1.25(subsurface_scale) scale is used to (roughly) preserve albedo
	//// Fss90 used to "flatten" retroreflection based on roughness
	//float Fss90 = LdotH * LdotH * v_roughness;
	//float Fss = mix(1.0, Fss90, FL) * mix(1.0, Fss90, FV);
	//float ss = subsurface_scale * (Fss * (1.0 / max(NdotL + NdotV, 0.0001) - 0.5) + 0.5);
	
	
	// Specular
	float aspect = sqrt(1.0 - anisotropic * 0.9);
	float ax = max(0.001, sqr(v_roughness) / aspect);
	float ay = max(0.001, sqr(v_roughness) * aspect);
	float Ds = GTR2_aniso(NdotH, dot(H, X), dot(H, Y), ax, ay);
	float FH = SchlickFresnel(LdotH);
	vec3 Fs = mix(Cspec0, vec3(1.0), FH);
	float Gs = smithG_GGX_aniso(NdotL, dot(L, X), dot(L, Y), ax, ay);
	Gs *= smithG_GGX_aniso(NdotV, dot(V, X), dot(V, Y), ax, ay);
	
	
	// Clearcoat
	float Dr = GTR1(NdotH, mix(0.1, 0.001, clearcoatGloss));
	float Fr = mix(clearcoat_f0, 1.0, FH);
	float Gr = smithG_GGX(NdotL, clearcoat_distribution) * smithG_GGX(NdotV, clearcoat_distribution);
	
	
	// Final Specular
	vec3 specularTerm = Gs * Fs * Ds;
	vec3 clearcoatTerm = vec3(0.25 * clearcoat * Gr * Fr * Dr);
	vec3 specularLight = (specularTerm + clearcoatTerm) * LIGHT_COLOR * ATTENUATION * NdotL;
	
	vec3 fd = diffuse_full_oren_nayar(N, L, V, ALBEDO, _Sigma);
	vec3 radiance = LIGHT_COLOR * ATTENUATION;
	
	DIFFUSE_LIGHT += radiance * fd;
	SPECULAR_LIGHT += specularLight;
}
Live Preview
Tags
brdf oren-nayar
The shader code and all code snippets in this post are under CC0 license and can be used freely without the author's permission. Images and videos, and assets depicted in those, do not fall under this license. For more info, see our License terms.

More from Skril4Ek

Related shaders

guest

0 Comments
Oldest
Newest Most Voted