четверг, 19 декабря 2019 г.

Portal shader

In this post you will find a portal shader for Unity 3D and a set of textures for it.



Create a file "Portal.shader", copy&paste the code of the shader into the file. Place the file into the assets catalog of your project.

In order to use the shader in your project you need to create material and to choose "My/PortalShader" as your shader.

The code of the shader. 

Shader "My/PortalShader"
{
    Properties
    {
        //0 - Main world (grab texture)
        //1 - Mix worlds
        //2 - Another world (_AnotherWorldTexture)
        _PortalFactor("Portal Factor", Range(-1.0, 1.0)) = 0
        [Toggle]_PortalOpening("Portal Opening", Float) = 1
        
        _LightingBorderSize("Lighting Border Size", Range(0, 0.1)) = 0.03
        _WorldColorFactor("World Color Factor", Range(0, 1)) = 0.7
        
        _AnotherWorldTexture("Another World Texture", 2D) = "white" {}
        _AnotherWorldPortalColor("Another World Portal Color", 2D) = "white" {}
        
        _SurfaceDistortion("Surface Distortion", 2D) = "white" {}
        _DistortionScale("Distortion Scale", Range(0.1, 3)) = 1
        _DistortionSize("Distortion Size", Range(0, 5)) = 3
        _DistortionSpeed("Distortion Speed", Range(0.01, 0.3)) = 0.05
        
        _PortalColors("Portal Colors", 2D) = "white" {}
        _PortalColorsWidthScale("Portal Colors Wid Scale", Float) = 0.0625
        
        _SurfaceNoise("Surface Noise", 2D) = "white" {}
        _NoiseSpeed("Noise Speed", Range(0.001, 0.03)) = 0.005
        _NoiseScale("Noise Scale", Range(0, 0.5)) = 0.1
        
        _PortalFrame("Portal Frame", 2D) = "black" {}
        _PortalFrameDistortionFactor("Frame Distortion factor", Range(1,20)) = 5
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" }
        GrabPass { "_BackgroundTexture" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            //Parameters
            float _PortalFactor;
            float _PortalOpening;
            float _LightingBorderSize;
            float _WorldColorFactor;
            float _PortalColorsWidthScale;
            float _DistortionScale;
            float _DistortionSize;
            float _DistortionSpeed;
            float _NoiseSpeed;
            float _NoiseScale;
            float _PortalFrameDistortionFactor;

            //Textures
            sampler2D _AnotherWorldTexture;
            sampler2D _AnotherWorldPortalColor;
            sampler2D _SurfaceDistortion;
            sampler2D _PortalColors;
            sampler2D _SurfaceNoise;
            sampler2D _PortalFrame;
            sampler2D _BackgroundTexture;
            
            //For TRANSFORM_TEX
            float4 _PortalFrame_ST;

            struct v2f
            {
                float4 grabPos : TEXCOORD0;
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD1;
                float2 noiseuv : TEXCOORD2;
                float2 distortuv : TEXCOORD3;
                float2 frameUV: TEXCOORD4;
            };
            
            struct appdata
            {
                float4 vertex : POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert(appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.grabPos = ComputeGrabScreenPos(o.pos);
                o.uv = v.uv;
                o.distortuv = v.uv * _DistortionScale;
                o.frameUV = TRANSFORM_TEX(v.uv, _PortalFrame);
                
                o.noiseuv = v.uv * _NoiseScale;
                return o;
            }

            half4 frag(v2f i) : SV_Target
            {
                float2 offset1 = float2(_Time.y * _DistortionSpeed, _Time.y * _DistortionSpeed);
                float2 offset2 = -float2(
                    _Time.y * _DistortionSpeed * 1.1, 
                    _Time.y * _DistortionSpeed * 0.9) + float2(0.5, 0.5);
                half4 distortionValue1 = (tex2D(_SurfaceDistortion, i.distortuv + offset1) * 2 - 1);
                half4 distortionValue2 = (tex2D(_SurfaceDistortion, i.distortuv + offset2) * 2 - 1);

                half4 distortionValue = (distortionValue1 + distortionValue2) * 0.5;
                half4 distortionNoise = distortionValue * _DistortionSize;

                //Calculate color texture UV
                float2 colorUV1 = (i.uv * float2(_PortalColorsWidthScale, 1)) + float2(_Time.y * 0.015, 1);
                float2 colorUV2 = (i.uv * float2(_PortalColorsWidthScale, 1)) + float2(_Time.y * 0.015, 1);
                
                //Portal color
                float2 colorNoise = float2(distortionValue.x, distortionValue.y) * 0.03;
                half4 color1 = tex2D(_PortalColors, colorUV1 + colorNoise);
                half4 color2 = tex2D(_PortalColors, colorUV2 + colorNoise);
                half4 color = (color1 + color2) * 0.5;
                
                //Another world portal color
                half4 anotherColor1 = tex2D(_AnotherWorldPortalColor, colorUV1 + colorNoise);
                half4 anotherColor2 = tex2D(_AnotherWorldPortalColor, colorUV2 + colorNoise);
                half4 anotherColor = (anotherColor1 + anotherColor2) * 0.5;
        
                //Sample noise texture
                float2 noiseOffset1 = float2(_Time.y * _NoiseSpeed, _Time.y * _NoiseSpeed);
                float2 noiseOffset2 = -float2(
                   _Time.y * _NoiseSpeed * 1.1, _Time.y * _NoiseSpeed * 0.9) + float2(0.5, 0.5);
                half4 noiseValue1 = tex2D(_SurfaceNoise, i.noiseuv + noiseOffset1);
                half4 noiseValue2 = tex2D(_SurfaceNoise, i.noiseuv + noiseOffset2);
                half4 noiseValuePow = pow(noiseValue1 * noiseValue2, 2);
                half4 noiseValueSum = noiseValue1 + noiseValue2 - 1;

                //Another world texture
                half4 anotherWorldColor = tex2Dproj(
                    _AnotherWorldTexture, 
                    i.grabPos + half4(distortionNoise.x, distortionNoise.y, 0, 0));

                //Portal world border size calculation.
                half worldBorder = 0.0;
                half worldBorderSize = _LightingBorderSize;
                half worldBorderMin = worldBorder - worldBorderSize * 0.5;
                half worldBorderMax = worldBorder + worldBorderSize * 0.5;
                
                //Portal frame
                half4 portalFrame = tex2D(
                    _PortalFrame, i.frameUV + colorNoise * _PortalFrameDistortionFactor);
                half portalBorderFactor = portalFrame.x * (1 - abs(_PortalFactor));
                
                half worldFactor = noiseValueSum.x + _PortalFactor;
                if(_PortalOpening > 0.5)
                    worldFactor += portalBorderFactor;
                else
                    worldFactor -= portalBorderFactor;
                
                //Add portal border lighting
                half4 noiseLighting;
                if(worldFactor > worldBorderMin && worldFactor < worldBorderMax)
                    noiseLighting = half4(1,1,1,0);
                else
                    noiseLighting = half4(0,0,0,1);
                
                //Mix world colors
                half4 portalWorldColor;
                half4 portalColor;
                if(worldFactor < worldBorderMax)
                {
                    portalWorldColor = tex2Dproj(
                        _BackgroundTexture, 
                        i.grabPos + half4(distortionNoise.x, distortionNoise.y, 0, 0));
                    portalColor = color;
                }
                else
                {
                    portalWorldColor = tex2Dproj(
                        _AnotherWorldTexture, 
                        i.grabPos + half4(distortionNoise.x, distortionNoise.y, 0, 0));
                    portalColor = anotherColor;
                }
                
                //Calculate result color
                half bgColorFactor = clamp(_WorldColorFactor, 0, 1);
                
                return 
                    portalWorldColor * bgColorFactor + 
                    portalColor * (1 - bgColorFactor) + 
                    noiseLighting;
            }
            ENDCG
        }

    }
}

Portal material parameters

The camera that renders the world behind the portal to texture is bound to the main camera.



The camera that renders the main world ignores the layer with the world behind the portal. 





The world behind the portal is in a separate layer and in a separate scene. The source of light is situated in the same layer and it lights up the world behind the portal only. It is possible to change the light in both scenes independently.

Noise texture

Textures with additional colors for the portal. The first one is for the main world. The second one is for the world behind the portal. They can be used for the portal to change its color from time to time.





The texture of the portal frame.


So that the border between the worlds shines, you can use a post process effect "Bloom".







The code and textures in this blog post are under the CC0 license. You can use them for free. No attribution/credits needed.


If this information was useful for you, you can support me by having a look at my game on Steam.




If you don't want to miss future shaders you may follow me on twitter.






понедельник, 16 декабря 2019 г.

Fire shader

In this post you will find a fire shader for Unity 3D and a set of textures for it.


In order to use the shader in your project you need to create material and choose "My/FireShader" as your shader.

Create a file "Fire.shader", copy&paste the code of the shader into the file. Place the file into the assets catalog of your project.

The code of the shader. 

Shader "My/FireShader"
{
    Properties
    {
        _FireTexture("Fire Texture", 2D) = "white" {}
        _FireAlpha("Fire Alpha", 2D) = "white" {}
        _SurfaceDistortion("Surface Noise", 2D) = "white" {}
        _SurfaceNoise("Surface Noise 2", 2D) = "white" {}
        
        _DistortionSpeed("Noise Speed", Float) = 1
        _DistortCoordFactor("Noise Coord Factor", Float) = 1
        _DistortUVFactor("Noise UV Factor", Float) = 0.2
        
        _NoiseSpeed("Noise 2", Float) = 0.05
        _NosieUVFactor("Noise 2 UV Factor", Float) = 0.2
        
        _HighlightAlphaCutoff("Highlight Alpha Cutoff", Range(0,1)) = 0.9
        _HighlightFactor("Highlight Factor", Range(0, 2)) = 0.45
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" }
        GrabPass { "_BackgroundTexture" }

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            //Parameters
            float _DistortionScale;
            float _DistortUVFactor;
            float _DistortionSpeed;
            float _DistortCoordFactor;
            float _NoiseSpeed;
            float _NosieUVFactor;
            float _HighlightAlphaCutoff;
            float _HighlightFactor;

            //Textures
            sampler2D _FireTexture;
            sampler2D _FireAlpha;
            sampler2D _SurfaceDistortion;
            sampler2D _SurfaceNoise;
            sampler2D _BackgroundTexture;
            
            //For TRANSFORM_TEX
            float4 _SurfaceDistortion_ST;
            float4 _SurfaceNoise_ST;

            struct v2f
            {
                float4 grabPos : TEXCOORD0;
                float4 pos : SV_POSITION;
                float2 uv : TEXCOORD1;
                float2 noiseuv : TEXCOORD2;
                float2 distortuv : TEXCOORD3;
            };
            
            struct appdata
            {
                float4 vertex : POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert(appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.grabPos = ComputeGrabScreenPos(o.pos);
                o.uv = v.uv;
                o.distortuv = TRANSFORM_TEX(v.uv, _SurfaceDistortion);
                o.noiseuv = TRANSFORM_TEX(v.uv, _SurfaceNoise);
                return o;
            }
            
            half4 SampleNoiseTexture(sampler2D _tex, float2 uv1, float2 uv2)
            {
                return ( (tex2D(_tex, uv1) * 2 - 1) + (tex2D(_tex, uv2) * 2 - 1) ) * 0.5;
            }

            half4 frag(v2f i) : SV_Target
            {
                //Calculate tex coords
                half4 distortionValue = SampleNoiseTexture(
                    _SurfaceDistortion, 
                    i.distortuv + float2(0, - _Time.y * _DistortionSpeed), 
                    i.distortuv -float2(0.5, 0.5 - _Time.y * _DistortionSpeed * 0.1));
                
                //Noise
                half4 noiseValue = SampleNoiseTexture(
                    _SurfaceNoise, 
                    i.noiseuv + float2(0, - _Time.y * _NoiseSpeed), 
                    i.noiseuv + -float2(0.5, 0.5 - _Time.y * _NoiseSpeed * 0.1));
                
                //Distortion
                half2 distortUV = half2(
                    distortionValue.x * _DistortUVFactor + noiseValue.x * _NosieUVFactor,
                    distortionValue.x * _DistortUVFactor * 0.2 + noiseValue.x * _NosieUVFactor * 0.2);
                    
                //Textures
                half4 fireColor = tex2D(_FireTexture, 
                    clamp(i.uv + distortUV, half2(0.01,0.01), half2(0.99,0.99)));
                half4 fireAlpha = tex2D(_FireAlpha, 
                    clamp(i.uv + distortUV, half2(0.01,0.01), half2(0.99,0.99)));
                half4 backColor = tex2Dproj(_BackgroundTexture, 
                    i.grabPos + (distortionValue * _DistortCoordFactor * fireAlpha.x));
                
                //Add light
                half4 colorAdd = half4(0,0,0,0);
                if(fireAlpha.x > _HighlightAlphaCutoff) {
                    colorAdd.xyz = (noiseValue + 0.5).xyz * 
                        (fireAlpha.x - _HighlightAlphaCutoff) * (1 / (1-_HighlightAlphaCutoff));
                    colorAdd *= _HighlightFactor;
                }
                
                //Mix result color
                half4 mixedColor = 
                    fireColor * fireAlpha + 
                    backColor * (1 - fireAlpha) + 
                    colorAdd;
                
                return mixedColor;
            }
            ENDCG
        }

    }
}


Texture of fire and smoke. Semitransparency texture. Two noise textures for a burning effect.


In the next picture you can see the material parameters that I have used.




In order to make the final picture look better I have added an effect called Bloom, as well as particles to imitate sparks.


   



The code and textures in this blog post are under the CC0 license. You can use them for free. No attribution/credits needed.


If this information was useful for you, you can support me by having a look at my game on Steam.




If you don't want to miss future shaders you may follow me on twitter.




My Train Arrives Trailer





My Train Arrives is a railway construction simulation game. Build railroads, open new routes for trains. Enjoy the pleasant atmosphere of the game.


среда, 21 августа 2019 г.

My Train Arrives on Steam



My Train Arrives is a railway construction simulation game. Build railroads, open new routes for trains. Enjoy the pleasant atmosphere of the game.




Transfer passengers and goods in four different game modes. Complete “Campaign” levels and open new cities. Test your skills in “Survival” and “Evacuation” modes, or enjoy the process of building a railway in “Relaxation” mode. The possibility to choose either a slow paced mode of the game or more dynamic ones, good graphics and nice music.



Features

  •  Combination of dynamic and relaxing game modes
  • 12 locomotives and 25 wagons with different characteristics
  • 7 cities
  • 4 game modes: campaign, survival, evacuation and relaxation 
  • Nice graphics and relaxing music


Game modes
 
Campaign. You need to transport a certain amount of passengers and cargo within the allotted time. During the game new stations will be opened and the number of passengers and cargo on stations will grow.

Survival. Your task is to hold out as long as you can and see to it that stations are not overcrowded or overloaded. Build a good railway system and spend your money wisely.

Evacuation. You need to evacuate all citizens to a particular station during a short period of time. It is not necessary to evacuate cargo, but it brings double income. Think how to manage the allocated time.

Relaxation. In relaxation mode you don’t need to think about income or the accuracy of your routes. Develop your railway system as you wish and enjoy the game.