Implement PBR
This commit is contained in:
parent
f34a9b01a0
commit
3503fc15d2
14 changed files with 659 additions and 10 deletions
BIN
assets/models/blue-sphere.bin
Normal file
BIN
assets/models/blue-sphere.bin
Normal file
Binary file not shown.
BIN
assets/models/blue-sphere.blend
Normal file
BIN
assets/models/blue-sphere.blend
Normal file
Binary file not shown.
121
assets/models/blue-sphere.gltf
Normal file
121
assets/models/blue-sphere.gltf
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v4.0.44",
|
||||
"version":"2.0"
|
||||
},
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Sphere"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"name":"Material.001",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.04434913769364357,
|
||||
0,
|
||||
0.8024659156799316,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0.8912280797958374,
|
||||
"roughnessFactor":0.3807017505168915
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Sphere",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":559,
|
||||
"max":[
|
||||
0.9999997019767761,
|
||||
1,
|
||||
0.9999993443489075
|
||||
],
|
||||
"min":[
|
||||
-0.9999990463256836,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":559,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":559,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":2880,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":6708,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":6708,
|
||||
"byteOffset":6708,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":4472,
|
||||
"byteOffset":13416,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":5760,
|
||||
"byteOffset":17888,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":23648,
|
||||
"uri":"blue-sphere.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
assets/models/crystal-monkey.bin
Normal file
BIN
assets/models/crystal-monkey.bin
Normal file
Binary file not shown.
BIN
assets/models/crystal-monkey.blend
Normal file
BIN
assets/models/crystal-monkey.blend
Normal file
Binary file not shown.
BIN
assets/models/crystal-monkey.blend1
Normal file
BIN
assets/models/crystal-monkey.blend1
Normal file
Binary file not shown.
134
assets/models/crystal-monkey.gltf
Normal file
134
assets/models/crystal-monkey.gltf
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
{
|
||||
"asset":{
|
||||
"generator":"Khronos glTF Blender I/O v4.0.44",
|
||||
"version":"2.0"
|
||||
},
|
||||
"extensionsUsed":[
|
||||
"KHR_materials_sheen"
|
||||
],
|
||||
"scene":0,
|
||||
"scenes":[
|
||||
{
|
||||
"name":"Scene",
|
||||
"nodes":[
|
||||
0
|
||||
]
|
||||
}
|
||||
],
|
||||
"nodes":[
|
||||
{
|
||||
"mesh":0,
|
||||
"name":"Suzanne"
|
||||
}
|
||||
],
|
||||
"materials":[
|
||||
{
|
||||
"doubleSided":true,
|
||||
"extensions":{
|
||||
"KHR_materials_sheen":{
|
||||
"sheenColorFactor":[
|
||||
1.0,
|
||||
1.0,
|
||||
1.0
|
||||
],
|
||||
"sheenRoughnessFactor":0.5
|
||||
}
|
||||
},
|
||||
"name":"Material.001",
|
||||
"pbrMetallicRoughness":{
|
||||
"baseColorFactor":[
|
||||
0.8002911806106567,
|
||||
0,
|
||||
0.04051172733306885,
|
||||
1
|
||||
],
|
||||
"metallicFactor":0.8771929740905762,
|
||||
"roughnessFactor":0.23684212565422058
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes":[
|
||||
{
|
||||
"name":"Suzanne",
|
||||
"primitives":[
|
||||
{
|
||||
"attributes":{
|
||||
"POSITION":0,
|
||||
"NORMAL":1,
|
||||
"TEXCOORD_0":2
|
||||
},
|
||||
"indices":3,
|
||||
"material":0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"accessors":[
|
||||
{
|
||||
"bufferView":0,
|
||||
"componentType":5126,
|
||||
"count":1966,
|
||||
"max":[
|
||||
1.3671875,
|
||||
0.984375,
|
||||
0.8515625
|
||||
],
|
||||
"min":[
|
||||
-1.3671875,
|
||||
-0.984375,
|
||||
-0.8515625
|
||||
],
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":1,
|
||||
"componentType":5126,
|
||||
"count":1966,
|
||||
"type":"VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView":2,
|
||||
"componentType":5126,
|
||||
"count":1966,
|
||||
"type":"VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView":3,
|
||||
"componentType":5123,
|
||||
"count":2904,
|
||||
"type":"SCALAR"
|
||||
}
|
||||
],
|
||||
"bufferViews":[
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":23592,
|
||||
"byteOffset":0,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":23592,
|
||||
"byteOffset":23592,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":15728,
|
||||
"byteOffset":47184,
|
||||
"target":34962
|
||||
},
|
||||
{
|
||||
"buffer":0,
|
||||
"byteLength":5808,
|
||||
"byteOffset":62912,
|
||||
"target":34963
|
||||
}
|
||||
],
|
||||
"buffers":[
|
||||
{
|
||||
"byteLength":68720,
|
||||
"uri":"crystal-monkey.bin"
|
||||
}
|
||||
]
|
||||
}
|
||||
160
assets/shaders/pbr.frag
Normal file
160
assets/shaders/pbr.frag
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 v_normal;
|
||||
in vec2 v_tex;
|
||||
in vec3 v_position; // view-space position
|
||||
|
||||
out vec4 frag_color;
|
||||
|
||||
// Material inputs
|
||||
uniform sampler2D u_base_color_map; // sRGB
|
||||
uniform int u_has_base_color_map;
|
||||
uniform vec4 u_base_color_factor;
|
||||
|
||||
uniform sampler2D u_metallic_roughness_map; // linear, (r=occlusion in glTF separate, g=roughness, b=metallic)
|
||||
uniform int u_has_metallic_roughness_map;
|
||||
uniform float u_metallic_factor;
|
||||
uniform float u_roughness_factor;
|
||||
|
||||
uniform sampler2D u_occlusion_map; // linear (r)
|
||||
uniform int u_has_occlusion_map;
|
||||
|
||||
uniform sampler2D u_emissive_map; // sRGB
|
||||
uniform int u_has_emissive_map;
|
||||
uniform vec3 u_emissive_factor;
|
||||
|
||||
// Directional light (in view space)
|
||||
uniform vec3 u_dir_light_dir; // direction from light towards the scene (L)
|
||||
uniform vec3 u_dir_light_color;
|
||||
uniform float u_dir_light_intensity;
|
||||
|
||||
// Point lights (in view space)
|
||||
#define MAX_POINT_LIGHTS 4
|
||||
uniform int u_point_light_count;
|
||||
uniform vec3 u_point_light_pos[MAX_POINT_LIGHTS];
|
||||
uniform vec3 u_point_light_color[MAX_POINT_LIGHTS];
|
||||
uniform float u_point_light_intensity[MAX_POINT_LIGHTS];
|
||||
uniform float u_point_light_range[MAX_POINT_LIGHTS];
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
vec3 fresnel_schlick(float cosTheta, vec3 F0) {
|
||||
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
|
||||
}
|
||||
|
||||
float distribution_ggx(float NdotH, float alpha) {
|
||||
float a2 = alpha * alpha;
|
||||
float denom = (NdotH * NdotH) * (a2 - 1.0) + 1.0;
|
||||
return a2 / (PI * denom * denom + 1e-6);
|
||||
}
|
||||
|
||||
float geometry_schlick_ggx(float NdotV, float k) {
|
||||
return NdotV / (NdotV * (1.0 - k) + k + 1e-6);
|
||||
}
|
||||
|
||||
float geometry_smith(float NdotV, float NdotL, float alpha) {
|
||||
// Schlick-GGX with k from alpha for direct lighting
|
||||
float k = (alpha + 1.0);
|
||||
k = (k * k) / 8.0;
|
||||
float ggx1 = geometry_schlick_ggx(NdotV, k);
|
||||
float ggx2 = geometry_schlick_ggx(NdotL, k);
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
||||
struct PBRMaterial {
|
||||
vec3 baseColor;
|
||||
float metallic;
|
||||
float roughness;
|
||||
float ao;
|
||||
vec3 emissive;
|
||||
};
|
||||
|
||||
PBRMaterial getMaterial() {
|
||||
// Base color (sampled as sRGB then assumed linear by GL; still gamma-correct final output)
|
||||
vec3 baseTex = (u_has_base_color_map != 0) ? texture(u_base_color_map, v_tex).rgb : vec3(1.0);
|
||||
vec3 baseColor = baseTex * u_base_color_factor.rgb;
|
||||
|
||||
float metallicTex = (u_has_metallic_roughness_map != 0) ? texture(u_metallic_roughness_map, v_tex).b : 1.0;
|
||||
float roughnessTex = (u_has_metallic_roughness_map != 0) ? texture(u_metallic_roughness_map, v_tex).g : 1.0;
|
||||
|
||||
float metallic = clamp(u_metallic_factor * metallicTex, 0.0, 1.0);
|
||||
float roughness = clamp(u_roughness_factor * roughnessTex, 0.04, 1.0);
|
||||
|
||||
float ao = (u_has_occlusion_map != 0) ? texture(u_occlusion_map, v_tex).r : 1.0;
|
||||
|
||||
vec3 emissiveTex = (u_has_emissive_map != 0) ? texture(u_emissive_map, v_tex).rgb : vec3(0.0);
|
||||
vec3 emissive = emissiveTex * u_emissive_factor;
|
||||
|
||||
PBRMaterial m;
|
||||
m.baseColor = baseColor;
|
||||
m.metallic = metallic;
|
||||
m.roughness = roughness;
|
||||
m.ao = ao;
|
||||
m.emissive = emissive;
|
||||
return m;
|
||||
}
|
||||
|
||||
vec3 evaluatePBR(vec3 N, vec3 V, vec3 L, vec3 lightColor, float lightIntensity, PBRMaterial m) {
|
||||
vec3 H = normalize(V + L);
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float NdotH = max(dot(N, H), 0.0);
|
||||
float HdotV = max(dot(H, V), 0.0);
|
||||
|
||||
if (NdotL <= 0.0 || NdotV <= 0.0) return vec3(0.0);
|
||||
|
||||
float alpha = m.roughness * m.roughness;
|
||||
|
||||
vec3 F0 = mix(vec3(0.04), m.baseColor, m.metallic);
|
||||
vec3 F = fresnel_schlick(HdotV, F0);
|
||||
float D = distribution_ggx(NdotH, alpha);
|
||||
float G = geometry_smith(NdotV, NdotL, alpha);
|
||||
|
||||
vec3 numerator = F * D * G;
|
||||
float denominator = max(4.0 * NdotV * NdotL, 1e-6);
|
||||
vec3 specular = numerator / denominator;
|
||||
|
||||
vec3 kS = F;
|
||||
vec3 kD = (vec3(1.0) - kS) * (1.0 - m.metallic);
|
||||
|
||||
vec3 diffuse = (m.baseColor / PI) * kD;
|
||||
|
||||
vec3 radiance = lightColor * lightIntensity;
|
||||
|
||||
return (diffuse + specular) * radiance * NdotL;
|
||||
}
|
||||
|
||||
void main() {
|
||||
PBRMaterial mat = getMaterial();
|
||||
|
||||
vec3 N = normalize(v_normal);
|
||||
vec3 V = normalize(-v_position); // camera at origin in view-space
|
||||
|
||||
vec3 color = vec3(0.0);
|
||||
|
||||
// Directional light contribution
|
||||
vec3 Ld = normalize(u_dir_light_dir);
|
||||
color += evaluatePBR(N, V, Ld, u_dir_light_color, u_dir_light_intensity, mat);
|
||||
|
||||
// Point lights
|
||||
for (int i = 0; i < u_point_light_count && i < MAX_POINT_LIGHTS; ++i) {
|
||||
vec3 toLight = u_point_light_pos[i] - v_position;
|
||||
float dist = length(toLight);
|
||||
vec3 L = toLight / max(dist, 1e-4);
|
||||
|
||||
float cutoff = 1.0 - smoothstep(0.9 * u_point_light_range[i], u_point_light_range[i], dist);
|
||||
float attenuation = (1.0 / max(dist * dist, 1e-4)) * cutoff;
|
||||
|
||||
vec3 contrib = evaluatePBR(N, V, L, u_point_light_color[i], u_point_light_intensity[i] * attenuation, mat);
|
||||
color += contrib;
|
||||
}
|
||||
|
||||
// Simple ambient and AO
|
||||
vec3 ambient = 0.03 * mat.baseColor * (1.0 - mat.metallic) * mat.ao;
|
||||
color += ambient + mat.emissive;
|
||||
|
||||
// Gamma correction to sRGB
|
||||
color = pow(color, vec3(1.0 / 2.2));
|
||||
|
||||
frag_color = vec4(color, u_base_color_factor.a);
|
||||
}
|
||||
23
assets/shaders/pbr.vert
Normal file
23
assets/shaders/pbr.vert
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
#version 330 core
|
||||
|
||||
in vec3 position;
|
||||
in vec3 normal;
|
||||
in vec2 tex_coords;
|
||||
|
||||
uniform mat4 model;
|
||||
uniform mat4 view;
|
||||
uniform mat4 projection;
|
||||
uniform vec2 uv_offset;
|
||||
uniform vec2 uv_scale;
|
||||
|
||||
out vec3 v_normal;
|
||||
out vec2 v_tex;
|
||||
out vec3 v_position; // view-space position
|
||||
|
||||
void main() {
|
||||
mat4 modelview = view * model;
|
||||
v_normal = transpose(inverse(mat3(modelview))) * normal;
|
||||
v_tex = tex_coords * uv_scale + uv_offset;
|
||||
v_position = (modelview * vec4(position, 1.0)).xyz;
|
||||
gl_Position = projection * modelview * vec4(position, 1.0);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue