我一直在WebGL中开发类似于本演示的区域照明实现:
arealights.html
three.js中的上述实现是从ArKano22 over在gamedev.net上的工作中移植过来的:
http://www.gamedev.net/topic/552315-glsl-area-light-implementation/
虽然这些解决方案令人印象深刻,但它们都有一些局限性。ArKano22最初实现的主要问题是,漫射项的计算不考虑表面法线。
几个星期以来,我一直在增加这个解决方案,通过redPlant的改进来解决这个问题。目前,我有正常的计算纳入解决方案,但结果也有缺陷。
下面是我当前实现的预览:

引言
计算每个片段的扩散项的步骤如下:
问题
这个解决方案的问题是,照明计算是从最近点进行的,没有考虑到灯光表面的其他点,这些点可能会更多地照亮碎片。让我来解释一下为什么…
请考虑以下图表:

区域光既垂直于表面,又与表面相交。表面上的每一个碎片都会返回一个最近的点,在表面与光相交的区域光上。由于曲面法向量和顶点到光矢量总是垂直的,它们之间的点积为零。随后,尽管表面有很大的光面积,但漫射贡献的计算为零。
势解
我建议,与其计算面积光上的最近点的光,不如从面积光上的一个点计算它,该点在顶点到光矢量(标准化)和顶点法线之间产生最大的点积。在上面的图表中,这将是紫色的点,而不是蓝点。
帮助!
所以这就是我需要你帮忙的地方。在我的头脑中,我很清楚这一点是如何推导出来的,但我没有数学能力来得出这个解。
目前,我在片段着色器中提供了以下信息:
为了将所有这些信息放到可视上下文中,我创建了这个图表(希望它有所帮助):

为了测试我的建议,我需要在用红色点表示的区域上的浇铸点,这样我就可以在顶点到浇铸点(标准化)和顶点法线之间执行点积。同样,这将产生最大可能的贡献值。
更新!
我在CodePen上创建了一个交互式草图,可视化了我目前实现的数学:
http://codepen.io/wagerfield/pen/ywqCp

您应该关注的相关代码是318行。
castingPoint.location是THREE.Vector3的一个实例,也是拼图中缺少的部分。您还应该注意到,在草图的左下角有两个值-这些值是动态更新的,以显示相关向量之间的点乘积。
我设想这个解将需要另一个伪平面,它与顶点法线的方向一致,并且垂直于光的平面,但我可能错了!
发布于 2013-06-18 03:35:42
注意: three.js现在支持THREE.RectAreaLight。这个答案与three.js的旧版本有关。
您使用点最大化点的方法从根本上讲是有缺陷的,而且在物理上是不可信的。
在上面的第一张插图中,假设你的区域光只有左半部分。
“紫色”点--使左半部分的点积最大化的点--和两个半部的点乘积最大化的点一样。
因此,如果一个人使用你提议的解决方案,你会得出结论,区域光的左半发出与整个光相同的辐射。显然,这是不可能的。
计算区域光在给定点上投射的总光量是相当复杂的,但作为参考,您可以在1994年的论文“部分遮挡的多面体源这里的辐射雅可比”中找到一个解释。
我建议您查看图1,以及Section1.2的几段--然后停止。:-)
为了简单起见,我编写了一个非常简单的着色器,它使用three.js WebGLRenderer实现了解决方案--而不是延迟的解决方案。
编辑:删除过时的小提琴

片段着色器的核心非常简单。
// direction vectors from point to area light corners
for( int i = 0; i < NVERTS; i ++ ) {
lPosition[ i ] = viewMatrix * lightMatrixWorld * vec4( lightverts[ i ], 1.0 ); // in camera space
lVector[ i ] = normalize( lPosition[ i ].xyz + vViewPosition.xyz ); // dir from vertex to areaLight
}
// vector irradiance at point
vec3 lightVec = vec3( 0.0 );
for( int i = 0; i < NVERTS; i ++ ) {
vec3 v0 = lVector[ i ];
vec3 v1 = lVector[ int( mod( float( i + 1 ), float( NVERTS ) ) ) ]; // ugh...
lightVec += acos( dot( v0, v1 ) ) * normalize( cross( v0, v1 ) );
}
// irradiance factor at point
float factor = max( dot( lightVec, normal ), 0.0 ) / ( 2.0 * 3.14159265 );更好的消息是:
注意事项:
WebGLRenderer不支持区域灯,所以您不能“将光线添加到场景中”并期望它能够工作。这就是为什么我传递所有必要的数据到自定义着色器。(当然,WebGLDeferredRenderer确实支持区域灯。)three.js r.73号
发布于 2013-06-16 06:46:11
嗯。奇怪的问题!这似乎是你从一个非常具体的近似开始,现在你的工作回到正确的解决方案。
如果我们只坚持漫射和一个平面(只有一个法线),什么是传入的漫射光?即使我们坚持每一个入射光都有一个方向和强度,而我们只是取allin =积分(发光)((普通))*光,这是很难的。所以整个问题就是解决这个积分。有了点光,你就会作弊,把它做个和,然后把灯拔出来。这对于没有阴影的点灯很好,现在你真正想要做的就是解决这个积分。这就是你可以用一些光探针,球形谐波或许多其他技术来做的。或者用一些技巧来估计矩形的光量。
对我来说,想到你想要点亮的那个半球总是有帮助的。你需要所有的光都进来。有些不那么重要,有些则更重要。这就是你正常的目的。在一个生产射线追踪器,你可以只是样本几千点,并有一个很好的猜测。在实时情况下,你必须猜得更快。这就是你的库代码所做的:一个好的(但有缺陷的)猜测的快速选择。
这就是我认为你在倒退的地方:你意识到他们在猜测,有时候很糟糕(这就是猜测的本质)。现在,不要试图纠正他们的猜测,而是想出一个更好的!也许试着去理解他们为什么选择这种猜测。一个好的近似不是指善于处理拐角处的案件,而是表现得很好。在我看来就是这样的。(再次,对不起,我现在太懒得读three.js代码了)。
因此,要回答你的问题:
希望这能有所帮助。我可能完全错了,我对一个正在寻找一些快速数学的人漫不经心,在这种情况下,我道歉。
发布于 2013-06-10 13:09:40
让我们一致认为,铸造点总是在边缘。
让我们说,“发光部分”是空间的一部分,它代表的是沿其正常方向的挤压光的四角体。
如果表面点位于发光部分,那么你需要计算出保持这个点的平面,它是法线矢量,光是法线。平面和光线之间的交点会给你两个点作为选择(只有两个,因为浇铸点总是在边缘)。所以,测试这两个人,看看谁的贡献更大。
如果点不在光的部分,那么你可以计算出四个平面,每个面都有表面点,它的法线和光的四角点之一。对于每个光四顶点,您将有两个点(顶点+多一个交点)来测试,这是贡献最大的。
这应该能起作用。如果你遇到任何反例,请给我反馈。
https://stackoverflow.com/questions/17021264
复制相似问题