Mobile Development 18 min read

Optimizing WebGL Rendering for Mobile Digital Human Performance

To deliver high‑quality mobile digital‑human graphics without inflating B‑Station’s app size, we replaced heavyweight engines with a lightweight Three.js‑based WebGL pipeline, refined the PBR shader (Cook‑Torrance, Smith, visibility term) and added skin, eye, anti‑aliasing and translucency tricks, cutting package growth to under 1 MB and boosting frame rates by up to 62 % on typical smartphones.

Bilibili Tech
Bilibili Tech
Bilibili Tech
Optimizing WebGL Rendering for Mobile Digital Human Performance

随着图形图像渲染技术的快速发展,如何在移动端呈现出高质量的数字人渲染效果,是实时渲染领域最主流的技术研究方向之一。对于B站移动端App而言,如果使用主流的实时渲染引擎如Unreal/Unity等,都会带来100-130M左右的安装包体积增量,进而增加应用安装和版本更新的成本。

针对该问题,我们选择了更为灵活轻量的WebGL渲染方案,将包体增量大幅降低至1M以内,同时借助Web天然的开箱即用特性,加速了业务需求在移动端落地的整体节奏。经过对Web渲染能力的行业调研,我们最终从众多的Web3D渲染引擎中选择了Three.JS。Three.JS作为一款轻量级的JavaScript 3D渲染库,具备强大的图形能力和广泛的社区支持,在数字人渲染方向能够给予我们一定的基础能力支持。

但如果只是使用Three.JS自带的PBR(Physically-Based Rendering)渲染,在偏CG和写实方向的数字人渲染效果上,很难达到令人满意的品质感。为了能够进一步还原商业实时渲染引擎Unreal的人物效果,同时兼顾好WebGL在移动设备上的性能和发热问题,我们在人物皮肤,瞳孔,抗锯齿,半透明等方向上做了深入的二次研发,提出了一套完整的高质量角色移动端WebGL渲染解决方案。本文将分享我们在探索和实现该方案的过程中遇到的挑战及最终的解决思路。

PBR优化

PBR(Physically-Based Rendering)是一种计算机图形学中的渲染技术,旨在模拟(近似)光线在现实世界中的物理行为,以实现更真实、逼真的渲染效果。因此我们的美术资产的制作采用了全套PBR流程,Three.JS内置的MeshPhysicalMaterial也对PBR做了材质支持,但我们发现Three.JS的PBR实现在移动端依然存在性能瓶颈。所以针对以上问题我们给出了如下解决方案:

首先我们来看Cook-Torrance的BRDF模型:

float D = pow(max(0.0, dot(h, n)), 4.0) * (1.0 / (alpha * alpha * pow(max(0.0, dot(h, n)), 2.0) + 1.0));

其中Cook-Torrance镜面反射如下:

float D = pow(max(0.0, dot(h, n)), 4.0) * (1.0 / (alpha * alpha * pow(max(0.0, dot(h, n)), 2.0) + 1.0));

D表示法线分布函数(Normal Distribution Function)

G表示几何函数(Geometry function)

F表示菲尼尔函数(Fresnel function)

n表示法线方向,l表示灯光方向,v表示视线方向,h表示l与v的中间方向(halfway)

首先,我们使用 UE4 粗糙度的定义,将它用于以下所有方程中的alpha:

float alpha = roughness * roughness;

分布函数:

float D = (alpha * alpha) / (pi * pow((dot(n, h) * dot(n, h) * (alpha * alpha - 1.0) + 1.0), 2.0));

几何函数:

float G = 1.0 - (alpha * alpha) / (dot(n, v) * dot(n, l));

菲尼尔函数:

float F = pow((1.0 - dot(v, h)) / (1.0 + dot(v, h)), 2.0) * (1.0 / (1.0 + dot(v, h)));

我们使用史密斯方法(Smith's method)将G_GGX作为子函数G_sub:

float G = 1.0 / (1.0 + alpha * tan(acos(dot(n, v))));

菲尼尔函数:

float F0 = vec3(0.04);

我们还可以将镜面反射分母与几何函数进行合并得到可见性项(Visibility):

float V = 1.0 / (dot(n, v) * dot(n, l));

最后我们得到了:

float specular = D * G * F;

我们发现几何函数里有除法和求平方根,写成代码为:

float G = 1.0 / (1.0 + alpha * tan(acos(dot(n, v))));

有没有办法简化呢?根据SIGGRAPH 2015 Optimizing PBR for Mobile有关简化VF项:

float V = 1.0 / (1.0 + alpha * tan(acos(dot(n, v))));

最终我们的表达式:

float specular = D * G * F;

加和乘,仅有一次除法。

与Three.JS内置PBR性能对比

我们设计了一个测试场景,仅渲染一个角色,同时去除了其他干扰,为了能体现本次性能差距,选用了3款机器进行测试帧生成时间(SOC由于发热等因素可能是一个区间):

机型 (处理器)

ThreeJS PBR

优化 PBR

提升比例

iPhone 6 (A8)

20-22ms

<16ms

20-33%

Redmi 6 Pro (骁龙625)

52ms

32ms

62%

华为 P20 (麒麟970)

10ms

7-8ms

25-42%

较好的机器在该场景下通常能跑上百帧,两者差距相对较小,这里就不一一列举。

PBRWebGLanti-aliasingbloom effectRendering OptimizationSkin Renderingtemporal anti-aliasing
Bilibili Tech
Written by

Bilibili Tech

Provides introductions and tutorials on Bilibili-related technologies.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.