Unity3d几个动态着色器Shader的例子

在国外网站上找到了一篇写动态着色器的帖子,担心翻译可能不准确,直接原文复制吧。

I'll build this up in a few layers so you can see how it comes together.

Start by creating a new shader in Unity by choosing Create --> Shader --> Unlit in either the Assets menu or the right-click context menu in the Project window.

In the top block we'll add a _ScrollSpeeds parameter to control how fast the texture moves in each direction:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5, -20, 0, 0)
}

This exposes a new 4-component float property in the material inspector, with the friendly name "Scroll Speeds" (similar to adding a public or Serialized variable to a MonoBehaviour script)

Next we'll use this variable in the Vertex shader to shift the texture coordinates (o.uv), by adding just two lines to the default shader:

sampler2D _MainTex;
float4 _MainTex_ST;
// Declare our new parameter here so it's visible to the CG shader
float4 _ScrollSpeeds;

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);
    o.uv = TRANSFORM_TEX(v.uv, _MainTex);

    // Shift the uvs over time.
    o.uv += _ScrollSpeeds * _Time.x;

    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

Slap that on a quad (with a lovely free giraffe texture by Kenney) and you get:

Unity3d几个动态着色器Shader的例子_第1张图片

 

To get the texture scrolling outwards in a ring, we could just use a mesh subdivided like a spiderweb, with the uv v coordinate increasing from the center out. But that will give some sawblade-shaped artifacts on its own. Instead, I'll show how we can calculate our UVs per fragment.

This is a bit more costly, both because of the trig & length operations (which are more expensive than basic math) and because it's not as efficient to predict & cache texture data in the hardware when calculating texcoords per fragment, compared to just interpolating them between vertices. But for a little special effect like this, it's not excessive.

v2f vert (appdata v)
{
    v2f o;
    o.vertex = UnityObjectToClipPos(v.vertex);

    // Shift the UVs so (0, 0) is in the middle of the quad.
    o.uv = v.uv - 0.5f;

    UNITY_TRANSFER_FOG(o,o.vertex);
    return o;
}

fixed4 frag (v2f i) : SV_Target
{
    // Convert our texture coordinates to polar form:
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x)/(2.0f * 3.141592653589f), // angle
           length(i.uv)                                    // radius
        );

    // Apply texture scale
    polar *= _MainTex_ST.xy;

    // Scroll the texture over time.
    polar += _ScrollSpeeds.xy * _Time.x;

    // Sample using the polar coordinates, instead of the original uvs.
    // Here I multiply by MainTex
    fixed4 col = tex2D(_MainTex, polar);

    // apply fog
    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

That gives us something like this (here I increased the tiling parameters in the material so it's clearer what's happening - wrapping only a single repeat of the tile around the circle looks distorted and weird)

Unity3d几个动态着色器Shader的例子_第2张图片

Lastly, to tint the texture by a scrolling gradient, we can just add the gradient as a second texture and multiply them together.

First we add the new texture parameter at the top:

Properties
{
    _MainTex ("Texture", 2D) = "white" {}
    _TintTex("Tint Texture", 2D) = "white" {}
    _ScrollSpeeds ("Scroll Speeds", vector) = (-5.0, -20.0, 0, 0)
}

And declare it in our CGPROGRAM block so the CG shader can see it:

sampler2D _MainTex;
float4 _MainTex_ST;

// Declare our second texture sampler and its Scale/Translate values
sampler2D _TintTex;
float4 _TintTex_ST;

float4 _ScrollSpeeds;

Then update our fragment shader to use both textures:

fixed4 frag(v2f i) : SV_Target
{
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
           length(i.uv)                                    // radius
        );

    // Copy the polar coordinates before we scale & shift them,
    // so we can scale & shift the tint texture independently.
    float2 tintUVs = polar * _TintTex_ST.xy;
    tintUVs += _ScrollSpeeds.zw * _Time.x;

    polar *= _MainTex_ST.xy;
    polar += _ScrollSpeeds.xy * _Time.x;

    fixed4 col = tex2D(_MainTex, polar);
    // Tint the colour by our second texture.
    col *= tex2D(_TintTex, tintUVs);

    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

And now our giraffe gets really trippy:

Unity3d几个动态着色器Shader的例子_第3张图片

With a slightly more artistic selection of textures and scrolling rates, this can create an effect quite similar to the one shown in the question.


You may notice two small artifacts with the version I've shown above:

  • The faces near the center of the circle get a stretched/skinny/pointy, then as they move toward the outside they get squashed/wide.

    This distortion happens because we have a fixed number of faces around the perimeter, but the circumference they're spanning gets wider as the radius increases, while their height stays the same.

    We can fix this by remapping the vertical component of the texture sample to follow a logarithmic curve, so repeats of the texture are further apart as the radius increases, and closer together toward the center. (In fact,this gives us an infinite regress of smaller and smaller giraffes...)

  • There's a row of one or two blurry pixels along the middle-left of the quad.

    This happens because the GPU looks at two adjacent texture sample coordinates to figure out what filtering to use. When the samples are close together, it figures the texture is being displayed large/close and shows the most detailed mip level. When the samples are far apart, it guesses we must be showing the texture at a tiny zoom or far away, and it samples from a smaller/blurrier mipmap to ensure we don't get sparkly aliasing artifacts.

    The trouble is here, we're at the wrap-around point in polar coordinates, from -180 to 180 degrees. So we're actually sampling from very similar points in our repeating texture space, even if their numeric coordinates make them look like they're far apart. So we can provide our own sampling gradient vectors to correct for this.

Here's a version with these corrections:

fixed4 frag(v2f i) : SV_Target
{
    float2 polar = float2(
           atan2(i.uv.y, i.uv.x) / (2.0f * 3.141592653589f), // angle
           log(dot(i.uv, i.uv)) * 0.5f                       // log-radius
        );

    // Check how much our texture sampling point changes between
    // neighbouring pixels to the sides (ddx) and above/below (ddy)
    float4 gradient = float4(ddx(polar), ddy(polar));

    // If our angle wraps around between adjacent samples,
    // discard one full rotation from its value and keep the fraction.
    gradient.xz = frac(gradient.xz + 1.5f) - 0.5f;

    // Copy the polar coordinates before we scale & shift them,
    // so we can scale & shift the tint texture independently.
    float2 tintUVs = polar * _TintTex_ST.xy;
    tintUVs += _ScrollSpeeds.zw * _Time.x;

    polar *= _MainTex_ST.xy;
    polar += _ScrollSpeeds.xy * _Time.x;

    // Sample with our custom gradients.
    fixed4 col = tex2Dgrad(_MainTex, polar, 
                           _MainTex_ST.xy * gradient.xy,
                           _MainTex_ST.xy * gradient.zw
                         );

    // Since our tint texture has its own scale,
    // its gradients also need to be scaled to match.
    col *= tex2Dgrad(_TintTex, tintUVs,
                          _TintTex_ST.xy * gradient.xy,
                          _TintTex_ST.xy * gradient.zw
                         );

    UNITY_APPLY_FOG(i.fogCoord, col);
    return col;
}

 

 

你可能感兴趣的:(Unity,unity3d,shader,着色器,动态,河流)