Welcome, Guest. Please login or register.

Login with username, password and session length

 
Advanced search

1411279 Posts in 69323 Topics- by 58380 Members - Latest Member: bob1029

March 28, 2024, 03:40:44 PM

Need hosting? Check out Digital Ocean
(more details in this thread)
TIGSource ForumsDeveloperTechnical (Moderator: ThemsAllTook)Getting a unique value for every pixel in a vertex shader, not using uv
Pages: [1]
Print
Author Topic: Getting a unique value for every pixel in a vertex shader, not using uv  (Read 1125 times)
brantkings
Level 0
***


I like games


View Profile WWW
« on: October 29, 2018, 03:52:10 PM »

Hey!

I am trying to make a distortion effect vertex-fragment shader! I am pretty new to shaders in general, probably don't know what I am doing, and I am having some trouble with it.

The effect is: from a distortion texture, I get a vector xy of the direction of the distortion through the rg part of the color, and get the intensity of the distortion from the blue. Then I use _Time to scroll where I sample from it.I am extending the Unity's default Sprite shader.


Sprite without the shader, with it, and then it combined with the animation.

What I did is the distortion effect in the middle. It's been pretty hard to do something pixel perfect. What I'm trying to do is taking the uv coordinate, and using the width and height of the MainTexture to get a integer value that's gonna be the same for every pixel of the texture. I do that so everyone from the same pixel can sample from the same position of the distortion texture.

Code:
int pixelX = round(uv.x * _MainTex_TexelSize.z) % _MainTex_TexelSize.z;
int pixelY = round(uv.y * _MainTex_TexelSize.w) % _MainTex_TexelSize.w;
The effect is more or less what I want (I still need to tweak the distortion texture).

The problem is that as I use the uv for the position of distortion, when I animate it (as I use Unity's sprite atlas, all the sprites are on the same texture, and it just jumps the uv around), the image becomes all shaky because it loses the information of where the distortion is at every frame.(It kinda looks OK in the image above, but it can potentially look terrible.)

The displacement code:

Code:
//Get the color of the displacement
float4 displacementValue = tex2D(_Distortion, uvDisplacement);

float timeGrowth = asin(_CosTime[3]) / (M_PI) + 0.5;
float displacementMultiplier = lerp(_DisplacementMinimum, 1, timeGrowth * displacementValue.b);

//Calculating the displacement pixel with the value
pixelX += displacementMultiplier * (displacementValue.r - 0.5) * _MaxDisplacement;
pixelY += displacementMultiplier * (displacementValue.g - 0.5) * _MaxDisplacement;

//Getting the UV
uv.x = (float)pixelX * _MainTex_TexelSize.x;
uv.y = (float)pixelY * _MainTex_TexelSize.y;

fixed4 c = tex2D(_MainTex, uv) * IN.color;

return c;

What I really want to do is not use UV at all to calculate the pixel position, but instead use screen position. Does anybody know how to do this? I don't need it to be pixel perfect and be purist pixel art or anything, I just need it to look acceptable.

I am using the position (the regular SV_POSITION from the default sprite fragment shader) as well with _Time to offset the position so two sprites next to each other have different distortions, but this caused some aberrations:

This is not pixel perfect AT ALL

Maybe because of precision in the float values?

Code:
int uvWhereX = round(pixelX + _Time[1] * _Speed.x + (IN.vertex.x / _PixelsByUnit));
int uvWhereY = round(pixelY + _Time[1] * _Speed.y + (IN.vertex.y / _PixelsByUnit));

This is kind of a copy of this, but nobody is helping me over there. Concerned

Can anyone point me in a good direction to solve this? Beer!

« Last Edit: October 31, 2018, 09:43:09 AM by brantkings » Logged

Tatlum Crane
Level 0
*



View Profile
« Reply #1 on: October 30, 2018, 04:27:52 AM »

If I got it right, you’ve got a vector which contains displacement. This displacement is expressed in texture pixels (texels). You need to get displaced pixel’s UV-coordinates.

There is a more suitable method for this. It’s fwidth function. It will return how much a value changes between a current pixel and the next one.

fwidth(uv) - will show you how the UV-coordinate changes from pixel to pixel
This is a rough code that should work:


Code:
float4 displacementValue = tex2D(_Distortion, uvDisplacement);

float timeGrowth = asin(_CosTime[3]) / (M_PI) + 0.5;
float displacementMultiplier = lerp(_DisplacementMinimum, 1, timeGrowth * displacementValue.b);

float2 displacementVector;
displacementVector.x = displacementMultiplier * (displacementValue.r - 0.5) * _MaxDisplacement;
displacementVector.y = displacementMultiplier * (displacementValue.g - 0.5) * _MaxDisplacement;

float2 step = fwidth(uv);

// You should round you vector if you want to get right into the center of a texel
uv += round(displacementVector) * step;

fixed4 c = tex2D(_MainTex, uv) * IN.color;

return c;

Be aware, that because of atlas usage you may end up within a neighbor image. And as a result, you’ll get its color. To prevent that you may increase the distance between images in the atlas or don’t use atlas at all.
Logged
brantkings
Level 0
***


I like games


View Profile WWW
« Reply #2 on: October 30, 2018, 11:18:10 AM »

Thank you kindly for pointing me out to fwidth  Toast Right! It really can simplify this mess a bit.

For the sake of simplifying this further more (my original code was unnecessarily confusing), consider the code like this, in my fragment shader:

Code:
float2 uv = IN.texcoord;

//Get the unique value for this pixel
int2 pixel;
pixel.x = round(uv.x * _MainTex_TexelSize.z) % _MainTex_TexelSize.z;
pixel.y = round(uv.y * _MainTex_TexelSize.w) % _MainTex_TexelSize.w;

//Animate the UV of the displacement texture
int2 walkPixel;
walkPixel.x = round(pixel.x + _Time[1] * _Speed.x);
walkPixel.y = round(pixel.y + _Time[1] * _Speed.y);

//Get the UV for the same place as the pixel
float2 uvDisplacement = float2(float(walkPixel.x % _Distortion_TexelSize.z) * _Distortion_TexelSize.x,
float(walkPixel.y %_Distortion_TexelSize.w) * _Distortion_TexelSize.y);

Now I have the uv I want to get the displacement from, using a integer value (pixel) that's unique for each pixel of the uv. I discover that using the size of the texture atlas (_MainTex_TexelSize).

Code:

float4 displacementValue = tex2D(_Distortion, uvDisplacement);

float2 displacementVector;
displacementVector.x = (displacementValue.r - 0.5) * displacementValue.b;
displacementVector.y = (displacementValue.g - 0.5) * displacementValue.b;

float2 step = fwidth(uv);

// You should round you vector if you want to get right into the center of a texel
uv += round(displacementVector) * step;

fixed4 c = tex2D(_MainTex, uv) * IN.color;

return c;

This way I'm only using the rgb to create displacement. (It might not be an ideal way).

But well, I'm still using the original uv as input to get my displacement and that's the problem. As I'm using the atlas, the animation makes the original uv used as input jump around with arbitrary values, so the displacement also jumps around and I lose the scrolling effect that makes it so smooth (and if the animation is fast the distortion looks almost random).

What I wanted is to use the world x,y (IN.vertex) coordinates to get the uv of the displacement texture I want to pick from. Is it possible?

Would be even better if these x,y coordinates could transform in a value that is unique for each one of the original texture's pixels (not necessarily unique for each of the screen's pixels). I really don't know if this is impossible or not. ):
Logged

brantkings
Level 0
***


I like games


View Profile WWW
« Reply #3 on: October 30, 2018, 12:09:31 PM »

Using this problem would also solve this problem: I don't want two sprites side by side to have the exact same distortion.

Logged

brantkings
Level 0
***


I like games


View Profile WWW
« Reply #4 on: October 31, 2018, 09:42:19 AM »

Then I actually discovered a workaround using fwidth!

In a parallax camera, if the object is far away from it, the fwidth(uv) will get bigger and bigger until it is next to 1, as when the image is so small that the next pixel of the screen covers the entire uv. Knowing that I can know how texels change between pixels and then not need to use uv for taking the distortion!

Code:
//Get the uv derivative
float2 derivativeUV = fwidth(IN.texcoord);

//Get the amount of texels are changing between this and the next pixel.
float2 distanceNearbyPixelinTexels = float2(
    derivativeUV.x / _MainTex_TexelSize.x,
    derivativeUV.y / _MainTex_TexelSize.y
);

Then instead of

Code:
//Animate the UV of the displacement texture
int2 walkPixel;
walkPixel.x = round(pixel.x + _Time[1] * _Speed.x);
walkPixel.y = round(pixel.y + _Time[1] * _Speed.y);

Do

Code:
//Animate the UV of the displacement texture
int2 walkPixel;
walkPixel.x = round(_Time[1] * _Speed.x + IN.vertex.x * distanceNearbyPixelinTexels.x);
walkPixel.y = round(_Time[1] * _Speed.y + IN.vertex.y * distanceNearbyPixelinTexels.y);

If I don't use that, the effect is that the distortion is always the same for camera and screen size, which is horrible when zooming in.

Of course the result isn't pixel perfect, I've got to come up with something that I can get the IN.vertex that's equal for every pixel in the uv.

I'm thinking on giving up in the pixel perfect thing, actually.
Logged

Pages: [1]
Print
Jump to:  

Theme orange-lt created by panic