体积光原理及WebGL实现
体积光(或叫上帝之光)在自然界中是十分常见的现象,如太阳光从云隙中透过时产生的云隙光,森林中阳光从树叶中穿过产生的光柱。
如果我们要在网页三维场景中模拟这种光效,需要深入了解大气物理模型和光散射原理。
大气物理模型
物体与其观察者之间存在着复杂的介质,比如太阳光到达我们眼睛是穿过了厚厚的大气层,大气层里面除了空气分子外,还包含云雨雾霾和灰尘等粒子。如果摄像机在地面上或者是在十分接近地面的位置,可以认为空气具有一个恒定的粒子密度,但准确而言,实际中的大气密度在地球引力的作用下,越靠近地表空气密度越高,越远离地表空气越稀薄。所以,我们假定空气粒子的密度是沿着海拔高度h呈指数递减的。按照经验,可以认为云雨雾霾尘埃等大颗粒粒子更多的存在于近地表的对流层中1200km以下,而在这之上到7994km之间为空气分子介质,在这个高度之外,我们认为近视于真空。
光散射
光在大气这种介质中传播会出现散射,散射按照粒子尺寸和光波长的相对关系如下图所示:
1. Rayleigh散射:
由空气中远小于波长的微粒(如空气分子)引起的散射称作瑞利散射。Rayleigh散射强度与光线波长的四次方成反比,这意味着白光中波长较短的颜色光(蓝色)会比波长较长的光(红色)有更强的散射强度,导致天空在白天偏向蓝色,而在黄昏偏向橙红色。 当日出或日落的时候,由于太阳的位置接近地平线,阳光斜射入大气,会在大气层中穿过很长的距离。在这个过程中,太阳光中的蓝色光几乎都会被散射殆尽无法抵达人眼,只剩下了波长较长的红色光,所以在太阳及其周围的天空都会呈现橘红色。
2. Mie散射:
在空气中直径与波长相当的微粒(如尘埃、雾滴等)所导致的散射现象称作Mie散射。与Rayleigh散射不同,Mie散射与波长无关,散射方向表现出明显的各向异性,光线会被粒子更多的向后方散射。而当阴雨天气时,空气中存在大量的水滴颗粒,Mie散射导致天空呈现灰白色。现今经常出现的雾霆天气,同样是因为空气中悬浮的大颗粒过多而导致的Mie散射现象。
结合上述的大气模型,我们可以认为Mie散射主要存在于1200km以下,而Rayleigh散射存在于7994km之下。
3. 内散射和外散射:
太阳光在大气中传输的时候会与空气中的微粒产生交互作用。有两种重要的交互方式:散射,它改变了光线的方向;吸收,它将光能吸收并转变为其它形态的能量(如热能)。而散射效果对场景中物体的影响又分为两个方面:一方面是一部分由物体反射的光被散射到视线之外,并不能到达摄像机,因而被衰减,称作外散射;另一方面是一部分太阳光被空气中的粒子散射正对向摄像机,这些正朝向视线的散射被称作内散射。
最后抵达视点被人眼所观察到的光线可分为两部分:衰减后的物体反射辐射度、被内散射的大气散射辐照度。
[方程1]
其中Lviewer为最终抵达摄像机的总光强,Lobject为物体的反射光(当视线不与物体相交时则为0),Linscatter为从O到C点路径上所有内散射光线的总和,这里暂时忽略太阳直射。
上面讲了大气物理模型和散射的物理原理,接下来看看我们具体该如何计算它。
为了计算每个像素的照明度,我们必须考虑光源到该像素的散射以及是否存在遮挡。
以阳光为例,我们从日光散射的分析模型开始,如下方程2:
[方程2]
上述方程式是方程1的具体化,s是光线穿过介质的距离,θ是视线和太阳光线之间的夹角。Esun是太阳源光,βex是由光吸收和外散射特性组成的消光常数,βsc是由瑞利和米氏散射特性组成的角散射项。该方程第一项计算从发射点到视点吸收到的光量,第二项计算由于光散射到视点射线路径而产生的附加量。由于阻塞物质(如云、建筑物和其他物体)而产生的影响在这里被简单地模拟为光照的衰减如方程3:
[方程3]
上述方程中,D(Φ)是太阳光线到视线位置之间不透明遮挡物的合成衰减率。
这样做引入了确定图像中每个点光源遮挡的复杂性。在屏幕上,我们没有完整的体积信息来决定遮挡。不过,我们可以通过在图像空间中,通过把从像素到光源的射线上的样本进行相加,估算每个像素的遮挡可能性。打到发射区域上的样本与打到遮挡物上的样本之间的比例,就是我们想要的遮挡百分比:D(Φ)。在发射区域比遮挡物亮度高的情况下,用该方法估算效果最好。如果我们把照明样本除以样本数目n,后处理(post-process)过程则可以化简为对图像取样进行求和,如方程式4:
[方程4]
更进一步,我们引入衰减系数来对求和结果进行参数化控制:
[方程5]
这里exposure控制后处理中的总体强度,weight控制每个样本的权重,decayi(介于[0, 1]之间)控制每个样本的衰减。这种指数式衰减因子实际上让每道光都能从光源处平滑的洒落下来。exposure和weight是简单的计量因子。增加它们其中任何一个都会在整体上增强计算出来的亮度。样本的weight通过微粒度(fine-grain)控制进行调整,exposure通过粗粒度(coarse-grain)控制进行调整。因为样本都是来自原图,不需要额外处理半透明物体。多光源的处理可以通过连续叠加屏间(screen-space)通道。虽然这个例子中,我们用的是日光分析模型,但实际上其它图像资源也适用。对于位于a点的太阳,以及每一个屏幕空间图像点φ,我们从原图开始,沿着射线矢量,按规定间隔,Δ(φ) = (φ-a)/n(density),连续地取样本然后求和。这里我们用密度(density)来控制样本间隔,这样可以在必要时减少样本迭代次数。当我们提高密度因子值时,样本间距相应减小,结果是光束更亮了,覆盖范围变短了。在下图中,来自φ1样本是没有被遮蔽的,结果就是正规评估L(s,θ)得到最大的散射照明。在φ2, 一部分样本沿途中碰到了建筑物,所以计算到的散射照明就少了。通过为图像中每一个像素进行射线求和,我们得出了包含遮挡物的光散射体结构。
有了以上这些公式,我们就可以编写相应的后处理着色器代码来进行光照计算了。
给定初始图像后,样本坐标沿着射线的方向,从像素点位置延伸到屏幕空间的光源位置上。屏幕空间中的光源位置是通过标准的world-view-project转换计算出来的,计量和偏移都在坐标[1-,1]的范围内。方程式5求和出来的连续样本L(s, θ, Φ ),通过weight常数和指数式衰减的衰减系数进行计量,目的是为了将控制效果的方法参数化。样本密度可以被调整以作为最终的控制因子,合成后的颜色值可以通过常量衰减系数exposure来缩放。
float4 main(float2 texCoord : TEXCOORD0) : COLOR0 { // Calculate vector from pixel to light source in screen space. half2 deltaTexCoord = (texCoord - ScreenLightPos.xy); // Divide by number of samples and scale by control factor. deltaTexCoord *= 1.0f / NUM_SAMPLES * Density; // Store initial sample. half3 color = tex2D(frameSampler, texCoord); // Set up illumination decay factor. half illuminationDecay = 1.0f; // Evaluate summation from Equation 3 NUM_SAMPLES iterations. for (int i = 0; i < NUM_SAMPLES; i++) { // Step sample location along ray. texCoord -= deltaTexCoord; // Retrieve sample at new location. half3 sample = tex2D(frameSampler, texCoord); // Apply sample attenuation scale/decay factors. sample *= illuminationDecay * Weight; // Accumulate combined color. color += sample; // Update exponential decay factor. illuminationDecay *= Decay; } // Output final color with a further scale control factor. return float4( color * Exposure, 1); }
事实上,屏幕空间采样并非只是遮挡采样,由于表面纹理的不同,会导致不理想的条纹出现。通常我们会采用遮挡预通道(pre-pass)和遮挡模板(stencil)来处理这些效果瑕疵。这里不做进一步的介绍。
- 相关文章
微信公众号在线生成二维码带参数怎么搞?
带参数二维码是微信公众号渠道二维码的一种实现
微信的带参数二维码有两种,一种是临时二维码,一种是永久二维码,但是永久二维码的生成是有个数限制的,微...2019年NodeJS框架Koa和Express选型比较
Koa和Express都是NodeJS的主流应用开发框架。
Express是一个完整的nodejs应用框架。Koa是由Express团队开发的,但是它有不同的关注点。Koa致力于核心中间件...Monaco Editor 编辑器拷贝粘贴功能调用和获取选中文本
有时候需要在monaco editor外部调用编辑器的内置功能比如希望在页面主工具栏实现一些快捷操作。button
HTML5 And Canvas 2D Specs Are Now Feature Complete, First HTML 5.1 Working Draft Published
We’ve been writing about HTML5 for quite a while, but, until today, the actual HTML5 specs and standards were still moving targets. Now, however, the...
CentOS6 Apache2.2用域名配置多虚拟机
在CentOS下使用域名配置多虚拟机的步骤如下:
1. 使用WebAssembly工作原理和JavaScript语言性能对比分析
本文简单说明WebAssembly(简称wasm)工作原理和高性能的原由(和JavaScript相比)。不过需要提醒的是Wasm并非设计来完全替代JS,而是对JS的一个强大补充,JS中...
JavaScript事件模型图解
在JavaScript中用户交互的核心部分就是事件处理。本文为对事件模型和处理机制的总体性描述。Event是什么?
event是用户操作网页时发生的交互动作,比如clic...常见面试题JS语言中四种函数调用方式实例讲解
JS的语言世界中函数(function)是一等公民,函数的调用有多种方法。普通调用这个是最常见和直接的方式:function
使用HTML5 FileReader和Canvas压缩用户上传的图片
手机用户拍的照片通常会有2M以上,这对服务器带宽产生较大压力。因此在某些应用下(对图片要求不那么高)我们可以在客户端来压缩图片,然后再提交给服务器。总体...
HTML5、Hybrid APP、Native APP对比和技术选型
HTML5和Native APP都很容易理解。为了获得HTML5的移植性和移动本地应用的高性能,搞出来一些混合APP的解决方案。比如Apache的Cordova(也就是以前的PhoneGap),...
深入理解Three.js(WebGL)贴图(纹理映射)和UV映射
本文将详细描述如何使用Three.js给3D对象添加贴图(Texture Map,也译作纹理映射,“贴图”的翻译要更直观,而“纹理映射”更准确。)。为了能够查看在线演示效...
如何使用Three.js加载obj和mtl文件
OBJ和MTL是3D模型的几何模型文件和材料文件。在最新的three.js版本(r78)中,以前的OBJMTLLoader类已废弃。现在要加载OBJ和MTL文件,需要结合OBJLoader和MTLLoade...
Three.js入门教程2 - 着色器(下)
这是WebGL着色器教程的后半部分,如果你没看过前一篇,阅读这一篇教程可能会使你感到困惑,建议你翻阅前面的教程。
更多...