球谐光照(Spherical Harmonics Lighting)及其应用-应用篇
上一篇介绍了球谐函数的一些原理和性质,本篇主要介绍如何实现球谐光照,将这种光照应用到实际的场景中去。
我们知道,球谐光照实际上就是将周围的环境光采样成几个系数,然后渲染的时候用这几个系数来对光照进行还原,这种过程可以看做是对周围环境光的简化,从而简化计算过程。因为如果按照采样的方法进行渲染,每次渲染的时候都得对周围环境采样,从而都会耗费大量的计算时间。所以球谐光照的实现可以分成两个部分,一是环境光贴图的采样和积分运算,生成球谐参数,二是利用球谐参数对模型进行渲染。
采样器
采样是从环境光上面采,而环境光我们可以用环境光贴图表示。环境光贴图则可以采用cubemap的形式,也就是上一篇里面十字状的贴图,不过这里我们为了方便,把cubemap分成6个面,分别表示一个立方体的正x、负x、正y、负y、正z、负z。有了这六个贴图,通过一种映射关系,我们就能知道空间中的一点周围各个方向的光照值。具体的映射方法可以参考cubemap的wiki页面:https://en.wikipedia.org/wiki/Cube_mapping。
知道了每一个方向的光照值,要进行采样,还需要计算出球谐基。球谐基实际上相当于某个方向上分量的多少,多个球谐基在不同的方向上分量不同,所以才能够利用球谐基和球谐参数进行光照的还原。球谐基的计算方法,上一篇已经给出,例如,前四个分量的球谐基实际计算过程如下:
basis[] = .f / .f * sqrt(.f / PI); basis[] = sqrt(.f / (.f*PI))*y / r; basis[] = sqrt(.f / (.f*PI))*z / r; basis[] = sqrt(.f / (.f*PI))*x / r;
这样,每采样到一个像素,就计算相应的球谐基,并且对像素与对应的球谐基相乘后再求和,这样就相当于每个球谐基在所有像素上的积分。不过,为了得到球谐基上的平均光照强度,还需要将积分得到的数值乘以立体角并且除以总像素。简单说来就是运用这个公式求得球谐系数:

至于具体的实现,可以借助opencv,读取图片上的各个像素。值得注意的是,因为环境光贴图往往比较大,比如我选用的贴图每张大小为2048*2048,这样如果全部一次性读入内存的话将会导致程序运行内存占用过大,从而导致分配内存失败。针对这个问题,一个比较好的方式是传入一个函数对象给环境光采样器,这样环境光采样器每采到一个像素就立即调用函数对象处理。这样的话,就能把球鞋系数积分器实现成一个函数对象,传递给环境光采样器,最后再从球谐系数积分器函数对象里面取出计算出的球谐系数即可。这样可能会损失一些性能,因为需要频繁调用函数对象,然而这种性能损失是完全可以忽略的,首先对于一组环境光贴图,只需要运行一次采样器就能得到球谐系数,运行时间长短不是很重要,其次是采样过程中需要对外部数据进行频繁访问,所以瓶颈主要在于IO方面。
渲染器
我们现在只考虑环境光对一个物体的光照影响,不去考虑自阴影等问题,所以场景很简单:一个贴上环境光贴图的天空盒和一个位于中心位置的模型。有一点值得注意的是,我们是使用球谐参数来对模型进行着色,而不会涉及到天空盒,天空盒只是为了可视化对比的方便而已。对于模型上的每一点,需要知道对应的法向量,这样就能计算出对应的球谐基,然后用下列公式进行光线的还原:

还原出来的亮度值L即为该点的光照。当然这里只是最简单的光照模型,其BRDF的入射光可以看做是垂直入射的,而出射光强度与入射光相同,并且各个方向也相同,也就是对于模型的每一个顶点,其光照值与观察点无关。
具体的实现可以采用OpenGL,并且在我实现的过程中,使用了Cinder来简化一些模型加载和初始化等繁琐的操作,只注重于光照模型的实现。程序的主要过程如下:
1. 纹理和模型的加载
2. 相机、模型、shader的初始化
3. 绘制
重点就在于shader的实现。光照的shading部分应该放在fragment shader里面,实现球谐光照需要两个部分的参数,一个是法向量,另一个是球谐光照参数。法向量首先从vbo(顶点缓冲对象)传入,由vertex shader进行接收并传递给fragment shader。球谐光照参数则通过OpenGL的uniform方式传递一个大小为16的vec3数组。然后fragment shader利用法向量首先计算出球谐基,渲染过程的球谐基计算方法与采样过程一致,利用这些球谐基再与球谐系数进行光照的还原,从而就能得到每个点的亮度值了。具体的实现参考源代码中的assets/sh_lighting.vert。
此外为了方便观察,需要添加一些交互式操作,这些Cinder里面都提供了相应的IO接口,通过旋转移动相机位置,从而可以实现视角的变化。
光照渲染结果
程序实际运行效果如下:


从程序运行的情况上看,效果比较理想。模型的各个面的朝向可以大致反映出该方向的光照情况,并且从整体上看,模型与周围的天空盒融合得比较好,达到了一定的真实感。
总结
利用球谐光照,能够很好的对空间中一个模型所受到的环境光进行采样和还原。特别是在复杂的场景当中,如果依靠实时的环境光采样,以现在硬件水平的计算能力是达不到实时的。而如果采用球谐函数进行预计算,然后在实时渲染中进行光照的还原,则可以兼顾效率与光照效果。
源代码: https://github.com/lianera/SphericalHarmonicsLighting
球谐光照(Spherical Harmonics Lighting)及其应用-应用篇的更多相关文章
- 球谐光照(Spherical Harmonics Lighting)及其应用-实验篇
简介 之前在一篇实时深度图优化的论文中看到球谐光照(Spherical Harmonics Lighting)的应用,在查阅了许许多多资料之后还是无法完全理解,我个人觉得如果之前对实时渲染技术不是很了 ...
- Spherical Harmonics Lighting
[转自:http://www.cnblogs.com/daniagger/archive/2012/05/29/2524133.html] 1.背景知识 1.1 光照表示 之前我们都只考虑光源点和物体 ...
- Luckily general gradient for spherical harmonics is defined
http://web4.cs.ucl.ac.uk/staff/j.kautz/publications/gradientSH_RS04.pdf
- Thinking in Unity3D:渲染管线中的Rendering Path
关于<Thinking in Unity3D> 笔者在研究和使用Unity3D的过程中,获得了一些Unity3D方面的信息,同时也感叹Unity3D设计之精妙.不得不说,笔者最近几年的 ...
- 基于预计算的全局光照(Global Illumination Based On Precomputation)
目录 基于图像的光照(Image Based Lighting,IBL) The Split Sum Approximation 过滤环境贴图 预计算BRDF积分 预计算辐射度传输(Precomput ...
- Unity3D光照前置知识——Rendering Paths(渲染路径)及LightMode(光照模式)译解
简述 Unity supports different Rendering Paths. You should choose which one you use depending on your g ...
- 全局光照:光线追踪、路径追踪与GI技术进化编年史
全局光照(Global Illumination,简称 GI), 作为图形学中比较酷的概念之一,是指既考虑场景中来自光源的直接光照,又考虑经过场景中其他物体反射后的间接光照的一种渲染技术. 大家常听到 ...
- 【Unity Shaders】Shader中的光照
写在前面 自己写过Vertex & Fragment Shader的童鞋,大概都会对Unity的光照痛恨不已.当然,我相信这是因为我们写得少...不过这也是由于官方文档对这方面介绍很少的缘故, ...
- 【Unity Shader】(六) ------ 复杂的光照(上)
笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Sha ...
随机推荐
- 公众号第三方平台开发-aes解密失败
公众号第三方平台开发-aes解密失败 问题:本地启动项目,配置域名,测试微信公众号,系统正常运行:将项目部署到测试环境执行同样的操作,系统报错,错误异常:aes解密失败..... 调试--寻找问题-- ...
- bzoj3571————2016——3——12(最小乘积匹配)
bzoj3571 传送门http://www.lydsy.com/JudgeOnline/problem.php?id=3571 题解: ——————来自伟大的thy大神 http://blog.c ...
- bzoj2049
2049: [Sdoi2008]Cave 洞穴勘测 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 7579 Solved: 3548[Submit] ...
- 把windows的bat用好了,也很不错
taskkill /f /t /im nginx.exe cp2nginx xcopy /e /i /y “D:\workspace\workspace1\aff\WebContent” “D:\ng ...
- JAVA语言中冒号的用法
近来由于本人要介入android平台的开发,所以就买了本JAVA语言的书学习.学习一段时间来,我的感觉是谭浩强就是厉害,编写的<C编程语言>系列丛书不愧是经典.书中对C语言的介绍既系统又全 ...
- PHP根据URL提取根域名
<?php #使用示例 echo getBaseDomain('http://blog.jp.goo.ne.jp/index.php')->domain;echo "\n&quo ...
- PHP新手之学习数组声明
数组是在程序设计中,为了处理方便, 把具有相同类型的若干变量按有序的形式组织起来的一种形式.这些按序排列的同类数据元素的集合称为数组.下面介绍PHP中的数组声明. 一.数组的概述 1.数组的本质:管理 ...
- Spring的IOC原理[通俗解释一下]
Spring的IOC原理[通俗解释一下] 1. IoC理论的背景我们都知道,在采用面向对象方法设计的软件系统中,它的底层实现都是由N个对象组成的,所有的对象通过彼此的合作,最终实现系统的业务逻辑. 图 ...
- html标签的嵌套规则分析
1.a标签最好不要嵌套块级元素,可以嵌套内联元素,但是不能嵌套a标签和input之类的标签.能嵌套的标签像,等等. 2.ul和ol的子元素不能是别的元素只能是li,不能是别的比如div等,但是li中可 ...
- javascript window.confirm确认 取消对话框实现代码小结
本文章讲述的三种都是基于了javascript confirm提示确认框的做法了,只是在不同的地方写哦,有需要的同学可参考一下 confirm() 方法 confirm() 方法用于显示一个带有指 ...