"Zoom" the screen texture uv to avoid edge distortion?
Asked Answered
G

9

0

I have a wave shader I'm using on a ColorRect for a fullscreen Quake underwater-type effect:

shader_type canvas_item;

uniform float amp_x : hint_range(0, 10) = 3.0;
uniform float amp_y : hint_range(0, 10) = 6.0;
uniform float size_x : hint_range(-1, 1) = 0.03;
uniform float size_y : hint_range(-1, 1) = 0.06;

void fragment(){
	vec2 uv;
	uv.x = cos(SCREEN_UV.y * amp_x + TIME) * size_x;
	uv.y = sin(SCREEN_UV.x * amp_y + TIME) * size_y;
	
	COLOR = texture(SCREEN_TEXTURE, SCREEN_UV + uv);

}

The trouble is there's a little edge distortion when the UV goes outsides of the 0-1 range:

Is there a way to either oversample the screen or zoom in slightly on the uv to avoid this?

Giddings answered 29/1, 2022 at 21:53 Comment(0)
C
0

So you can multiply SCREEN_UV by some number (like 0.95) that should be enough to zoom it in. It won't be a 1:1 pixel anymore, but for a water shader it should look okay.

    shader_type canvas_item;
    uniform float amp_x : hint_range(0, 10) = 3.0;
    uniform float amp_y : hint_range(0, 10) = 6.0;
    uniform float size_x : hint_range(-1, 1) = 0.03;
    uniform float size_y : hint_range(-1, 1) = 0.06;
    const zoom = 0.95;
    void fragment(){
        vec2 uv;
        uv.x = cos(SCREEN_UV.y * amp_x * zoom + TIME) * size_x;
        uv.y = sin(SCREEN_UV.x * amp_y * zoom + TIME) * size_y;
        COLOR = texture(SCREEN_TEXTURE, SCREEN_UV * zoom + uv);
    }
Cockerham answered 29/1, 2022 at 22:9 Comment(0)
G
0

@cybereality said: So you can multiply SCREEN_UV by some number (like 0.95) that should be enough to zoom it in. It won't be a 1:1 pixel anymore, but for a water shader it should look okay.

    shader_type canvas_item;
    uniform float amp_x : hint_range(0, 10) = 3.0;
    uniform float amp_y : hint_range(0, 10) = 6.0;
    uniform float size_x : hint_range(-1, 1) = 0.03;
    uniform float size_y : hint_range(-1, 1) = 0.06;
    const zoom = 0.95;
    void fragment(){
        vec2 uv;
        uv.x = cos(SCREEN_UV.y * amp_x * zoom + TIME) * size_x;
        uv.y = sin(SCREEN_UV.x * amp_y * zoom + TIME) * size_y;
        COLOR = texture(SCREEN_TEXTURE, SCREEN_UV * zoom + uv);
    }

Thanks, I just tried this and it kinda just seems to reduce the amp_ amount, the edge still distorts. Should the zoom be happening earlier?

Giddings answered 29/1, 2022 at 22:14 Comment(0)
C
0

I didn't test the code. You can try removing zoom from the uv/x/y lines and then just use it for the texture sample, maybe 0.9 would work better.

Cockerham answered 29/1, 2022 at 22:16 Comment(0)
G
0

@cybereality said: I didn't test the code. You can try removing zoom from the uv/x/y lines and then just use it for the texture sample, maybe 0.9 would work better.

Like this?

texture(SCREEN_TEXTURE * zoom, SCREEN_UV + uv)

Unfortunately I can't * sampler2D and float. As you may notice, I'm very bad with shader code haha!

Giddings answered 29/1, 2022 at 22:19 Comment(0)
C
0

See if this works:

COLOR = texture(SCREEN_TEXTURE, SCREEN_UV * 0.8 + uv);
Cockerham answered 29/1, 2022 at 22:20 Comment(0)
G
0

@cybereality said: See if this works:

COLOR = texture(SCREEN_TEXTURE, SCREEN_UV * 0.8 + uv);

It seems to fix it on one side, as if it's offsetting rather than scaling, I've been sort of plugging different combinations in to try and actually make it scale, but nothing seems to make much difference. Here's a Unity shader that seems to account for it by zoom (the -0.5 and +0.5 part I think?)

Shader "Hidden/QuakeUnderWater"
{
	Properties
	{
		_MainTex("Texture", 2D) = "white" {}
		_WaveFrequency("Wave Frequency", Float) = 0.5
		_WaveScale("Wave Scale", Float) = 0.8
		_WaveAmplitude("Wave Amplitude", Range(0,0.33)) = 0.15
		_Color("Color Tint", Color) = (1,1,1,1)
	}
		SubShader
		{
			// No culling or depth
			Cull Off ZWrite Off ZTest Always

			Pass
			{
				CGPROGRAM
				#pragma vertex vert
				#pragma fragment frag

				#include "UnityCG.cginc"

				struct appdata
				{
					float4 vertex : POSITION;
					float2 uv : TEXCOORD0;
				};

				struct v2f
				{
					float2 uv : TEXCOORD0;
					float4 vertex : SV_POSITION;
				};

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

				sampler2D _MainTex;
				float _WaveScale, _WaveAmplitude, _WaveFrequency;
				float4 _Color;

				fixed4 frag(v2f i) : SV_Target
				{
					float aspect = _ScreenParams.y / _ScreenParams.x;
					float2 waveAmplitude = float2(aspect * _WaveAmplitude, _WaveAmplitude) / _WaveScale;

					float2 uv = (i.uv - 0.5) * (1.0 - abs(waveAmplitude) * 2.0) + 0.5;

					float2 sinTime = (float2((uv.y - 0.5) * aspect, uv.x - 0.5) * _WaveScale + _Time.y * _WaveFrequency) * UNITY_PI;

					uv += float2(sin(sinTime.x), sin(sinTime.y)) * waveAmplitude;

					fixed4 col = tex2D(_MainTex, uv) * _Color;
					return col;
				}
				ENDCG
			}
		}
}
Giddings answered 29/1, 2022 at 22:26 Comment(0)
C
0

Sorry, yeah I wrote that really quickly.

const scale = 0.8;
vec2 scaled_uv = (SCREEN_UV - 0.5) * scale + 0.5;
COLOR = texture(SCREEN_TEXTURE, scaled_uv + uv);
Cockerham answered 29/1, 2022 at 22:33 Comment(0)
G
0

@cybereality said: Sorry, yeah I wrote that really quickly.

const scale = 0.8;
vec2 scaled_uv = (SCREEN_UV - 0.5) * scale + 0.5;
COLOR = texture(SCREEN_TEXTURE, scaled_uv + uv);

Wow, thank you, that's the first thing in hours that's actually worked!

	vec2 uv = SCREEN_UV;
	vec2 scaled_uv = (SCREEN_UV - 0.5) * scale + 0.5;
	uv.x = cos(uv.y * amp_x + TIME) * size_x;
	uv.y = sin(uv.x * amp_y + TIME) * size_y;
	
	COLOR = texture(SCREEN_TEXTURE, scaled_uv+ uv);

Out of interest, should much difference be made if I use:

uv.x = cos(uv.y * amp_x + TIME) * size_x;
uv.y = sin(uv.x * amp_y + TIME) * size_y;

or

uv.x = cos(scaled_uv .y * amp_x + TIME) * size_x;
uv.y = sin(scaled_uv .x * amp_y + TIME) * size_y;

Since the scaled_uv is being used as the texture, I can't see much difference between the two. Really grateful for you helping me with this!!

Giddings answered 29/1, 2022 at 22:45 Comment(0)
C
0

You probably don't need it on the sin/cos, since we will be scaling it at the end anyhow.

Cockerham answered 29/1, 2022 at 22:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.