summaryrefslogtreecommitdiffhomepage
path: root/examples
diff options
context:
space:
mode:
Diffstat (limited to 'examples')
-rw-r--r--examples/shaders/resources/models/old_car_new.glbbin0 -> 467788 bytes
-rw-r--r--examples/shaders/resources/models/plane.glbbin0 -> 1144 bytes
-rw-r--r--examples/shaders/resources/old_car_d.pngbin0 -> 1684886 bytes
-rw-r--r--examples/shaders/resources/old_car_e.pngbin0 -> 1775 bytes
-rw-r--r--examples/shaders/resources/old_car_mra.pngbin0 -> 1136009 bytes
-rw-r--r--examples/shaders/resources/old_car_n.pngbin0 -> 1287246 bytes
-rw-r--r--examples/shaders/resources/road_a.pngbin0 -> 638390 bytes
-rw-r--r--examples/shaders/resources/road_mra.pngbin0 -> 673099 bytes
-rw-r--r--examples/shaders/resources/road_n.pngbin0 -> 660787 bytes
-rw-r--r--examples/shaders/resources/shaders/glsl100/pbr.fs156
-rw-r--r--examples/shaders/resources/shaders/glsl100/pbr.vs75
-rw-r--r--examples/shaders/resources/shaders/glsl120/pbr.fs156
-rw-r--r--examples/shaders/resources/shaders/glsl120/pbr.vs75
-rw-r--r--examples/shaders/resources/shaders/glsl330/pbr.fs159
-rw-r--r--examples/shaders/resources/shaders/glsl330/pbr.vs49
-rw-r--r--examples/shaders/rpbr.h466
-rw-r--r--examples/shaders/shaders_basic_pbr.c171
17 files changed, 1307 insertions, 0 deletions
diff --git a/examples/shaders/resources/models/old_car_new.glb b/examples/shaders/resources/models/old_car_new.glb
new file mode 100644
index 00000000..119995ca
--- /dev/null
+++ b/examples/shaders/resources/models/old_car_new.glb
Binary files differ
diff --git a/examples/shaders/resources/models/plane.glb b/examples/shaders/resources/models/plane.glb
new file mode 100644
index 00000000..452e1c5b
--- /dev/null
+++ b/examples/shaders/resources/models/plane.glb
Binary files differ
diff --git a/examples/shaders/resources/old_car_d.png b/examples/shaders/resources/old_car_d.png
new file mode 100644
index 00000000..d8b3c833
--- /dev/null
+++ b/examples/shaders/resources/old_car_d.png
Binary files differ
diff --git a/examples/shaders/resources/old_car_e.png b/examples/shaders/resources/old_car_e.png
new file mode 100644
index 00000000..23f01c0f
--- /dev/null
+++ b/examples/shaders/resources/old_car_e.png
Binary files differ
diff --git a/examples/shaders/resources/old_car_mra.png b/examples/shaders/resources/old_car_mra.png
new file mode 100644
index 00000000..0fb46b33
--- /dev/null
+++ b/examples/shaders/resources/old_car_mra.png
Binary files differ
diff --git a/examples/shaders/resources/old_car_n.png b/examples/shaders/resources/old_car_n.png
new file mode 100644
index 00000000..11f689fe
--- /dev/null
+++ b/examples/shaders/resources/old_car_n.png
Binary files differ
diff --git a/examples/shaders/resources/road_a.png b/examples/shaders/resources/road_a.png
new file mode 100644
index 00000000..10377739
--- /dev/null
+++ b/examples/shaders/resources/road_a.png
Binary files differ
diff --git a/examples/shaders/resources/road_mra.png b/examples/shaders/resources/road_mra.png
new file mode 100644
index 00000000..988c839c
--- /dev/null
+++ b/examples/shaders/resources/road_mra.png
Binary files differ
diff --git a/examples/shaders/resources/road_n.png b/examples/shaders/resources/road_n.png
new file mode 100644
index 00000000..a5f3548c
--- /dev/null
+++ b/examples/shaders/resources/road_n.png
Binary files differ
diff --git a/examples/shaders/resources/shaders/glsl100/pbr.fs b/examples/shaders/resources/shaders/glsl100/pbr.fs
new file mode 100644
index 00000000..1ada8331
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/pbr.fs
@@ -0,0 +1,156 @@
+#version 100
+
+precision mediump float;
+
+#define MAX_LIGHTS 4
+#define LIGHT_DIRECTIONAL 0
+#define LIGHT_POINT 1
+#define PI 3.14159265358979323846
+
+struct Light {
+ int enabled;
+ int type;
+ vec3 position;
+ vec3 target;
+ vec4 color;
+ float intensity;
+};
+
+// Input vertex attributes (from vertex shader)
+varying in vec3 fragPosition;
+varying in vec2 fragTexCoord;
+varying in vec4 fragColor;
+varying in vec3 fragNormal;
+varying in vec4 shadowPos;
+varying in mat3 TBN;
+
+
+// Input uniform values
+uniform int numOfLights;
+uniform sampler2D albedoMap;
+uniform sampler2D mraMap;
+uniform sampler2D normalMap;
+uniform sampler2D emissiveMap; // r: Hight g:emissive
+
+uniform vec2 tiling;
+uniform vec2 offset;
+
+uniform int useTexAlbedo;
+uniform int useTexNormal;
+uniform int useTexMRA;
+uniform int useTexEmissive;
+
+uniform vec4 albedoColor;
+uniform vec4 emissiveColor;
+uniform float normalValue;
+uniform float metallicValue;
+uniform float roughnessValue;
+uniform float aoValue;
+uniform float emissivePower;
+
+// Input lighting values
+uniform Light lights[MAX_LIGHTS];
+uniform vec3 viewPos;
+
+uniform vec3 ambientColor;
+uniform float ambient;
+
+// refl in range 0 to 1
+// returns base reflectivity to 1
+// incrase reflectivity when surface view at larger angle
+vec3 schlickFresnel(float hDotV,vec3 refl)
+{
+ return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
+}
+
+float ggxDistribution(float nDotH,float roughness)
+{
+ float a = roughness * roughness * roughness * roughness;
+ float d = nDotH * nDotH * (a - 1.0) + 1.0;
+ d = PI * d * d;
+ return a / max(d,0.0000001);
+}
+
+float geomSmith(float nDotV,float nDotL,float roughness)
+{
+ float r = roughness + 1.0;
+ float k = r * r / 8.0;
+ float ik = 1.0 - k;
+ float ggx1 = nDotV / (nDotV * ik + k);
+ float ggx2 = nDotL / (nDotL * ik + k);
+ return ggx1 * ggx2;
+}
+
+vec3 pbr(){
+ vec3 albedo = texture2D(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
+ albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
+ float metallic = clamp(metallicValue,0.0,1.0);
+ float roughness = clamp(roughnessValue,0.0,1.0);
+ float ao = clamp(aoValue,0.0,1.0);
+ if(useTexMRA == 1) {
+ vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y));
+ metallic = clamp(mra.r+metallicValue,0.04,1.0);
+ roughness = clamp(mra.g+roughnessValue,0.04,1.0);
+ ao = (mra.b+aoValue)*0.5;
+ }
+
+
+
+ vec3 N = normalize(fragNormal);
+ if(useTexNormal == 1) {
+ N = texture2D(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
+ N = normalize(N * 2.0 - 1.0);
+ N = normalize(N * TBN);
+ }
+
+ vec3 V = normalize(viewPos - fragPosition);
+
+ vec3 e = vec3(0);
+ e = (texture2D(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * float(useTexEmissive);
+
+ //return N;//vec3(metallic,metallic,metallic);
+ //if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
+ vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
+ vec3 Lo = vec3(0.0); // acumulate lighting lum
+
+ for(int i=0;i<numOfLights;++i){
+
+ vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
+ vec3 H = normalize(V + L); // calc halfway bisecting vector
+ float dist = length(lights[i].position - fragPosition); // calc distance to light
+ float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
+ vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
+
+ //Cook-Torrance BRDF distribution function
+ float nDotV = max(dot(N,V),0.0000001);
+ float nDotL = max(dot(N,L),0.0000001);
+ float hDotV = max(dot(H,V),0.0);
+ float nDotH = max(dot(N,H),0.0);
+ float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
+ float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
+ vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
+
+ vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
+ // difuse and spec light can't be above 1.0
+ // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
+ vec3 kD = vec3(1.0) - F;
+ //mult kD by the inverse of metallnes , only non-metals should have diffuse light
+ kD *= 1.0 - metallic;
+ Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*float(lights[i].enabled); // angle of light has impact on result
+ }
+ vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
+ return ambient_final+Lo*ao+e;
+}
+
+void main()
+{
+ vec3 color = pbr();
+
+ //HDR tonemapping
+ color = pow(color,color + vec3(1.0));
+ //gamma correction
+ color = pow(color,vec3(1.0/2.2));
+
+ gl_FragColor = vec4(color,1.0);
+
+} \ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl100/pbr.vs b/examples/shaders/resources/shaders/glsl100/pbr.vs
new file mode 100644
index 00000000..3fa651f7
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl100/pbr.vs
@@ -0,0 +1,75 @@
+#version 100
+
+// Input vertex attributes
+attribute vec3 vertexPosition;
+attribute vec2 vertexTexCoord;
+attribute vec3 vertexNormal;
+attribute vec3 vertexTangent;
+attribute vec4 vertexColor;
+
+// Input uniform values
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+uniform vec3 lightPos;
+uniform vec4 difColor;
+
+// Output vertex attributes (to fragment shader)
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+varying mat3 TBN;
+
+const float normalOffset = 0.1;
+
+// https://github.com/glslify/glsl-inverse
+mat3 inverse(mat3 m)
+{
+ float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
+ float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
+ float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
+
+ float b01 = a22*a11 - a12*a21;
+ float b11 = -a22*a10 + a12*a20;
+ float b21 = a21*a10 - a11*a20;
+
+ float det = a00*b01 + a01*b11 + a02*b21;
+
+ return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11),
+ b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10),
+ b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det;
+}
+
+// https://github.com/glslify/glsl-transpose
+mat3 transpose(mat3 m)
+{
+ return mat3(m[0][0], m[1][0], m[2][0],
+ m[0][1], m[1][1], m[2][1],
+ m[0][2], m[1][2], m[2][2]);
+}
+
+void main()
+{
+
+ // calc binormal from vertex normal and tangent
+ vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
+ // calc fragment normal based on normal transformations
+ mat3 normalMatrix = transpose(inverse(mat3(matModel)));
+ // calc fragment position based on model transformations
+
+ fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
+
+ fragTexCoord = vertexTexCoord*2.0;
+
+ fragNormal = normalize(normalMatrix*vertexNormal);
+ vec3 fragTangent = normalize(normalMatrix*vertexTangent);
+ fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
+ vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
+ fragBinormal = cross(fragNormal, fragTangent);
+
+ TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
+
+ // Calculate final vertex position
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+} \ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl120/pbr.fs b/examples/shaders/resources/shaders/glsl120/pbr.fs
new file mode 100644
index 00000000..935bced3
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/pbr.fs
@@ -0,0 +1,156 @@
+#version 120
+
+precision mediump float;
+
+#define MAX_LIGHTS 4
+#define LIGHT_DIRECTIONAL 0
+#define LIGHT_POINT 1
+#define PI 3.14159265358979323846
+
+struct Light {
+ int enabled;
+ int type;
+ vec3 position;
+ vec3 target;
+ vec4 color;
+ float intensity;
+};
+
+// Input vertex attributes (from vertex shader)
+varying in vec3 fragPosition;
+varying in vec2 fragTexCoord;
+varying in vec4 fragColor;
+varying in vec3 fragNormal;
+varying in vec4 shadowPos;
+varying in mat3 TBN;
+
+
+// Input uniform values
+uniform int numOfLights;
+uniform sampler2D albedoMap;
+uniform sampler2D mraMap;
+uniform sampler2D normalMap;
+uniform sampler2D emissiveMap; // r: Hight g:emissive
+
+uniform vec2 tiling;
+uniform vec2 offset;
+
+uniform int useTexAlbedo;
+uniform int useTexNormal;
+uniform int useTexMRA;
+uniform int useTexEmissive;
+
+uniform vec4 albedoColor;
+uniform vec4 emissiveColor;
+uniform float normalValue;
+uniform float metallicValue;
+uniform float roughnessValue;
+uniform float aoValue;
+uniform float emissivePower;
+
+// Input lighting values
+uniform Light lights[MAX_LIGHTS];
+uniform vec3 viewPos;
+
+uniform vec3 ambientColor;
+uniform float ambient;
+
+// refl in range 0 to 1
+// returns base reflectivity to 1
+// incrase reflectivity when surface view at larger angle
+vec3 schlickFresnel(float hDotV,vec3 refl)
+{
+ return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
+}
+
+float ggxDistribution(float nDotH,float roughness)
+{
+ float a = roughness * roughness * roughness * roughness;
+ float d = nDotH * nDotH * (a - 1.0) + 1.0;
+ d = PI * d * d;
+ return a / max(d,0.0000001);
+}
+
+float geomSmith(float nDotV,float nDotL,float roughness)
+{
+ float r = roughness + 1.0;
+ float k = r * r / 8.0;
+ float ik = 1.0 - k;
+ float ggx1 = nDotV / (nDotV * ik + k);
+ float ggx2 = nDotL / (nDotL * ik + k);
+ return ggx1 * ggx2;
+}
+
+vec3 pbr(){
+ vec3 albedo = texture2D(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
+ albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
+ float metallic = clamp(metallicValue,0.0,1.0);
+ float roughness = clamp(roughnessValue,0.0,1.0);
+ float ao = clamp(aoValue,0.0,1.0);
+ if(useTexMRA == 1) {
+ vec4 mra = texture2D(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y));
+ metallic = clamp(mra.r+metallicValue,0.04,1.0);
+ roughness = clamp(mra.g+roughnessValue,0.04,1.0);
+ ao = (mra.b+aoValue)*0.5;
+ }
+
+
+
+ vec3 N = normalize(fragNormal);
+ if(useTexNormal == 1) {
+ N = texture2D(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
+ N = normalize(N * 2.0 - 1.0);
+ N = normalize(N * TBN);
+ }
+
+ vec3 V = normalize(viewPos - fragPosition);
+
+ vec3 e = vec3(0);
+ e = (texture2D(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * float(useTexEmissive);
+
+ //return N;//vec3(metallic,metallic,metallic);
+ //if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
+ vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
+ vec3 Lo = vec3(0.0); // acumulate lighting lum
+
+ for(int i=0;i<numOfLights;++i){
+
+ vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
+ vec3 H = normalize(V + L); // calc halfway bisecting vector
+ float dist = length(lights[i].position - fragPosition); // calc distance to light
+ float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
+ vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
+
+ //Cook-Torrance BRDF distribution function
+ float nDotV = max(dot(N,V),0.0000001);
+ float nDotL = max(dot(N,L),0.0000001);
+ float hDotV = max(dot(H,V),0.0);
+ float nDotH = max(dot(N,H),0.0);
+ float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
+ float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
+ vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
+
+ vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
+ // difuse and spec light can't be above 1.0
+ // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
+ vec3 kD = vec3(1.0) - F;
+ //mult kD by the inverse of metallnes , only non-metals should have diffuse light
+ kD *= 1.0 - metallic;
+ Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*float(lights[i].enabled); // angle of light has impact on result
+ }
+ vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
+ return ambient_final+Lo*ao+e;
+}
+
+void main()
+{
+ vec3 color = pbr();
+
+ //HDR tonemapping
+ color = pow(color,color + vec3(1.0));
+ //gamma correction
+ color = pow(color,vec3(1.0/2.2));
+
+ gl_FragColor = vec4(color,1.0);
+
+} \ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl120/pbr.vs b/examples/shaders/resources/shaders/glsl120/pbr.vs
new file mode 100644
index 00000000..4c00261f
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl120/pbr.vs
@@ -0,0 +1,75 @@
+#version 120
+
+// Input vertex attributes
+attribute vec3 vertexPosition;
+attribute vec2 vertexTexCoord;
+attribute vec3 vertexNormal;
+attribute vec3 vertexTangent;
+attribute vec4 vertexColor;
+
+// Input uniform values
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+uniform vec3 lightPos;
+uniform vec4 difColor;
+
+// Output vertex attributes (to fragment shader)
+varying vec3 fragPosition;
+varying vec2 fragTexCoord;
+varying vec4 fragColor;
+varying vec3 fragNormal;
+varying mat3 TBN;
+
+const float normalOffset = 0.1;
+
+// https://github.com/glslify/glsl-inverse
+mat3 inverse(mat3 m)
+{
+ float a00 = m[0][0], a01 = m[0][1], a02 = m[0][2];
+ float a10 = m[1][0], a11 = m[1][1], a12 = m[1][2];
+ float a20 = m[2][0], a21 = m[2][1], a22 = m[2][2];
+
+ float b01 = a22*a11 - a12*a21;
+ float b11 = -a22*a10 + a12*a20;
+ float b21 = a21*a10 - a11*a20;
+
+ float det = a00*b01 + a01*b11 + a02*b21;
+
+ return mat3(b01, (-a22*a01 + a02*a21), (a12*a01 - a02*a11),
+ b11, (a22*a00 - a02*a20), (-a12*a00 + a02*a10),
+ b21, (-a21*a00 + a01*a20), (a11*a00 - a01*a10))/det;
+}
+
+// https://github.com/glslify/glsl-transpose
+mat3 transpose(mat3 m)
+{
+ return mat3(m[0][0], m[1][0], m[2][0],
+ m[0][1], m[1][1], m[2][1],
+ m[0][2], m[1][2], m[2][2]);
+}
+
+void main()
+{
+
+ // calc binormal from vertex normal and tangent
+ vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
+ // calc fragment normal based on normal transformations
+ mat3 normalMatrix = transpose(inverse(mat3(matModel)));
+ // calc fragment position based on model transformations
+
+ fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
+
+ fragTexCoord = vertexTexCoord*2.0;
+
+ fragNormal = normalize(normalMatrix*vertexNormal);
+ vec3 fragTangent = normalize(normalMatrix*vertexTangent);
+ fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
+ vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
+ fragBinormal = cross(fragNormal, fragTangent);
+
+ TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
+
+ // Calculate final vertex position
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+} \ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl330/pbr.fs b/examples/shaders/resources/shaders/glsl330/pbr.fs
new file mode 100644
index 00000000..5375d449
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/pbr.fs
@@ -0,0 +1,159 @@
+#version 330
+
+#define MAX_LIGHTS 4
+#define LIGHT_DIRECTIONAL 0
+#define LIGHT_POINT 1
+#define PI 3.14159265358979323846
+
+struct Light {
+ int enabled;
+ int type;
+ vec3 position;
+ vec3 target;
+ vec4 color;
+ float intensity;
+};
+
+// Input vertex attributes (from vertex shader)
+in vec3 fragPosition;
+in vec2 fragTexCoord;
+in vec4 fragColor;
+in vec3 fragNormal;
+in vec4 shadowPos;
+in mat3 TBN;
+
+// Output fragment color
+out vec4 finalColor;
+// Input uniform values
+
+uniform int numOfLights;
+uniform sampler2D albedoMap;
+uniform sampler2D mraMap;
+uniform sampler2D normalMap;
+uniform sampler2D emissiveMap; // r: Hight g:emissive
+
+uniform vec2 tiling;
+uniform vec2 offset;
+
+uniform int useTexAlbedo;
+uniform int useTexNormal;
+uniform int useTexMRA;
+uniform int useTexEmissive;
+
+uniform vec4 albedoColor;
+uniform vec4 emissiveColor;
+uniform float normalValue;
+uniform float metallicValue;
+uniform float roughnessValue;
+uniform float aoValue;
+uniform float emissivePower;
+
+// Input lighting values
+uniform Light lights[MAX_LIGHTS];
+uniform vec3 viewPos;
+
+uniform vec3 ambientColor;
+uniform float ambient;
+
+// refl in range 0 to 1
+// returns base reflectivity to 1
+// incrase reflectivity when surface view at larger angle
+vec3 schlickFresnel(float hDotV,vec3 refl)
+{
+ return refl + (1.0 - refl) * pow(1.0 - hDotV,5.0);
+}
+
+float ggxDistribution(float nDotH,float roughness)
+{
+ float a = roughness * roughness * roughness * roughness;
+ float d = nDotH * nDotH * (a - 1.0) + 1.0;
+ d = PI * d * d;
+ return a / max(d,0.0000001);
+}
+
+float geomSmith(float nDotV,float nDotL,float roughness)
+{
+ float r = roughness + 1.0;
+ float k = r * r / 8.0;
+ float ik = 1.0 - k;
+ float ggx1 = nDotV / (nDotV * ik + k);
+ float ggx2 = nDotL / (nDotL * ik + k);
+ return ggx1 * ggx2;
+}
+
+vec3 pbr(){
+ vec3 albedo = texture(albedoMap,vec2(fragTexCoord.x*tiling.x+offset.x,fragTexCoord.y*tiling.y+offset.y)).rgb;
+ albedo = vec3(albedoColor.x*albedo.x,albedoColor.y*albedo.y,albedoColor.z*albedo.z);
+ float metallic = clamp(metallicValue,0.0,1.0);
+ float roughness = clamp(roughnessValue,0.0,1.0);
+ float ao = clamp(aoValue,0.0,1.0);
+ if(useTexMRA == 1) {
+ vec4 mra = texture(mraMap, vec2(fragTexCoord.x * tiling.x + offset.x, fragTexCoord.y * tiling.y + offset.y)) * useTexMRA;
+ metallic = clamp(mra.r+metallicValue,0.04,1.0);
+ roughness = clamp(mra.g+roughnessValue,0.04,1.0);
+ ao = (mra.b+aoValue)*0.5;
+ }
+
+
+
+ vec3 N = normalize(fragNormal);
+ if(useTexNormal == 1) {
+ N = texture(normalMap, vec2(fragTexCoord.x * tiling.x + offset.y, fragTexCoord.y * tiling.y + offset.y)).rgb;
+ N = normalize(N * 2.0 - 1.0);
+ N = normalize(N * TBN);
+ }
+
+ vec3 V = normalize(viewPos - fragPosition);
+
+ vec3 e = vec3(0);
+ e = (texture(emissiveMap, vec2(fragTexCoord.x*tiling.x+offset.x, fragTexCoord.y*tiling.y+offset.y)).rgb).g * emissiveColor.rgb*emissivePower * useTexEmissive;
+
+
+
+
+
+ //return N;//vec3(metallic,metallic,metallic);
+ //if dia-electric use base reflectivity of 0.04 otherwise ut is a metal use albedo as base reflectivity
+ vec3 baseRefl = mix(vec3(0.04),albedo.rgb,metallic);
+ vec3 Lo = vec3(0.0); // acumulate lighting lum
+
+ for(int i=0;i<numOfLights;++i){
+
+ vec3 L = normalize(lights[i].position - fragPosition); // calc light vector
+ vec3 H = normalize(V + L); // calc halfway bisecting vector
+ float dist = length(lights[i].position - fragPosition); // calc distance to light
+ float attenuation = 1.0 / (dist * dist * 0.23); // calc attenuation
+ vec3 radiance = lights[i].color.rgb * lights[i].intensity * attenuation; // calc input radiance,light energy comming in
+
+ //Cook-Torrance BRDF distribution function
+ float nDotV = max(dot(N,V),0.0000001);
+ float nDotL = max(dot(N,L),0.0000001);
+ float hDotV = max(dot(H,V),0.0);
+ float nDotH = max(dot(N,H),0.0);
+ float D = ggxDistribution(nDotH,roughness); // larger the more micro-facets aligned to H
+ float G = geomSmith(nDotV,nDotL,roughness); // smaller the more micro-facets shadow
+ vec3 F = schlickFresnel(hDotV, baseRefl); // fresnel proportion of specular reflectance
+
+ vec3 spec = (D * G * F) / (4.0 * nDotV * nDotL);
+ // difuse and spec light can't be above 1.0
+ // kD = 1.0 - kS diffuse component is equal 1.0 - spec comonent
+ vec3 kD = vec3(1.0) - F;
+ //mult kD by the inverse of metallnes , only non-metals should have diffuse light
+ kD *= 1.0 - metallic;
+ Lo += ((kD * albedo.rgb / PI + spec) * radiance * nDotL)*lights[i].enabled; // angle of light has impact on result
+ }
+ vec3 ambient_final = (ambientColor + albedo)* ambient * 0.5;
+ return ambient_final+Lo*ao+e;
+}
+
+void main()
+{
+ vec3 color = pbr();
+
+ //HDR tonemapping
+ color = pow(color,color + vec3(1.0));
+ //gamma correction
+ color = pow(color,vec3(1.0/2.2));
+
+ finalColor = vec4(color,1.0);
+} \ No newline at end of file
diff --git a/examples/shaders/resources/shaders/glsl330/pbr.vs b/examples/shaders/resources/shaders/glsl330/pbr.vs
new file mode 100644
index 00000000..a03898cb
--- /dev/null
+++ b/examples/shaders/resources/shaders/glsl330/pbr.vs
@@ -0,0 +1,49 @@
+#version 330
+
+// Input vertex attributes
+in vec3 vertexPosition;
+in vec2 vertexTexCoord;
+in vec3 vertexNormal;
+in vec3 vertexTangent;
+in vec4 vertexColor;
+
+// Input uniform values
+uniform mat4 mvp;
+uniform mat4 matModel;
+uniform mat4 matNormal;
+uniform vec3 lightPos;
+uniform vec4 difColor;
+
+// Output vertex attributes (to fragment shader)
+out vec3 fragPosition;
+out vec2 fragTexCoord;
+out vec4 fragColor;
+out vec3 fragNormal;
+out mat3 TBN;
+
+const float normalOffset = 0.1;
+
+void main()
+{
+
+ // calc binormal from vertex normal and tangent
+ vec3 vertexBinormal = cross(vertexNormal, vertexTangent);
+ // calc fragment normal based on normal transformations
+ mat3 normalMatrix = transpose(inverse(mat3(matModel)));
+ // calc fragment position based on model transformations
+
+ fragPosition = vec3(matModel*vec4(vertexPosition, 1.0f));
+
+ fragTexCoord = vertexTexCoord*2.0;
+
+ fragNormal = normalize(normalMatrix*vertexNormal);
+ vec3 fragTangent = normalize(normalMatrix*vertexTangent);
+ fragTangent = normalize(fragTangent - dot(fragTangent, fragNormal)*fragNormal);
+ vec3 fragBinormal = normalize(normalMatrix*vertexBinormal);
+ fragBinormal = cross(fragNormal, fragTangent);
+
+ TBN = transpose(mat3(fragTangent, fragBinormal, fragNormal));
+
+ // Calculate final vertex position
+ gl_Position = mvp * vec4(vertexPosition, 1.0);
+} \ No newline at end of file
diff --git a/examples/shaders/rpbr.h b/examples/shaders/rpbr.h
new file mode 100644
index 00000000..a8b7e939
--- /dev/null
+++ b/examples/shaders/rpbr.h
@@ -0,0 +1,466 @@
+/**********************************************************************************************
+*
+* raylib.pbr - Some useful functions to deal with pbr materials and lights
+*
+* CONFIGURATION:
+*
+* #define RPBR_IMPLEMENTATION
+* Generates the implementation of the library into the included file.
+* If not defined, the library is in header only mode and can be included in other headers
+* or source files without problems. But only ONE file should hold the implementation.
+*
+* LICENSE: zlib/libpng
+*
+* Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2017-2020 Victor Fisac(@victorfisac),Ramon Santamaria (@raysan5)
+*
+* This software is provided "as-is", without any express or implied warranty. In no event
+* will the authors be held liable for any damages arising from the use of this software.
+*
+* Permission is granted to anyone to use this software for any purpose, including commercial
+* applications, and to alter it and redistribute it freely, subject to the following restrictions:
+*
+* 1. The origin of this software must not be misrepresented; you must not claim that you
+* wrote the original software. If you use this software in a product, an acknowledgment
+* in the product documentation would be appreciated but is not required.
+*
+* 2. Altered source versions must be plainly marked as such, and must not be misrepresented
+* as being the original software.
+*
+* 3. This notice may not be removed or altered from any source distribution.
+*
+**********************************************************************************************/
+
+#ifndef RPBR_H
+#define RPBR_H
+#include "raylib.h"
+
+
+//----------------------------------------------------------------------------------
+// Defines and Macros
+//----------------------------------------------------------------------------------
+#define MAX_LIGHTS 4 // Max dynamic lights supported by shader
+#define SHADER_LOC_MAP_MRA SHADER_LOC_MAP_METALNESS //METALLIC, ROUGHNESS and AO
+#define SHADER_LOC_MAP_EMISSIVE SHADER_LOC_MAP_HEIGHT //EMISSIVE
+#define MATERIAL_MAP_MRA MATERIAL_MAP_METALNESS
+#define MATERIAL_MAP_EMISSIVE MATERIAL_MAP_HEIGHT
+#define NULL 0
+#define COLOR_TO_ARRAY(c)
+
+typedef struct {
+ int enabled;
+ int type;
+ Vector3 position;
+ Vector3 target;
+ float color[4];
+ float intensity;
+
+ int enabledLoc;
+ int typeLoc;
+ int positionLoc;
+ int targetLoc;
+ int colorLoc;
+ int intensityLoc;
+} PBRLight;
+
+typedef enum {
+ LIGHT_DIRECTIONAL = 0,
+ LIGHT_POINT,
+ LIGHT_SPOT
+} PBRLightType;
+
+typedef struct{
+ Shader pbrShader;
+ Shader skyShader;
+ unsigned int cubemap;
+ unsigned int irradiance;
+ unsigned int prefilter;
+ unsigned int brdf;
+ int modelMatrixLoc;
+ int pbrViewLoc;
+ int skyViewLoc;
+ int skyResolutionLoc;
+} PBREnvironment;
+
+typedef enum{
+ PBR_COLOR_ALBEDO = 0,
+ PBR_COLOR_EMISSIVE
+}PBRColorType;
+
+typedef enum{
+ PBR_VEC2_TILING = 0,
+ PBR_VEC2_OFFSET
+}PBRVec2Type;
+
+typedef enum{
+ PBR_PARAM_NORMAL =0,
+ PBR_PARAM_METALLIC,
+ PBR_PARAM_ROUGHNESS,
+ PBR_PARAM_EMISSIVE,
+ PBR_PARAM_AO
+}PBRFloatType;
+
+typedef enum{
+ PBR_TEXTURE_ALBEDO = 0,
+ PBR_TEXTURE_NORMAL,
+ PBR_TEXTURE_MRA,
+ PBR_TEXTURE_EMISSIVE
+}PBRTexType;
+
+// Textures are moved to material from params to pack better and use less textures on the end
+// texture MRAE 4Channel R: Metallic G: Roughness B: A: Ambient Occlusion
+// texEmissive use just one channel, so we have 3 channels still to use if we need
+typedef struct {
+ Shader pbrShader;
+ float albedo[4];
+ float normal;
+ float metallic;
+ float roughness;
+ float ao;
+ float emissive[4];
+ float ambient[3];
+ float emissivePower;
+
+ Texture2D texAlbedo;
+ Texture2D texNormal;
+ Texture2D texMRA;//r: Metallic g: Roughness b: AO a:Empty
+ Texture2D texEmissive; //Emissive Texture
+ // Using float4 to store tilling at 1st and 2nd position and offset at 3rd and 4th
+ float texTiling[2];
+ float texOffset[2];
+
+ int useTexAlbedo;
+ int useTexNormal;
+ int useTexMRA;
+ int useTexEmissive;
+
+ int albedoLoc;
+ int normalLoc;
+ int metallicLoc;
+ int roughnessLoc;
+ int aoLoc;
+ int emissiveColorLoc;
+ int emissivePowerLoc;
+
+ int texTilingLoc;
+ int texOffsetLoc;
+
+ int useTexAlbedoLoc;
+ int useTexNormalLoc;
+ int useTexMRAELoc;
+ int useTexEmissiveLoc;
+} PBRMaterial;
+
+typedef struct{
+ Model model;
+ PBRMaterial pbrMat;
+}PBRModel;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+//----------------------------------------------------------------------------------
+// Module Functions Declaration
+//----------------------------------------------------------------------------------
+
+// Create a light and get shader locations
+PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color,float intensity, Shader shader);
+// Send light properties to shader
+void PBRLightUpdate(Shader shader, PBRLight light);
+
+//For now until we do real skylight
+void PBRSetAmbient(Shader shader, Color color, float intensity);
+
+PBRModel PBRModelLoad(const char *fileName);
+PBRModel PBRModelLoadFromMesh(Mesh mesh);
+
+void PBRLoadTextures(PBRMaterial *pbrMat,PBRTexType pbrTexType,const char *fileName);
+void UnloadPBRMaterial(PBRMaterial pbrMat);
+void PBRSetColor(PBRMaterial *pbrMat,PBRColorType pbrColorType,Color color);
+void PBRSetVec2(PBRMaterial *pbrMat,PBRVec2Type type,Vector2 value);
+void PBRSetFloat(PBRMaterial *pbrMat, PBRFloatType pbrParamType, float value);
+
+void PBRMaterialSetup( PBRMaterial *pbrMat,Shader pbrShader, PBREnvironment* environment);
+void PBRSetMaterial(PBRModel* model,PBRMaterial* pbrMat,int matIndex);
+void PBRDrawModel(PBRModel pbrModel, Vector3 position, float scale);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //RPBR_H
+
+/***********************************************************************************
+*
+* RPBR IMPLEMENTATION
+*
+************************************************************************************/
+
+#if defined(RPBR_IMPLEMENTATION)
+
+//----------------------------------------------------------------------------------
+// Global Variables Definition
+//----------------------------------------------------------------------------------
+static int lightsCount = 0; // Current amount of created lights
+
+// Create a light and get shader locations
+PBRLight PBRLightCreate(int type, Vector3 position, Vector3 target, Color color,float intensity, Shader shader)
+{
+ PBRLight light = { 0 };
+
+ if (lightsCount < MAX_LIGHTS)
+ {
+ light.enabled = 1;
+ light.type = type;
+ light.position = position;
+ light.target = target;
+ light.color[0] = (float)color.r/(float)255;
+ light.color[1] = (float)color.g/(float)255;
+ light.color[2] = (float)color.b/(float)255;
+ light.color[3] = (float)color.a/(float)255;
+ light.intensity = intensity;
+ // NOTE: Lighting shader naming must be the provided ones
+ light.enabledLoc = GetShaderLocation(shader, TextFormat("lights[%i].enabled", lightsCount));
+ light.typeLoc = GetShaderLocation(shader, TextFormat("lights[%i].type", lightsCount));
+ light.positionLoc = GetShaderLocation(shader, TextFormat("lights[%i].position", lightsCount));
+ light.targetLoc = GetShaderLocation(shader, TextFormat("lights[%i].target", lightsCount));
+ light.colorLoc = GetShaderLocation(shader, TextFormat("lights[%i].color", lightsCount));
+ light.intensityLoc = GetShaderLocation(shader, TextFormat("lights[%i].intensity", lightsCount));
+ PBRLightUpdate(shader, light);
+
+ lightsCount++;
+ }
+
+ return light;
+}
+
+// Send light properties to shader
+// NOTE: Light shader locations should be available
+void PBRLightUpdate(Shader shader, PBRLight light)
+{
+ SetShaderValue(shader, light.enabledLoc, &light.enabled, SHADER_UNIFORM_INT);
+ SetShaderValue(shader, light.typeLoc, &light.type, SHADER_UNIFORM_INT);
+ // Send to shader light position values
+ float position[3] = { light.position.x, light.position.y, light.position.z };
+ SetShaderValue(shader, light.positionLoc, position, SHADER_UNIFORM_VEC3);
+
+ // Send to shader light target position values
+ float target[3] = { light.target.x, light.target.y, light.target.z };
+ SetShaderValue(shader, light.targetLoc, target, SHADER_UNIFORM_VEC3);
+ SetShaderValue(shader, light.colorLoc, light.color, SHADER_UNIFORM_VEC4);
+ SetShaderValue(shader, light.intensityLoc, &light.intensity, SHADER_UNIFORM_FLOAT);
+}
+
+void PBRSetAmbient(Shader shader, Color color, float intensity){
+ float col[3] = {color.r/255,color.g/255,color.b/255};
+ SetShaderValue(shader, GetShaderLocation(shader, "ambientColor"), col, SHADER_UNIFORM_VEC3);
+ SetShaderValue(shader, GetShaderLocation(shader, "ambient"), &intensity, SHADER_UNIFORM_FLOAT);
+}
+
+void PBRMaterialSetup(PBRMaterial *pbrMat, Shader pbrShader, PBREnvironment* environment){
+ pbrMat->pbrShader = pbrShader;
+
+ pbrMat->texAlbedo = (Texture2D){0};
+ pbrMat->texNormal = (Texture2D){0};
+ pbrMat->texMRA = (Texture2D){0};
+ pbrMat->texEmissive = (Texture2D){0};
+
+ //PBRParam
+ pbrMat->albedo[0] = 1.0;
+ pbrMat->albedo[1] = 1.0;
+ pbrMat->albedo[2] = 1.0;
+ pbrMat->albedo[3] = 1.0;
+ pbrMat->metallic = 0;
+ pbrMat->roughness = 0;
+ pbrMat->ao = 1.0;
+ pbrMat->normal = 1;
+ pbrMat->emissive[0] = 0;
+ pbrMat->emissive[1] = 0;
+ pbrMat->emissive[2] = 0;
+ pbrMat->emissive[3] = 0;
+
+ pbrMat->texTiling[0] = 1.0;
+ pbrMat->texTiling[1] = 1.0;
+ pbrMat->texOffset[0] = 0.0;
+ pbrMat->texOffset[1] = 0.0;
+ pbrMat->emissivePower = 1.0;
+ // Set up PBR shader material locations
+
+ pbrMat->albedoLoc = GetShaderLocation(pbrMat->pbrShader, "albedoColor");
+ pbrMat->normalLoc = GetShaderLocation(pbrMat->pbrShader, "normalValue");
+ pbrMat->metallicLoc = GetShaderLocation(pbrMat->pbrShader, "metallicValue");
+ pbrMat->roughnessLoc = GetShaderLocation(pbrMat->pbrShader, "roughnessValue");
+ pbrMat->aoLoc = GetShaderLocation(pbrMat->pbrShader, "aoValue");
+ pbrMat->emissiveColorLoc = GetShaderLocation(pbrMat->pbrShader, "emissiveColor");
+ pbrMat->emissivePowerLoc = GetShaderLocation(pbrMat->pbrShader, "emissivePower");
+
+ pbrMat->texTilingLoc = GetShaderLocation(pbrMat->pbrShader, "tiling");
+ pbrMat->texOffsetLoc = GetShaderLocation(pbrMat->pbrShader, "offset");
+
+ pbrMat->useTexAlbedoLoc = GetShaderLocation(pbrMat->pbrShader, "useTexAlbedo");
+ pbrMat->useTexNormalLoc = GetShaderLocation(pbrMat->pbrShader, "useTexNormal");
+ pbrMat->useTexMRAELoc = GetShaderLocation(pbrMat->pbrShader, "useTexMRA");
+ pbrMat->useTexEmissiveLoc = GetShaderLocation(pbrMat->pbrShader, "useTexEmissive");
+
+ SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->emissivePowerLoc, &pbrMat->emissivePower, SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,pbrMat->texTiling,SHADER_UNIFORM_VEC2);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,pbrMat->texOffset,SHADER_UNIFORM_VEC2);
+}
+
+void PBRLoadTextures(PBRMaterial *pbrMat,PBRTexType pbrTexType,const char *fileName){
+ if(pbrMat == NULL) return;
+ switch(pbrTexType){
+ case PBR_TEXTURE_ALBEDO:
+ pbrMat->texAlbedo = LoadTexture(fileName);
+ pbrMat->useTexAlbedo = 1;
+ break;
+ case PBR_TEXTURE_MRA:
+ pbrMat->texMRA = LoadTexture(fileName);
+ pbrMat->useTexMRA = 1;
+ break;
+ case PBR_TEXTURE_NORMAL:
+ pbrMat->texNormal = LoadTexture(fileName);
+ pbrMat->useTexNormal = 1;
+ break;
+ case PBR_TEXTURE_EMISSIVE:
+ pbrMat->texEmissive = LoadTexture(fileName);
+ pbrMat->useTexEmissive = 1;
+ break;
+ }
+}
+
+void UnloadPBRMaterial(PBRMaterial pbrMat){
+ if(pbrMat.useTexAlbedo == 1) UnloadTexture(pbrMat.texAlbedo);
+ if(pbrMat.useTexNormal == 1) UnloadTexture(pbrMat.texNormal);
+ if(pbrMat.useTexMRA == 1) UnloadTexture(pbrMat.texMRA);
+ if(pbrMat.useTexEmissive == 1) UnloadTexture(pbrMat.texEmissive);
+}
+
+void PBRSetColor(PBRMaterial *pbrMat,PBRColorType pbrColorType,Color color){
+ if(pbrMat == NULL) return;
+ switch(pbrColorType){
+ case PBR_COLOR_ALBEDO:
+ pbrMat->albedo[0] = (float) color.r / 255;
+ pbrMat->albedo[1] = (float) color.g / 255;
+ pbrMat->albedo[2] = (float) color.b / 255;
+ pbrMat->albedo[3] = (float) color.a / 255;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
+ break;
+ case PBR_COLOR_EMISSIVE:
+ pbrMat->emissive[0] = (float) color.r / 255;
+ pbrMat->emissive[1] = (float) color.g / 255;
+ pbrMat->emissive[2] = (float) color.b / 255;
+ pbrMat->emissive[3] = (float) color.a / 255;
+ SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
+ break;
+ }
+}
+
+void PBRSetFloat(PBRMaterial *pbrMat, PBRFloatType pbrParamType, float value){
+ if(pbrMat == NULL) return;
+ switch(pbrParamType){
+ case PBR_PARAM_METALLIC:
+ pbrMat->metallic = value;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
+ break;
+ case PBR_PARAM_ROUGHNESS:
+ pbrMat->roughness = value;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
+ break;
+ case PBR_PARAM_NORMAL:
+ pbrMat->normal = value;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
+ break;
+ case PBR_PARAM_AO:
+ pbrMat->ao = value;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
+ break;
+ case PBR_PARAM_EMISSIVE:
+ pbrMat->emissivePower = value;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->emissivePowerLoc,&pbrMat->emissivePower,SHADER_UNIFORM_FLOAT);
+ break;
+ }
+}
+
+
+void PBRSetVec2(PBRMaterial *pbrMat,PBRVec2Type type,Vector2 value){
+ switch(type){
+ case PBR_VEC2_TILING:
+ pbrMat->texTiling[0] = value.x;
+ pbrMat->texTiling[1] = value.y;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,&pbrMat->texTiling,SHADER_UNIFORM_VEC2);
+ break;
+ case PBR_VEC2_OFFSET:
+ pbrMat->texOffset[0] = value.x;
+ pbrMat->texOffset[1] = value.y;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,&pbrMat->texOffset,SHADER_UNIFORM_VEC2);
+ break;
+ }
+}
+
+void PBRSetMaterial(PBRModel* model,PBRMaterial* pbrMat,int matIndex){
+
+
+ model->pbrMat = *pbrMat;
+ model->model.materials[matIndex].shader = model->pbrMat.pbrShader;
+ pbrMat->pbrShader.locs[SHADER_LOC_MAP_MRA] = GetShaderLocation(pbrMat->pbrShader, "mraMap");
+ pbrMat->pbrShader.locs[SHADER_LOC_MAP_EMISSIVE] = GetShaderLocation(pbrMat->pbrShader, "emissiveMap");
+ pbrMat->pbrShader.locs[SHADER_LOC_MAP_NORMAL] = GetShaderLocation(pbrMat->pbrShader, "normalMap");
+
+ if(pbrMat->useTexAlbedo) {
+ model->model.materials[matIndex].maps[MATERIAL_MAP_ALBEDO].texture = pbrMat->texAlbedo;
+ }
+ if(pbrMat->useTexMRA) {
+ model->model.materials[matIndex].maps[MATERIAL_MAP_MRA].texture = pbrMat->texMRA;
+ }
+ if(pbrMat->useTexNormal) {
+ model->model.materials[matIndex].maps[MATERIAL_MAP_NORMAL].texture = pbrMat->texNormal;
+ }
+ if(pbrMat->useTexEmissive) {
+ model->model.materials[matIndex].maps[MATERIAL_MAP_EMISSIVE].texture = pbrMat->texEmissive;
+ }
+
+ SetShaderValue(pbrMat->pbrShader,pbrMat->useTexAlbedoLoc,&pbrMat->useTexAlbedo,SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->useTexNormalLoc,&pbrMat->useTexNormal,SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->useTexMRAELoc, &pbrMat->useTexMRA, SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->useTexEmissiveLoc, &pbrMat->useTexEmissive, SHADER_UNIFORM_INT);
+}
+
+void PBRDrawModel(PBRModel pbrModel, Vector3 position, float scale){
+ PBRMaterial *pbrMat = &pbrModel.pbrMat;
+ SetShaderValue(pbrMat->pbrShader,pbrMat->albedoLoc,pbrMat->albedo,SHADER_UNIFORM_VEC4);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->emissiveColorLoc, pbrMat->emissive, SHADER_UNIFORM_VEC4);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->metallicLoc,&pbrMat->metallic,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->roughnessLoc,&pbrMat->roughness,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->aoLoc,&pbrMat->ao,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->normalLoc,&pbrMat->normal,SHADER_UNIFORM_FLOAT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texTilingLoc,pbrMat->texTiling,SHADER_UNIFORM_VEC2);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->texOffsetLoc,pbrMat->texOffset,SHADER_UNIFORM_VEC2);
+
+ SetShaderValue(pbrMat->pbrShader,pbrMat->useTexAlbedoLoc,&pbrMat->useTexAlbedo,SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader,pbrMat->useTexNormalLoc,&pbrMat->useTexNormal,SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->useTexMRAELoc, &pbrMat->useTexMRA, SHADER_UNIFORM_INT);
+ SetShaderValue(pbrMat->pbrShader, pbrMat->useTexEmissiveLoc, &pbrMat->useTexEmissive, SHADER_UNIFORM_INT);
+
+ DrawModel(pbrModel.model,position,scale,WHITE);
+}
+
+PBRModel PBRModelLoad(const char *fileName){
+ PBRModel pbrModel = (PBRModel){0};
+ pbrModel.model = LoadModel(fileName);
+ return pbrModel;
+}
+
+PBRModel PBRModelLoadFromMesh(Mesh mesh){
+ PBRModel pbrModel = (PBRModel){0};
+ pbrModel.model = LoadModelFromMesh(mesh);
+ return pbrModel;
+}
+#endif // RPBR_IMPLEMENTATION
diff --git a/examples/shaders/shaders_basic_pbr.c b/examples/shaders/shaders_basic_pbr.c
new file mode 100644
index 00000000..92a88881
--- /dev/null
+++ b/examples/shaders/shaders_basic_pbr.c
@@ -0,0 +1,171 @@
+/*******************************************************************************************
+*
+* raylib [core] example - Model Defuse Normal Shader (adapted for HTML5 platform)
+*
+* This example is prepared to compile for PLATFORM_WEB and PLATFORM_DESKTOP
+* As you will notice, code structure is slightly different to the other examples...
+* To compile it for PLATFORM_WEB just uncomment #define PLATFORM_WEB at beginning
+*
+* This example has been created using raylib 5.0 (www.raylib.com)
+* raylib is licensed under an unmodified zlib/libpng license (View raylib.h for details)
+*
+* Copyright (c) 2023-2024 Afan OLOVCIC (@_DevDad) 2015 Ramon Santamaria (@raysan5)
+* Model: "Old Rusty Car" (https://skfb.ly/LxRy) by Renafox is licensed under Creative Commons Attribution-NonCommercial (http://creativecommons.org/licenses/by-nc/4.0/).
+********************************************************************************************/
+
+#include "raylib.h"
+
+#if defined(PLATFORM_WEB)
+#include <emscripten/emscripten.h>
+#endif
+
+#define RPBR_IMPLEMENTATION
+#include "rpbr.h"
+
+#if defined(PLATFORM_DESKTOP)
+#define GLSL_VERSION 330
+#else // PLATFORM_ANDROID, PLATFORM_WEB
+#define GLSL_VERSION 120
+#endif
+
+
+//----------------------------------------------------------------------------------
+// Main Entry Point
+//----------------------------------------------------------------------------------
+int main()
+{
+ // Initialization
+ //--------------------------------------------------------------------------------------
+ const int screenWidth = 800;
+ const int screenHeight = 450;
+
+ SetConfigFlags(FLAG_MSAA_4X_HINT);
+ InitWindow(screenWidth, screenHeight, "raylib [shaders] example - basic pbr");
+
+ // Define the camera to look into our 3d world
+ Camera camera = { 0 };
+ camera.position = (Vector3){ 2.0f, 2.0f, 6.0f }; // Camera position
+ camera.target = (Vector3){ 0.0f, 0.5f, 0.0f }; // Camera looking at point
+ camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
+ camera.fovy = 45.0f; // Camera field-of-view Y
+ camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
+
+
+ Shader shader = LoadShader(TextFormat("resources/shaders/glsl%i/pbr.vs",GLSL_VERSION),
+ TextFormat("resources/shaders/glsl%i/pbr.fs",GLSL_VERSION));
+
+
+
+ PBRModel model = PBRModelLoad("resources/models/old_car_new.glb");
+ //if we use obj file formator if model doesn't have tangents we have to calculate MeshTangents
+ //by using raylib function GenMeshTangents(mesh) for example: obj file doesn't support tangents
+ //GenMeshTangents(&model.model.meshes[0]);
+
+ PBRMaterial model_mat = (PBRMaterial){0};
+ PBRMaterialSetup(&model_mat, shader, NULL); //environment = NULL for now
+ PBRLoadTextures(&model_mat, PBR_TEXTURE_ALBEDO, "resources/old_car_d.png");
+ PBRLoadTextures(&model_mat, PBR_TEXTURE_MRA, "resources/old_car_mra.png");
+ PBRLoadTextures(&model_mat, PBR_TEXTURE_NORMAL, "resources/old_car_n.png");
+ PBRLoadTextures(&model_mat, PBR_TEXTURE_EMISSIVE, "resources/old_car_e.png");
+ PBRSetColor(&model_mat,PBR_COLOR_EMISSIVE, (Color){255,162,0,255});
+ PBRSetVec2(&model_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5});
+ PBRSetMaterial(&model,&model_mat,0);
+
+ PBRModel floor = PBRModelLoad("resources/models/plane.glb");
+
+ PBRMaterial floor_mat = (PBRMaterial){0};
+ PBRMaterialSetup(&floor_mat, shader, NULL);
+ PBRLoadTextures(&floor_mat, PBR_TEXTURE_ALBEDO, "resources/road_a.png");
+ PBRLoadTextures(&floor_mat, PBR_TEXTURE_MRA, "resources/road_mra.png");
+ PBRLoadTextures(&floor_mat, PBR_TEXTURE_NORMAL, "resources/road_n.png");
+ PBRSetVec2(&floor_mat, PBR_VEC2_TILING,(Vector2){0.5,0.5});
+ PBRSetMaterial(&floor,&floor_mat,0);
+
+ shader.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(shader, "viewPos");
+ int numOfLightsLoc = GetShaderLocation(shader, "numOfLights");
+ int numOfLights = 4;
+ SetShaderValue(shader, numOfLightsLoc, &numOfLights, SHADER_UNIFORM_INT);
+
+ Color ambCol = (Color){26,32,135,255};
+ float ambIntens = 0.02;
+
+ int albedoLoc = GetShaderLocation(shader, "albedo");
+ PBRSetAmbient(shader,ambCol,ambIntens);
+
+ // Create lights
+ PBRLight lights[MAX_LIGHTS] = { 0 };
+ lights[0] = PBRLightCreate(LIGHT_POINT, (Vector3){ -1, 1, -2 }, (Vector3){0,0,0}, YELLOW,4, shader);
+ lights[1] = PBRLightCreate(LIGHT_POINT, (Vector3){ 2, 1, 1 }, (Vector3){0,0,0}, GREEN,3.3, shader);
+ lights[2] = PBRLightCreate(LIGHT_POINT, (Vector3){ -2, 1, 1 }, (Vector3){0,0,0}, RED,8.3, shader);
+ lights[3] = PBRLightCreate(LIGHT_POINT, (Vector3){ 1, 1, -2 }, (Vector3){0,0,0}, BLUE,2, shader);
+ SetShaderValueV(shader, GetShaderLocation(shader, "lights"), lights, SHADER_UNIFORM_FLOAT, numOfLights);
+
+ SetTargetFPS(60); // Set our game to run at 60 frames-per-second-------------------------------------------------------------
+
+ int emissiveCnt = 0;
+ // Main game loop
+ while (!WindowShouldClose()) // Detect window close button or ESC key
+ {
+ // Update
+ //----------------------------------------------------------------------------------
+ UpdateCamera(&camera, CAMERA_ORBITAL);
+
+ // Update the shader with the camera view vector (points towards { 0.0f, 0.0f, 0.0f })
+ float cameraPos[3] = {camera.position.x, camera.position.y, camera.position.z};
+ SetShaderValue(shader, shader.locs[SHADER_LOC_VECTOR_VIEW], cameraPos, SHADER_UNIFORM_VEC3);
+
+ // Check key inputs to enable/disable lights
+ if (IsKeyPressed(KEY_Y)) { lights[0].enabled = !lights[0].enabled; }
+ if (IsKeyPressed(KEY_G)) { lights[1].enabled = !lights[1].enabled; }
+ if (IsKeyPressed(KEY_R)) { lights[2].enabled = !lights[2].enabled; }
+ if (IsKeyPressed(KEY_B)) { lights[3].enabled = !lights[3].enabled; }
+
+ // Update light values (actually, only enable/disable them)
+ for (int i = 0; i < MAX_LIGHTS; i++) PBRLightUpdate(shader, lights[i]);
+ emissiveCnt--;
+ if(emissiveCnt<=0){
+ emissiveCnt = GetRandomValue(0,20);
+ PBRSetFloat(&model_mat,PBR_PARAM_EMISSIVE,(float)GetRandomValue(0,100)/100);
+ }
+ //----------------------------------------------------------------------------------
+
+ // Draw
+ //----------------------------------------------------------------------------------
+ BeginDrawing();
+ ClearBackground(BLACK);
+ BeginMode3D(camera);
+
+ PBRDrawModel(floor, (Vector3){0,0,0}, 5.0f);
+ PBRDrawModel(model, (Vector3) {0, 0.0, 0}, 0.005);
+
+ // Draw spheres to show where the lights are
+ for (int i = 0; i < MAX_LIGHTS; i++) {
+ Color col = (Color) {lights[i].color[0] * 255, lights[i].color[1] * 255, lights[i].color[2] * 255,
+ lights[i].color[3] * 255};
+ if (lights[i].enabled) DrawSphereEx(lights[i].position, 0.2f, 8, 8, col);
+ else DrawSphereWires(lights[i].position, 0.2f, 8, 8, ColorAlpha(col, 0.3f));
+ }
+ EndMode3D();
+
+ DrawText("(c) Old Rusty Car model by Renafox (https://skfb.ly/LxRy)", screenWidth - 320, screenHeight - 20, 10, GRAY);
+ DrawFPS(10, 10);
+
+ EndDrawing();
+ //----------------------------------------------------------------------------------
+ }
+
+ //--------------------------------------------------------------------------------------
+ // De-Initialization
+ //--------------------------------------------------------------------------------------
+
+ UnloadModel(floor.model); // Unload model
+ UnloadModel(model.model); // Unload model
+ UnloadShader(shader); // Unload Shader
+ UnloadPBRMaterial(floor_mat); // Unload PBRMaterial
+ UnloadPBRMaterial(model_mat); // Unload PBRMaterial
+
+ CloseWindow(); // Close window and OpenGL context
+ //--------------------------------------------------------------------------------------
+
+ return 0;
+ }