Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Need some help writing a simple scanlines shader:

Discussion in 'Shaders' started by esco1979, Dec 23, 2015.

  1. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Hey everyone. I currently have a scanlines shader setup that I am using in my game. However, it doesn't look quite right. It looks like the middle one in the pic below, and what I really want is for it to look like the image on the right:



    Now on the same page of that image the author included some code, however as I am very green at shaders I could not figure out how to adapt it to work in Unity. Can someone please assist with this?

    Code (csharp):
    1.  
    2. //
    3. // PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
    4. //
    5. //   by Timothy Lottes
    6. //
    7. // This is more along the style of a really good CGA arcade monitor.
    8. // With RGB inputs instead of NTSC.
    9. // The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
    10. //
    11. // Left it unoptimized to show the theory behind the algorithm.
    12. //
    13. // It is an example what I personally would want as a display option for pixel art games.
    14. // Please take and use, change, or whatever.
    15. //
    16.  
    17. // Emulated input resolution.
    18. #if 0
    19.   // Fix resolution to set amount.
    20.   vec2 res=vec2(320.0/1.0,160.0/1.0);
    21. #else
    22.   // Optimize for resize.
    23.   vec2 res=iResolution.xy/6.0;
    24. #endif
    25.  
    26. // Hardness of scanline.
    27. //  -8.0 = soft
    28. // -16.0 = medium
    29. float hardScan=-8.0;
    30.  
    31. // Hardness of pixels in scanline.
    32. // -2.0 = soft
    33. // -4.0 = hard
    34. float hardPix=-3.0;
    35.  
    36. // Display warp.
    37. // 0.0 = none
    38. // 1.0/8.0 = extreme
    39. vec2 warp=vec2(1.0/32.0,1.0/24.0);
    40.  
    41. // Amount of shadow mask.
    42. float maskDark=0.5;
    43. float maskLight=1.5;
    44.  
    45. //------------------------------------------------------------------------
    46.  
    47. // sRGB to Linear.
    48. // Assuing using sRGB typed textures this should not be needed.
    49. float ToLinear1(float c){return(c<=0.04045)?c/12.92:pow((c+0.055)/1.055,2.4);}
    50. vec3 ToLinear(vec3 c){return vec3(ToLinear1(c.r),ToLinear1(c.g),ToLinear1(c.b));}
    51.  
    52. // Linear to sRGB.
    53. // Assuing using sRGB typed textures this should not be needed.
    54. float ToSrgb1(float c){return(c<0.0031308?c*12.92:1.055*pow(c,0.41666)-0.055);}
    55. vec3 ToSrgb(vec3 c){return vec3(ToSrgb1(c.r),ToSrgb1(c.g),ToSrgb1(c.b));}
    56.  
    57. // Nearest emulated sample given floating point position and texel offset.
    58. // Also zero's off screen.
    59. vec3 Fetch(vec2 pos,vec2 off){
    60.   pos=floor(pos*res+off)/res;
    61.   if(max(abs(pos.x-0.5),abs(pos.y-0.5))>0.5)return vec3(0.0,0.0,0.0);
    62.   return ToLinear(texture2D(iChannel0,pos.xy,-16.0).rgb);}
    63.  
    64. // Distance in emulated pixels to nearest texel.
    65. vec2 Dist(vec2 pos){pos=pos*res;return -((pos-floor(pos))-vec2(0.5));}
    66.    
    67. // 1D Gaussian.
    68. float Gaus(float pos,float scale){return exp2(scale*pos*pos);}
    69.  
    70. // 3-tap Gaussian filter along horz line.
    71. vec3 Horz3(vec2 pos,float off){
    72.   vec3 b=Fetch(pos,vec2(-1.0,off));
    73.   vec3 c=Fetch(pos,vec2( 0.0,off));
    74.   vec3 d=Fetch(pos,vec2( 1.0,off));
    75.   float dst=Dist(pos).x;
    76.   // Convert distance to weight.
    77.   float scale=hardPix;
    78.   float wb=Gaus(dst-1.0,scale);
    79.   float wc=Gaus(dst+0.0,scale);
    80.   float wd=Gaus(dst+1.0,scale);
    81.   // Return filtered sample.
    82.   return (b*wb+c*wc+d*wd)/(wb+wc+wd);}
    83.  
    84. // 5-tap Gaussian filter along horz line.
    85. vec3 Horz5(vec2 pos,float off){
    86.   vec3 a=Fetch(pos,vec2(-2.0,off));
    87.   vec3 b=Fetch(pos,vec2(-1.0,off));
    88.   vec3 c=Fetch(pos,vec2( 0.0,off));
    89.   vec3 d=Fetch(pos,vec2( 1.0,off));
    90.   vec3 e=Fetch(pos,vec2( 2.0,off));
    91.   float dst=Dist(pos).x;
    92.   // Convert distance to weight.
    93.   float scale=hardPix;
    94.   float wa=Gaus(dst-2.0,scale);
    95.   float wb=Gaus(dst-1.0,scale);
    96.   float wc=Gaus(dst+0.0,scale);
    97.   float wd=Gaus(dst+1.0,scale);
    98.   float we=Gaus(dst+2.0,scale);
    99.   // Return filtered sample.
    100.   return (a*wa+b*wb+c*wc+d*wd+e*we)/(wa+wb+wc+wd+we);}
    101.  
    102. // Return scanline weight.
    103. float Scan(vec2 pos,float off){
    104.   float dst=Dist(pos).y;
    105.   return Gaus(dst+off,hardScan);}
    106.  
    107. // Allow nearest three lines to effect pixel.
    108. vec3 Tri(vec2 pos){
    109.   vec3 a=Horz3(pos,-1.0);
    110.   vec3 b=Horz5(pos, 0.0);
    111.   vec3 c=Horz3(pos, 1.0);
    112.   float wa=Scan(pos,-1.0);
    113.   float wb=Scan(pos, 0.0);
    114.   float wc=Scan(pos, 1.0);
    115.   return a*wa+b*wb+c*wc;}
    116.  
    117. // Distortion of scanlines, and end of screen alpha.
    118. vec2 Warp(vec2 pos){
    119.   pos=pos*2.0-1.0;    
    120.   pos*=vec2(1.0+(pos.y*pos.y)*warp.x,1.0+(pos.x*pos.x)*warp.y);
    121.   return pos*0.5+0.5;}
    122.  
    123. // Shadow mask.
    124. vec3 Mask(vec2 pos){
    125.   pos.x+=pos.y*3.0;
    126.   vec3 mask=vec3(maskDark,maskDark,maskDark);
    127.   pos.x=fract(pos.x/6.0);
    128.   if(pos.x<0.333)mask.r=maskLight;
    129.   else if(pos.x<0.666)mask.g=maskLight;
    130.   else mask.b=maskLight;
    131.   return mask;}    
    132.  
    133. // Draw dividing bars.
    134. float Bar(float pos,float bar){pos-=bar;return pos*pos<4.0?0.0:1.0;}
    135.  
    136. // Entry.
    137. void mainImage( out vec4 fragColor, in vec2 fragCoord ){
    138.   // Unmodified.
    139.   if(fragCoord.x<iResolution.x*0.333){
    140.     fragColor.rgb=Fetch(fragCoord.xy/iResolution.xy+vec2(0.333,0.0),vec2(0.0,0.0));}
    141.   else{
    142.     vec2 pos=Warp(fragCoord.xy/iResolution.xy+vec2(-0.333,0.0));
    143.     if(fragCoord.x<iResolution.x*0.666){
    144.       hardScan=-12.0;
    145.       maskDark=maskLight=1.0;
    146.       pos=Warp(fragCoord.xy/iResolution.xy);}
    147.     fragColor.rgb=Tri(pos)*Mask(fragCoord.xy);}    
    148.   fragColor.a=1.0;  
    149.   fragColor.rgb*=
    150.     Bar(fragCoord.x,iResolution.x*0.333)*
    151.     Bar(fragCoord.x,iResolution.x*0.666);
    152.   fragColor.rgb=ToSrgb(fragColor.rgb);}
    153.  
     
  2. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    Also here is the current scanlines shader that I am using to achieve the effect of the image in the middle above:

    Code (csharp):
    1.  
    2. Shader "SOTN Custom/Scanlines" {
    3. Properties {
    4.     _Color("Color", Color) = (0,0,0,1)
    5.     _LinesSize("LinesSize", Range(1,10)) = 1
    6. }
    7. SubShader {
    8.     Tags {"IgnoreProjector" = "True" "Queue" = "Overlay"}
    9.     Fog { Mode Off }
    10.     Pass {
    11.         ZTest Always
    12.         ZWrite Off
    13.         Blend SrcAlpha OneMinusSrcAlpha
    14.  
    15. CGPROGRAM
    16.  
    17. #pragma vertex vert
    18. #pragma fragment frag
    19. #include "UnityCG.cginc"
    20.  
    21.         fixed4 _Color; half _LinesSize;
    22.  
    23.         struct v2f {
    24.             half4 pos:POSITION;
    25.             fixed4 sPos:TEXCOORD;
    26.         };
    27.  
    28.         v2f vert(appdata_base v) {
    29.             v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
    30.             o.sPos = ComputeScreenPos(o.pos);
    31.             return o;
    32.         }
    33.  
    34.         fixed4 frag(v2f i) : COLOR {
    35.             fixed p = i.sPos.y / i.sPos.w;
    36.  
    37.             if((int)(p*_ScreenParams.y/floor(_LinesSize))%2==0) discard;
    38.                return _Color;
    39.          }
    40.  
    41. ENDCG
    42.        }
    43.  
    44. }
    45. }
    46.  
     
  3. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    I'm not paying someone $50 when I just want this one simple effect. That would be a huge rip-off. Nice shameless plug BTW. o_O That would be issue #1 with the stuff in the store that I found. The other issue being with store purchases is that I really don't know for sure if the effect will look just like I want until I have bought it, and if it doesn't then I would be out of luck.

    If I had someone who could do it I would just pay them $10 to code this because it's something that could be done easily by someone with shader knowledge in about 30 minutes or less. While I can code C sadly I do not know CG Shader or I would just do it myself. (though I do have an interest in learning it).
     
    Ilingis likes this.
  4. Flailer

    Flailer

    Joined:
    Apr 1, 2014
    Posts:
    66
    The only thing you're missing from your middle image to the right one is the 1D gaussian blur. Should be easy enough to implement if you just look at how that's done in the code. Start with the 1DGaus function and the one below it.
     
  5. Flailer

    Flailer

    Joined:
    Apr 1, 2014
    Posts:
    66
    I'd take $10 if I was allowed, but unfortunately I'm contractually obliged to help for free :p
     
  6. aubergine

    aubergine

    Joined:
    Sep 12, 2009
    Posts:
    2,878
    You concluded that paying 50$ for an asset which includes 60+ more effects that is going to teach you how your problem is going to be solved and also used by many big and released titles is a huge rip-off?

    Further more, when i directed you to an alternative solution in case you cant find a free solution, you think i am shameless?

    And finally, you decide your problem can be solved for 10$?

    Good luck with your endavours :)
     
  7. esco1979

    esco1979

    Joined:
    Mar 18, 2014
    Posts:
    138
    I would never have a use for 99% of those shaders. Also quite a few of them can already be found online elsewhere. That also wouldn't teach me anything; it's not a tutorial. I'm not just going to learn by staring at a mess of code.

    Trying to make money off someone seeking help for such a simple problem? Yes. It's pretty sad when someone posts on a forum asking for assistance with something and is sent to a link to purchase something else. Especially something where the person would not need 95-99% of what is being sold. The fact that you are ok with this, says a lot for your character.

    Yes because while I don't know the syntax for the code, I do know what would go into making it. And yes with assistance like yours from people I would need a lot of luck. It's truly sad how many in this "community" act when someone asks for help. You had all the time in the world to make sarcastic remarks back, but none to offer any helpful advice other than "hey wanna buy from me?". #NoEsBueno

    Thanks. Now this is a lot more helpful than someone trying to make a buck off of me. :D So just the 1D Gaussian and the 3-tap Gaussian filter along horz. line are needed for that effect? Interesting. I'll look up a few tutorial vids and see what I can learn about CG shader and frag/surf shaders over the next few days.

    If you don't mind, I'll try and message you over the weekend? I'd rather learn how to do this stuff rather than just asking people for code (which doesn't really help me learn).
     
    4-bit and Ilingis like this.
  8. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    Alright, I'm feeling very christmassy today so I will help you :) I updated that shader to make it work with Unity. But let me say few things first. ShaderToy shaders are generaly not a very good place to learn the basics. Those shaders are often hard to read, unoptimized or simply not suited for realtime rendering. The shader you posted actually has three different effects for different parts of the screen for example.
    If you want to learn how to write shaders, then I suggest to read some tutorials and try to write some simple shaders first. There's a ton of resources online.
    With that being said, translating shaders from ShaderToy (GLSL) to Unity (HLSL/Cg) isn't very difficult. GLSL and HLSL are very similar languages and you can copy and paste between them with minor edits. But you do need some basic knowledge first. And if you want to truly understand what the shader does then you need to know a bit more than the basics.
    Anyway, here is your shader
    Code (CSharp):
    1. Shader "Hidden/CRT"
    2. {
    3.     Properties
    4.     {
    5.         //_MainTex("Texture", 2D) = "white" {}
    6.  
    7.         // Hardness of scanline.
    8.         //  -8.0 = soft
    9.         // -16.0 = medium
    10.         hardScan("HardScan", Range(-8, -16)) = -8
    11.         // Hardness of pixels in scanline.
    12.         // -2.0 = soft
    13.         // -4.0 = hard
    14.         hardPix("HardPix", Range(-2, -4)) = -3
    15.         maskDark("maskDark", Range(0, 2)) = 0.5
    16.         maskLight("maskLight", Range(0, 2)) = 1.5
    17.         // Display warp.
    18.         // 0.0 = none
    19.         // 1.0/8.0 = extreme
    20.         warp("Warp", Vector) = (0.03125, 0.04166, 0, 0)
    21.         // resolution scale
    22.         resScale("ResolutionScale", Range(1, 16)) = 4
    23.     }
    24.     SubShader
    25.     {
    26.         // No culling or depth
    27.         Cull Off ZWrite Off ZTest Always
    28.  
    29.         Pass
    30.         {
    31.             CGPROGRAM
    32.             #pragma vertex vert
    33.             #pragma fragment frag
    34.  
    35.             #include "UnityCG.cginc"
    36.  
    37.             struct appdata
    38.             {
    39.                 float4 vertex : POSITION;
    40.                 float2 uv : TEXCOORD0;
    41.             };
    42.  
    43.             struct v2f
    44.             {
    45.                 float2 uv : TEXCOORD0;
    46.                 float4 vertex : SV_POSITION;
    47.             };
    48.  
    49.             v2f vert(appdata v)
    50.             {
    51.                 v2f o;
    52.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    53.                 o.uv = v.uv;
    54.                 return o;
    55.             }
    56.  
    57.             sampler2D _MainTex;
    58.  
    59.             float maskDark = 0.5;
    60.             float maskLight = 1.5;
    61.             float hardScan = -8.0;
    62.             float hardPix = -3.0;
    63.             float2 warp = float2(1.0 / 32.0, 1.0 / 24.0);
    64.             float2 res;
    65.             float resScale;
    66.  
    67.             // Nearest emulated sample given floating point position and texel offset.
    68.             float3 Fetch(float2 pos, float2 off)
    69.             {
    70.                 pos = floor(pos * res + off) / res;
    71.                 //if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
    72.                     //return float4(0, 0, 0, 0);
    73.                 return tex2Dlod(_MainTex, float4(pos.xy, 0, -16.0));
    74.             }
    75.            
    76.             // Distance in emulated pixels to nearest texel.
    77.             float2 Dist(float2 pos) { pos = pos*res; return -((pos - floor(pos)) - float2(0.5, 0.5)); }
    78.  
    79.             // 1D Gaussian.
    80.             float Gaus(float pos, float scale) { return exp2(scale*pos*pos); }
    81.  
    82.             // 3-tap Gaussian filter along horz line.
    83.             float3 Horz3(float2 pos, float off)
    84.             {
    85.                 float3 b = Fetch(pos, float2(-1.0, off));
    86.                 float3 c = Fetch(pos, float2(0.0, off));
    87.                 float3 d = Fetch(pos, float2(1.0, off));
    88.                 float dst = Dist(pos).x;
    89.                 // Convert distance to weight.
    90.                 float scale = hardPix;
    91.                 float wb = Gaus(dst - 1.0, scale);
    92.                 float wc = Gaus(dst + 0.0, scale);
    93.                 float wd = Gaus(dst + 1.0, scale);
    94.                 // Return filtered sample.
    95.                 return (b*wb + c*wc + d*wd) / (wb + wc + wd);
    96.             }
    97.  
    98.             // 5-tap Gaussian filter along horz line.
    99.             float3 Horz5(float2 pos, float off)
    100.             {
    101.                 float3 a = Fetch(pos, float2(-2.0, off));
    102.                 float3 b = Fetch(pos, float2(-1.0, off));
    103.                 float3 c = Fetch(pos, float2(0.0, off));
    104.                 float3 d = Fetch(pos, float2(1.0, off));
    105.                 float3 e = Fetch(pos, float2(2.0, off));
    106.                 float dst = Dist(pos).x;
    107.                 // Convert distance to weight.
    108.                 float scale = hardPix;
    109.                 float wa = Gaus(dst - 2.0, scale);
    110.                 float wb = Gaus(dst - 1.0, scale);
    111.                 float wc = Gaus(dst + 0.0, scale);
    112.                 float wd = Gaus(dst + 1.0, scale);
    113.                 float we = Gaus(dst + 2.0, scale);
    114.                 // Return filtered sample.
    115.                 return (a*wa + b*wb + c*wc + d*wd + e*we) / (wa + wb + wc + wd + we);
    116.             }
    117.  
    118.             // Return scanline weight.
    119.             float Scan(float2 pos, float off)
    120.             {
    121.                 float dst = Dist(pos).y;
    122.                 return Gaus(dst + off, hardScan);
    123.             }
    124.  
    125.             // Allow nearest three lines to effect pixel.
    126.             float3 Tri(float2 pos)
    127.             {
    128.                 float3 a = Horz3(pos, -1.0);
    129.                 float3 b = Horz5(pos, 0.0);
    130.                 float3 c = Horz3(pos, 1.0);
    131.                 float wa = Scan(pos, -1.0);
    132.                 float wb = Scan(pos, 0.0);
    133.                 float wc = Scan(pos, 1.0);
    134.                 return a*wa + b*wb + c*wc;
    135.             }
    136.  
    137.             // Distortion of scanlines, and end of screen alpha.
    138.             float2 Warp(float2 pos)
    139.             {
    140.                 pos = pos*2.0 - 1.0;
    141.                 pos *= float2(1.0 + (pos.y*pos.y)*warp.x, 1.0 + (pos.x*pos.x)*warp.y);
    142.                 return pos*0.5 + 0.5;
    143.             }
    144.  
    145.             // Shadow mask.
    146.             float3 Mask(float2 pos)
    147.             {
    148.                 pos.x += pos.y*3.0;
    149.                 float3 mask = float3(maskDark, maskDark, maskDark);
    150.                 pos.x = frac(pos.x / 6.0);
    151.                 if (pos.x < 0.333)mask.r = maskLight;
    152.                 else if (pos.x < 0.666)mask.g = maskLight;
    153.                 else mask.b = maskLight;
    154.                 return mask;
    155.             }
    156.  
    157.             // main
    158.             fixed4 frag(v2f i) : SV_Target
    159.             {
    160.                 res = _ScreenParams.xy / resScale;
    161.  
    162.                 float2 fragCoord = i.vertex.xy;
    163.                 float4 fragColor = 0;
    164.  
    165. #ifdef UNITY_UV_STARTS_AT_TOP
    166.                 fragCoord.y = _ScreenParams.y - fragCoord.y;
    167. #endif
    168.                 float2 pos = Warp(fragCoord.xy / _ScreenParams.xy);
    169.                 //fragColor.rgb = tex2D(_MainTex, i.uv)* Mask(fragCoord);
    170.                 fragColor.rgb = Tri(pos) *Mask(fragCoord);
    171.  
    172.                 return fragColor;
    173.             }
    174.             ENDCG
    175.         }
    176.     }
    177. }
    178.  
    You can attach this script to your camera to test it.
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. [ExecuteInEditMode]
    4. public class CRT : MonoBehaviour
    5. {
    6.     public Material material;
    7.     // Use this for initialization
    8.     void Start()
    9.     {
    10.         material = new Material(Shader.Find("Hidden/CRT"));
    11.     }
    12.  
    13.     public void OnRenderImage(RenderTexture source, RenderTexture destination)
    14.     {
    15.         material.SetTexture("_MainTex", source);
    16.         Graphics.Blit(source, destination, material);
    17.     }
    18. }
    Not the best/fastest shader out there but it should work. Happy Holidays
     
    zeropointblack, Grzzld, Schow and 2 others like this.
  9. Marco-Sperling

    Marco-Sperling

    Joined:
    Mar 5, 2012
    Posts:
    620
    It doesn't go unnoticed by others that you chime in ever so much when there's a shader question to promote your own shader package not leaving any other answer behind than "pay me 50 bucks".
    While I highly respect your effort put into these assets (they seem to be really good) I would say there's a more polite way of pointing others in your wallet's direction. These guys are not asking for a payed solution in the first place. So why offer it - at least post the general approach/direction to take when tackling a specific problem. If the OP doesn't get it done post your asset store link. All fine. Everyone happy. No?
     
    Ilingis and Mauri like this.
  10. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Michal_ great work.

    But having some problems trying to get this shader to work in WebGL.

    Especially when I want to include the UI Canvas in Camera 'Space' mode.

    Works fine in Editor, well there is the problem that when the UI is in Camera mode the image flips, but when I add a Vignette and Bloom effect it appears the correct way up???

    But in WebGL it fails, anyone else had these kinds of problems?
     
  11. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    Is the image vertically flipped? Probably OpenGL vs DirectX problem. Following line from the shader flips the image.
    Code (CSharp):
    1. #ifdef UNITY_UV_STARTS_AT_TOP
    2.                 fragCoord.y = _ScreenParams.y - fragCoord.y;
    3. #endif
    You can try to remove #ifdef #endif and then use the shader with/without that line and see what happens.
     
  12. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Michal_ cheers.

    Tried removing the line but the texture does not appear in the correct screen position, combined with it not quite working in WebGL builds that makes it confusing.

    That reminds me doesn't WebGL GLSL and DirectX HLSL require separate sub shaders.

    http://forum.unity3d.com/threads/webgl-conways-game-of-life.374215/#post-2427457

    Will I need to re-write the shader in GLSL or can I just setup a sub shader that takes the shadertoy version as that should be GLSL right?
     
  13. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    My attempt at a HLSL and GLSL shader, but no luck so far.

    Code (CSharp):
    1. Shader "Hidden/CRTShader"
    2. {
    3.     Properties
    4.     {
    5.         //_MainTex("Texture", 2D) = "white" {}
    6.  
    7.         // Hardness of scanline.
    8.         //  -8.0 = soft
    9.         // -16.0 = medium
    10.         hardScan("HardScan", Range(-8, -16)) = -8
    11.         // Hardness of pixels in scanline.
    12.         // -2.0 = soft
    13.         // -4.0 = hard
    14.         hardPix("HardPix", Range(-2, -4)) = -3
    15.         maskDark("maskDark", Range(0, 2)) = 0.5
    16.         maskLight("maskLight", Range(0, 2)) = 1.5
    17.         // Display warp.
    18.         // 0.0 = none
    19.         // 1.0/8.0 = extreme
    20.         warp("Warp", Vector) = (0.03125, 0.04166, 0, 0)
    21.         // resolution scale
    22.         resScale("ResolutionScale", Range(1, 16)) = 4
    23.     }
    24.    
    25.     SubShader
    26.     {
    27.         // No culling or depth
    28.         Cull Off ZWrite Off ZTest Always
    29.  
    30.         Pass
    31.         {
    32.         CGPROGRAM
    33.         #pragma vertex vert
    34.         #pragma fragment frag
    35.  
    36.         #include "UnityCG.cginc"
    37.  
    38.         struct appdata
    39.         {
    40.             float4 vertex : POSITION;
    41.             float2 uv : TEXCOORD0;
    42.         };
    43.  
    44.         struct v2f
    45.         {
    46.             float2 uv : TEXCOORD0;
    47.             float4 vertex : SV_POSITION;
    48.         };
    49.  
    50.         v2f vert(appdata v)
    51.         {
    52.             v2f o;
    53.             o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    54.             o.uv = v.uv;
    55.             return o;
    56.         }
    57.  
    58.         sampler2D _MainTex;
    59.  
    60.         float maskDark = 0.5;
    61.         float maskLight = 1.5;
    62.         float hardScan = -8.0;
    63.         float hardPix = -3.0;
    64.         float2 warp = float2(1.0 / 32.0, 1.0 / 24.0);
    65.         float2 res;
    66.         float2 invRes;
    67.         float resScale;
    68.  
    69.         //float2 invScreenRes = 1.0 / _ScreenParams.xy;
    70.  
    71.         // Nearest emulated sample given floating point position and texel offset.
    72.         float3 Fetch(float2 pos, float2 off)
    73.         {
    74.             //pos = floor(pos * res + off) / res;
    75.             //if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
    76.             //return float4(0, 0, 0, 0);
    77.             return tex2Dlod(_MainTex, float4(pos.xy, 0, -16.0));
    78.         }
    79.  
    80.         // Distance in emulated pixels to nearest texel.
    81.         float2 Dist(float2 pos)
    82.         {
    83.             pos = pos*res;
    84.             return -((pos - floor(pos)) - float2(0.5, 0.5));
    85.         }
    86.  
    87.         // 1D Gaussian.
    88.         float Gaus(float pos, float scale) { return exp2(scale*pos*pos); }
    89.  
    90.         // 3-tap Gaussian filter along horz line.
    91.         float3 Horz3(float2 pos, float off)
    92.         {
    93.             float2 p = floor(pos * res + off) * invRes;
    94.  
    95.             float3 b = Fetch(p, float2(-1.0, off));
    96.             float3 c = Fetch(p, float2(0.0, off));
    97.             float3 d = Fetch(p, float2(1.0, off));
    98.  
    99.             float dst = Dist(pos).x;
    100.             // Convert distance to weight.      
    101.  
    102.             float scale = hardPix;
    103.             float wb = Gaus(dst - 1.0, scale);
    104.             float wc = Gaus(dst + 0.0, scale);
    105.             float wd = Gaus(dst + 1.0, scale);
    106.             // Return filtered sample.
    107.             return (b*wb + c*wc + d*wd) / (wb + wc + wd);
    108.         }
    109.  
    110.         // 5-tap Gaussian filter along horz line.
    111.         float3 Horz5(float2 pos, float off)
    112.         {
    113.             float2 p = floor(pos * res + off) * invRes;
    114.  
    115.             float3 a = Fetch(p, float2(-2.0, off));
    116.             float3 b = Fetch(p, float2(-1.0, off));
    117.             float3 c = Fetch(p, float2(0.0, off));
    118.             float3 d = Fetch(p, float2(1.0, off));
    119.             float3 e = Fetch(p, float2(2.0, off));
    120.  
    121.             float dst = Dist(pos).x;
    122.             // Convert distance to weight.
    123.             float scale = hardPix;
    124.             float wa = Gaus(dst - 2.0, scale);
    125.             float wb = Gaus(dst - 1.0, scale);
    126.             float wc = Gaus(dst + 0.0, scale);
    127.             float wd = Gaus(dst + 1.0, scale);
    128.             float we = Gaus(dst + 2.0, scale);
    129.             // Return filtered sample.
    130.             return (a*wa + b*wb + c*wc + d*wd + e*we) / (wa + wb + wc + wd + we);
    131.         }
    132.  
    133.         // Return scanline weight.
    134.         float Scan(float2 pos, float off)
    135.         {
    136.             float dst = Dist(pos).y;
    137.             return Gaus(dst + off, hardScan);
    138.         }
    139.  
    140.         // Allow nearest three lines to effect pixel.
    141.         float3 Tri(float2 pos)
    142.         {
    143.             float3 a = Horz3(pos, -1.0);
    144.             float3 b = Horz5(pos, 0.0);
    145.             float3 c = Horz3(pos, 1.0);
    146.             float wa = Scan(pos, -1.0);
    147.             float wb = Scan(pos, 0.0);
    148.             float wc = Scan(pos, 1.0);
    149.             return a*wa + b*wb + c*wc;
    150.         }
    151.  
    152.         // Distortion of scanlines, and end of screen alpha.
    153.         float2 Warp(float2 pos)
    154.         {
    155.             pos = pos*2.0 - 1.0;
    156.             pos *= float2(1.0 + (pos.y*pos.y)*warp.x, 1.0 + (pos.x*pos.x)*warp.y);
    157.             return pos*0.5 + 0.5;
    158.         }
    159.  
    160.         // Shadow mask.
    161.         float3 Mask(float2 pos)
    162.         {
    163.             pos.x += pos.y*3.0;
    164.             float3 mask = float3(maskDark, maskDark, maskDark);
    165.             pos.x = frac(pos.x * 0.16666); // / 6.0);
    166.             if (pos.x < 0.333) mask.r = maskLight;
    167.             else if (pos.x < 0.666) mask.g = maskLight;
    168.             else mask.b = maskLight;
    169.             return mask;
    170.         }
    171.  
    172.         // main
    173.         fixed4 frag(v2f i) : SV_Target
    174.         {
    175.             res = _ScreenParams.xy / resScale;
    176.  
    177.             invRes.x = 1.0 / res.x;
    178.             invRes.y = 1.0 / res.y;
    179.  
    180.             float2 fragCoord = i.vertex.xy;
    181.             float4 fragColor = 0;
    182.  
    183.         #ifdef UNITY_UV_STARTS_AT_TOP              
    184.             fragCoord.y = _ScreenParams.y - fragCoord.y;
    185.         #endif      
    186.  
    187.             float2 pos = Warp(fragCoord.xy / _ScreenParams.xy);
    188.  
    189.             //fragColor.rgb = tex2D(_MainTex, i.uv)* Mask(fragCoord);
    190.             fragColor.rgb = Tri(pos) * Mask(fragCoord);
    191.  
    192.             return fragColor;
    193.         }
    194.         ENDCG
    195.     }
    196.     }
    197.    
    198.    
    199.     SubShader
    200.     {
    201.         // No culling or depth
    202.         Cull Off ZWrite Off ZTest Always
    203.  
    204.         Pass
    205.         {
    206.             GLSLPROGRAM
    207.  
    208.     #include "UnityCG.glslinc"
    209.  
    210. #ifdef VERTEX
    211.  
    212.     //attribute vec4 _glesVertex;
    213.  
    214.     //attribute vec4 _glesMultiTexCoord0;
    215.  
    216.     //uniform highp mat4 glstate_matrix_mvp;
    217.  
    218.     //varying mediump vec2 xlv_TEXCOORD0;
    219.  
    220.  
    221.     void main()
    222.     {
    223.         gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    224.         //gl_Position = (glstate_matrix_mvp * _glesVertex);
    225.         //xlv_TEXCOORD0 = _glesMultiTexCoord0.xy;
    226.     }
    227.  
    228. #endif // VERTEX
    229.  
    230.  
    231. #ifdef FRAGMENT
    232.     //
    233.     // PUBLIC DOMAIN CRT STYLED SCAN-LINE SHADER
    234.     //
    235.     //   by Timothy Lottes
    236.     //
    237.     // This is more along the style of a really good CGA arcade monitor.
    238.     // With RGB inputs instead of NTSC.
    239.     // The shadow mask example has the mask rotated 90 degrees for less chromatic aberration.
    240.     //
    241.     // Left it unoptimized to show the theory behind the algorithm.
    242.     //
    243.     // It is an example what I personally would want as a display option for pixel art games.
    244.     // Please take and use, change, or whatever.
    245.     //
    246.  
    247.     // Emulated input resolution.
    248.  
    249.         vec2 res;
    250.  
    251.         // Hardness of scanline.
    252.         //  -8.0 = soft
    253.         // -16.0 = medium
    254.         float hardScan = -8.0;
    255.  
    256.         // Hardness of pixels in scanline.
    257.         // -2.0 = soft
    258.         // -4.0 = hard
    259.         float hardPix = -3.0;
    260.  
    261.         // Display warp.
    262.         // 0.0 = none
    263.         // 1.0/8.0 = extreme
    264.         vec2 warp = vec2(1.0 / 32.0, 1.0 / 24.0);
    265.  
    266.         // Amount of shadow mask.
    267.         float maskDark = 0.5;
    268.         float maskLight = 1.5;
    269.  
    270.         float resScale;
    271.  
    272.         //------------------------------------------------------------------------
    273.  
    274.         // sRGB to Linear.
    275.         // Assuing using sRGB typed textures this should not be needed.
    276.         float ToLinear1(float c) { return(c <= 0.04045) ? c / 12.92 : pow((c + 0.055) / 1.055, 2.4); }
    277.         vec3 ToLinear(vec3 c) { return vec3(ToLinear1(c.r), ToLinear1(c.g), ToLinear1(c.b)); }
    278.  
    279.         // Linear to sRGB.
    280.         // Assuing using sRGB typed textures this should not be needed.
    281.         float ToSrgb1(float c) { return(c<0.0031308 ? c*12.92 : 1.055*pow(c, 0.41666) - 0.055); }
    282.         vec3 ToSrgb(vec3 c) { return vec3(ToSrgb1(c.r), ToSrgb1(c.g), ToSrgb1(c.b)); }
    283.  
    284.         // Nearest emulated sample given floating point position and texel offset.
    285.         // Also zero's off screen.
    286.         vec3 Fetch(vec2 pos, vec2 off) {
    287.             pos = floor(pos*res + off) / res;
    288.             if (max(abs(pos.x - 0.5), abs(pos.y - 0.5))>0.5)return vec3(0.0, 0.0, 0.0);
    289.             return ToLinear(texture2D(iChannel0, pos.xy, -16.0).rgb);
    290.         }
    291.  
    292.         // Distance in emulated pixels to nearest texel.
    293.         vec2 Dist(vec2 pos) { pos = pos*res; return -((pos - floor(pos)) - vec2(0.5)); }
    294.  
    295.         // 1D Gaussian.
    296.         float Gaus(float pos, float scale) { return exp2(scale*pos*pos); }
    297.  
    298.         // 3-tap Gaussian filter along horz line.
    299.         vec3 Horz3(vec2 pos, float off) {
    300.             vec3 b = Fetch(pos, vec2(-1.0, off));
    301.             vec3 c = Fetch(pos, vec2(0.0, off));
    302.             vec3 d = Fetch(pos, vec2(1.0, off));
    303.             float dst = Dist(pos).x;
    304.             // Convert distance to weight.
    305.             float scale = hardPix;
    306.             float wb = Gaus(dst - 1.0, scale);
    307.             float wc = Gaus(dst + 0.0, scale);
    308.             float wd = Gaus(dst + 1.0, scale);
    309.             // Return filtered sample.
    310.             return (b*wb + c*wc + d*wd) / (wb + wc + wd);
    311.         }
    312.  
    313.         // 5-tap Gaussian filter along horz line.
    314.         vec3 Horz5(vec2 pos, float off) {
    315.             vec3 a = Fetch(pos, vec2(-2.0, off));
    316.             vec3 b = Fetch(pos, vec2(-1.0, off));
    317.             vec3 c = Fetch(pos, vec2(0.0, off));
    318.             vec3 d = Fetch(pos, vec2(1.0, off));
    319.             vec3 e = Fetch(pos, vec2(2.0, off));
    320.             float dst = Dist(pos).x;
    321.             // Convert distance to weight.
    322.             float scale = hardPix;
    323.             float wa = Gaus(dst - 2.0, scale);
    324.             float wb = Gaus(dst - 1.0, scale);
    325.             float wc = Gaus(dst + 0.0, scale);
    326.             float wd = Gaus(dst + 1.0, scale);
    327.             float we = Gaus(dst + 2.0, scale);
    328.             // Return filtered sample.
    329.             return (a*wa + b*wb + c*wc + d*wd + e*we) / (wa + wb + wc + wd + we);
    330.         }
    331.  
    332.         // Return scanline weight.
    333.         float Scan(vec2 pos, float off) {
    334.             float dst = Dist(pos).y;
    335.             return Gaus(dst + off, hardScan);
    336.         }
    337.  
    338.         // Allow nearest three lines to effect pixel.
    339.         vec3 Tri(vec2 pos) {
    340.             vec3 a = Horz3(pos, -1.0);
    341.             vec3 b = Horz5(pos, 0.0);
    342.             vec3 c = Horz3(pos, 1.0);
    343.             float wa = Scan(pos, -1.0);
    344.             float wb = Scan(pos, 0.0);
    345.             float wc = Scan(pos, 1.0);
    346.             return a*wa + b*wb + c*wc;
    347.         }
    348.  
    349.         // Distortion of scanlines, and end of screen alpha.
    350.         vec2 Warp(vec2 pos) {
    351.             pos = pos*2.0 - 1.0;
    352.             pos *= vec2(1.0 + (pos.y*pos.y)*warp.x, 1.0 + (pos.x*pos.x)*warp.y);
    353.             return pos*0.5 + 0.5;
    354.         }
    355.  
    356.         // Shadow mask.
    357.         vec3 Mask(vec2 pos) {
    358.             pos.x += pos.y*3.0;
    359.             vec3 mask = vec3(maskDark, maskDark, maskDark);
    360.             pos.x = fract(pos.x / 6.0);
    361.             if (pos.x < 0.333)mask.r = maskLight;
    362.             else if (pos.x < 0.666)mask.g = maskLight;
    363.             else mask.b = maskLight;
    364.             return mask;
    365.         }
    366.  
    367.         // Draw dividing bars.
    368.         //float Bar(float pos, float bar) { pos -= bar; return pos*pos < 4.0 ? 0.0 : 1.0; }
    369.  
    370.         // Entry.
    371.         void main(out vec4 fragColor, in vec2 fragCoord) {
    372.  
    373.             res = _ScreenParams.xy / resScale;
    374.  
    375.             vec2 pos = Warp(fragCoord.xy / res.xy);
    376.             fragColor.rgb = Tri(pos)*Mask(fragCoord.xy);
    377.  
    378.             fragColor.a = 1.0;
    379.         }
    380.     #endif
    381.         ENDGLSL
    382.     }
    383.     }
    384.    
    385. }
    Progress is it still works in editor but still nothing in WebGL???
     
  14. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    Unity will automatically cross-compile all hlsl shaders to glsl for opengl platforms.

    Is the image flipped in WebGL or it doesn't work at all? Is there any error in the app log? Maybe the shader wasn't included in the build. You can try to force it. Go to Edit->Project Settings->Graphics and add you shader to the list of "Always Included Shaders".
     
  15. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    It doesn't work at all (just black).

    Are you sure as I needed a WebGL GLSL version of the Mandlebrot shader to work in WebGL?

    http://forum.unity3d.com/threads/webgl-conways-game-of-life.374215/#post-2427457

    Tried building to standalone PC but using OpenGL and it worked. The editor changed to OpenGL 4.5 and it worked in editor, but the UI Canvas was flickering vanishing and smearing.
     
    Last edited: Jan 13, 2016
  16. Michal_

    Michal_

    Joined:
    Jan 14, 2015
    Posts:
    365
    Automatic cross-compiling from HLSL to GLSL certainly works for Win OpenGL, Mac, iOS and Android. I never used WebGL but I assume it works the same way. The shader is probably just doing something that WebGL doesn't support.
    The only thing that comes to mind is the "SV_POSITION" semantic used in pixel shader. That wasn't allowed before DX10 and newer OpenGL versions. Here's slightly modified version of the shader that doesn't use it
    Code (CSharp):
    1. Shader "Hidden/CRT"
    2. {
    3.     Properties
    4.     {
    5.         //_MainTex("Texture", 2D) = "white" {}
    6.         // Hardness of scanline.
    7.         //  -8.0 = soft
    8.         // -16.0 = medium
    9.         hardScan("HardScan", Range(-8, -16)) = -8
    10.         // Hardness of pixels in scanline.
    11.         // -2.0 = soft
    12.         // -4.0 = hard
    13.         hardPix("HardPix", Range(-2, -4)) = -3
    14.         maskDark("maskDark", Range(0, 2)) = 0.5
    15.         maskLight("maskLight", Range(0, 2)) = 1.5
    16.         // Display warp.
    17.         // 0.0 = none
    18.         // 1.0/8.0 = extreme
    19.         warp("Warp", Vector) = (0.03125, 0.04166, 0, 0)
    20.         // resolution scale
    21.         resScale("ResolutionScale", Range(1, 16)) = 4
    22.     }
    23.     SubShader
    24.     {
    25.         // No culling or depth
    26.         Cull Off ZWrite Off ZTest Always
    27.         Pass
    28.         {
    29.             CGPROGRAM
    30.             #pragma vertex vert
    31.             #pragma fragment frag
    32.  
    33.             #pragma target 2.0
    34.             #include "UnityCG.cginc"
    35.             struct appdata
    36.             {
    37.                 float4 vertex : POSITION;
    38.                 float2 uv : TEXCOORD0;
    39.             };
    40.             struct v2f
    41.             {
    42.                 float2 uv : TEXCOORD0;
    43.                 float4 screenPos : TEXCOORD1;
    44.                 float4 vertex : SV_POSITION;
    45.             };
    46.             v2f vert(appdata v)
    47.             {
    48.                 v2f o;
    49.                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    50.                 o.uv = v.uv;
    51.                 o.screenPos = ComputeScreenPos(o.vertex);
    52.                 return o;
    53.             }
    54.             sampler2D _MainTex;
    55.             float maskDark = 0.5;
    56.             float maskLight = 1.5;
    57.             float hardScan = -8.0;
    58.             float hardPix = -3.0;
    59.             float2 warp = float2(1.0 / 32.0, 1.0 / 24.0);
    60.             float2 res;
    61.             float resScale;
    62.             // Nearest emulated sample given floating point position and texel offset.
    63.             float3 Fetch(float2 pos, float2 off)
    64.             {
    65.                 pos = floor(pos * res + off) / res;
    66.                 //if (max(abs(pos.x - 0.5), abs(pos.y - 0.5)) > 0.5)
    67.                     //return float4(0, 0, 0, 0);
    68.                 return tex2Dlod(_MainTex, float4(pos.xy, 0, -16.0));
    69.             }
    70.          
    71.             // Distance in emulated pixels to nearest texel.
    72.             float2 Dist(float2 pos) { pos = pos*res; return -((pos - floor(pos)) - float2(0.5, 0.5)); }
    73.             // 1D Gaussian.
    74.             float Gaus(float pos, float scale) { return exp2(scale*pos*pos); }
    75.             // 3-tap Gaussian filter along horz line.
    76.             float3 Horz3(float2 pos, float off)
    77.             {
    78.                 float3 b = Fetch(pos, float2(-1.0, off));
    79.                 float3 c = Fetch(pos, float2(0.0, off));
    80.                 float3 d = Fetch(pos, float2(1.0, off));
    81.                 float dst = Dist(pos).x;
    82.                 // Convert distance to weight.
    83.                 float scale = hardPix;
    84.                 float wb = Gaus(dst - 1.0, scale);
    85.                 float wc = Gaus(dst + 0.0, scale);
    86.                 float wd = Gaus(dst + 1.0, scale);
    87.                 // Return filtered sample.
    88.                 return (b*wb + c*wc + d*wd) / (wb + wc + wd);
    89.             }
    90.             // 5-tap Gaussian filter along horz line.
    91.             float3 Horz5(float2 pos, float off)
    92.             {
    93.                 float3 a = Fetch(pos, float2(-2.0, off));
    94.                 float3 b = Fetch(pos, float2(-1.0, off));
    95.                 float3 c = Fetch(pos, float2(0.0, off));
    96.                 float3 d = Fetch(pos, float2(1.0, off));
    97.                 float3 e = Fetch(pos, float2(2.0, off));
    98.                 float dst = Dist(pos).x;
    99.                 // Convert distance to weight.
    100.                 float scale = hardPix;
    101.                 float wa = Gaus(dst - 2.0, scale);
    102.                 float wb = Gaus(dst - 1.0, scale);
    103.                 float wc = Gaus(dst + 0.0, scale);
    104.                 float wd = Gaus(dst + 1.0, scale);
    105.                 float we = Gaus(dst + 2.0, scale);
    106.                 // Return filtered sample.
    107.                 return (a*wa + b*wb + c*wc + d*wd + e*we) / (wa + wb + wc + wd + we);
    108.             }
    109.             // Return scanline weight.
    110.             float Scan(float2 pos, float off)
    111.             {
    112.                 float dst = Dist(pos).y;
    113.                 return Gaus(dst + off, hardScan);
    114.             }
    115.             // Allow nearest three lines to effect pixel.
    116.             float3 Tri(float2 pos)
    117.             {
    118.                 float3 a = Horz3(pos, -1.0);
    119.                 float3 b = Horz5(pos, 0.0);
    120.                 float3 c = Horz3(pos, 1.0);
    121.                 float wa = Scan(pos, -1.0);
    122.                 float wb = Scan(pos, 0.0);
    123.                 float wc = Scan(pos, 1.0);
    124.                 return a*wa + b*wb + c*wc;
    125.             }
    126.             // Distortion of scanlines, and end of screen alpha.
    127.             float2 Warp(float2 pos)
    128.             {
    129.                 pos = pos*2.0 - 1.0;
    130.                 pos *= float2(1.0 + (pos.y*pos.y)*warp.x, 1.0 + (pos.x*pos.x)*warp.y);
    131.                 return pos*0.5 + 0.5;
    132.             }
    133.             // Shadow mask.
    134.             float3 Mask(float2 pos)
    135.             {
    136.                 pos.x += pos.y*3.0;
    137.                 float3 mask = float3(maskDark, maskDark, maskDark);
    138.                 pos.x = frac(pos.x / 6.0);
    139.                 if (pos.x < 0.333)mask.r = maskLight;
    140.                 else if (pos.x < 0.666)mask.g = maskLight;
    141.                 else mask.b = maskLight;
    142.                 return mask;
    143.             }
    144.             // main
    145.             fixed4 frag(v2f i) : SV_Target
    146.             {
    147.                 res = _ScreenParams.xy / resScale;
    148.                 float2 fragCoord = i.screenPos.xy * _ScreenParams.xy;//i.vertex.xy;
    149.                 float4 fragColor = 0;              
    150. #ifdef UNITY_UV_STARTS_AT_TOP
    151.                 //fragCoord.y = _ScreenParams.y - fragCoord.y;
    152. #endif
    153.                 float2 pos = Warp(fragCoord.xy / _ScreenParams.xy);
    154.                 //fragColor.rgb = tex2D(_MainTex, i.uv)* Mask(fragCoord);
    155.                 fragColor.rgb = Tri(pos) *Mask(fragCoord);
    156.                 return fragColor;
    157.             }
    158.             ENDCG
    159.         }
    160.     }
    161. }
     
    Ony and Nith666 like this.
  17. Arowx

    Arowx

    Joined:
    Nov 12, 2009
    Posts:
    8,194
    @Michal_ Wow well done, it's working in WebGL.

    It needs the UNITY_UV_STARTS_AT_TOP line to be the right way up.

    Thank You.