basic PBR example (#3621)

* basic pbr example

pbr implementation  includes rpbr.h and few shader files header only file, which self contain everything needed for pbr rendering. Few textures and one model of the car which is under free licence which is included inside basic_pbr.c example file  currently supported shader versions are 120 and 330 , version 100 has small issue which I have to resolve

* Unloading PBRMAterial

I forgot unloading PBRMaterial

* fix small issue with texOffset assigment.

value was Vector4 at first but I found out it would be unclear for and users, so I change to have two Vector2 instead, but forgot to assign offset .

* Changed size of textures and file name changed

Changed size of textures from 2048x2048 to 1024x1024 and file name changed to shaders_basic_pbr.c ,
Added the function PBRModel PBRModelLoadFromMesh(Mesh mesh);
but GenMeshPlane(2, 2.0, 3, 3) culdn't be used because it crash once GenMeshTangents() is used with that plane mesh
This commit is contained in:
devdad 2023-12-15 17:13:44 +01:00 committed by GitHub
parent 489f0b93f9
commit 9bdc217987
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 1307 additions and 0 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 623 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 657 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 645 KiB

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

466
examples/shaders/rpbr.h Normal file
View File

@ -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

View File

@ -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;
}