LCD Shader

Today I used my lunch break to get started on an LCD shader for my current project. While the montior casing looks more likley to be CRT, I really like the subpixel element and moire looks that LCD provides.


To create the effect above, I pixelated the main image then multiplied it with the RBG component as a texture.


To pixelate the texture, I multipled the texture by the number of pixels I wanted on screen, rounded that to create a grid, then divided it by num pixels to bring the tiling back down, but clamped to the grid. (Shown below with a 10 pixel grid for ease of understanding – the image at the top uses a 100 pixel grid.)

1  2  3

fixed4 pixel = tex2D(_MainTex, (round(i.uv * _Pixel) / _Pixel));

I then made sure that the uv used to sample the rgb component texture was multiplied by the pixel value, to have them match up.

Capture5   Capture6


Starting Something New!

This might be the fastest I’ve started something new after finishing a project! I really liked the style I was working in with my pool shader, so wanted to continue.

I’m taking the rose tinted skybox, blown out lighting and simple shapes to a project I’ve had floating about in my head for a while – a sort of adventure game, sort of visual novel, based on internet mysteries and creepypasta. I’m hoping the juxtaposition of cutesy art direction and darker subject matter will make for something interesting!

I’m letting this one lead with art, as I know if I do mechanics first I’ll never get done! The 3D art will be a computer screen and then the majority of the rest of the art will be UI based.


Below I’ve got a quick first pass of the model with some unity default UI stuff in there. The blinking light is an incredibly simple shader – just a couple lines for that effect!

float blink = round(sin(_Time.y * _Speed) * 0.5 + 0.5);
fixed4 col = _Color * blink;


Finished Water Shader

Finally done with this! Its been a while since I posted and can’t quite remember what I’ve done since then, so I’ll just post the full shader code at the bottom of this post so you can take a look!

As far as I know, I added some smaller waves, changed the uv scroll to be sin based rather than a scroll and made the wee sparkle particles.


Deciding how to present this one was interesting – I wanted to emphasise the shader but just having a shaderball didn’t really show what it could do. I opted for this super simple style so that the main piece could shine.


Here’s a video and the full code.

Shader "Unlit/Sh_Water_Unlit_River"
	{	//Texture pack u freak
		_Color("Body Color 1", Color) = (1,1,1,1)
		_Color2("Edge Color", Color) = (1,1,1,1)
		_Color3("Body Color 2", Color) = (1,1,1,1)
		_MainTexture("Body Texture", 2D) = "white" {}
		_EdgeTexture("Edge Texture", 2D) = "white" {}
		_Distance("Edge Thickness", Float) = 1.0
		_Normal("Normal Map", 2D) = "bump"{}
		_Speed("Wave Speed", Range (0,10)) = 1.0
		_Noise("Wave Texture", 2D) = "white"{}
		_Amount("Wave Amount", Float) = 1.0
		_Speed2("Scroll Speed", Range(0,10)) = 1.0
		_TexAmt("Little Waves Amount", Float) = 1.0

		Tags { "RenderType" = "Transparent" "Queue" = "Transparent" "Lightmode" = "ForwardBase"}
		LOD 100
		Blend SrcAlpha OneMinusSrcAlpha

			#pragma vertex vert
			#pragma fragment frag
			#pragma multi_compile_fog

			#include "UnityCG.cginc"

			struct appdata
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
				float4 normal : NORMAL;

			struct v2f
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
				float4 screenPos : TEXCOORD1; //Custom Data in V2f
				float2 uv2 : TEXCOORD2;
				float4 normal : NORMAL;
				float2 viewDir : TEXCOORD3;

			sampler2D _EdgeTexture;
			float4 _EdgeTexture_ST;
			float4 _Color;
			float4 _Color2;
			float4 _Color3;
			uniform sampler2D _CameraDepthTexture;
			float _Distance;
			sampler2D _Normal;
			float4 _Normal_ST;
			float _Speed;
			float _Speed2;
			sampler2D _Noise;
			float _Amount;
			sampler2D _MainTexture;
			float4 _MainTexture_ST;
			float _TexAmt;

			v2f vert (appdata v)
				v2f o;
				float4 noiseTex = tex2Dlod(_Noise, float4(v.uv, 0, 0) * 20);

				//y pos = sin for general up down movement * texture for waves and sides need to come up at different times
				v.vertex.y += (sin(_Time.y * _Speed + v.vertex.x + v.vertex.z) * _Amount * (noiseTex * 2)); //also fix normal
				v.vertex.y += (sin(_Time.y * _Speed * 0.5) * _TexAmt) * round(noiseTex);

				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _EdgeTexture);
				o.uv2 = TRANSFORM_TEX(v.uv, _MainTexture);
				o.screenPos = ComputeScreenPos(o.vertex); //Get vertex in clip space, compute screen position
				o.normal = v.normal;
				o.viewDir = normalize(ObjSpaceViewDir(o.vertex));
				return o;

			fixed4 frag (v2f i) : SV_Target
				half depth = LinearEyeDepth(SAMPLE_DEPTH_TEXTURE_PROJ(_CameraDepthTexture, UNITY_PROJ_COORD(i.screenPos)));
				half screenDepth = saturate((depth - i.screenPos.w) / _Distance); 

				float4 noiseTex = tex2D(_Noise, i.uv2 * 0.5);

				float2 pan_uv = float2((i.uv.x * noiseTex.r), (i.uv.y + (sin(_Time.y * _Speed * 1.5) * _Amount * _Speed2)));
				float2 pan_uv2 = float2((i.uv2.x * noiseTex.r), (i.uv2.y + (sin(_Time.y * _Speed * 1.5) * _Amount * _Speed2)));

				float4 noiseTex2 = tex2D(_Noise, pan_uv2);

				fixed4 edge = saturate(tex2D(_EdgeTexture, pan_uv).b) * _Color2;
				fixed4 body = saturate(tex2D(_MainTexture, pan_uv2).b) + lerp(_Color, _Color3, smoothstep(0.25, 0.75, noiseTex2.r));

				float fresnel = max(0, 1 - (dot(i.viewDir, i.normal)));
				float wobbly_fresnel = fresnel;

				fixed4 color = lerp(edge + body, body, screenDepth);
				fixed4 col = saturate(lerp(color * 0.1, color, wobbly_fresnel));
				col.a = saturate(lerp(edge.a, _Color.a, screenDepth) + wobbly_fresnel * col.a);

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