Physically Based Shading on Mobile

This post is the first in hopefully a series of in depth, highly technical discussions of features in UE4.

This is a follow up to my talk Real Shading in Unreal Engine 4. That talk explained the shading model used in UE4. This post assumes the reader is familiar with the course notes.

There have been a few minor changes since that presentation. I removed Disney's "hotness" modification to the geometry term. With Eric Heitz's recent work I may be improving this again to add a correlation between the shadowing and masking. The Cavity parameter never made it in. We were poised to make that change right before I presented but have found uses for controllable dielectric specular reflectance. Instead we use the Specular parameter as defined in Disney's model.

What I really want to talk about is how this shading model was adapted for mobile platforms. For a great run down of UE4's mobile renderer check out these slides from GDC. For some examples of its use check out the Zen tech demo which was running 4xMSAA 1080p at 30hz on an iPad Air or the Soul tech demo.

A major goal from the beginning was that we wanted the same content creation workflow as high end PC. We also wanted to support rendering the same assets so that when running on a mobile device, everything looks as close as we can to the high end renderer. This means many tiny things like getting the gamma right but it also means shading needs to work in the same way so that a material appears to behave the same on PC, consoles and mobile devices. To do that, we support the same material model I explained in my talk. BaseColor, Metallic, Specular, Roughness. These parameters operate in the exact same way.

Obviously there is far less compute power on mobile devices so we can't evaluate the BRDF for every light. In fact, we only support a single directional sun light that is calculated dynamically. The shadows are precomputed and stored as signed distance fields in texture space. All other lights are baked into lightmaps. The lightmaps use a similar HDR encoded SH directional representation as high end but adapted to better compress with PVRTC which doesn't have separate, uncorrelated, alpha compression. Like high end, precomputed specular uses preconvolved environment maps. The exact same preconvolution is used and they are similarly normalized and scaled by the lightmap. For mobile, only one environment map is fetched per pixel.

Environment BRDF

The interesting bit comes from how we calculate Environment BRDF which for high end is precomputed with Monte Carlo integration and stored in a 2D LUT. Dependent texture fetches are really expensive on some mobile hardware but even worse is the extremely limiting 8 sampler limit of OpenGL ES2. Chewing up a sampler with this LUT was totally unacceptable. Instead, I made an approximate analytic version based on Dimitar Lazarov's work. The form is similar but adapted to fit our shading model and roughness parameter. The function is listed below:

half3 EnvBRDFApprox( half3 SpecularColor, half Roughness, half NoV )
{
	const half4 c0 = { -1, -0.0275, -0.572, 0.022 };
	const half4 c1 = { 1, 0.0425, 1.04, -0.04 };
	half4 r = Roughness * c0 + c1;
	half a004 = min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
	half2 AB = half2( -1.04, 1.04 ) * a004 + r.zw;
	return SpecularColor * AB.x + AB.y;
}

This means we have plausible Fresnel and roughness behavior for image based lighting.

EnvBRDF used on high end

EnvBRDF used on high end

 

EnvBRDFApprox Used On Mobile

EnvBRDFApprox used on mobile

 

We detect when Metallic and Specular material outputs aren't connected (meaning the defaults are used). This is true for a typical nonmetal. In this case we can further optimize the function.

half EnvBRDFApproxNonmetal( half Roughness, half NoV )
{
	// Same as EnvBRDFApprox( 0.04, Roughness, NoV )
	const half2 c0 = { -1, -0.0275 };
	const half2 c1 = { 1, 0.0425 };
	half2 r = Roughness * c0 + c1;
	return min( r.x * r.x, exp2( -9.28 * NoV ) ) * r.x + r.y;
}

A similar optimization is made to avoid the environment map altogether. Some games optimize for no specular but that isn't energy conserving. We instead have a "Fully rough" flag which sets Roughness=1 and optimizes out constant factors. We can see that:

EnvBRDFApprox( SpecularColor, 1, 1 ) == SpecularColor * 0.4524 - 0.0024

From there I make the simplification:

DiffuseColor += SpecularColor * 0.45;
SpecularColor = 0;

We only use this flag on select objects that really need the extra performance.

Directional Light

The last bit I want to touch on is how the sun light is calculated. I mentioned earlier that the sun is the only light computed dynamically. Unfortunately, even for a single light the full high end shading model is still too heavy. We have just calculated a portion of it in an approximate preintegrated form with the EnvBRDF though. We also already have the reflection vector which was used to sample the environment map. The idea is to analytically evaluate the radially symmetric lobe that is used to prefiler the environment map and then multiply the result with EnvBRDF just like we do for IBL. Think of this as analytically integrating the lobe against the incoming light direction instead of numerically integrating like we do with the environment map.

First, we replace the GGX NDF with Blinn. Blinn is then approximated with a radially symmetric Phong lobe.

 

DBlinn(h)=1πα2(n⋅h)(2α2−2)≈1πα2(r⋅l)(12α2−12)DBlinn(h)=1πα2(n⋅h)(2α2−2)≈1πα2(r⋅l)(12α2−12)

Where $\rv$ is the reflection direction $\rv = 2(\ndotv)\nv - \vv$

Phong is further approximated with a Spherical Gaussian:

xn≈e(n+0.775)(x−1)xn≈e(n+0.775)(x−1)

This brings us to the final optimized form:

half D_Approx( half Roughness, half RoL )
{
	half a = Roughness * Roughness;
	half a2 = a * a;
	float rcp_a2 = rcp(a2);
	// 0.5 / ln(2), 0.275 / ln(2)
	half c = 0.72134752 * rcp_a2 + 0.39674113;
	return rcp_a2 * exp2( c * RoL - c );
}

Where $\frac{1}{\pi}$ is factored into the light's color.

In the end we have a very cheap but reasonable shading model. Specular is energy conserving, behaves sensibly at grazing angles, and works with both analytic and image based light sources. Not even mobile hardware is an excuse to not to be physically based anymore.

Epic Games, Inc. gives you permission to use, copy, modify, and distribute the code samples in this article as you see fit with no attribution required.

极化码(Polar Code)是由土耳其科学家Erdal Arıkan在2009年提出的一种新型纠错编码技术。它通过利用信道的极化现象,将虚拟信道分为误码率接近0和接近1/2的两类。在编码设计中,数据被放置在误码率极低的信道上,从而实现高效的数据传输。极化码的主要优势在于其理论编码容量能够达到香农限,并且构造方法较为简单。 MATLAB是一种功能强大的数学计算和编程工具,广泛应用于科学研究和工程领域。在极化码的研究中,MATLAB可用于构建编码和解码算法,模拟数据在不同信道条件下的传输效果,验证理论性能,并优化相关参数。 SC(Successive Cancellation,逐位取消)译码是极化码的基本解码方法。它从最可靠的比特开始,依次解码每个虚拟信道,且每个比特的解码结果会影响后续比特的解码,因为它们之间存在依赖关系。虽然SC译码的实现较为简单,但其计算复杂度较高,随着码长的增加,解码时间会线性增长。 SCL(Successive Cancellation List,逐位取消列表)译码是SC译码的改进版本。它通过引入列表机制,同时处理多个路径,从而增强了错误校正能力,并在一定程度上降低了错误率。与SC译码相比,SCL译码虽然需要消耗更多的计算资源,但能够提供更好的性能。 一个完整的MATLAB仿真资源通常包含以下内容: 编码模块:用于实现极化码的生成,包括码字构造和极化矩阵操作等。 信道模型:用于模拟各种通信信道,例如AWGN(加性高斯白噪声)信道或衰落信道。 SC/SCL译码模块:包含SC译码和SCL译码的算法实现。 误码率(BER)计算:通过比较发送和接收的码字,计算误码率,以评估编码性能。 性能曲线绘制:绘制误码率与信噪比(SNR)之间的关系曲线,展示不同译码策略的性能差异。 使用说明:指导用户如何运行仿真,理解代码结构,以及如何调整参数以进行自定义实验。 代码注
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值