如何在GLSL中实现2D射线投射光照效果

8

这个问题最初是由@sydd在这里提出的。我很感兴趣,所以尝试编码,但在我回答之前,它已经被关闭/删除了,所以在这里。

问题:如何在GLSL中复制/实现 2D 光线投射照明效果?

该效果本身从鼠标位置向每个方向发射射线,并累积背景地图的 alpha 和颜色,影响像素强度。

因此,输入应包括:

  • 鼠标位置
  • 背景 RGBA 地图纹理
1个回答

9
  1. 背景地图

    我创建了一个测试的 RGBA 地图,由两张图片组成,一张包含 RGB(在左侧),第二张包含 alpha 通道(在右侧),因此您可以同时看到它们。当然,它们被合并为单个 RGBA 纹理。

    RGB map alpha map

    我稍微模糊了它们,以获得更好的边缘视觉效果。

  2. 光线投射

    由于这应该在 GLSL 中运行,我们需要在某个地方进行光线投射。我决定在 片段着色器 中完成。因此,算法如下:

    1. GL 端传递所需的着色器 uniform,包括鼠标位置作为纹理坐标、纹理的最大分辨率和光线传输强度。
    2. GL 端绘制覆盖整个屏幕的四边形,使用背景纹理(无混合)
    3. 在顶点着色器中仅传递所需的纹理和片段坐标
    4. 对于每个片段,在片段着色器中:

      • 从鼠标位置到实际片段位置(以纹理坐标表示)投射光线
      • 在光线行进过程中累积/整合光属性
      • 如果光强度接近零或目标片段位置达到,则停止。

顶点着色器

// Vertex
#version 420 core
layout(location=0) in vec2 pos;     // glVertex2f <-1,+1>
layout(location=8) in vec2 txr;     // glTexCoord2f  Unit0 <0,1>
out smooth vec2 t1;                 // texture end point <0,1>
void main()
    {
    t1=txr;
    gl_Position=vec4(pos,0.0,1.0);
    }

片元着色器

// Fragment
#version 420 core
uniform float transmit=0.99;// light transmition coeficient <0,1>
uniform int txrsiz=512;     // max texture size [pixels]
uniform sampler2D txrmap;   // texture unit for light map
uniform vec2 t0;            // texture start point (mouse position) <0,1>
in smooth vec2 t1;          // texture end point, direction <0,1>
out vec4 col;
void main()
    {
    int i;
    vec2 t,dt;
    vec4 c0,c1;
    dt=normalize(t1-t0)/float(txrsiz);
    c0=vec4(1.0,1.0,1.0,1.0);   // light ray strength
    t=t0;
    if (dot(t1-t,dt)>0.0)
     for (i=0;i<txrsiz;i++)
        {
        c1=texture2D(txrmap,t);
        c0.rgb*=((c1.a)*(c1.rgb))+((1.0f-c1.a)*transmit);
        if (dot(t1-t,dt)<=0.000f) break;
        if (c0.r+c0.g+c0.b<=0.001f) break;
        t+=dt;
        }
    col=0.90*c0+0.10*texture2D(txrmap,t1);  // render with ambient light
//  col=c0;                                 // render without ambient light
    }

最终结果如下:

输出图像

动画256色GIF:

输出动画

GIF中的颜色由于8位截断而略有失真。如果动画停止,请刷新页面或在合适的图形查看器中打开。

半透明物体内部的光线


1
@sydd 看看这个。 (重新提出你的问题以便回答)。如果你想要,你仍然需要添加那个闪烁噪音纹理... - Spektre
1
@NicolBolas 很好知道...我刚刚在他的一个问题下添加了一条评论以确保。阴影贴图可以用于基本照明,但我现在无法想象如何使用这种方法添加背景(次表面光)的透明度和颜色(光的颜色和强度取决于它穿过的材料)。 - Spektre
1
@sydd 是的,与 CPU 方法相比,由于 GLSL 架构限制,计算存在很大的冗余。但我认为它仍然足够快,尽管我没有对其进行基准测试。有时间时我会试一下。无论如何,它只是单个 QUAD,没有任何复杂的计算。最慢的是纹理获取。您可以通过混合或添加更多循环(每个光源一个)将结果相加来添加更多灯光。您可以通过扩大 dt 步长或使用小分辨率映射纹理来加速该过程。我不在手机上编码,所以很难说你需要自己尝试一下... - Spektre
1
@sydd 在512x512窗口和256x256地图上,透射率为0.97时,我的GeForce GT440渲染时间最慢,约为6.5毫秒。在移动设备上,分辨率会更低,我想应该不会有这么慢的情况... - Spektre
1
@sydd,目前的硬件不支持这种操作。如果你有足够的时间,可以读取尽可能多的纹素,但每个片段着色器调用只能输出一个像素。 - Spektre
显示剩余9条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接