2D yukarıdan aşağıya döşeli, yönlendirilmiş su akışını nasıl sağlayabilirim?


9

Ben Cüce Kalesi esinlenerek yukarıdan aşağı kiremit tabanlı oldukça grafik 2D oyun üzerinde çalışıyorum. Oyun dünyasında bir dizi fayans içeren bir nehir uygulama noktasındayım ve her bir karo için akış yönünü, her karodaki kırmızı çizgi ile aşağıda gösterildiği gibi hesapladım.

Yönlü nehir çinileri örneği

Grafik stile referans olarak, oyunum şu anda şu şekilde görünüyor:

Grafik stili oyun içi çekim

İhtiyacım olan şey, nehir karolarının her birinde akan suyu canlandırmak için bazı tekniklerdir, böylece akış çevre kiremitlere karışır, böylece karo kenarları görünür olmaz.

Sonra ne olduğumu bulduğum en yakın örnek http://www.rug.nl/society-business/centre-for-information-technology/research/hpcv/publications/watershader/ adresinde açıklanıyor ama tam olarak değilim içinde neler olup bittiğini anlayabiliyor musunuz? Kendi dinamik aydınlatmamı uygulamak için gölgelendirici programlaması hakkında yeterince bilgim var, ancak bağlantılı makalede ele alınan yaklaşımın etrafını dolamıyorum.

Birisi yukarıdaki etkinin nasıl elde edildiğini açıklayabilir veya istediğim sonucu elde etmek için başka bir yaklaşım önerebilir mi? Yukarıdaki çözümün bir parçası fayanslarla çakışıyor (ancak hangi kombinasyonlarda emin olmadığım) ve bozulma için kullanılan normal haritayı (yine spesifikasyonlar hakkında hiçbir fikir yok) ve biraz kaybolduğum geçmişi döndürdüğümü düşünüyorum. herhangi bir yardım!


Suyun kendisi için görsel bir hedefiniz var mı? Alıntı yaptığınız bağlantının aynasal yansıma için normal haritaları kullandığını fark ediyorum - gösterdiğiniz düz / karikatür tarzı sanat yönünde tam olarak uyuşmayabilecek bir şey. Tekniği diğer stillere uyarlamanın yolları vardır, ancak bazı yönergelere ihtiyacımız var, bu yüzden neyi hedefleyeceğimizi biliyoruz.
DMGregory

Akış çözümünüzü, akışta serbest bıraktığınız parçacıklar için bir gradyan olarak kullanabilirsiniz. Muhtemelen pahalı, çünkü birçoğuna ihtiyacınız olacak.
Bram

Bunu bir gölgelendirici ile çözmezdim, yüzyıllar boyunca kullanılan basit bir şekilde yapardım, sadece çizin ve suyun 8 farklı çizimine ve ayrıca kıyıya çarpan suyun 8 farklı çizimine sahip olacaktım. Daha sonra farklı bir araziye sahip olmak ve serpme taşları, balık veya nehre herhangi bir şey gibi rastgele eklemek istiyorsanız bir renk kaplaması ekleyin. 8 farklı Btw farklı bir sprite sahip rotasyon her 45 derece için demek
istedim

@YoshSynergi Nehir akışının 8 yön yerine herhangi bir yönde olmasını istiyorum ve bağlantılı gölgelendiricide elde edilen sonuca benzer şekilde, karo kenarları arasında görünür sınırlara sahip olmaktan kaçınmak istiyorum
Ross Taylor-Turner

@Bram, başarabileceğimi düşündüğüm bir seçenek, ancak özellikle kamera çok yakınlaştırıldığında etkili olmak için çok fazla parçacığa ihtiyaç duyacağını düşünüyorum
Ross Taylor-Turner

Yanıtlar:


11

Bozulma ile iyi görünen hiçbir fayansım yoktu, bu yüzden bunun yerine bu Kenney fayanslarıyla alay ettiğim etkinin bir versiyonu :

Tilemap akan su gösteren animasyon.

Ben böyle bir akış haritası kullanıyorum, burada kırmızı = sağa akış ve yeşil = yukarı, sarı her ikisi de. Her piksel bir döşemeye karşılık gelir, sol alt piksel dünya koordinat sistemimdeki (0, 0) döşemedir.

8x8

Ve böyle bir dalga deseni dokusu:

resim açıklamasını buraya girin

En çok Unity'nin hlsl / CG tarzı sözdizimine aşinayım, bu nedenle bu gölgelendiriciyi glsl bağlamınız için biraz uyarlamanız gerekir, ancak bunu yapmak basit olmalıdır.

// Colour texture / atlas for my tileset.
sampler2D _Tile;
// Flowmap texture.
sampler2D _Flow;
// Wave surface texture.
sampler2D _Wave;

// Tiling of the wave pattern texture.
float _WaveDensity = 0.5f;
// Scrolling speed for the wave flow.
float _WaveSpeed  = 5.0f;

// Scaling from my world size of 8x8 tiles 
// to the 0...1
float2 inverseFlowmapSize = (float2)(1.0f/8.0f);

struct v2f
{
    // Projected position of tile vertex.
    float4 vertex   : SV_POSITION;
    // Tint colour (not used in this effect, but handy to have.
    fixed4 color    : COLOR;
    // UV coordinates of the tile in the tile atlas.
    float2 texcoord : TEXCOORD0;
    // Worldspace coordinates, used to look up into the flow map.
    float2 flowPos  : TEXCOORD1;
};

v2f vert(appdata_t IN)
{
    v2f OUT;

    // Save xy world position into flow UV channel.
    OUT.flowPos = mul(ObjectToWorldMatrix, IN.vertex).xy;

    // Conventional projection & pass-throughs...
    OUT.vertex = mul(MVPMatrix, IN.vertex);
    OUT.texcoord = IN.texcoord;
    OUT.color = IN.color;

    return OUT;
}

// I use this function to sample the wave contribution
// from each of the 4 closest flow map pixels.
// uv = my uv in world space
// sample site = world space        
float2 WaveAmount(float2 uv, float2 sampleSite) {
    // Sample from the flow map texture without any mipmapping/filtering.
    // Convert to a vector in the -1...1 range.
    float2 flowVector = tex2Dgrad(_Flow, sampleSite * inverseFlowmapSize, 0, 0).xy 
                        * 2.0f - 1.0f;
    // Optionally, you can skip this step, and actually encode
    // a flow speed into the flow map texture too.
    // I just enforce a 1.0 length for consistency without getting fussy.
    flowVector = normalize(flowVector);

    // I displace the UVs a little for each sample, so that adjacent
    // tiles flowing the same direction don't repeat exactly.
    float2 waveUV = uv * _WaveDensity + sin((3.3f * sampleSite.xy + sampleSite.yx) * 1.0f);

    // Subtract the flow direction scaled by time
    // to make the wave pattern scroll this way.
    waveUV -= flowVector * _Time * _WaveSpeed;

    // I use tex2DGrad here to avoid mipping down
    // undesireably near tile boundaries.
    float wave = tex2Dgrad(_Wave, waveUV, 
                           ddx(uv) * _WaveDensity, ddy(uv) * _WaveDensity);

    // Calculate the squared distance of this flowmap pixel center
    // from our drawn position, and use it to fade the flow
    // influence smoothly toward 0 as we get further away.
    float2 offset = uv - sampleSite;
    float fade = 1.0 - saturate(dot(offset, offset));

    return float2(wave * fade, fade);
}

fixed4 Frag(v2f IN) : SV_Target
{
    // Sample the tilemap texture.
    fixed4 c = tex2D(_MainTex, IN.texcoord);

    // In my case, I just select the water areas based on
    // how blue they are. A more robust method would be
    // to encode this into an alpha mask or similar.
    float waveBlend = saturate(3.0f * (c.b - 0.4f));

    // Skip the water effect if we're not in water.
    if(waveBlend == 0.0f)
        return c * IN.color;

    float2 flowUV = IN.flowPos;
    // Clamp to the bottom-left flowmap pixel
    // that influences this location.
    float2 bottomLeft = floor(flowUV);

    // Sum up the wave contributions from the four
    // closest flow map pixels.     
    float2 wave = WaveAmount(flowUV, bottomLeft);
    wave += WaveAmount(flowUV, bottomLeft + float2(1, 0));
    wave += WaveAmount(flowUV, bottomLeft + float2(1, 1));
    wave += WaveAmount(flowUV, bottomLeft + float2(0, 1));

    // We store total influence in the y channel, 
    // so we can divide it out for a weighted average.
    wave.x /= wave.y;

    // Here I tint the "low" parts a darker blue.
    c = lerp(c, c*c + float4(0, 0, 0.05, 0), waveBlend * 0.5f * saturate(1.2f - 4.0f * wave.x));

    // Then brighten the peaks.
    c += waveBlend * saturate((wave.x - 0.4f) * 20.0f) * 0.1f;

    // And finally return the tinted colour.
    return c * IN.color;
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.