体积光原理及WebGL实现

techbrood 发表于 2019-07-16 17:43:36

标签: webgl, volume light, godrays, shader

- +

体积光(或叫上帝之光)在自然界中是十分常见的现象,如太阳光从云隙中透过时产生的云隙光,森林中阳光从树叶中穿过产生的光柱。

如果我们要在网页三维场景中模拟这种光效,需要深入了解大气物理模型和光散射原理。

image.png

大气物理模型

物体与其观察者之间存在着复杂的介质,比如太阳光到达我们眼睛是穿过了厚厚的大气层,大气层里面除了空气分子外,还包含云雨雾霾和灰尘等粒子。如果摄像机在地面上或者是在十分接近地面的位置,可以认为空气具有一个恒定的粒子密度,但准确而言,实际中的大气密度在地球引力的作用下,越靠近地表空气密度越高,越远离地表空气越稀薄。所以,我们假定空气粒子的密度是沿着海拔高度h呈指数递减的。按照经验,可以认为云雨雾霾尘埃等大颗粒粒子更多的存在于近地表的对流层中1200km以下,而在这之上到7994km之间为空气分子介质,在这个高度之外,我们认为近视于真空。

光散射

光在大气这种介质中传播会出现散射,散射按照粒子尺寸和光波长的相对关系如下图所示:

image.png

1. Rayleigh散射:

由空气中远小于波长的微粒(如空气分子)引起的散射称作瑞利散射。Rayleigh散射强度与光线波长的四次方成反比,这意味着白光中波长较短的颜色光(蓝色)会比波长较长的光(红色)有更强的散射强度,导致天空在白天偏向蓝色,而在黄昏偏向橙红色。 当日出或日落的时候,由于太阳的位置接近地平线,阳光斜射入大气,会在大气层中穿过很长的距离。在这个过程中,太阳光中的蓝色光几乎都会被散射殆尽无法抵达人眼,只剩下了波长较长的红色光,所以在太阳及其周围的天空都会呈现橘红色。

2. Mie散射:

在空气中直径与波长相当的微粒(如尘埃、雾滴等)所导致的散射现象称作Mie散射。与Rayleigh散射不同,Mie散射与波长无关,散射方向表现出明显的各向异性,光线会被粒子更多的向后方散射。而当阴雨天气时,空气中存在大量的水滴颗粒,Mie散射导致天空呈现灰白色。现今经常出现的雾霆天气,同样是因为空气中悬浮的大颗粒过多而导致的Mie散射现象。

结合上述的大气模型,我们可以认为Mie散射主要存在于1200km以下,而Rayleigh散射存在于7994km之下。

3. 内散射和外散射:

太阳光在大气中传输的时候会与空气中的微粒产生交互作用。有两种重要的交互方式:散射,它改变了光线的方向;吸收,它将光能吸收并转变为其它形态的能量(如热能)。而散射效果对场景中物体的影响又分为两个方面:一方面是一部分由物体反射的光被散射到视线之外,并不能到达摄像机,因而被衰减,称作外散射;另一方面是一部分太阳光被空气中的粒子散射正对向摄像机,这些正朝向视线的散射被称作内散射。

20190106184629851.png

最后抵达视点被人眼所观察到的光线可分为两部分:衰减后的物体反射辐射度、被内散射的大气散射辐照度。

image.png[方程1]

其中Lviewer为最终抵达摄像机的总光强,Lobject为物体的反射光(当视线不与物体相交时则为0),Linscatter为从O到C点路径上所有内散射光线的总和,这里暂时忽略太阳直射。

image.png


上面讲了大气物理模型和散射的物理原理,接下来看看我们具体该如何计算它。

为了计算每个像素的照明度,我们必须考虑光源到该像素的散射以及是否存在遮挡。

以阳光为例,我们从日光散射的分析模型开始,如下方程2:

equ277-01.jpg[方程2]

上述方程式是方程1的具体化,s是光线穿过介质的距离,θ是视线和太阳光线之间的夹角。Esun是太阳源光,βex是由光吸收和外散射特性组成的消光常数,βsc是由瑞利和米氏散射特性组成的角散射项。该方程第一项计算从发射点到视点吸收到的光量,第二项计算由于光散射到视点射线路径而产生的附加量。由于阻塞物质(如云、建筑物和其他物体)而产生的影响在这里被简单地模拟为光照的衰减如方程3:

image.png[方程3]

上述方程中,D(Φ)是太阳光线到视线位置之间不透明遮挡物的合成衰减率。

这样做引入了确定图像中每个点光源遮挡的复杂性。在屏幕上,我们没有完整的体积信息来决定遮挡。不过,我们可以通过在图像空间中,通过把从像素到光源的射线上的样本进行相加,估算每个像素的遮挡可能性。打到发射区域上的样本与打到遮挡物上的样本之间的比例,就是我们想要的遮挡百分比:D(Φ)。在发射区域比遮挡物亮度高的情况下,用该方法估算效果最好。如果我们把照明样本除以样本数目n,后处理(post-process)过程则可以化简为对图像取样进行求和,如方程式4:

image.png[方程4]

更进一步,我们引入衰减系数来对求和结果进行参数化控制:

image.png[方程5]

这里exposure控制后处理中的总体强度,weight控制每个样本的权重,decayi(介于[0, 1]之间)控制每个样本的衰减。这种指数式衰减因子实际上让每道光都能从光源处平滑的洒落下来。exposureweight是简单的计量因子。增加它们其中任何一个都会在整体上增强计算出来的亮度。样本的weight通过微粒度(fine-grain)控制进行调整,exposure通过粗粒度(coarse-grain)控制进行调整。因为样本都是来自原图,不需要额外处理半透明物体。多光源的处理可以通过连续叠加屏间(screen-space)通道。虽然这个例子中,我们用的是日光分析模型,但实际上其它图像资源也适用。对于位于a点的太阳,以及每一个屏幕空间图像点φ,我们从原图开始,沿着射线矢量,按规定间隔,Δ(φ) = (φ-a)/n(density),连续地取样本然后求和。这里我们用密度(density)来控制样本间隔,这样可以在必要时减少样本迭代次数。当我们提高密度因子值时,样本间距相应减小,结果是光束更亮了,覆盖范围变短了。在下图中,来自φ1样本是没有被遮蔽的,结果就是正规评估L(s,θ)得到最大的散射照明。在φ2, 一部分样本沿途中碰到了建筑物,所以计算到的散射照明就少了。通过为图像中每一个像素进行射线求和,我们得出了包含遮挡物的光散射体结构。

image.png

有了以上这些公式,我们就可以编写相应的后处理着色器代码来进行光照计算了。

给定初始图像后,样本坐标沿着射线的方向,从像素点位置延伸到屏幕空间的光源位置上。屏幕空间中的光源位置是通过标准的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)来处理这些效果瑕疵。这里不做进一步的介绍。

possitive(3) views2753 comments1

发送私信

最新评论

iefreer 2019-07-18 13:47:34

reference: https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch13.html


请先 登录 再评论.
相关文章
  • 2019年开源WebRTC媒体服务器选型比较

    什么是WebRTC服务器?在WebRTC的早期开始,该技术的主要卖点之一是它允许点对点(浏览器到浏览器)通信,几乎没有服务器的干预,服务器通常仅用于信令(比如用于...

  • CentOS6 Apache2.2用域名配置多虚拟机

    在CentOS下使用域名配置多虚拟机的步骤如下:
    1. 使用

  • A-Frame WebVR(网页虚拟现实)快速开发入门教程

    WebVR和WebGL应用程序接口使得我们已经可以在浏览器上创建虚拟现实(VR)体验,但从工程化的角度而言,开发社区还需要更多方便强大的开发库来简化编程,Mozilla的

  • JavaScript语言多编程范式简介

    和C++等语言类似,JS支持多范式(paradigms)编程。我们常常混合这些范式来完成一些大型Web项目。JS支持3种编程范式:命令式、面向对象和函数式。命令式(Imperative JavaScript)命令式就是简单的从上而下完成任务,流水账过程式编码风格:function

  • 使用HTML5 FileReader和Canvas压缩用户上传的图片

    手机用户拍的照片通常会有2M以上,这对服务器带宽产生较大压力。因此在某些应用下(对图片要求不那么高)我们可以在客户端来压缩图片,然后再提交给服务器。总体...

  • Three.js入门教程2 - 着色器(上)

    之前我已经给出了一篇

  • CSS3图片混合(Blend)效果及其参考计算公式一览表

    在Photoshop软件中,混合是将两个图层的色彩值进行合成,从而创造出大量的效果。在这些效果的背后实际是一些简单的数学公式在起作用。下面所介绍的公式仅适用于R...

  • Processing.js和P5.js的功能简介和区别

    什么是ProcessingProcessing是关于数字艺术的编程语言,支持跨平台,语言本身是一个类Java语言,程序文件的后缀为.pde。
    什么是Processing.js为了能让Proce...

  • 使用纯CSS3实现一个3D旋转的书本

    有一些前沿的电商网站已经开始使用3D模型来展示商品并支持在线定制,而其中图书的展示是最为简单的一种,无需复杂的建模过程,使用图片和CSS3的一些变换即可实现...

  • SVG过滤器feColorMatrix矩阵变换效果用法详解

    在计算机图形学(数学)中,矩阵乘法可用于把空间向量进行几何变换。我们可以把颜色的值(RGBA)表示成一个四维空间向量:color = (r, g, b, a);那么就可以应用...

  • 使用CSS3实现流星雨动画教程

    很多营销页面中需要实现类似流星雨的动画背景,营造节日浪漫的气氛。要实现这样的效果,有两种方法,一个是使用Canvas,一个是使用纯CSS3,我们这里介绍第2种方...

  • 在PHP网页程序中执行Sass/Compass命令

    我们需要在wow云开发平台支持sass/compass等预编译样式语言,为此我们首先尝试了scssphp扩展,但是在支持最新语法上,经常会出现异常。所以我们采用了代理的方式...

  • 更多...