• 2D Lighting System in Monogame


    翻译自:http://www.gamedev.net/page/resources/_/technical/apis-and-tools/2d-lighting-system-in-monogame-r4131

    翻译的很随意~~~~~

    2D Lighting System in Monogame

    MonoGame中的二维光照系统

    This tutorial will walk you through a simple lighting/shadow system.

    本教程将会教你实现一个简单的光照/阴影系统。

    Go into your current Monogame project, and make a new file called

    lighteffect.fx

    打开你现在的Monogame项目,新建一个名为 lighteffect.fx的文件。

    This file will control the way our light will be drawn to the screen. This is an HLSL style program at this point. Other tutorials on HLSL will be available in the main website which will allow you to do some wicked cool things like; distorting space and the map, spinning, dizzyness, neon glowing, perception warping, and a bunch of other f?#%! amazing things!

    这个文件将会控制 光照 如何显示在屏幕中。本教程使用hlsl语言进行编程。其他hlsl相关的教程也可以在该 网址 中找到。hlsl语言可以让你实现一些十分酷炫的东西,如:扭曲空间和地图、物体旋转、眩晕效果、霓虹灯光效、视觉偏差等一堆神奇的东西!

    Here is the full lighteffect file.

    这里先给出整个 lighteffect 代码。

    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();  
        }  
    }  
    

    Now, don’t get overwhelmed at this code if you aren’t familiar with HLSL. Basically, this effect will be called every time we draw the screen (in the Draw() function). This .fx file manipulates each pixel on the texture that is loaded into it, in this case it would be the sampler variable.

    sampler s0;
    

    如果你对hlsl不熟悉,可能会对以上代码感到困惑。首先,这个 effect 会在每一次刷新屏幕时被调用(在Draw()函数中)。这个 .fx 文件 获取其加载的 texture 中的每一个 pixel,在文件中texture 将保存在 sampler 变量中。

    This represents the texture that you are manipulating. It will be automatically loaded when we call the effect. s0 is a sample register that SpriteBatch uses to draw textures, so it is already initialized. Your last draw function initializes this register, so you don’t need to worry about it!

    这表示你将操作的 texture。它会在我们调用这个 effect 时自动加载。s0 是一个 采样器 寄存器,SpriteBatch 会用它来绘制 texture,所以它已经是被初始化了的。最后一个 draw 函数会初始化这个 寄存器,所以你不必担心它。

    RenderTarget2D

    Render targets are textures that are made on the fly by drawing onto them using spriteBatch, rather than drawing directly to the back buffer.

    在使用 SpriteBatch 时,渲染对象并不会直接渲染到 back buffer 中,而是渲染到一些 texture中, 它们动态创建于 渲染 的时侯。

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

    The lightMask variable is our render target that will be created on the fly using additive blending and our light’s locations. I’ll explain more about this soon, here we are just putting the render target into a register that HLSL can use (called lightSampler).

    变量lightMask是我们的渲染对象,它创建于 使用 加性混合(additive blending) 和 光源位置 的时候,
    这我一会将会解释说明。这里我们只是将其放置于一个寄存器中,以便hlsl使用。

    Before I can explain the main part of the HLSL effect, I need to show you what exactly is happening behind the scenes.

    在解释这 hlsl effect 的主要部分之前,先看看这个 effect 在屏幕上的显示效果。

    First, we need the actual light effect that will appear over our lights.

    首先,我们需要一个实际的光照图,它将会用于我们的程序中。

    I’m showing you this version because the one that I use in the demo is a white transparent gradient, it won’t show up on the website.

    我把这图放上来的原因是,该教程使用的是一个白色带 透明通道 的渐变色图,它通常比较难在网上找到。

    If you want a link to the gradient that I used in the demos above, you can find that at my main website.

    如果你想要找其他渐变色图,你也可以在我的主页中找到。

    Otherwise, your demo will look like the image below. You can see black outlines around the circles if you look close.

    如果不使用 带透明通道 的图,你的程序将会像如下所示。你会发现在园的周围带黑色的外框。

    Whatever gradient you download, call it

    当你下载好渐变图后,重命名为

    lightmask.png

    Moving into your main game’s class, create a couple variables to store your textures in:

    在你程序的主要游戏类中,创建一些变量来存储你的 texture:

    public static Texture2D lightMask;
    public static Effect effect1;
    RenderTarget2D lightsTarget;
    RenderTarget2D mainTarget;
    

    Now load these in the LoadContent() function. lightMask is going to be lightmask.png
    effect1 will be lighteffect.fx
    This is how I initialize my render targets:

    通过 LoadContent() 函数来加载以上texture。lightMask对应lightmask.png

    effect1 对应 lighteffect.fx

    通过如下方式初始化 渲染对象:

    var pp = GraphicsDevice.PresentationParameters;
    lightsTarget = new RenderTarget2D(
    GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight);
    mainTarget = new RenderTarget2D(
    GraphicsDevice, pp.BackBufferWidth, pp.BackBufferHeight);
    

    With that stuff out of the way, now we can finally focus on the drawing.

    把这个完成后,现在我们可以专注于 渲染 函数了。

    In your Draw() function, lets begin by drawing the lightsTarget:

    在 Draw() 中,首先开始绘制 lightsTarget:

    GraphicsDevice.SetRenderTarget(lightsTarget);
    GraphicsDevice.Clear(Color.Black);
    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.Additive);
    //draw light mask where there should be torches etc...
    // 可以把light 渲染到点击的地方
    spriteBatch.Draw(lightMask, new Vector2(X, Y), Color.White);
    spriteBatch.Draw(lightMask, new Vector2(X, Y), Color.White);
    
    spriteBatch.End();
    

    Some of that is psuedo code, you have to put in your own coordinates for the lightMask. Basically you want to draw a lightMask at every location you want a light, simple right?

    以上代码有部分是伪代码,你必须传入实际的坐标给lightmask。通常,你会在 想要有光照的地方 都绘制一个 lightMask,这样就可以产生光照效果了,是不是很简单?

    What you get is something like this: (The light gradient is highlighted in red just for demonstration)

    你将会得到像这样的图:(为了突出光的渐变,色彩被设成红色)

    Now in simple, basic theory, we want to draw the game under this texture, with the ability to blend into it so it looks like a natural lighting scene.

    现在根据这个简单的 blend 方法,将 texture 绘制在游戏层上,以实现自然光照的场景。

    If you noticed above, we draw the light render scene with BlendState.Additive because we will end up adding this on top of our main scene.

    可能你已经注意到了,我们通过 BlendState.Additive 语句来绘制光照场景。事实上,我们将会把 texture 增加到游戏主场景的顶层。

    What I do next is I draw the main game scene onto mainTarget.

    接着,我们把主游戏场景绘制到 mainTarget中。

    GraphicsDevice.SetRenderTarget(mainTarget);
    GraphicsDevice.Clear(Color.Transparent);          
    spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, null, null, null, cam.Transform);
    cam.Draw(gameTime, spriteBatch);
    spriteBatch.End();
    

    Okay, we are in the home stretch! Note: All this code is sequential to the last bit and is all located under the Draw function, just so I don’t lose any of you.

    现在我们大功告成了。注意:所有这些代码都是按顺序执行的,并且都是在 Draw() 中,以便不会遗漏。

    So we have our light scene drawn and our main scene drawn. Now we need to surgically splice them together, without anything getting too bloody.

    在光照和游戏场景都绘制好后,我们把它们混合起来。

    We set our program’s render target to the screen’s back buffer. This is just the default drawing space for the client’s screen. Then we color it black.

    现在我们将程序的 渲染对象 设成屏幕的 back buffer。它是显示在屏幕的默认渲染空间。先将其设为黑色。

    GraphicsDevice.SetRenderTarget(null);
    GraphicsDevice.Clear(Color.Black);
    

    Now we are ready to begin our splice!

    现在我们准备开始混合。

    spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
    
    effect1.Parameters["lightMask"].SetValue(lightsTarget);
    effect1.CurrentTechnique.Passes[1].Apply();                         
    spriteBatch.Draw(mainTarget, Vector2.Zero, Color.White);               
    spriteBatch.End();
    

    We begin a spriteBatch whose blendstate is AlphaBlend, which is how we can blend this light scene so smoothly on top of our game.

    我们将一个 spriteBatch 的 blendstate 设为 AlphaBlend,这样我们就可以将 光照场景 混合到游戏场景上面。

    Now we can begin to understand the lighteffect.fx file.

    现在我们开始理解 lighteffect.fx 文件。

    Remember from earlier;

    回忆一下;

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

    We pass the lightsTarget texture into our effect’s lightMask texture, so lightSampler will hold our light rendered scene.

    我们将 lightsTarget 纹理传入到 effect文件中的 lightMask 纹理中,所以 lightSampler 会保存我们的 光照场景。

    tex2D is a built-in HLSL function that grabs a pixel on the texture at coords vector.

    tex2D 是一个 hlsl 内部提供的函数,它可以获取 texture 中某个坐标上的 pixel值。

    Looking back at the main guts of the effect function:

    现在看看 effect 的主要部分:

    float4 color = tex2D(s0, coords);  
    float4 lightColor = tex2D(lightSampler, coords);  
    return color * lightColor;
    

    Each pixel that we find in the game’s main scene (s0 variable), we look for the pixel in the same coordinates on our second render scene — the light mask (lightSampler variable).

    对于每个从游戏主场景中获取到的 pixel,我们都同时获取 光照场景中(lightSampler) 相应位置上的 pixel。

    This is where the magic happens, this line of code;

    最后这行代码就是奇迹发生的地方。

    return color * lightColor;
    

    Takes the color from our main scene and multiplies it by the color in our light rendered scene, the gradient. If lightColor is pure white (very center of the light), it leaves the color alone. If lightColor is completely black, it turns that pixel black. Colors in between(grey) simply tint the final color, which is how our light effect works!

    将主场景的颜色和光照场景的颜色(渐变色图)相乘。如果 lightColor 是纯白色(在光的中心),那将保留 color 的颜色值。如果 lightColor 是黑色的,那将使该像素值变黑。颜色在两者中间的,相乘得到最后的颜色。这就是我们的光照特效的工作方式。

    Our final result (honoring the color red for demonstration):

    以下现实最终效果(红色是为了突出演示):

    One more thing worth mentioning, effect1.Apply() only gets the next Draw() function ready. When we finally call spritebatch.Draw(mainTarget), it kicks in the effect file. s0‘s register is loaded with this mainTarget and the final color effect is applied to the texture as it is drawn to the player’s screen.

    还有个值得注意的地方, effect1.Apply() 只是为下一个 Draw() 做准备。当我们最后调用 spritebatch.Draw(mainTarget) 时, 它将读取 effect 文件。s0寄存器 存储 mainTarget,以及最后的颜色效果将应用到texture中,并被绘制到玩家屏幕。

    Be careful using this in your existing games, changing drawing blend states and sort modes could funk up some of your game’s visuals.

    当在你现有的游戏中使用该方法的时候,要留意 drawing blend states 和 sort modes,这些的改变有可能会让你的游戏画面出现问题。

    You can see a live example of what this system does in a top-down 2D rpg.

    你可以看一个使用该系统的例子,这是一个简单的顶部视角的 2D rpg 场景。

    The live gif lost some quality, the second screen shot shows you how it really looks.

    gif文件将损失一些图像质量,第二张截图显示实际的效果。

    You can learn more and ask questions at the original post;

    你可以通过以下网站进一步学习,或者在原文章那里问问题。

    http://www.xnahub.com/simple-2d-lighting-system-in-c-and-monogame/

  • 相关阅读:
    mr程序无法输出日志进行调试的解决方法
    2015年度总结
    Hadoop数据目录迁移
    Oracle数据迁移至HBase操作记录
    Hadoop端口一览表
    Hadoop Maven pom文件示例
    十个书写Node.js REST API的最佳实践(下)
    iOS 程序从开发完到上 AppStore 那点事儿
    拿什么拯救你,我的三十五岁
    Spark 以及 spark streaming 核心原理及实践
  • 原文地址:https://www.cnblogs.com/foxianmo/p/4719674.html
Copyright © 2020-2023  润新知