如何在GLSL中实现2D光线投射光效
How to implement 2D raycasting light effect in GLSL
这最初是由@sydd here 提出的。我对此很好奇,所以我尝试对其进行编码,但它是 closed/deleted 之前我可以回答所以在这里。
问题:如何在GLSL中reproduce/implementthis二维光线投射光照效果?
效果本身从鼠标位置向各个方向投射光线,累积影响像素强度的背景贴图 alpha 和颜色。
所以输入应该是:
- 鼠标位置
- 背景 RGBA 贴图纹理
背景图
好的,我创建了一个测试 RGBA 贴图作为 2 个图像,其中一个包含 RGB(在左侧),第二个带有 alpha 通道(在右边)所以你可以看到他们两个。粗糙的它们组合在一起形成单个 RGBA 纹理。
我对它们都进行了模糊处理,以获得更好的边缘视觉效果。
光线投射
因为在 GLSL 中应该 运行 我们需要将光线投射到某个地方。我决定在 片段着色器 中进行。所以算法是这样的:
- On GL 着色器所需的侧向传递制服这里是鼠标位置作为纹理坐标、纹理的最大分辨率和光传输强度。
- 在 GL 侧边绘制四边形覆盖整个屏幕,背景纹理(o 混合)
- 在顶点着色器上只需传递所需的纹理和片段坐标
每个片段的片段着色器:
- 从鼠标位置投射光线到实际片段位置(在纹理坐标中)
- cumulate/integrate光线传播过程中的光属性
- 如果光强度接近零或达到目标片段位置则停止。
顶点着色器
// 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:
由于 8 位 t运行阳离子,GIF 中的颜色略有失真。此外,如果动画停止刷新页面或改为在 decend gfx 查看器中打开。
这最初是由@sydd here 提出的。我对此很好奇,所以我尝试对其进行编码,但它是 closed/deleted 之前我可以回答所以在这里。
问题:如何在GLSL中reproduce/implementthis二维光线投射光照效果?
效果本身从鼠标位置向各个方向投射光线,累积影响像素强度的背景贴图 alpha 和颜色。
所以输入应该是:
- 鼠标位置
- 背景 RGBA 贴图纹理
背景图
好的,我创建了一个测试 RGBA 贴图作为 2 个图像,其中一个包含 RGB(在左侧),第二个带有 alpha 通道(在右边)所以你可以看到他们两个。粗糙的它们组合在一起形成单个 RGBA 纹理。
我对它们都进行了模糊处理,以获得更好的边缘视觉效果。
光线投射
因为在 GLSL 中应该 运行 我们需要将光线投射到某个地方。我决定在 片段着色器 中进行。所以算法是这样的:
- On GL 着色器所需的侧向传递制服这里是鼠标位置作为纹理坐标、纹理的最大分辨率和光传输强度。
- 在 GL 侧边绘制四边形覆盖整个屏幕,背景纹理(o 混合)
- 在顶点着色器上只需传递所需的纹理和片段坐标
每个片段的片段着色器:
- 从鼠标位置投射光线到实际片段位置(在纹理坐标中)
- cumulate/integrate光线传播过程中的光属性
- 如果光强度接近零或达到目标片段位置则停止。
顶点着色器
// 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:
由于 8 位 t运行阳离子,GIF 中的颜色略有失真。此外,如果动画停止刷新页面或改为在 decend gfx 查看器中打开。