在黑暗的环境光上应用聚光灯 - HLSL - Monogame

拉马尔

我为我的 Monogame 项目编写了一个 HLSL 着色器,它使用环境照明来创建日/夜循环。

#if OPENGL
    #define SV_POSITION POSITION
    #define VS_SHADERMODEL vs_3_0
    #define PS_SHADERMODEL ps_3_0
#else
    #define VS_SHADERMODEL vs_4_0_level_9_1
    #define PS_SHADERMODEL ps_4_0_level_9_1
#endif

sampler s0;

struct VertexShaderOutput
{
    float4 Position : SV_POSITION;
    float4 Color : COLOR0;
    float2 TextureCoordinates : TEXCOORD0;
};


float ambient = 1.0f;
float percentThroughDay = 0.0f;

float4 MainPS(VertexShaderOutput input) : COLOR
{
    float4 pixelColor = tex2D(s0, input.TextureCoordinates);
    float4 outputColor = pixelColor;

    // lighting intensity is gradient of pixel position
    float Intensity = 1 + (1  - input.TextureCoordinates.y) * 1.3;
    outputColor.r = outputColor.r / ambient * Intensity;
    outputColor.g = outputColor.g / ambient * Intensity;
    outputColor.b = outputColor.b / ambient * Intensity;

    // sun set/rise blending 
    float exposeRed = (1 + (.39 - input.TextureCoordinates.y) * 8); // overexpose red
    float exposeGreen = (1 + (.39 - input.TextureCoordinates.y) * 2); // some extra green for the blue pixels
    float exposeBlue = (1 + (.39 - input.TextureCoordinates.y) * 6); // some extra blue 

    // happens over full screen
    if (input.TextureCoordinates.y < 1.0f) {

        float redAdder = max(1, (exposeRed * (percentThroughDay/0.25f))); // be at full exposure at 25% of day gone
        float greenAdder = max(1, (exposeGreen * (percentThroughDay/0.25f))); // be at full exposure at 25% of day gone
        float blueAdder = max(1, (exposeBlue * (percentThroughDay/0.25f))); // be at full exposure at 25% of day gone

        // begin reducing adders
        if (percentThroughDay >= 0.25f && percentThroughDay < 0.50f) {
            redAdder = max(1, (exposeRed * (1-(percentThroughDay - 0.25f)/0.25f)));
            greenAdder = max(1, (exposeGreen * (1-(percentThroughDay - 0.25f)/0.25f)));
            blueAdder = max(1, (exposeGreen * (1-(percentThroughDay - 0.25f)/0.25f)));
        }

        //mid day
        else if (percentThroughDay >= 0.50f && percentThroughDay < 0.75f) {
            redAdder = 1;
            greenAdder = 1;
            blueAdder = 1;
        }

        // add adders back for sunset
        else if (percentThroughDay >= 0.75f && percentThroughDay < 0.85f) {
            redAdder = max(1, (exposeRed * ((percentThroughDay - 0.75f)/0.10f)));
            greenAdder = max(1, (exposeGreen * ((percentThroughDay - 0.75f)/0.10f)));
            blueAdder = max(1, (exposeBlue * ((percentThroughDay - 0.75f)/0.10f)));
        }

        // begin reducing adders
        else if (percentThroughDay >= 0.85f) {
            redAdder = max(1, (exposeRed * (1-(percentThroughDay - 0.85f)/0.15f)));
            greenAdder = max(1, (exposeGreen * (1-(percentThroughDay - 0.85f)/0.15f)));
            blueAdder = max(1, (exposeBlue * (1-(percentThroughDay - 0.85f)/0.15f)));
        }

        outputColor.r = outputColor.r * redAdder;
        outputColor.g = outputColor.g * greenAdder;
        outputColor.b = outputColor.b * blueAdder;
    }

    return outputColor;
}

technique ambientLightDayNight
{
    pass P0
    {
        PixelShader = compile ps_2_0 MainPS();
    }
};

这在很大程度上是我想要的(尽管它肯定可以使用一些计算优化)。

但是,我现在正在考虑在我的游戏中添加聚光灯以供玩家使用。我遵循了这种独立于环境光着色器工作的方法这是一个使用 lightMask 的非常简单的着色器。

sampler s0;  

texture lightMask;  
sampler lightSampler = sampler_state{Texture = lightMask;};  

float4 PixelShaderLight(float2 coords: TEXCOORD0) : COLOR0  
{  
    float4 color = tex2D(s0, coords);  
    float4 lightColor = tex2D(lightSampler, coords);  
    return color * lightColor;  
}  


technique Technique1  
{  
    pass Pass1  
    {  
        PixelShader = compile ps_2_0 PixelShaderLight();  
    }  
}  

我的问题现在是同时使用这两个着色器。我目前的方法是将我的游戏场景绘制到渲染目标,应用环境光着色器,然后在应用聚光灯着色器的同时将游戏场景(现在使用环境光)绘制到客户端屏幕。

这带来了多个问题:

  • 在环境光完全遮住灯光周围的任何东西后应用聚光灯着色器,而实际上灯光周围的区域应该是环境光。
  • 聚光灯着色器中计算的光强度(光的亮度)在“夜晚”时过于暗淡,因为它是根据环境光着色器的输出计算光色。

我尝试在聚光灯着色器之后应用环境光着色器,但这只会将大部分内容渲染为黑色,因为环境光是针对大部分黑色背景计算的。

我已经尝试在聚光灯着色器中添加一些代码,将黑色像素着色为白色,以显示环境光背景,但是仍然根据较暗的环境光计算光强度 - 导致非常暗淡的光线。

另一个想法是修改我的环境光着色器以将 lightMask 作为参数,而不是将环境光应用于光罩上标记的灯光。然后我可以使用聚光灯着色器来应用光的渐变并修改颜色。但我不确定是否应该将这两个看似独立的灯光效果塞进一个像素着色器中。当我尝试这个时,我的着色器也没有编译,因为有太多的算术操作。

所以我想问大家的问题是:

  • 我应该避免将多种效果塞进一个像素着色器中吗?
  • 一般来说,我如何将聚光灯应用于可能是“黑暗”的环境光效果?

编辑

我的解决方案 - 最终没有使用聚光灯着色器,但仍然使用文章中给出的纹理绘制光蒙版,然后将该光蒙版传递给这个环境光着色器并抵消纹理渐变。

float4 MainPS(VertexShaderOutput input) : COLOR
{

    float4 constant = 1.5f;
    float4 pixelColor = tex2D(s0, input.TextureCoordinates);
    float4 outputColor = pixelColor;

    // lighting intensity is gradient of pixel position
    float Intensity = 1 + (1  - input.TextureCoordinates.y) * 1.05;
    outputColor.r = outputColor.r / ambient * Intensity;
    outputColor.g = outputColor.g / ambient * Intensity;
    outputColor.b = outputColor.b / ambient * Intensity;

    // sun set/rise blending  
    float gval = (1 - input.TextureCoordinates.y); // replace 1 with .39 to lock to 39 percent of screen (this is how it was before)
    float exposeRed = (1 + gval * 8); // overexpose red
    float exposeGreen = (1 + gval * 2); // some extra green
    float exposeBlue = (1 + gval * 4); // some extra blue 

    float quarterDayPercent = (percentThroughDay/0.25f);
    float redAdder = max(1, (exposeRed * quarterDayPercent)); // be at full exposure at 25% of day gone
    float greenAdder = max(1, (exposeGreen * quarterDayPercent)); // be at full exposure at 25% of day gone
    float blueAdder = max(1, (exposeBlue * quarterDayPercent)); // be at full exposure at 25% of day gone

    // begin reducing adders
    if (percentThroughDay >= 0.25f ) {
        float gradientVal1 = (1-(percentThroughDay - 0.25f)/0.25f);
        redAdder = max(1, (exposeRed * gradientVal1));
        greenAdder = max(1, (exposeGreen * gradientVal1));
        blueAdder = max(1, (exposeGreen * gradientVal1));
    }

    //mid day
    if (percentThroughDay >= 0.50f) {
        redAdder = 1;
        greenAdder = 1;
        blueAdder = 1;
    }

    // add adders back for sunset
    if (percentThroughDay >= 0.75f) {
        float gradientVal2 = ((percentThroughDay - 0.75f)/0.10f);
        redAdder = max(1, (exposeRed * gradientVal2));
        greenAdder = max(1, (exposeGreen * gradientVal2));
        blueAdder = max(1, (exposeBlue * gradientVal2));
    }

    // begin reducing adders
    if (percentThroughDay >= 0.85f) {

        float gradientVal3 = (1-(percentThroughDay - 0.85f)/0.15f);
        redAdder = max(1, (exposeRed * gradientVal3));
        greenAdder = max(1, (exposeGreen * gradientVal3));
        blueAdder = max(1, (exposeBlue * gradientVal3));
    }

    outputColor.r = outputColor.r * redAdder;
    outputColor.g = outputColor.g * greenAdder;
    outputColor.b = outputColor.b * blueAdder;

    // first check if we are in a lightMask light
    float4 lightMaskColor = tex2D(lightSampler, input.TextureCoordinates);
    if (lightMaskColor.r != 0.0f || lightMaskColor.g != 0.0f || lightMaskColor.b != 0.0f) 
    {
        // we are in the light so don't apply ambient light
        return pixelColor * (lightMaskColor + outputColor) * constant; // have to offset by outputColor here because the lightMask is pure black
    }

    return outputColor * pixelColor * constant; // must multiply by pixelColor here to offset the lightMask bounds. TODO: could try to restore original color by removing this multiplaction and factoring in more of an offset on ln 91
}
格涅肖

要根据需要链接灯光,您需要一种不同的方法。正如您已经遇到的那样,仅在颜色上链接灯是行不通的,因为一旦颜色变为黑色,就无法再突出显示。处理多个灯光有两种典型的方法:前向着色和延迟着色。每个都有其优点和缺点,所以你需要看看哪个更适合你的情况。


正向着色

这种方法是您通过在单个着色通道中填充所有光照计算来测试的方法。您将所有光强度加在一起以形成最终光强度,然后将其与颜色相乘。

优点是性能和简单性,缺点是灯光数量和更复杂的着色器代码的限制。


延迟着色

这种方法将单个灯光彼此分离,可用于绘制具有非常多灯光的场景。每个灯光都需要原始场景颜色(反照率)来计算其在最终图像中的部分。因此,您首先将没有任何照明的场景渲染到纹理上(通常称为颜色缓冲区或反照率缓冲区)。然后,您可以通过将其与反照率相乘并将其添加到最终图像中来单独渲染每个灯光。因此,即使在黑暗的部分,原始颜色也会随着光线再次恢复。

优点是结构更简洁,可以使用大量灯,即使形状不同。缺点是必须进行额外的缓冲区和绘制调用。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

SpriteKit聚光灯效果

来自分类Dev

D3D11:HLSL中的可变灯数

来自分类Dev

在OpenGL场景中创建聚光灯

来自分类Dev

聚光灯搜索中消失的“东西”

来自分类Dev

聚光灯阴影贴图的计算偏差

来自分类Dev

聚光灯列内的计算

来自分类Dev

为什么聚光灯关了没有灯出来?

来自分类Dev

聚光灯在libGDX中不起作用

来自分类Dev

iPhone中类似HTML的聚光灯框

来自分类Dev

使用Python在图像中创建“聚光灯”

来自分类Dev

iPhone中类似HTML的聚光灯框

来自分类Dev

尝试在Android画布上进行聚光灯效果

来自分类Dev

Three.js聚光灯不会投射阴影

来自分类Dev

如何查看当前正在索引哪些文件聚光灯

来自分类Dev

HLSL多通

来自分类Dev

HLSL中的流控制

来自分类Dev

HLSL对齐问题

来自分类Dev

GLSL到HLSL问题

来自分类Dev

使用HLSL附加纹理

来自分类Dev

使用monogame开发的Windows应用商店游戏中未显示鼠标光标

来自分类Dev

MonoGame应用程序说缺少SDL.dll,即使它存在。为什么?

来自分类Dev

使用monogame开发的Windows应用商店游戏中鼠标光标未显示

来自分类Dev

无法在 Monogame 应用程序 C# 中访问 System.Console

来自分类Dev

应用搜索结果未在iOS聚光灯下显示

来自分类Dev

MonoGame只是XNA吗?

来自分类Dev

MonoGame按键琴弦

来自分类Dev

创建Backgroundthread Monogame

来自分类Dev

MonoGame GetData实现的异常

来自分类Dev

MonoGame / XNA旋转图像