Three PBR 直接光照 (面光源)改版

396 阅读3分钟

直接光照

void Direct_Physical( const in IncidentLight directLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
        float dotNL = saturate( dot( geometry.normal, directLight.direction ) ); 
        vec3 irradiance = dotNL * directLight.color * PI; 

        #ifdef ANISOTROPY
                vec3 specularGGX = BRDF_Specular_GGX_Anisotropy( directLight, geometry, geometry.normal, material.specularColor, material.specularRoughness, material.anisotropyFactor);
        #else
                vec3 specularGGX = BRDF_Specular_GGX( directLight, geometry, geometry.normal, material.specularColor, material.specularRoughness);
        #endif

        reflectedLight.directSpecular += irradiance * specularGGX;
        reflectedLight.directDiffuse += irradiance * BRDF_Diffuse_Lambert( material.diffuseColor );

        // ToCheck
        #ifdef CLEARCOAT
                vec3 H = normalize(geometry.viewDir + directLight.direction);
    float clearCoatNoH = saturate(dot(geometry.clearcoatNormal, H));
    float clearCoatLoH = saturate(dot(directLight.direction, H));
                float clearCoatNoL = saturate(dot(geometry.clearcoatNormal, directLight.direction));

                float Fcc;
    float clearCoat = clearCoatLobe(clearCoatNoH, clearCoatLoH, material.clearcoat, material.clearcoatRoughness, Fcc);
    float clearCoatAttenuation = 1.0 - Fcc;

                reflectedLight.directDiffuse *= clearCoatAttenuation;
                reflectedLight.directSpecular *= clearCoatAttenuation * material.energyCompensation;
                reflectedLight.directClearCoat = clearCoat;
        #endif
        // Todo: USE_SHEEN
}
  • 各项同性的 DFG
// GGX Distribution, Schlick Fresnel, GGX-Smith Visibility
vec3 BRDF_Specular_GGX( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 normal, const in vec3 specularColor, const in float roughness ) {

	float alpha = pow2( roughness ); // UE4's roughness

	vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );

	float dotNL = saturate( dot( normal, incidentLight.direction ) );
	float dotNV = saturate( dot( normal, geometry.viewDir ) );
	float dotNH = saturate( dot( normal, halfDir ) );
	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );

	vec3 F = F_Schlick( specularColor, dotLH );
	float D = D_GGX( alpha, dotNH );
	float G = G_GGX_SmithCorrelated( alpha, dotNL, dotNV );

	return F * ( G * D );

} // validated
vec3 F_Schlick( const in vec3 specularColor, const in float dotLH ) {

	// Original approximation by Christophe Schlick '94
	// float fresnel = pow( 1.0 - dotLH, 5.0 );

	// Optimized variant (presented by Epic at SIGGRAPH '13)
	// https://cdn2.unrealengine.com/Resources/files/2013SiggraphPresentationsNotes-26915738.pdf
	float fresnel = exp2( ( -5.55473 * dotLH - 6.98316 ) * dotLH );

	return ( 1.0 - specularColor ) * fresnel + specularColor;

} // validated
// Moving Frostbite to Physically Based Rendering 3.0 - page 12, listing 2
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
float G_GGX_SmithCorrelated( const in float alpha, const in float dotNL, const in float dotNV ) {

	float a2 = pow2( alpha );

	// dotNL and dotNV are explicitly swapped. This is not a mistake.
	float gv = dotNL * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNV ) );
	float gl = dotNV * sqrt( a2 + ( 1.0 - a2 ) * pow2( dotNL ) );

	return 0.5 / max( gv + gl, EPSILON );

}
// Microfacet Models for Refraction through Rough Surfaces - equation (33)
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
// alpha is "roughness squared" in Disney?s reparameterization
float D_GGX( const in float alpha, const in float dotNH ) {

	float a2 = pow2( alpha );

	float denom = pow2( dotNH ) * ( a2 - 1.0 ) + 1.0; // avoid alpha = 0 with dotNH = 1

	return RECIPROCAL_PI * a2 / pow2( denom );

}
vec3 BRDF_Diffuse_Lambert( const in vec3 diffuseColor ) {
    return RECIPROCAL_PI * diffuseColor;
} // validated
  • 各项异性的 DFG
vec3 BRDF_Specular_GGX_Anisotropy( const in IncidentLight incidentLight, const in GeometricContext geometry, const in vec3 normal, const in vec3 specularColor, const in float roughness, const in float anisotropyFactor) {

	float alpha = pow2( roughness ); // UE4's roughness

	vec3 halfDir = normalize( incidentLight.direction + geometry.viewDir );

	float dotNL = saturate( dot( normal, incidentLight.direction ) );
	float dotNV = saturate( dot( normal, geometry.viewDir ) );
	float dotNH = saturate( dot( normal, halfDir ) );
	float dotLH = saturate( dot( incidentLight.direction, halfDir ) );

	vec3 F = F_Schlick( specularColor, dotLH );

	float ToH = dot(geometry.anisotropicT, halfDir);
	float BoH = dot(geometry.anisotropicB, halfDir);

	// Anisotropic parameters: at and ab are the roughness along the tangent and bitangent
	// to simplify materials, we derive them from a single roughness parameter
	// Kulla 2017, "Revisiting Physically Based Shading at Imageworks"
	float at = max(alpha * (1.0 + anisotropyFactor), 0.001);
	float ab = max(alpha * (1.0 - anisotropyFactor), 0.001);

	float D = D_GGX_Anisotropic(at, ab, ToH, BoH, dotNH);

	float G = G_GGX_SmithCorrelated_Anisotropic(
		at, ab,
		dot( geometry.anisotropicT, geometry.viewDir ),
		dot( geometry.anisotropicB, geometry.viewDir ),
		dot( geometry.anisotropicT, incidentLight.direction ),
		dot( geometry.anisotropicB, incidentLight.direction ),
		dotNV,
		dotNL
	);

	return F * ( G * D );

}
// Added (From Filament)
float D_GGX_Anisotropic(float at, float ab, float ToH, float BoH, float NoH) {
	// The values at and ab are perceptualRoughness^2, a2 is therefore perceptualRoughness^4
    // The dot product below computes perceptualRoughness^8. We cannot fit in fp16 without clamping
    // the roughness to too high values so we perform the dot product and the division in fp32
    float a2 = at * ab;
    vec3 v = vec3(ab * ToH, at * BoH, a2 * NoH);
    float v2 = dot(v, v);
    float w2 = a2 / v2;
    return a2 * w2 * w2 * (1.0 / PI);
}
float G_GGX_SmithCorrelated_Anisotropic(
	float at, float ab,
	float ToV, float BoV,
	float ToL, float BoL,
	float NoV, float NoL
) {
	float lambdaV = NoL * length( vec3( at * ToV, ab * BoV, NoV ) );
	float lambdaL = NoV * length( vec3( at * ToL, ab * BoL, NoL ) );
	float v = 0.5 / ( lambdaV + lambdaL );
	return saturate( v );
}

直接光照 面光源

  • 这个是没更改Three里面的
void RE_Direct_RectArea_Physical( const in RectAreaLight rectAreaLight, const in GeometricContext geometry, const in PhysicalMaterial material, inout ReflectedLight reflectedLight ) {
        // Anisotropy support here
        #if defined( ANISOTROPY ) && defined( TYPE_SILK )
                vec3 normal = geometry.bentNormal;
                vec3 lightColor = rectAreaLight.color * 0.699;
        #else
                vec3 normal = geometry.normal;
                vec3 lightColor = rectAreaLight.color;
        #endif

        vec3 viewDir = geometry.viewDir;
        vec3 position = geometry.position;
        vec3 lightPos = rectAreaLight.position;
        vec3 halfWidth = rectAreaLight.halfWidth;
        vec3 halfHeight = rectAreaLight.halfHeight;

        float roughness = material.specularRoughness;
        vec3 rectCoords[ 4 ];
        rectCoords[ 0 ] = lightPos + halfWidth - halfHeight; // counterclockwise; light shines in local neg z direction
        rectCoords[ 1 ] = lightPos - halfWidth - halfHeight;
        rectCoords[ 2 ] = lightPos - halfWidth + halfHeight;
        rectCoords[ 3 ] = lightPos + halfWidth + halfHeight;
        vec2 uv = LTC_Uv( normal, viewDir, roughness );
        vec4 t1 = texture2D( ltc_1, uv );
        vec4 t2 = texture2D( ltc_2, uv );
        mat3 mInv = mat3(
                vec3( t1.x, 0, t1.y ),
                vec3(    0, 1,    0 ),
                vec3( t1.z, 0, t1.w )
        );

        // LTC Fresnel Approximation by Stephen Hill
        reflectedLight.directDiffuse += lightColor * material.diffuseColor * LTC_Evaluate( normal, viewDir, position, mat3( 1.0 ), rectCoords );

        // http://blog.selfshadow.com/publications/s2016-advances/s2016_ltc_fresnel.pdf
        vec3 fresnel = ( material.specularColor * t2.x + ( vec3( 1.0 ) - material.specularColor ) * t2.y );
        vec3 bottomSpec = lightColor * fresnel * LTC_Evaluate( normal, viewDir, position, mInv, rectCoords );

        #ifdef CLEARCOAT
                vec3 clearcoatNormal = geometry.clearcoatNormal;
                const float LUT_SIZE = 64.0;
                const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
                const float LUT_BIAS = 0.5 / LUT_SIZE;

                float clearCoatNoV = saturate(dot(clearcoatNormal, viewDir));
                vec2 clearCoatUV = vec2(material.clearcoatRoughness, sqrt(1.0 - clearCoatNoV));
                clearCoatUV = clearCoatUV * LUT_SCALE + LUT_BIAS;
                vec4 clearCoatT1 = texture2D(ltc_1, clearCoatUV);
                vec4 clearCoatT2 = texture2D(ltc_2, clearCoatUV);
                mat3 clearCoatInv = mat3(
                        vec3(clearCoatT1.x, 0, clearCoatT1.y),
                        vec3(0,    1,    0),
                        vec3(clearCoatT1.z, 0, clearCoatT1.w)
                );
                vec3 clearCoatFactor = material.specularColor * clearCoatT2.x + (1.0 - material.specularColor) * clearCoatT2.y;
                vec3 clearCoatSpec = clearCoatFactor * LTC_Evaluate(clearcoatNormal, viewDir, position, clearCoatInv, rectCoords);

                reflectedLight.directSpecular += mix(bottomSpec, clearCoatSpec, material.clearcoat);

        #else
                reflectedLight.directSpecular += bottomSpec;
        #endif
}
vec3 LTC_Evaluate( const in vec3 N, const in vec3 V, const in vec3 P, const in mat3 mInv, const in vec3 rectCoords[ 4 ] ) {

	// bail if point is on back side of plane of light
	// assumes ccw winding order of light vertices
	vec3 v1 = rectCoords[ 1 ] - rectCoords[ 0 ];
	vec3 v2 = rectCoords[ 3 ] - rectCoords[ 0 ];
	vec3 lightNormal = cross( v1, v2 );

	if( dot( lightNormal, P - rectCoords[ 0 ] ) < 0.0 ) return vec3( 0.0 );

	// construct orthonormal basis around N
	vec3 T1, T2;
	T1 = normalize( V - N * dot( V, N ) );
	T2 = - cross( N, T1 ); // negated from paper; possibly due to a different handedness of world coordinate system

	// compute transform
	mat3 mat = mInv * transposeMat3( mat3( T1, T2, N ) );

	// transform rect
	vec3 coords[ 4 ];
	coords[ 0 ] = mat * ( rectCoords[ 0 ] - P );
	coords[ 1 ] = mat * ( rectCoords[ 1 ] - P );
	coords[ 2 ] = mat * ( rectCoords[ 2 ] - P );
	coords[ 3 ] = mat * ( rectCoords[ 3 ] - P );

	// project rect onto sphere
	coords[ 0 ] = normalize( coords[ 0 ] );
	coords[ 1 ] = normalize( coords[ 1 ] );
	coords[ 2 ] = normalize( coords[ 2 ] );
	coords[ 3 ] = normalize( coords[ 3 ] );

	// calculate vector form factor
	vec3 vectorFormFactor = vec3( 0.0 );
	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 0 ], coords[ 1 ] );
	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 1 ], coords[ 2 ] );
	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 2 ], coords[ 3 ] );
	vectorFormFactor += LTC_EdgeVectorFormFactor( coords[ 3 ], coords[ 0 ] );

	// adjust for horizon clipping
	float result = LTC_ClippedSphereFormFactor( vectorFormFactor );

/*
	// alternate method of adjusting for horizon clipping (see referece)
	// refactoring required
	float len = length( vectorFormFactor );
	float z = vectorFormFactor.z / len;

	const float LUT_SIZE = 64.0;
	const float LUT_SCALE = ( LUT_SIZE - 1.0 ) / LUT_SIZE;
	const float LUT_BIAS = 0.5 / LUT_SIZE;

	// tabulated horizon-clipped sphere, apparently...
	vec2 uv = vec2( z * 0.5 + 0.5, len );
	uv = uv * LUT_SCALE + LUT_BIAS;

	float scale = texture2D( ltc_2, uv ).w;

	float result = len * scale;
*/

	return vec3( result );

}