From f56e524d05c3f6f10cb2762914a2117794345e79 Mon Sep 17 00:00:00 2001 From: admin Date: Wed, 8 Oct 2025 19:13:05 +0200 Subject: [PATCH] feat: pbr try --- src/renderer/renderer.cpp | 2 +- src/renderer/wavefront.cpp | 96 +++++++++++++++++++++++++++++------ src/shaders/pbr.fs | 100 +++++++++++++++++++++---------------- 3 files changed, 139 insertions(+), 59 deletions(-) diff --git a/src/renderer/renderer.cpp b/src/renderer/renderer.cpp index 8812e96..0878065 100644 --- a/src/renderer/renderer.cpp +++ b/src/renderer/renderer.cpp @@ -23,7 +23,7 @@ Renderer::Renderer() m_shader.init( FileManager::read("./src/shaders/simple.vs"), - FileManager::read("./src/shaders/simple.fs") + FileManager::read("./src/shaders/pbr.fs") ); m_model = glm::mat4(1.f); diff --git a/src/renderer/wavefront.cpp b/src/renderer/wavefront.cpp index 601a978..0224267 100644 --- a/src/renderer/wavefront.cpp +++ b/src/renderer/wavefront.cpp @@ -393,29 +393,97 @@ Object* Object::LoadFile(const std::string& filename) { } +// void Object::Render(Shader& shader) +// { +// for (auto &mesh : m_meshes) { +// auto material = GetMaterial(mesh.materialName); + +// shader.setFloat("ambientStrength", 0.2f); +// shader.setFloat("shininess", material->GetSpecularWeight()); +// shader.setFloat("opacity", material->GetOpacity()); +// shader.setBool("useSpecular", material->GetIllumination() >= 2); +// shader.setFloat("specularStrength", 1.0f); +// shader.setVec3("ambientColor", material->GetAmbientColor()); +// shader.setVec3("diffuseColor", material->GetDiffuseColor()); +// shader.setVec3("specularColor", material->GetSpecularColor()); + +// if (material->HasDiffuseTexture()) { +// shader.setBool("useTexture", true); +// glActiveTexture(GL_TEXTURE0); +// glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID()); +// shader.setInt("diffuseTex", 0); +// } else { +// shader.setBool("useTexture", false); +// } + +// mesh.Render(); +// } +// } + void Object::Render(Shader& shader) { - for (auto &mesh : m_meshes) { + for (auto &mesh : m_meshes) + { auto material = GetMaterial(mesh.materialName); - - shader.setFloat("ambientStrength", 0.2f); - shader.setFloat("shininess", material->GetSpecularWeight()); - shader.setFloat("opacity", material->GetOpacity()); - shader.setBool("useSpecular", material->GetIllumination() >= 2); - shader.setFloat("specularStrength", 1.0f); - shader.setVec3("ambientColor", material->GetAmbientColor()); - shader.setVec3("diffuseColor", material->GetDiffuseColor()); - shader.setVec3("specularColor", material->GetSpecularColor()); + // --- Basic material properties --- + shader.setFloat("opacity", material->GetOpacity()); + + // Albedo (base color) + shader.setVec3("albedo", material->GetDiffuseColor()); + + // Metallic and roughness (defaults) + shader.setFloat("metallic", 0.8f); + shader.setFloat("roughness", 0.5f); + shader.setFloat("ao", 1.0f); // default ambient occlusion if none + + // --- Optional textures --- + int texUnit = 0; + + // Albedo texture if (material->HasDiffuseTexture()) { - shader.setBool("useTexture", true); - glActiveTexture(GL_TEXTURE0); + shader.setBool("useAlbedoMap", true); + glActiveTexture(GL_TEXTURE0 + texUnit); glBindTexture(GL_TEXTURE_2D, material->GetDiffuseTexture()->GetID()); - shader.setInt("diffuseTex", 0); + shader.setInt("albedoTex", texUnit++); } else { - shader.setBool("useTexture", false); + shader.setBool("useAlbedoMap", false); } + // Metallic texture + // if (material->HasMetallicTexture()) { + if (false) { + shader.setBool("useMetallicMap", true); + glActiveTexture(GL_TEXTURE0 + texUnit); + // glBindTexture(GL_TEXTURE_2D, material->GetMetallicTexture()->GetID()); + shader.setInt("metallicTex", texUnit++); + } else { + shader.setBool("useMetallicMap", false); + } + + // Roughness texture + // if (material->HasRoughnessTexture()) { + if (false) { + shader.setBool("useRoughnessMap", true); + glActiveTexture(GL_TEXTURE0 + texUnit); + // glBindTexture(GL_TEXTURE_2D, material->GetRoughnessTexture()->GetID()); + shader.setInt("roughnessTex", texUnit++); + } else { + shader.setBool("useRoughnessMap", false); + } + + // AO texture + // if (material->HasAoTexture()) { + if (false) { + shader.setBool("useAoMap", true); + glActiveTexture(GL_TEXTURE0 + texUnit); + // glBindTexture(GL_TEXTURE_2D, material->GetAoTexture()->GetID()); + shader.setInt("aoTex", texUnit++); + } else { + shader.setBool("useAoMap", false); + } + + // --- Render mesh --- mesh.Render(); } } diff --git a/src/shaders/pbr.fs b/src/shaders/pbr.fs index cb930aa..803f50f 100644 --- a/src/shaders/pbr.fs +++ b/src/shaders/pbr.fs @@ -1,37 +1,42 @@ #version 410 core -// Output color out vec4 FragColor; in vec3 vertexPos; in vec3 vertexNormal; in vec2 TexCoords; +// Lighting inputs uniform vec3 lightPos; uniform vec3 viewPos; -// From Object Renderer -uniform vec3 ambientColor; -uniform vec3 diffuseColor; -uniform vec3 specularColor; // used as F0 (base reflectance) +// Material parameters +uniform vec3 albedo; // Base color (replaces diffuseColor) +uniform float metallic; // 0 = dielectric, 1 = metal +uniform float roughness; // 0 = smooth mirror, 1 = rough +uniform float ao; // Ambient occlusion -uniform float ambientStrength; -uniform float specularStrength; -uniform float shininess; // mapped to roughness -uniform bool useSpecular; +// Textures +uniform sampler2D albedoTex; +uniform sampler2D metallicTex; +uniform sampler2D roughnessTex; +uniform sampler2D aoTex; +uniform bool useAlbedoMap; +uniform bool useMetallicMap; +uniform bool useRoughnessMap; +uniform bool useAoMap; uniform float opacity; -uniform sampler2D diffuseTex; -uniform bool useTexture; +// Used for emissive light sources +uniform bool isLight; +#define PI 3.14159265359 #define LIGHT_COLOR vec3(1.0, 1.0, 1.0) // ---------------------------------------------------------------------------- -// Helper functions for Cook-Torrance BRDF +// Helper functions // ---------------------------------------------------------------------------- - -// Normal Distribution Function (GGX/Trowbridge-Reitz) float DistributionGGX(vec3 N, vec3 H, float roughness) { float a = roughness * roughness; @@ -41,16 +46,15 @@ float DistributionGGX(vec3 N, vec3 H, float roughness) float num = a2; float denom = (NdotH2 * (a2 - 1.0) + 1.0); - denom = 3.14159265 * denom * denom; + denom = PI * denom * denom; return num / denom; } -// Geometry function (Schlick-GGX) float GeometrySchlickGGX(float NdotV, float roughness) { float r = (roughness + 1.0); - float k = (r * r) / 8.0; // remapped for direct lighting + float k = (r * r) / 8.0; float num = NdotV; float denom = NdotV * (1.0 - k) + k; @@ -58,69 +62,77 @@ float GeometrySchlickGGX(float NdotV, float roughness) return num / denom; } -// Smith's geometry function float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) { float NdotV = max(dot(N, V), 0.0); float NdotL = max(dot(N, L), 0.0); - float ggx1 = GeometrySchlickGGX(NdotV, roughness); - float ggx2 = GeometrySchlickGGX(NdotL, roughness); + float ggx2 = GeometrySchlickGGX(NdotV, roughness); + float ggx1 = GeometrySchlickGGX(NdotL, roughness); + return ggx1 * ggx2; } -// Fresnel term (Schlick's approximation) -vec3 FresnelSchlick(float cosTheta, vec3 F0) +vec3 fresnelSchlick(float cosTheta, vec3 F0) { return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); } +// ---------------------------------------------------------------------------- +// Main // ---------------------------------------------------------------------------- void main() { + if (isLight) { + vec3 emissive = LIGHT_COLOR * 10.0; // bright light source + FragColor = vec4(emissive, 1.0); + return; + } + + // Inputs vec3 N = normalize(vertexNormal); vec3 V = normalize(viewPos - vertexPos); vec3 L = normalize(lightPos - vertexPos); vec3 H = normalize(V + L); - // Texture or uniform color - vec3 albedo = (useTexture) - ? texture(diffuseTex, TexCoords).rgb - : diffuseColor; + // Base color (albedo) + vec3 baseColor = useAlbedoMap ? texture(albedoTex, TexCoords).rgb : albedo; - // Map shininess to roughness (inverse relationship) - float roughness = clamp(1.0 - (shininess / 256.0), 0.05, 1.0); + float metal = useMetallicMap ? texture(metallicTex, TexCoords).r : metallic; + float rough = useRoughnessMap ? texture(roughnessTex, TexCoords).r : roughness; + float aoValue = useAoMap ? texture(aoTex, TexCoords).r : ao; - // Base reflectivity (F0) - vec3 F0 = mix(vec3(0.04), specularColor, specularStrength); + // Reflectance at normal incidence (F0) + vec3 F0 = vec3(0.04); // typical dielectric reflectance + F0 = mix(F0, baseColor, metal); // metals use albedo as F0 // Cook-Torrance BRDF - float NDF = DistributionGGX(N, H, roughness); - float G = GeometrySmith(N, V, L, roughness); - vec3 F = FresnelSchlick(max(dot(H, V), 0.0), F0); + float NDF = DistributionGGX(N, H, rough); + float G = GeometrySmith(N, V, L, rough); + vec3 F = fresnelSchlick(max(dot(H, V), 0.0), F0); vec3 numerator = NDF * G * F; float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001; vec3 specular = numerator / denominator; - // Energy conservation + // kS is specular reflection, kD is diffuse reflection (energy conservation) vec3 kS = F; vec3 kD = vec3(1.0) - kS; - kD *= (useSpecular ? 1.0 : 1.0); // keep diffuse always unless specular is off + kD *= 1.0 - metal; float NdotL = max(dot(N, L), 0.0); - vec3 diffuse = kD * albedo / 3.14159265; - vec3 radiance = LIGHT_COLOR; + vec3 radiance = LIGHT_COLOR; // single light source color/intensity - vec3 Lo = (diffuse + specular) * radiance * NdotL; + vec3 Lo = (kD * baseColor / PI + specular) * radiance * NdotL; - // Ambient (simple, not IBL) - vec3 ambient = ambientStrength * ambientColor * albedo; + // Ambient (IBL approximation using ao) + vec3 ambient = vec3(0.03) * baseColor * aoValue; - vec3 result = ambient + Lo; + vec3 color = ambient + Lo; - // Gamma correction - result = pow(result, vec3(1.0/2.2)); + // HDR tonemapping and gamma correction + color = color / (color + vec3(1.0)); + color = pow(color, vec3(1.0 / 2.2)); - FragColor = vec4(result, opacity); + FragColor = vec4(color, opacity); }