改进了WebGL和ThreeJS中的区域照明

wag*_*eld 59 math 3d lighting webgl three.js

我一直在研究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的改进来解决这个问题.目前我将正常的计算结合到解决方案中,但结果也存在缺陷.

以下是我当前实施的预览:

区域照明预告片

介绍

计算每个片段的扩散项的步骤如下:

  1. 将顶点投影到区域光所在的平面上,以使投影向量与光的法线/方向一致.
  2. 通过将投影矢量与光线法线进行比较,检查顶点是否位于区域光平面的正确一侧.
  3. 从光的中心/位置计算该投影点在平面上的2D偏移.
  4. 夹住此2D偏移矢量,使其位于灯光区域内(由其宽度和高度定义).
  5. 导出投影和夹紧2D点的3D世界位置.这是区域光到顶点的最近点.
  6. 通过获取顶点到最近点矢量(标准化)和顶点法线之间的点积来执行通常的点光计算.

问题

该解决方案的问题在于,照明计算是从最近的点完成的,并且不考虑光表面上可能照亮片段的其他点.让我试着解释一下为什么......

请考虑以下图表:

有问题的区域照明情况

区域光线垂直于表面并与之相交.表面上的每个碎片将始终返回表面和光相交的区域光上的最近点.由于曲面法线和顶点到光线矢量总是垂直的,因此它们之间的点积为零.随后,尽管在表面上隐藏着大面积的光,但漫射贡献的计算为零.

潜在解决方案

I propose that rather than calculate the light from the nearest point on the area light, we calculate it from a point on the area light that yields the greatest dot product between the vertex-to-light vector (normalised) and the vertex normal. In the diagram above, this would be the purple dot, rather than the blue dot.

Help!

And so, this is where I need your help. In my head, I have a pretty good idea of how this point can be derived, but don't have the mathematical competence to arrive at the solution.

Currently I have the following information available in my fragment shader:

  • vertex position
  • vertex normal (unit vector)
  • light position, width and height
  • light normal (unit vector)
  • light right (unit vector)
  • light up (unit vector)
  • projected point from the vertex onto the lights plane (3D)
  • projected point offset from the lights center (2D)
  • clamped offset (2D)
  • world position of this clamped offset – the nearest point (3D)

To put all this information into a visual context, I created this diagram (hope it helps):

可用的照明信息

To test my proposal, I need the casting point on the area light – represented by the red dots, so that I can perform the dot product between the vertex-to-casting-point (normalised) and the vertex normal. Again, this should yield the maximum possible contribution value.

UPDATE!!!

I have created an interactive sketch over on CodePen that visualises the mathematics that I currently have implemented:

http://codepen.io/wagerfield/pen/ywqCp

codepen

The relavent code that you should focus on is line 318.

castingPoint.locationTHREE.Vector3拼图的一个实例并且是缺失的部分.您还应该注意到草图左下方有2个值 - 这些值会动态更新以显示相关矢量之间的点积.

我想这个解决方案需要另一个与顶点法线方向对齐并且垂直于光线平面的伪平面,但我可能错了!

Wes*_*ley 40

好消息是有一个解决方案; 但首先是坏消息.

使用最大化点积的点的方法从根本上是有缺陷的,并且在物理上是不合理的.

在上面的第一张图中,假设您的区域光仅由左半部分组成.

"紫色"点 - 最大化左半部分点积的点 - 与最大化两半点积相结合的点相同.

因此,如果要使用您提出的解决方案,可以得出结论,区域光的左半部分发出与整个光相同的辐射.显然,这是不可能的.

用于计算该地区光在给定的点投射出总量的解决方案是相当复杂的,但供参考,你可以找到在1994年的论文解释辐照雅可比部分遮挡多面体源 在这里.

我建议你看一下图11.2节的几段- 然后停下来.:-)

为了方便起见,我编写了一个非常简单的着色器,它使用three.js实现解决方案WebGLRenderer- 而不是延迟的.

编辑:这是一个更新的小提琴:http://jsfiddle.net/hh74z2ft/1/

在此输入图像描述

片段着色器的核心非常简单

// 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 );
Run Code Online (Sandbox Code Playgroud)

更多好消息:

  1. 这种方法是物理上正确的.
  2. 衰减自动处理.(请注意,较小的灯光需要较大的强度值.)
  3. 从理论上讲,这种方法应该适用于任意多边形,而不仅仅是矩形多边形.

注意事项:

  1. 我只实现了漫反射组件,因为这是你的问题所解决的.
  2. 您将不得不使用合理的启发式实现镜面反射分量 - 类似于您已编码的内容,我希望如此.
  3. 这个简单的例子不处理区域光"部分低于地平线"的情况 - 即并非所有4个顶点都在面的平面之上.
  4. 由于WebGLRenderer不支持区域灯光,因此无法"将灯光添加到场景中"并期望它能够正常工作.这就是我将所有必要的数据传递到自定义着色器的原因.(WebGLDeferredRenderer当然,确实支持区域灯.)
  5. 阴影不受支持.

three.js r.73

  • @WestLangley如果可以,我会给你500分.:) (2认同)