间接光照,diffuse
- 总公式:材质diffuse * 辐照度(ibl间接光) * 亮度
- 与光源的间接光无关
void IndirectDiffuse_Physical( const in vec3 specularDFG, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
vec3 worldNormal = inverseTransformDirection( geometry.normal, viewMatrix );
worldNormal = envMapRotationMat * worldNormal;
vec3 irradiance = computeDiffuseSPH( worldNormal, envMapSH );
reflectedLight.indirectDiffuse += material.diffuseColor * irradiance * envBrightness;
//osg.js color += uBrightness * albedo * ao * evaluateDiffuseSphericalHarmonics(normal, view );
// Filament:
// reflectedLight.indirectDiffuse += BRDF_Diffuse_Lambert( material.diffuseColor ) * irradiance * envBrightness;
}
//三阶球谐 获取得到的是颜色
vec3 computeDiffuseSPH(vec3 normal, vec3 sphericalHarmonics[9]) {
float x = normal.x;
float y = normal.y;
float z = normal.z;
vec3 result = (
sphericalHarmonics[0] +
sphericalHarmonics[1] * y +
sphericalHarmonics[2] * z +
sphericalHarmonics[3] * x +
sphericalHarmonics[4] * y * x +
sphericalHarmonics[5] * y * z +
sphericalHarmonics[6] * (3.0 * z * z - 1.0) +
sphericalHarmonics[7] * (z * x) +
sphericalHarmonics[8] * (x*x - y*y)
);
return max(result, vec3(0.0));
}
//旋转矩阵
vec3 inverseTransformDirection( in vec3 dir, in mat4 matrix ) {
// dir can be either a direction vector or a normal vector
// upper-left 3x3 of matrix is assumed to be orthogonal
return normalize( ( vec4( dir, 0.0 ) * matrix ).xyz );
}
//filament 里面用的
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
return RECIPROCAL_PI * diffuseColor;
}
间接光照,specular
- brdflut * prefiliterEnv
- 这里进行了多重散射的能量守恒
void IndirectSpecular_Physical( const in vec3 specularDFG, const in vec3 radiance, const in vec3 irradiance, const in vec3 clearcoatRadiance, const in vec3 sheenRadiance, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight) {
reflectedLight.indirectSpecular = specularDFG * radiance * envBrightness;
// EnergyCompensation
reflectedLight.indirectSpecular *= material.energyCompensation;
reflectedLight.indirectDiffuse *= 1. - specularDFG;
// Sheen
#ifdef USE_SHEEN
reflectedLight.indirectDiffuse *= material.sheenScaling;
reflectedLight.indirectSpecular *= material.sheenScaling;
vec3 reflectance = material.sheenDFG * material.sheenColor;
reflectedLight.indirectSpecular += reflectance * sheenRadiance;
#endif
// ClearCoat
#ifdef CLEARCOAT
float clearCoatNoV = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) );
// The clear coat layer assumes an IOR of 1.5 (4% reflectance)
vec3 materialSpecular = vec3(0.04);
float materialF90 = 1.0;
float Fc = F_Schlick(clearCoatNoV, materialSpecular.x, materialF90) * material.clearcoat;
float attenuation = 1.0 - Fc;
reflectedLight.indirectDiffuse *= attenuation;
reflectedLight.indirectSpecular *= attenuation;
reflectedLight.indirectSpecular += clearcoatRadiance * Fc * envBrightness;
#endif
}
- energyCompensation变量
- 能量守恒,有点搞不懂里面的dfg.xxx dfg.yyy 为啥这么用了
void getEnergyCompensationParams(inout PhysicalMaterial material) {
// Energy compensation for multiple scattering in a microfacet model
// See "Multiple-Scattering Microfacet BSDFs with the Smith Model"
material.energyCompensation = 1.0 + material.specularColor * (1.0 / material.dfg.y - 1.0);
}
- specularDFG变量 跟一般的brdflut不一样
//vec3 specularDFG = getSpecularDFG(material);
vec3 getSpecularDFG(const in PhysicalMaterial material) {
return mix(material.dfg.xxx, material.dfg.yyy, material.specularColor);
}
//material.dfg = prefilteredDFG(material.specularRoughness, NoV);
vec3 prefilteredDFG(float roughness, float NoV) {
return sRGBToLinear(texture2D(envMapLUTMap, vec2(NoV, roughness))).rgb;
}
- radiance变量
- 就是 prefilteredColor 预计算的环境图
vec3 getLightProbeIndirectRadiance( /*const in SpecularLightProbe specularLightProbe, */ const in vec3 viewDir, const in vec3 normal, const in float roughness, const in int maxMIPLevel ) {
vec3 R = reflect(-viewDir, normal); //有其他计算方法
// From Sebastien Lagarde Moving Frostbite to PBR page 69
vec3 dominantR = getSpecularDominantDir(normal, R, roughness * roughness);
// Sync viewMatrix
vec3 dir = inverseTransformDirection( dominantR, viewMatrix );
// EnvironmentTransform
dir = envMapRotationMat * dir;
vec3 prefilteredColor = prefilterEnvMap(roughness, dir);
// marmoset tricks
prefilteredColor *= occlusionHorizon( dominantR, normal );
#if defined( ANISOTROPY ) && defined( TYPE_SILK )
return prefilteredColor * envMapIntensity * 0.799; // 0.699
#else
return prefilteredColor * envMapIntensity;
#endif
}
// From Sebastien Lagarde Moving Frostbite to PBR page 69
// We have a better approximation of the off specular peak
// but due to the other approximations we found this one performs better.
// N is the normal direction
// R is the mirror vector
// This approximation works fine for G smith correlated and uncorrelated
vec3 getSpecularDominantDir(vec3 N, vec3 R, float realRoughness) {
// float smoothness = 1.0 - realRoughness;
// float lerpFactor = smoothness * (sqrt(smoothness) + realRoughness);
// return mix(N, R, lerpFactor);
// Filament: getSpecularDominantDirection
return mix(R, N, realRoughness * realRoughness);
}
vec3 prefilterEnvMap(float roughness, vec3 R) {
vec3 dir = R;
float rLinear = sqrt(roughness);
float lod = rLinear * envMapLODRange[1];
lod = min(envMapLODRange[0], lod);
#ifdef CUBEMAP_LOD
// http://seblagarde.wordpress.com/2012/06/10/amd-cubemapgen-for-physically-based-rendering/
float scale = 1.0 - exp2(lod) / envMapSize[0];
vec3 absDir = abs(dir);
float M = max(max(absDir.x, absDir.y), absDir.z);
// cubemapSeamlessFixDirection
if (absDir.x ! = M) dir.x *= scale;
if (absDir.y ! = M) dir.y *= scale;
if (absDir.z ! = M) dir.z *= scale;
// sync Filament's cmgen mirror
// dir.x = -dir.x;
return envMapTexelToLinear(textureCubeLodEXT(envMap, dir, lod)).rgb;
#else
return envMapTexelToLinear(texturePanoramaLod(envMap, envMapSize, dir, lod, envMapLODRange[0])).rgb;
#endif
}
float occlusionHorizon( const in vec3 R, const in vec3 normal) {
// http://marmosetco.tumblr.com/post/81245981087
// marmoset uses 1.3, we force it to 1.0
float factor = clamp( 1.0 + dot(R, normal), 0.0, 1.0 );
return factor * factor;
}
// PanoramaLod Support
// For y up
vec2 normalToPanoramaUV(vec3 dir) {
float n = length(dir.xz);
// to avoid bleeding the max(-1.0, dir.x / n) is needed
vec2 pos = vec2( (n>0.0000001) ? max(-1.0, dir.x / n) : 0.0, dir.y);
// fix edge bleeding
if ( pos.x > 0.0 ) pos.x = min( 0.999999, pos.x );
pos = acos(pos)*0.31830988618; // RECIPROCAL_PI
pos.x = (dir.z > 0.0) ? pos.x*0.5 : 1.0-(pos.x*0.5);
// shift u to center the panorama to -z
pos.x = mod(pos.x-0.25+1.0, 1.0 );
pos.y = 1.0-pos.y;
return pos;
}
vec2 computeUVForMipmap(float level, vec2 uv, float size, float maxLOD) {
// width for level
float widthForLevel = exp2( maxLOD-level);
// the height locally for the level in pixel
// to opimitize a bit we scale down the v by two in the inputs uv
float heightForLevel = widthForLevel * 0.5;
// compact version
float texelSize = 1.0/size;
vec2 uvSpaceLocal = vec2(1.0) + uv * vec2(widthForLevel - 2.0, heightForLevel - 2.0);
uvSpaceLocal.y += size - widthForLevel;
return uvSpaceLocal * texelSize;
}
vec4 texturePanoramaLod(sampler2D tex, vec2 size, vec3 direction, float lod, float maxLOD) {
vec2 uvBase = normalToPanoramaUV(direction);
// we scale down v here because it avoid to do twice in sub functions
// uvBase.y *= 0.5;
float lod0 = floor(lod);
vec2 uv0 = computeUVForMipmap(lod0, uvBase, size.x, maxLOD);
vec4 texel0 = texture2D(tex, uv0.xy);
float lod1 = ceil(lod);
vec2 uv1 = computeUVForMipmap(lod1, uvBase, size.x, maxLOD);
vec4 texel1 = texture2D(tex, uv0.xy);
return mix(texel0, texel1, fract(lod));
}