二维水凹凸贴图-Monogame [英] 2D Water Bump Mapping - Monogame

查看:121
本文介绍了二维水凹凸贴图-Monogame的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

感谢您抽出宝贵时间来检查我的问题.

Thanks for taking the time to check out my issue.

在我第一次尝试游戏时,我正在努力改善海洋环境.我决定对我的海洋瓷砖使用凹凸贴图,以在水中添加一些纹理.为此,我将水瓦片绘制到renderTarget,然后在将渲染目标绘制到后缓冲区时应用像素着色器.

I am working on improving the ocean in my first attempt at a game. I have decided on a using a bump map against my ocean tiles to add a little texture to the water. To do this, I draw my water tiles to a renderTarget and then apply a pixel shader while drawing the render target to the backbuffer.

我遇到的问题是像素着色器似乎偏移或置换了绘制的渲染目标的位置.观察这两张照片:

The problem I am having is that the pixel shader seems to offset or displace the position of render target that is drawn. Observe these two photos:

此图像是不运行像素着色器的游戏.请注意,岛周围的"浅水"在此处是纯色.

This image is the game without running the pixel shader. Notice the "shallow water" around the islands which is a solid color here.

运行像素着色器后,浅水将始终向右偏移.

With the pixel shader is run, that shallow water is offset to the right consistently.

我正在使用.我可能想到的是此凹凸贴图的尺寸与我要对其应用的渲染目标不匹配.但是,我不确定要如何创建/调整凹凸图的大小.

I am using the bump map provided in riemers novice bump mapping. One possible thought I had was that the dimensions of this bump map do not match the render target I am applying it on. However, I'm not entirely sure how I would create/resize this bump map.

我的HLSL像素着色器看起来像这样:

My HLSL pixel shader looks like this:

#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

matrix WorldViewProjection;
float xWaveLength;
float xWaveHeight;

texture bumpMap;
sampler2D bumpSampler = sampler_state
{
    Texture = <bumpMap>;
};

texture water;
sampler2D waterSampler = sampler_state
{
    Texture = <water>;
};
// MAG,MIN,MIRRR SETTINGS? SEE RIEMERS

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TextureCords : TEXCOORD;
    float4 Color : COLOR0;
};

struct VertexShaderOutput
{
    float4 Pos : SV_POSITION;
    float2 BumpMapSamplingPos : TEXCOORD2;
    float4 Color : COLOR0;
};

VertexShaderOutput MainVS(in VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

    output.BumpMapSamplingPos = input.TextureCords/xWaveLength;
    output.Pos = mul(input.Position, WorldViewProjection);
    output.Color = input.Color;

    return output;
}

float4 MainPS(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 texCoord : TEXCOORD0) : COLOR
{
    float4 bumpColor = tex2D(bumpSampler, texCoord.xy);
    //get offset 
    float2 perturbation = xWaveHeight * (bumpColor.rg - 0.5f)*2.0f;

    //apply offset to coordinates in original texture
    float2 currentCoords = texCoord.xy;
    float2 perturbatedTexCoords = currentCoords + perturbation;

    //return the perturbed values
    float4 color = tex2D(waterSampler, perturbatedTexCoords);
    return color;
}

technique oceanRipple
{
    pass P0
    {
        //VertexShader = compile VS_SHADERMODEL MainVS();
        PixelShader = compile PS_SHADERMODEL MainPS();
    }
};

我的单机游戏抽奖电话看起来像这样:

And my monogame draw call looks like this:

    public void DrawMap(SpriteBatch sbWorld, SpriteBatch sbStatic, RenderTarget2D worldScene, GameTime gameTime)
    {

        // Set Water RenderTarget
        _graphics.SetRenderTarget(waterScene);
        _graphics.Clear(Color.CornflowerBlue);
        sbWorld.Begin(_cam, SpriteSortMode.Texture);
        foreach (var t in BoundingBoxLocations.OceanTileLocationList)
        {
            TilePiece tile = (TilePiece)t;
            tile.DrawTile(sbWorld);
        }
        sbWorld.End();

        // set up gamescene draw
        _graphics.SetRenderTarget(worldScene);
        _graphics.Clear(Color.PeachPuff);

        // water
        sbWorld.Begin(SpriteSortMode.Immediate, BlendState.AlphaBlend);
        oceanRippleEffect.Parameters["bumpMap"].SetValue(waterBumpMap);
        oceanRippleEffect.Parameters["water"].SetValue(waterScene);
        //oceanRippleEffect.Parameters["xWaveLength"].SetValue(3f);
        oceanRippleEffect.Parameters["xWaveHeight"].SetValue(0.3f);
        ExecuteTechnique("oceanRipple");
        sbWorld.Draw(waterScene, Vector2.Zero, Color.White);
        sbWorld.End();

        // land
        sbWorld.Begin(_cam, SpriteSortMode.Texture);
        foreach (var t in BoundingBoxLocations.LandTileLocationList)
        {
            TilePiece tile = (TilePiece)t;
            tile.DrawTile(sbWorld);
        }
        sbWorld.End();

    }

任何人都可以看到我的代码有任何问题,否则可能导致此偏移问题?

Can anyone see any issues with my code or otherwise that might be causing this offset issue?

我们非常感谢您的帮助.谢谢!

Any help is much appreciated. Thanks!

编辑

如果修改xWaveHeight着色器参数,它将更改偏移的显示位置.值为0不会偏移,但不会应用凹凸贴图.有什么办法解决吗?

If I modify the xWaveHeight shader parameter, it changes where the offset appears. A value of 0 will not offset, but then the bump mapping is not applied. Is there any way around this?

我知道偏移是由像素着色器的摄动引起的,但是我想知道是否有一种方法可以在保留凹凸贴图的同时撤消该偏移.在链接的riemer的教程中,包含了一个顶点着色器.我不确定是否需要此功能,但是当我将顶点着色器包括在该技术中并将像素着色器修改为以下内容时,不会绘制水.

I understand that the offset is being caused by the pixel shader perturbation, but I'm wondering if there is a way to undo this offset while preserving the bump mapping. In the linked riemer's tutorial, a vertex shader is included. I'm not quite sure if I need this, but when I include my vertex shader in the technique, and modify the pixel shader to the following, no water is drawn.

float4 MainPS(in VertexShaderOutput output) : COLOR
{
    float4 bumpColor = tex2D(bumpSampler, output.BumpMapSamplingPos.xy);
    //get offset 
    float2 perturbation = xWaveHeight * (bumpColor.rg - 0.5f)*2.0f;

    //apply offset to coordinates in original texture
    float2 currentCoords = output.BumpMapSamplingPos.xy;
    float2 perturbatedTexCoords = currentCoords + perturbation;

    //return the perturbed values
    float4 color = tex2D(waterSampler, perturbatedTexCoords);
    return color;
}

推荐答案

首先,对于您似乎想做的事情,凹凸贴图实际上是错误的方法:凹凸贴图是关于更改表面法线(基本上是旋转"在3D空间中的像素),因此在进行光计算(例如反射)后,您的表面实际上要复杂得多(请注意,该像素的纹理会保留在原处).因此,凹凸贴图将完全不会修改海洋瓷砖纹理的位置,但会修改海洋反射的(例如,通过更改天窗,因此水中的天空反射会失真).实现的方式更像是如果我的屏幕是海洋,并且会反射出具有海洋纹理的图块,该怎么办?".

First of all, for what you seem to be wanting to do, bump mapping is actually the wrong approach: bump mapping is about changing the surface normal (basicly "rotating" the pixel in 3D space), so following light calculations (such as reflection) see your surface as more complex then it really is (Notice that the texture of that pixel stays where it is). So, bump mapping would not at all modify the position of the ocean tile texture, but modify what is reflected by the ocean (for example, by changing the sample position of a skybox, so the reflection of the sky in the water is distorted). The way you are implementing it is more like "What if my screen would be an ocean and would reflect an image of tiles with ocean textures".

如果您确实要使用凹凸贴图,则需要某种大的天空纹理,然后同时(不要在此之后)绘制海洋瓷砖,然后计算出反射此天空纹理(基于屏幕上图块的位置),然后使用凹凸贴图修改该采样位置.所有同时绘制图块,而不是将它们绘制到渲染目标之后.

If you really want to use bump mapping, you would need some kind of big sky texture, and then, while (not after) drawing the ocean tiles, you would calculate a sample position of the reflection of this sky texture (based on the position of the tile on the screen) and then modify that sample position with bump mapping. All while drawing the tiles, not after drawing them to a render target.

也可以按照不同的方式进行此操作(与您现在正在执行的操作更相似)-实际上,有多种方法可以这样做-但无论哪种方式,您仍然需要从天空纹理中采样最终的颜色,<从您的图块绘制所在的渲染目标中获得的不是> strong 的效果.瓦片中的渲染目标将改为包含元"信息(取决于您要执行此操作的精确程度).这些信息可以是与天空纹理中的颜色相乘的颜色(创建有色"水,例如,用于不同的生物或模拟日落/太阳升起),或者简单的1或0表示是否存在是任何海洋,还是每块凹凸图(您可以一次性应用屏幕全局"和每块"凹凸图).您仍然需要一种方式来表示此像素不是海洋,则不要在渲染目标"中为此做任何事情),或者-如果您使用多个渲染目标,则一次全部完成.无论如何,凹凸贴图不会修改要从渲染目标中采样的采样位置,只有海洋反射的纹理的采样位置会被修改.这样一来,海洋也就不会移位,因为我们根本没有接触到该样本位置.

It is also possible to do this deffered (more similar to what you are doing now) - actually, there are multiple ways of doing so - but either way you would still need to sample the final color from a sky texture, not from the render target your tiles were drawn on. The render target from your tiles would instead contain "meta" informations (depending on how exactly you want to do this). These informations could be a color that is multiplied with the color from the sky texture (creating "colored" water, eg. for different bioms or to simulate sun sets/sun rises), or a simple 1 or 0 to tell wether or not there is any ocean, or a per-tile bump map (which would you allow to apply a "screen global" and a "per tile" bump mapping in one go. You would still need a way to say "this pixel is not an ocean, don't do anything for that" in the render target), or - if you use multiple render targets - all of these at once. In any way, the sample position to sample from your render target(s) is not modified by bump mapping, only the sample position of the texture that is reflected by the ocean is. That way, there's also no displacement of the ocean, since we aren't touching that sample positions at all.

现在,要创建与您想要的外观更相似(根据图像)的外观,您将不使用凹凸贴图,而是向像素着色器中的样本位置施加少量噪点(其余代码无需更改).为此,您的着色器将更像这样:

Now, to create a look that is more similar to what you seem to be wanting (according to your images), you wouldn't use bump mapping, but instead apply a small noise to the sample position in your pixel shader (the rest of the code doesn't need to change). For that, your shader would look more like this:

texture noiseTexture;
sampler2D noiseSampler = sampler_state
{
    Texture = <noiseTexture>;
    MipFilter = LINEAR;
    MinFilter = LINEAR;
    MagFilter = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};
float2 noiseOffset;
float2 noisePower;
float noiseFrequency;
VertexShaderOutput MainVS(in VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

    output.Pos = mul(input.Position, WorldViewProjection);
    output.Color = input.Color;

    return output;
}
float4 MainPS(float4 pos : SV_POSITION, float4 color1 : COLOR0, float2 texCoord : TEXCOORD0) : COLOR
{
    float4 noise = tex2D(noiseSampler, (texCoord.xy + noiseOffset.xy) * noiseFrequency);
    float2 offset = noisePower * (noise.xy - 0.5f) * 2.0f;

    float4 color = tex2D(waterSampler, texCoord.xy + offset.xy);
    return color;
}

noisePower最多(约为)的地方.屏幕上水平/垂直图块的数量为1,noiseOffset可用于随时间在屏幕上移动"噪声(应在[-1; 1]范围内),并且noiseFrequency是一个艺术参数(我会从最大噪声功率的两倍开始,然后从那里进行修改,更高的值会使海洋更失真.这样,图块的边界会变形,但在任何方向上移动的幅度都不能超过一个图块(由于noisePower参数).在这里使用正确的噪声纹理也很重要:白噪声,蓝噪声,可能是由窦波建立的不是真的噪声"纹理等.重要的是每个像素的平均值"值大约为0.5,因此不会发生整体位移,并且值在纹理中分布良好. Appart由此可见,哪种噪音最适合您.

Where noisePower would be (at most) approx. 1 over the number of horizontal/vertical tiles on the screen, noiseOffset can be used to "move" the noise over time on the screen (should be in range [-1;1]), and noiseFrequency is an artistic parameter (I would start with twice the max noise power, and then modify it from there, with higher values making the ocean more distorted). This way, the border of the tiles is distorted, but never moved more then one tile in any direction (thanks to the noisePower parameter). It is also important to use the correct kind of noise texture here: white noise, blue noise, maybe a "not really noise" texture that's build out of sinus waves, etc. Important is the fact that the "average" value of each pixel is about 0.5, so there's no overall displacement happening, and that the values are well distributed in the texture. Appart from that, see what kind of noise looks best for you.

着色器代码的注释:我尚未测试该代码.只是您知道,并不是错误的余地很大.

Side note to the shader code: I haven't tested that code. Just that you know, not that there would be much room for mistakes.

作为边节点:当然,天空纹理实际上不必看起来像天空;)

As a side node: Of course the sky texture doesn't need to actualy look like a sky ;)

这篇关于二维水凹凸贴图-Monogame的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆