Compare commits
1 commit
master
...
2025-09-26
| Author | SHA1 | Date | |
|---|---|---|---|
| 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);
|
||||||
|
}
|
||||||
|
|
@ -15,3 +15,10 @@ impl Transform {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ModelHandle(pub ModelID);
|
pub struct ModelHandle(pub ModelID);
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub struct PointLight {
|
||||||
|
pub color: Vec3,
|
||||||
|
pub intensity: f32,
|
||||||
|
pub range: f32,
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ use raidillon_engine::system::SystemContext;
|
||||||
use raidillon_platform::{Platform, Camera};
|
use raidillon_platform::{Platform, Camera};
|
||||||
use raidillon_assets::model_path;
|
use raidillon_assets::model_path;
|
||||||
use raidillon_core::engine::EngineTrait;
|
use raidillon_core::engine::EngineTrait;
|
||||||
use raidillon_ecs::components::ModelHandle;
|
use raidillon_ecs::components::{ModelHandle, PointLight};
|
||||||
use raidillon_ecs::Transform;
|
use raidillon_ecs::Transform;
|
||||||
use raidillon_core::scene::Scene;
|
use raidillon_core::scene::Scene;
|
||||||
#[cfg(feature = "glium")]
|
#[cfg(feature = "glium")]
|
||||||
use raidillon_glium::GliumPlatform;
|
use raidillon_glium::GliumPlatform;
|
||||||
use winit::event::{Event, WindowEvent};
|
use winit::event::{Event, WindowEvent};
|
||||||
|
|
||||||
const TEST_GLTF: &str = "pink-monkey.gltf";
|
const TEST_GLTF: &str = "blue-sphere.gltf";
|
||||||
|
|
||||||
const MAIN_SCENE_ID: &str = "main_scene";
|
const MAIN_SCENE_ID: &str = "main_scene";
|
||||||
|
|
||||||
|
|
@ -58,12 +58,25 @@ impl System for RenderingTestSystem {
|
||||||
|
|
||||||
ctx.scene.world.spawn((
|
ctx.scene.world.spawn((
|
||||||
Transform {
|
Transform {
|
||||||
translation: Vec3::new(0.0, 0.0, 0.0),
|
translation: Vec3::new(0.0, 0.0, 0.0),
|
||||||
rotation: Quat::IDENTITY,
|
rotation: Quat::IDENTITY,
|
||||||
scale: Vec3::new(1.0, 1.0, 1.0),
|
scale: Vec3::new(1.0, 1.0, 1.0),
|
||||||
},
|
},
|
||||||
ModelHandle(TEST_GLTF),
|
ModelHandle(TEST_GLTF),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
ctx.scene.world.spawn((
|
||||||
|
Transform {
|
||||||
|
translation: Vec3::new(0.0, 3.0, 0.0),
|
||||||
|
rotation: Quat::IDENTITY,
|
||||||
|
scale: Vec3::ONE,
|
||||||
|
},
|
||||||
|
PointLight {
|
||||||
|
color: Vec3::ONE,
|
||||||
|
intensity: 1.0,
|
||||||
|
range: 10.0,
|
||||||
|
},
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn frame_update(&mut self, ctx: &mut SystemContext) {
|
fn frame_update(&mut self, ctx: &mut SystemContext) {
|
||||||
|
|
@ -79,7 +92,6 @@ impl System for RenderingTestSystem {
|
||||||
t.rotation *= Quat::from_rotation_y(*self.rotation_speed.borrow() * ctx.platform_context.time_ctx.fixed_dt);
|
t.rotation *= Quat::from_rotation_y(*self.rotation_speed.borrow() * ctx.platform_context.time_ctx.fixed_dt);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ use raidillon_core::engine::EngineTrait;
|
||||||
use raidillon_core::time;
|
use raidillon_core::time;
|
||||||
use raidillon_core::time::Time;
|
use raidillon_core::time::Time;
|
||||||
use crate::render::debug_ui::ImguiBridge;
|
use crate::render::debug_ui::ImguiBridge;
|
||||||
use crate::render::BasicMeshRenderingSystem;
|
use crate::render::PbrMeshRenderingSystem;
|
||||||
use crate::GliumAssetManager;
|
use crate::GliumAssetManager;
|
||||||
|
|
||||||
pub struct GliumPlatform<E: EngineTrait<PlatformCtx = PlatformContext>> {
|
pub struct GliumPlatform<E: EngineTrait<PlatformCtx = PlatformContext>> {
|
||||||
|
|
@ -45,7 +45,7 @@ impl<E: EngineTrait<PlatformCtx = PlatformContext>> Platform<E> for GliumPlatfor
|
||||||
let time = time::Time::new(time_cfg);
|
let time = time::Time::new(time_cfg);
|
||||||
|
|
||||||
// Install rendering systems
|
// Install rendering systems
|
||||||
rendering_system_manager.add::<BasicMeshRenderingSystem>(&display, &window);
|
rendering_system_manager.add::<PbrMeshRenderingSystem>(&display, &window);
|
||||||
rendering_system_manager.add::<ImguiBridge>(&display, &window);
|
rendering_system_manager.add::<ImguiBridge>(&display, &window);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
mod basic;
|
mod pbr;
|
||||||
pub mod debug_ui;
|
pub mod debug_ui;
|
||||||
|
|
||||||
pub use basic::BasicMeshRenderingSystem;
|
pub use pbr::PbrMeshRenderingSystem;
|
||||||
|
|
|
||||||
192
glium_platform/src/render/pbr.rs
Normal file
192
glium_platform/src/render/pbr.rs
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
// AI Generated
|
||||||
|
|
||||||
|
use glium::{uniform, Display, Program, Surface};
|
||||||
|
use glium::glutin::surface::WindowSurface;
|
||||||
|
use glium::texture::{RawImage2d, SrgbTexture2d, Texture2d};
|
||||||
|
use glium::uniforms::{MagnifySamplerFilter, MinifySamplerFilter, SamplerWrapFunction};
|
||||||
|
use glam::{Vec3, Vec4, Mat4};
|
||||||
|
use crate::RenderingSystem;
|
||||||
|
use crate::system::RenderingContext;
|
||||||
|
use raidillon_assets::include_shader;
|
||||||
|
pub use raidillon_platform::Camera;
|
||||||
|
use raidillon_ecs::components::{ModelHandle, PointLight};
|
||||||
|
use raidillon_ecs::Transform;
|
||||||
|
use crate::model::Model;
|
||||||
|
|
||||||
|
pub struct PbrMeshRenderingSystem {
|
||||||
|
program: Program,
|
||||||
|
white_srgb: SrgbTexture2d,
|
||||||
|
black_srgb: SrgbTexture2d,
|
||||||
|
white_linear: Texture2d,
|
||||||
|
params: glium::DrawParameters<'static>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RenderingSystem for PbrMeshRenderingSystem {
|
||||||
|
fn initialize(display: &Display<WindowSurface>, _window: &glium::winit::window::Window) -> Self {
|
||||||
|
const VERT_SRC: &str = include_shader!("pbr.vert");
|
||||||
|
const FRAG_SRC: &str = include_shader!("pbr.frag");
|
||||||
|
|
||||||
|
let program = Program::from_source(display, VERT_SRC, FRAG_SRC, None).unwrap();
|
||||||
|
|
||||||
|
let white_srgb = {
|
||||||
|
let data = vec![255u8, 255u8, 255u8, 255u8];
|
||||||
|
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
|
||||||
|
SrgbTexture2d::new(display, raw).unwrap()
|
||||||
|
};
|
||||||
|
let black_srgb = {
|
||||||
|
let data = vec![0u8, 0u8, 0u8, 255u8];
|
||||||
|
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
|
||||||
|
SrgbTexture2d::new(display, raw).unwrap()
|
||||||
|
};
|
||||||
|
let white_linear = {
|
||||||
|
let data = vec![255u8, 255u8, 255u8, 255u8];
|
||||||
|
let raw = RawImage2d::from_raw_rgba(data, (1, 1));
|
||||||
|
Texture2d::new(display, raw).unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let params = glium::DrawParameters {
|
||||||
|
depth: glium::Depth {
|
||||||
|
test: glium::draw_parameters::DepthTest::IfLess,
|
||||||
|
write: true,
|
||||||
|
.. Default::default()
|
||||||
|
},
|
||||||
|
.. Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
Self {
|
||||||
|
program,
|
||||||
|
white_srgb,
|
||||||
|
black_srgb,
|
||||||
|
white_linear,
|
||||||
|
params,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, ctx: &mut RenderingContext) {
|
||||||
|
// Acquire camera
|
||||||
|
let cam = match ctx.scene.world.query::<&Camera>().iter().next() {
|
||||||
|
Some((_, cam)) => *cam,
|
||||||
|
None => {
|
||||||
|
eprintln!("[renderer] No camera component found. Skipping frame");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let view: Mat4 = cam.view();
|
||||||
|
let projection: Mat4 = cam.projection();
|
||||||
|
|
||||||
|
// Directional light setup
|
||||||
|
let dir_light_dir_world: Vec3 = Vec3::new(-0.5, 3.0, -0.25).normalize();
|
||||||
|
let dir_light_dir_view: Vec3 = (view * Vec4::new(dir_light_dir_world.x, dir_light_dir_world.y, dir_light_dir_world.z, 0.0)).truncate().normalize();
|
||||||
|
let dir_light_color: Vec3 = Vec3::splat(1.0);
|
||||||
|
let dir_light_intensity: f32 = 3.5;
|
||||||
|
|
||||||
|
// Collect point lights
|
||||||
|
const MAX_POINT_LIGHTS: usize = 3;
|
||||||
|
let mut pl_pos = [[0.0f32; 3]; MAX_POINT_LIGHTS];
|
||||||
|
let mut pl_col = [[0.0f32; 3]; MAX_POINT_LIGHTS];
|
||||||
|
let mut pl_int = [0.0f32; MAX_POINT_LIGHTS];
|
||||||
|
let mut pl_range = [0.0f32; MAX_POINT_LIGHTS];
|
||||||
|
let mut pl_count: i32 = 0;
|
||||||
|
|
||||||
|
for (_, (tr, pl)) in ctx.scene.world.query::<(&Transform, &PointLight)>().iter() {
|
||||||
|
if (pl_count as usize) >= MAX_POINT_LIGHTS { break; }
|
||||||
|
let pos_view = (view * Vec4::new(tr.translation.x, tr.translation.y, tr.translation.z, 1.0)).truncate();
|
||||||
|
pl_pos[pl_count as usize] = [pos_view.x, pos_view.y, pos_view.z];
|
||||||
|
pl_col[pl_count as usize] = [pl.color.x, pl.color.y, pl.color.z];
|
||||||
|
pl_int[pl_count as usize] = pl.intensity;
|
||||||
|
pl_range[pl_count as usize] = pl.range;
|
||||||
|
pl_count += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let asset_manager = ctx.asset_manager.borrow();
|
||||||
|
|
||||||
|
for (_, (tr, mh)) in ctx.scene.world.query::<(&Transform, &ModelHandle)>().iter() {
|
||||||
|
let model_any = match asset_manager.get_model(&mh.0) { Some(m) => m, _ => continue };
|
||||||
|
let model = match model_any.downcast_ref::<Model>() { Some(m) => m, None => continue };
|
||||||
|
|
||||||
|
let mesh = &model.mesh;
|
||||||
|
let mat = &model.material;
|
||||||
|
|
||||||
|
// Base color texture (sRGB)
|
||||||
|
let base_color_tex: &SrgbTexture2d = mat.base_color.as_ref().unwrap_or(&self.white_srgb);
|
||||||
|
let mut base_sampler = base_color_tex.sampled();
|
||||||
|
base_sampler = base_sampler.wrap_function(SamplerWrapFunction::Repeat);
|
||||||
|
base_sampler = base_sampler.minify_filter(MinifySamplerFilter::Linear);
|
||||||
|
base_sampler = base_sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
||||||
|
let has_base_color_map: i32 = if mat.base_color.is_some() { 1 } else { 0 };
|
||||||
|
|
||||||
|
// MR map (linear)
|
||||||
|
let mr_tex: &Texture2d = mat.metallic_roughness.as_ref().unwrap_or(&self.white_linear);
|
||||||
|
let mut mr_sampler = mr_tex.sampled();
|
||||||
|
mr_sampler = mr_sampler.wrap_function(SamplerWrapFunction::Repeat);
|
||||||
|
mr_sampler = mr_sampler.minify_filter(MinifySamplerFilter::Linear);
|
||||||
|
mr_sampler = mr_sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
||||||
|
let has_mr_map: i32 = if mat.metallic_roughness.is_some() { 1 } else { 0 };
|
||||||
|
|
||||||
|
// Occlusion map (linear, R)
|
||||||
|
let occlusion_tex: &Texture2d = mat.occlusion.as_ref().unwrap_or(&self.white_linear);
|
||||||
|
let mut occl_sampler = occlusion_tex.sampled();
|
||||||
|
occl_sampler = occl_sampler.wrap_function(SamplerWrapFunction::Repeat);
|
||||||
|
occl_sampler = occl_sampler.minify_filter(MinifySamplerFilter::Linear);
|
||||||
|
occl_sampler = occl_sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
||||||
|
let has_occlusion_map: i32 = if mat.occlusion.is_some() { 1 } else { 0 };
|
||||||
|
|
||||||
|
// Emissive map (sRGB)
|
||||||
|
let emissive_tex: &SrgbTexture2d = mat.emissive.as_ref().unwrap_or(&self.black_srgb);
|
||||||
|
let mut emissive_sampler = emissive_tex.sampled();
|
||||||
|
emissive_sampler = emissive_sampler.wrap_function(SamplerWrapFunction::Repeat);
|
||||||
|
emissive_sampler = emissive_sampler.minify_filter(MinifySamplerFilter::Linear);
|
||||||
|
emissive_sampler = emissive_sampler.magnify_filter(MagnifySamplerFilter::Linear);
|
||||||
|
let has_emissive_map: i32 = if mat.emissive.is_some() { 1 } else { 0 };
|
||||||
|
|
||||||
|
let bc = mat.base_color_factor;
|
||||||
|
let ef = mat.emissive_factor;
|
||||||
|
|
||||||
|
let uniforms = uniform! {
|
||||||
|
model: tr.matrix().to_cols_array_2d(),
|
||||||
|
view: view.to_cols_array_2d(),
|
||||||
|
projection: projection.to_cols_array_2d(),
|
||||||
|
uv_offset: [mat.uv_offset.x, mat.uv_offset.y],
|
||||||
|
uv_scale: [mat.uv_scale.x, mat.uv_scale.y],
|
||||||
|
|
||||||
|
// Material
|
||||||
|
u_base_color_map: base_sampler,
|
||||||
|
u_has_base_color_map: has_base_color_map,
|
||||||
|
u_base_color_factor: [bc[0], bc[1], bc[2], bc[3]],
|
||||||
|
|
||||||
|
u_metallic_roughness_map: mr_sampler,
|
||||||
|
u_has_metallic_roughness_map: has_mr_map,
|
||||||
|
u_metallic_factor: mat.metal_factor,
|
||||||
|
u_roughness_factor: mat.roughness_factor,
|
||||||
|
|
||||||
|
u_occlusion_map: occl_sampler,
|
||||||
|
u_has_occlusion_map: has_occlusion_map,
|
||||||
|
|
||||||
|
u_emissive_map: emissive_sampler,
|
||||||
|
u_has_emissive_map: has_emissive_map,
|
||||||
|
u_emissive_factor: [ef[0], ef[1], ef[2]],
|
||||||
|
|
||||||
|
// Directional light (view-space)
|
||||||
|
u_dir_light_dir: [dir_light_dir_view.x, dir_light_dir_view.y, dir_light_dir_view.z],
|
||||||
|
u_dir_light_color: [dir_light_color.x, dir_light_color.y, dir_light_color.z],
|
||||||
|
u_dir_light_intensity: dir_light_intensity,
|
||||||
|
|
||||||
|
// Point lights (view-space)
|
||||||
|
u_point_light_count: pl_count,
|
||||||
|
u_point_light_pos: pl_pos,
|
||||||
|
u_point_light_color: pl_col,
|
||||||
|
u_point_light_intensity: pl_int,
|
||||||
|
u_point_light_range: pl_range,
|
||||||
|
};
|
||||||
|
|
||||||
|
ctx.target.draw(
|
||||||
|
&mesh.vbuf,
|
||||||
|
&mesh.ibuf,
|
||||||
|
&self.program,
|
||||||
|
&uniforms,
|
||||||
|
&self.params,
|
||||||
|
).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue