oren-nayer lighting (godot 3.x)
done for practice
oren-nayer lighting model, credit goes to these sites for helping me out (especially Fabrizio Spindola of the gdshader bible)
https://www.linkedin.com/in/fabrizioespindola/
https://www.jordanstevenstechart.com/lighting-models
https://developer.download.nvidia.com/cg/saturate.html
ElSuicio corrected me, you can see the corrected code here
https://godotshaders.com/shader/oren-nayer-lighting/#comment-4048
Shader code
shader_type spatial;
render_mode ambient_light_disabled;
render_mode skip_vertex_transform;
varying vec4 vector_ws;
varying vec4 vector_os;
float saturate_f(float x)
{
return max(0,min(1,x));
}
vec3 saturate_v3(vec3 value)
{
vec3 result;
result.x = max(0,min(1,value.x));
result.y= max(0,min(1,value.y));
result.z== max(0,min(1,value.z));
return result;
}
void vertex()
{
vector_os.xyz = VERTEX;
vector_ws= WORLD_MATRIX* vec4(vector_os.xyz,1.0);
vec4 vertex_vs = INV_CAMERA_MATRIX*vector_ws ; //INV_CAMERA_MATRIX in godot 3.5 is equivalent to VIEW_MATRIX in godot 4
VERTEX = vertex_vs.xyz;
}
void light()
{
vec3 i = ATTENUATION;
vec3 l = LIGHT;
vec3 n = NORMAL;
vec3 viewDirection= normalize(CAMERA_MATRIX[3]-vector_ws).xyz;
float roughness=ROUGHNESS;
float roughnessSqr = roughness *roughness;
vec3 o_n_fraction = roughnessSqr / (roughnessSqr+vec3(0.33,0.13,0.09));
vec3 oren_nayer = vec3(1,0,0)+ vec3(-0.3,0.17,0.45)+o_n_fraction;
float cos_ndotl = saturate_f(dot(n,l));
float cos_ndotv = saturate_f(dot(n,viewDirection));
float oren_nayar_s = saturate_f(dot(l,viewDirection)) - cos_ndotl * cos_ndotv;
oren_nayar_s /= mix(max(cos_ndotl,cos_ndotv),1,step(oren_nayar_s,0));
vec3 lightingModel = ALBEDO* cos_ndotl * (oren_nayer.x+ALBEDO*oren_nayer.y+oren_nayer.z*oren_nayar_s);
vec3 attenColor = i * LIGHT_COLOR;
vec4 finalDiffuse = vec4(lightingModel*attenColor,1);
vec3 nDotL = max(0.0, dot(n, l)) * i;
vec3 vDotL = max(0.0, dot(n, l)) * i;
DIFFUSE_LIGHT += finalDiffuse.xyz;
}

The full Oren-Nayar model is based on angles, not roughness. I recommend you read this article: A tiny improvement of Oren-Nayar reflectance model
This page contains an interactive example: Visual Shader Full-Oren-Nayar Light Model Node – Godot 4.4+ by InterDreamSoft
I think this might be useful to you:
// ElSuicio, 2025. // GODOT v4.5.1.stable. // x.com/ElSuicio // github.com/ElSuicio // Contact email [interdreamsoft@gmail.com] shader_type spatial; render_mode skip_vertex_transform; #define TRANSLATION_MATRIX(t) mat4(\ vec4(1, 0, 0, t.x),\ vec4(0, 1, 0, t.y),\ vec4(0, 0, 1, t.z),\ vec4(0, 0, 0, 1)\ ); #define ROTATION_MATRIX_X(a) mat4(\ vec4(1, 0, 0, 0),\ vec4(0, cos(a), -sin(a), 0),\ vec4(0, sin(a), cos(a), 0),\ vec4(0, 0, 0, 1)\ ); #define ROTATION_MATRIX_Y(b) mat4(\ vec4( cos(b), 0, sin(b), 0),\ vec4(0, 1, 0, 0),\ vec4(-sin(b), 0, cos(b), 0),\ vec4(0, 0, 0, 1)\ ); #define ROTATION_MATRIX_Z(c) mat4(\ vec4(cos(c), -sin(c), 0, 0),\ vec4(sin(c), cos(c), 0, 0),\ vec4(0, 0, 1, 0),\ vec4(0, 0, 0, 1)\ ); #define ESCALATION_MATRIX(s) mat4(\ vec4(s.x, 0, 0, 0),\ vec4(0, s.y, 0, 0),\ vec4(0, 0, s.z, 0),\ vec4(0, 0, 0, 1)\ ); group_uniforms _Transform; uniform vec3 _Position = vec3(0.0); uniform vec3 _Rotation = vec3(0.0); uniform vec3 _Scale = vec3(1.0); group_uniforms _FullOrenNayar; uniform vec3 _DiffuseColor : source_color = vec3(1.0); uniform float _Rho : hint_range(0.0, 1.0, 1e-3) = 0.9; uniform float _Sigma : hint_range(0.0, 90.0, 1e-3) = 30.0; varying vec4 _vertex_os; varying vec4 _normal_os; varying vec4 _binormal_os; varying vec4 _tangent_os; mat4 ROTATION_MATRIX(vec3 euler_angles_rad) { mat4 x = ROTATION_MATRIX_X(euler_angles_rad.x) mat4 y = ROTATION_MATRIX_Y(euler_angles_rad.y) mat4 z = ROTATION_MATRIX_Z(euler_angles_rad.z) return y * x * z; } vec3 normalizeLinf(vec3 x) { float norm = max(max(abs(x.x), abs(x.y)), abs(x.z)); return x / norm; } void vertex() { /* Rotation */ vec3 angle_rad = radians(_Rotation); /* Vertex */ _vertex_os = vec4(VERTEX, 1.0); _vertex_os *= ESCALATION_MATRIX(_Scale) _vertex_os *= ROTATION_MATRIX(angle_rad); vec4 vertex_ws = MODEL_MATRIX * _vertex_os; vertex_ws *= TRANSLATION_MATRIX(_Position) vec4 vertex_vs = VIEW_MATRIX * vertex_ws; VERTEX = vertex_vs.xyz; /* Normal */ _normal_os = vec4(NORMAL, 0.0); _normal_os *= ROTATION_MATRIX(angle_rad); vec4 normal_ws = MODEL_MATRIX * _normal_os; vec4 normal_vs = VIEW_MATRIX * normal_ws; NORMAL = normalize(normal_vs.xyz); /* Binormal */ _binormal_os = vec4(BINORMAL, 0.0); _binormal_os *= ROTATION_MATRIX(angle_rad); vec4 binormal_ws = MODEL_MATRIX * _binormal_os; vec4 binormal_vs = VIEW_MATRIX * binormal_ws; BINORMAL = normalize(binormal_vs.xyz); /* Tangent */ _tangent_os = vec4(TANGENT, 0.0); _tangent_os *= ROTATION_MATRIX(angle_rad); vec4 tangent_ws = MODEL_MATRIX * _tangent_os; vec4 tangent_vs = VIEW_MATRIX * tangent_ws; TANGENT = normalize(tangent_vs.xyz); /* Projection */ vec4 vertex_proj = PROJECTION_MATRIX * vertex_vs; POSITION = vertex_proj; } void fragment() { ALBEDO = _DiffuseColor; } void light() { vec3 n = normalize(NORMAL); vec3 l = normalize(LIGHT); vec3 v = normalize(VIEW); float cNdotL = max(dot(n, l), 0.0); float cNdotV = max(dot(n, v), 0.0); // https://dl.acm.org/doi/pdf/10.1145/192161.192213 float sigma2 = pow(_Sigma * PI / 180.0, 2.0); float theta_i = acos(cNdotL); float theta_r = acos(cNdotV); vec3 l_proj = normalize(l - n * cNdotL); vec3 v_proj = normalize(v - n * cNdotV); 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) / (PI * PI), 2.0); float L1 = _Rho / PI * (C1 + cos_phi * C2 * tan(beta) + (1.0 - abs(cos_phi)) * C3 * tan((alpha + beta) / 2.0)); float L2 = 0.17 * _Rho * _Rho / PI * sigma2 / (sigma2 + 0.13) * (1.0 - cos_phi * (4.0 * beta * beta) / (PI * PI)); float f_d = max(min(L1 + L2, 1.0), 0.0) * cNdotL; DIFFUSE_LIGHT += normalizeLinf(LIGHT_COLOR) * ATTENUATION * f_d; }Check: specular highlights of low roughness surfaces break conservation of energy for big light sources · Issue #111853 · godotengine/godot
I am grateful for this! Can I correct this and credit you? I am still new to shaders and got into them because there weren’t a lot made for Godot 3.x
Sure, you can use it. I’m glad it might be helpful!
Do you have any contact info, I want to credit you
//function is borrowed from https://developer.download.nvidia.com/cg/index_stdlib.html //credit goes to ElSuicio https://interdreamsoft.itch.io/ https://mimosa-pudica.net/improved-oren-nayar.html shader_type spatial; render_mode ambient_light_disabled; render_mode skip_vertex_transform; uniform float _rho : hint_range(0.0, 1.0, 1e-3) = 0.9; uniform float _sigma : hint_range(0.0, 90.0, 1e-3) = 30.0; const float PI = 3.141592654; varying vec4 vector_ws; varying vec4 vector_os; vec3 sin_v3(vec3 x) { vec3 result; result.x= sin(x.x); result.y= sin(x.y); result.z= sin(x.z); return result; } vec3 cos_v3(vec3 x) { vec3 result; result.x= cos(x.x); result.y= cos(x.y); result.z= cos(x.z); return result; } vec3 tan_v3(vec3 x) { vec3 result; result = tan(x); return result; } vec3 pow_v3(vec3 x, float amt) { vec3 result; ///using recursion since for loops aren't available in GLES2. might be slow int i = 0; //start of loop int total = int(amt); // end of loop if (i < total)//condition of loop { x*=x; // operation i+=1; //incrementing loop } return result; } float saturate_f(float x) { return clamp(x,0.0,1.0);//max(0,min(1,x)); } vec3 saturate_v3(vec3 value) { vec3 result; result.x = max(0,min(1,value.x)); result.y= max(0,min(1,value.y)); result.z== max(0,min(1,value.z)); return result; } void vertex() { vector_os.xyz = VERTEX; vector_ws= WORLD_MATRIX* vec4(vector_os.xyz,1.0); vec4 vertex_vs = INV_CAMERA_MATRIX*vector_ws ; //INV_CAMERA_MATRIX in godot 3.5 is equivalent to VIEW_MATRIX in godot 4 VERTEX = vertex_vs.xyz; } void light() { vec3 i = ATTENUATION; vec3 l = LIGHT; vec3 n = NORMAL; vec3 viewDirection= normalize(CAMERA_MATRIX[3]-vector_ws).xyz; vec3 cNdotL = max(0.0,dot(n,l))*i; vec3 cNdotV=max(0.0,dot(n, viewDirection))*i; float sigma2 = pow(_sigma*PI/180.0,2.0); vec3 theta_i =acos(cNdotL); vec3 theta_r = acos(cNdotV); vec3 l_proj = normalize(l-n*cNdotL); vec3 v_proj = normalize(viewDirection-n*cNdotV); float cos_phi=dot(v_proj,l_proj); vec3 alpha = max(theta_i,theta_r); vec3 beta = min(theta_i,theta_r); float C1 = 1.0 - 0.5 * sigma2 / (sigma2 +0.33); vec3 C2 = vec3(0.45 * sigma2 / (sigma2 +0.09)); if (cos_phi >=0.0) { C2 *= sin(alpha); } else { C2 *= vec3(sin(alpha)-pow_v3(2.0*beta/PI,3.0)); } vec3 C3 = 0.125*sigma2/(sigma2+0.09)* pow_v3((4.0*alpha*beta)/(PI*PI),2.0); vec3 L1= _rho/PI * (C1+cos_phi * C2.x * tan_v3(beta) + (1.0-abs(cos_phi)) * C3 * tan_v3((alpha+beta)/2.0)); float L2 = 0.17*_rho * _rho /PI *sigma2/ (sigma2 + 0.13) * (1.0*cos_phi*(4.0*beta.x*beta.x)/(PI*PI)); vec3 f_d = max(min(L1+L2,1.0), 0.0)*cNdotL; DIFFUSE_LIGHT += LIGHT_COLOR*ATTENUATION*f_d; }