我一直在使用WebGL实现一个区域照明,类似于这个演示:
http://threejs.org/examples/webgldeferred_arealights.html
上述的 three.js 实现是从 gamedev.net 上的 ArKano22 的工作进行移植的:
http://www.gamedev.net/topic/552315-glsl-area-light-implementation/
尽管这些解决方案非常令人印象深刻,但它们都有一些限制。ArKano22 原始实现的主要问题是漫反射项的计算没有考虑表面法线。
我已经花费几周时间来改进这个解决方案,并与 redPlant 合作解决了这个问题。目前,我已经将法线计算合并到该解决方案中,但结果也有缺陷。
这是我的当前实现的预览:
介绍
计算每个片段的漫反射项的步骤如下:
- 将顶点投影到区域光所在的平面上,使得投影向量与光的法线/方向重合。
- 通过比较投影向量和光的法线来检查顶点是否在区域光平面的正确侧面。
- 计算此投影点在平面上相对于光中心/位置的 2D 偏移量。
- 将这个 2D 偏移向量夹紧在光的区域内(由其宽度和高度定义)。
- 推导出被投影且夹紧的 2D 点的三维世界位置。这是离顶点最近的区域光点。
- 通过取顶点到最近点向量(归一化)和顶点法线的点积来执行通常针对点光源的漫反射计算。
问题
这种解决方案的问题在于,光照计算是从最近点开始进行的,并没有考虑到灯光表面上可能会更加照亮片段的其他点。让我来尝试解释一下为什么...
考虑下面的图示:
区域光源既垂直于表面又与其相交。表面上的每个片段始终返回光源上的最近点,即表面和光线相交的点。由于表面法向量和顶点到光线向量始终垂直,它们之间的点积为零。因此,尽管有大片的光线悬挂在表面上,漫反射贡献的计算却为零。
潜在解决方案
我建议我们不要从区域光源的最近点计算光线,而要从使顶点到光线向量(标准化)与顶点法向量点积最大的区域光源上的一个点进行计算。在上面的图中,这将是紫色点,而不是蓝色点。
帮帮我!
因此,这就是我需要您的帮助的地方。在我的脑海中,我对如何推导出这个点有一个很好的想法,但没有数学能力得到解决方案。
目前,在我的片段着色器中可用以下信息:
- 顶点位置
- 顶点法向量(单位向量)
- 光源位置、宽度和高度
- 光源法向量(单位向量)
- 光源右侧向量(单位向量)
- 光源上方向量(单位向量)
- 从顶点投影到光线平面上的点(3D)
- 从光源中心偏移的投影点(2D)
- 夹紧偏移量(2D)
- 这个夹紧偏移量的世界位置 - 最近点(3D)
为了测试我的建议,我需要区域光源上的投射点 —— 由红点表示,以便我可以在顶点法线与顶点到投射点(已归一化)之间执行点积。同样,这应该产生最大可能的贡献值。
更新!!!
我在CodePen上创建了一个交互式草图,用于可视化我目前实现的数学方法:http://codepen.io/wagerfield/pen/ywqCp
你应该聚焦于第318行的相关代码。
castingPoint.location
是THREE.Vector3
的实例,也是谜题中缺少的部分。你还应该注意到草图左下角有2个值——这些值是动态更新的,以显示相关向量之间的点积。我想解决方案可能需要另一个伪平面,与顶点法线方向对齐并且垂直于光源平面,但我可能错了!
castingPoint.location
- 这在第332行。 - wagerfield