overload operator,overloaded operator
墨初 知识笔记 1481阅读
PBR基于物理的渲染可以实现更加真实的效果其Shader值得分析一下。但PBR需要较多的基础知识不适合不会OpenGL的朋友。
一、PBR理论
PBR指基于物理的渲染其理论较多需要的基础知识也较多我在这就不再写一遍了具体可以参看
LearnOpenGL PBR理论-英文 或者 LearnOpenGL PBR理论-中文

Overload也提供了这种材料借助贴图可以实现非常真实的材质效果。下面这个例子的贴图来自LearnOpenGL大家可以自己去下载。
二、PBR Shader分析
顶点着色器

#shader vertex#version 430 corelayout (location 0) in vec3 geo_Pos;layout (location 1) in vec2 geo_TexCoords;layout (location 2) in vec3 geo_Normal;layout (location 3) in vec3 geo_Tangent;layout (location 4) in vec3 geo_Bitangent;/* Global information sent by the engine */layout (std140) uniform EngineUBO{ mat4 ubo_Model; mat4 ubo_View; mat4 ubo_Projection; vec3 ubo_ViewPos; float ubo_Time;};/* Information passed to the fragment shader */out VS_OUT{ vec3 FragPos; vec3 Normal; vec2 TexCoords; mat3 TBN; flat vec3 TangentViewPos; vec3 TangentFragPos;} vs_out;void main(){ vs_out.TBN mat3 ( normalize(vec3(ubo_Model * vec4(geo_Tangent, 0.0))), normalize(vec3(ubo_Model * vec4(geo_Bitangent, 0.0))), normalize(vec3(ubo_Model * vec4(geo_Normal, 0.0))) ); mat3 TBNi transpose(vs_out.TBN); vs_out.FragPos vec3(ubo_Model * vec4(geo_Pos, 1.0)); vs_out.Normal normalize(mat3(transpose(inverse(ubo_Model))) * geo_Normal); vs_out.TexCoords geo_TexCoords; vs_out.TangentViewPos TBNi * ubo_ViewPos; vs_out.TangentFragPos TBNi * vs_out.FragPos; gl_Position ubo_Projection * ubo_View * vec4(vs_out.FragPos, 1.0);}
顶点着色器基本与standard材质一致这里就不再分析了具体可看standard材质Shader
片元着色器
#shader fragment#version 430 core/** 模型视图矩阵、摄像机位置使用UBO传入 *//* Global information sent by the engine */layout (std140) uniform EngineUBO{ mat4 ubo_Model; mat4 ubo_View; mat4 ubo_Projection; vec3 ubo_ViewPos; float ubo_Time;};/* 顶点着色器的输出 *//* Information passed from the fragment shader */in VS_OUT{ vec3 FragPos; vec3 Normal; vec2 TexCoords; mat3 TBN; flat vec3 TangentViewPos; vec3 TangentFragPos;} fs_in;/* 光源数据用SSBO传入 *//* Light information sent by the engine */layout(std430, binding 0) buffer LightSSBO{ mat4 ssbo_Lights[];};out vec4 FRAGMENT_COLOR;uniform sampler2D u_AlbedoMap; // 反照率贴图uniform sampler2D u_MetallicMap; // 金属度贴图uniform sampler2D u_RoughnessMap; // 粗糙度贴图uniform sampler2D u_AmbientOcclusionMap; // 环境光遮蔽贴图uniform sampler2D u_NormalMap; // 法线贴图uniform vec4 u_Albedo vec4(1.0); // 反照率系数控制反照率贴图的权重uniform vec2 u_TextureTiling vec2(1.0, 1.0);uniform vec2 u_TextureOffset vec2(0.0, 0.0);uniform bool u_EnableNormalMapping false; // 是否使用法线贴图uniform float u_HeightScale 0.0;uniform float u_Metallic 1.0; // 金属度uniform float u_Roughness 1.0; // 粗糙度const float PI 3.14159265359;// 计算法向分布函数D使用Trowbridge-Reitz GGX float DistributionGGX(vec3 N, vec3 H, float roughness){ float a roughness*roughness; float a2 a*a; float NdotH max(dot(N, H), 0.0); float NdotH2 NdotH*NdotH; float num a2; float denom (NdotH2 * (a2 - 1.0) 1.0); denom PI * denom * denom; return num / denom;}float GeometrySchlickGGX(float NdotV, float roughness){ float r (roughness 1.0); float k (r*r) / 8.0; float num NdotV; float denom NdotV * (1.0 - k) k; return num / denom;}// Smith’s methodfloat 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 ggx2 GeometrySchlickGGX(NdotV, roughness); float ggx1 GeometrySchlickGGX(NdotL, roughness); return ggx1 * ggx2;}// 菲涅尔项使用Fresnel-Schlick方程vec3 fresnelSchlick(float cosTheta, vec3 F0){ return F0 (1.0 - F0) * pow(1.0 - cosTheta, 5.0);}/* 将32位数字变成RGBA颜色 */vec3 UnPack(float p_Target){ return vec3 ( // CPU传入的数据是0-255转换成0-1.0 float((uint(p_Target) >> 24) & 0xff) * 0.003921568627451, float((uint(p_Target) >> 16) & 0xff) * 0.003921568627451, float((uint(p_Target) >> 8) & 0xff) * 0.003921568627451 );}bool PointInAABB(vec3 p_Point, vec3 p_AabbCenter, vec3 p_AabbHalfSize){ return ( p_Point.x > p_AabbCenter.x - p_AabbHalfSize.x && p_Point.x < p_AabbCenter.x p_AabbHalfSize.x && p_Point.y > p_AabbCenter.y - p_AabbHalfSize.y && p_Point.y < p_AabbCenter.y p_AabbHalfSize.y && p_Point.z > p_AabbCenter.z - p_AabbHalfSize.z && p_Point.z < p_AabbCenter.z p_AabbHalfSize.z );}/*光照衰减系数LearnOpenGL中有具体公式*/float LuminosityFromAttenuation(mat4 p_Light){ const vec3 lightPosition p_Light[0].rgb; const float constant p_Light[0][3]; const float linear p_Light[1][3]; const float quadratic p_Light[2][3]; const float distanceToLight length(lightPosition - fs_in.FragPos); const float attenuation (constant linear * distanceToLight quadratic * (distanceToLight * distanceToLight)); return 1.0 / attenuation;}/* 盒状环境光 */vec3 CalcAmbientBoxLight(mat4 p_Light){ const vec3 lightPosition p_Light[0].rgb; const vec3 lightColor UnPack(p_Light[2][0]); const float intensity p_Light[3][3]; const vec3 size vec3(p_Light[0][3], p_Light[1][3], p_Light[2][3]); return PointInAABB(fs_in.FragPos, lightPosition, size) ? lightColor * intensity : vec3(0.0);}/* 球状环境光 */vec3 CalcAmbientSphereLight(mat4 p_Light){ const vec3 lightPosition p_Light[0].rgb; const vec3 lightColor UnPack(p_Light[2][0]); const float intensity p_Light[3][3]; const float radius p_Light[0][3]; return distance(lightPosition, fs_in.FragPos) < radius ? lightColor * intensity : vec3(0.0);}void main(){ vec2 texCoords u_TextureOffset vec2(mod(fs_in.TexCoords.x * u_TextureTiling.x, 1), mod(fs_in.TexCoords.y * u_TextureTiling.y, 1)); vec4 albedoRGBA texture(u_AlbedoMap, texCoords) * u_Albedo; // Albedo反照率贴图数据 vec3 albedo pow(albedoRGBA.rgb, vec3(2.2)); // 这种反照率处理方式与LearOpenGL一致 float metallic texture(u_MetallicMap, texCoords).r * u_Metallic; // 金属度 float roughness texture(u_RoughnessMap, texCoords).r * u_Roughness; // 粗糙度 float ao texture(u_AmbientOcclusionMap, texCoords).r; // 环境光遮蔽AO vec3 normal; if (u_EnableNormalMapping) // 是否使用法线贴图 { normal texture(u_NormalMap, texCoords).rgb; // 法线贴图的原始值 normal normalize(normal * 2.0 - 1.0); // 法线贴图矢量坐标范围变成-1到1 normal normalize(fs_in.TBN * normal); // 变换到全局坐标系下 } else { normal normalize(fs_in.Normal); // 使用顶点着色器输出的法线 } vec3 N normalize(normal); vec3 V normalize(ubo_ViewPos - fs_in.FragPos); // 计算视线方向 vec3 F0 vec3(0.04); F0 mix(F0, albedo, metallic); // 插值方式得到平面的基础反射率F0 // reflectance equation vec3 Lo vec3(0.0); vec3 ambientSum vec3(0.0); // 环境光结果 for (int i 0; i < ssbo_Lights.length(); i) { // 两种环境光灯光 if (int(ssbo_Lights[i][3][0]) 3) { ambientSum CalcAmbientBoxLight(ssbo_Lights[i]); } else if (int(ssbo_Lights[i][3][0]) 4) { ambientSum CalcAmbientSphereLight(ssbo_Lights[i]); } else { // calculate per-light radiance // 光源方向 vec3 L int(ssbo_Lights[i][3][0]) 1 ? -ssbo_Lights[i][1].rgb : normalize(ssbo_Lights[i][0].rgb - fs_in.FragPos); vec3 H normalize(V L);// 半程向量 float distance length(ssbo_Lights[i][0].rgb - fs_in.FragPos); float lightCoeff 0.0; // 最终到片元处的光强系数 switch(int(ssbo_Lights[i][3][0])) { case 0: lightCoeff LuminosityFromAttenuation(ssbo_Lights[i]) * ssbo_Lights[i][3][3]; // 点光源要考虑随距离衰减 break; case 1: lightCoeff ssbo_Lights[i][3][3]; // 方向光无衰减 break; // 聚光灯的计算 case 2: const vec3 lightForward ssbo_Lights[i][1].rgb; const float cutOff cos(radians(ssbo_Lights[i][3][1])); const float outerCutOff cos(radians(ssbo_Lights[i][3][1] ssbo_Lights[i][3][2])); const vec3 lightDirection normalize(ssbo_Lights[i][0].rgb - fs_in.FragPos); const float luminosity LuminosityFromAttenuation(ssbo_Lights[i]); /* Calculate the spot intensity */ const float theta dot(lightDirection, normalize(-lightForward)); const float epsilon cutOff - outerCutOff; const float spotIntensity clamp((theta - outerCutOff) / epsilon, 0.0, 1.0); lightCoeff luminosity * spotIntensity * ssbo_Lights[i][3][3]; break; } vec3 radiance UnPack(ssbo_Lights[i][2][0]) * lightCoeff; // 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); // 菲涅尔项 vec3 kS F; vec3 kD vec3(1.0) - kS; kD * 1.0 - metallic; vec3 numerator NDF * G * F; float denominator 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0); vec3 specular numerator / max(denominator, 0.001); // add to outgoing radiance Lo float NdotL max(dot(N, L), 0.0); Lo (kD * albedo / PI specular) * radiance * NdotL; } } vec3 ambient ambientSum * albedo * ao;// 环境光最终贡献 vec3 color ambient Lo; // 环境光与cook-torrance模型累加 // HDR色调映射 color color / (color vec3(1.0)); // gamma 矫正 color pow(color, vec3(1.0/2.2)); FRAGMENT_COLOR vec4(color, albedoRGBA.a); // alpha使用反照率贴图}
Fragment Shader大体分为三部分
从贴图中获取反照率、金属度、粗糙度、法线数据计算灯光光照环境光灯光只影响环境光方向光、聚光灯、点光源会影响光强lightCoeff最终的光照使用cook-torrance模型进行计算公式可以参考LearnOpenGL最后进行环境光与PBR模型结果进行叠加并进行色调映射与gamma矫正这里使用的公式在LearnOpenGL中都有的总结
这个PBR Shader整体上与LearnOpenGL中的理论一致看完LearnOpenGL之后再看这个Shader就比较简单了。
标签: