带有着色器的 2D 照明 - 受窗口大小影响的光半径

Posted

技术标签:

【中文标题】带有着色器的 2D 照明 - 受窗口大小影响的光半径【英文标题】:2D lighting with shaders - light radius affected by window size 【发布时间】:2014-02-20 22:23:08 【问题描述】:

我有一个着色器,可以为其他 2D 场景添加照明(灯光略高于 2D 平面)。在我的片段着色器中,我循环遍历每个灯光,通过将我的正交矩阵应用于灯光的位置来计算方向和距离。

问题是,灯光的“半径”受窗口大小和纵横比的影响。我认为使用正交矩阵转换坐标可以确保屏幕大小无关紧要,但是宽窗口会产生椭圆形的光,而较小的窗口会比较大的窗口产生更小的椭圆形。我应该使用其他某种矩阵吗?

此处的完整着色器(更改窗口大小以查看不需要的效果):http://glsl.heroku.com/e#14464.0

uniform vec2 resolution;

void main(void)

    //orthographic matrix
    mat4 ortho_matrix = mat4(
        2.0/resolution.x, 0, 0, 0,
        0, 2.0/-resolution.y, 0, 0,
        0, 0, -1, 0,
        -1, 1, 0, 1
        );

    //surface normal of the 2D plane (looking straight down)
    vec3 surface_normal = vec3(0.0, 0.0, 1.0);

    //screen position of the light
    vec2 light_screen_pos = vec2(650, 150);

    //translate light's position to normalized coordinates
    //the z value makes sure it is slightly above the 2D plane
    vec4 light_ortho_pos = ortho_matrix * vec4(light_screen_pos, -0.03, 1.0);

    //calculate the light for this fragment
    vec3 light_direction = light_ortho_pos.xyz - vec3(gl_FragCoord.x / resolution.x, gl_FragCoord.y / resolution.y, 0);
    float dist = length(light_direction);
    light_direction = normalize(light_direction);
    vec3 light = clamp(dot(surface_normal, light_direction), 0.0, 1.0) * vec3(0.5, 0.5, 0.5);
    vec3 cel_light = step(0.15, (light.r + light.g + light.b) / 3.0) * light;

    gl_FragColor = vec4(pow(light + cel_light, vec3(0.4545)), 1.0);

注意:我知道对每个灯光、每个像素进行此计算并不是最佳选择 - 我应该将灯光的位置传递到另一个统一的位置。

【问题讨论】:

我不确定为什么“正常”涉及纹理。你能解释一下 pixcoord 是什么或稍微简化一下吗?只需要一盏灯就能找出问题所在,对吧?而且由于这完全是关于 current_light 并且它实际上只取决于 light_direction 和 normal,您可能可以从该行移除衰减,看看会发生什么。 我试着稍微编辑一下……精灵有一个基础纹理和一个带有法线贴图的纹理,由碎片着色器使用。 Pixcoord 只是应用正交矩阵后顶点着色器的位置。我使用正交矩阵来转换精确的屏幕像素坐标。 谢谢。您是否将相同的 ortho_matrix 应用于 gl_Position 以获取 pixcoord?在我看来,在片段着色器中这样做会更好。如果您完全删除 cel_light 会发生什么?如果问题仍然存在,那么我们会有所进展。我不确定你对mix(vec3(-1), vec3(1), normal) 的目的是什么,你能解释一下吗? 我确实在顶点着色器中使用了相同的正交矩阵。我进一步简化了它,并发布了一个带有可编辑代码的 WebGL 版本的链接,以便于实验。 mix() 函数将法线贴图颜色从法线纹理转换为标准化范围(即 -1 到 1 而不是 0 到 1)。为了清楚起见,我已将其删除并仅对值进行硬编码。 我尝试将light_screen_pos 放在视图的中间,将其写成resolution,例如:light_screen_pos = vec2(0.5*resolution.x, 0.5*resolution.y),但我得到了奇怪的结果。我会玩这个。 【参考方案1】:

光线方向需要根据屏幕分辨率进行缩放。我最终添加了以下代码以使其工作,任意亮度为 500 左右:

light_direction *= vec3(resolution.x / brightness, resolution.y / brightness, 1.0);

【讨论】:

正交矩阵将光线置于屏幕上的正确位置,但在我也缩放方向计算之前,光线的形状(其明显的“半径”)并未受到影响。

以上是关于带有着色器的 2D 照明 - 受窗口大小影响的光半径的主要内容,如果未能解决你的问题,请参考以下文章

Unity - 2D 着色器/照明,例如 Terraria 或 Starbound

WebGL使用点光源照明(WebGL进阶06)

初步了解3D着色器的概念

OpenGL/C++:将多个纹理传递给一个着色器的问题

如何使用着色器程序将 2D 场景放入 QOpenGLWidget 窗口?

带有片段着色器的OpenGL 3.3不同颜色