Fish-eye warping about mouse position - fragment shader - glsl

I'm trying to create a fish-eye effect but only in a small radius around the mouse position. I've been able to modify this code to work about the mouse position (demo) but I can't figure out where the zooming is coming from. I'm expecting the output to warp the image similarly to this (ignore the color inversion for the sake of this question):
Relevant code:
// Check if within given radius of the mouse
vec2 diff = myUV - u_mouse - 0.5;
float distance = dot(diff, diff); // square of distance, saves a square-root
// Add fish-eye
if(distance <= u_radius_squared) {
vec2 xy = 2.0 * (myUV - u_mouse) - 1.0;
float d = length(xy * maxFactor);
float z = sqrt(1.0 - d * d);
float r = atan(d, z) / PI;
float phi = atan(xy.y, xy.x);
myUV.x = d * r * cos(phi) + 0.5 + u_mouse.x;
myUV.y = d * r * sin(phi) + 0.5 + u_mouse.y;
}
vec3 tex = texture2D(tMap, myUV).rgb;
gl_FragColor.rgb = tex;
This is my first shader, so other improvements besides fixing this issue are also welcome.

Compute the vector from the current fragment to the mouse and the length of the vector:
vec2 diff = myUV - u_mouse;
float distance = length(diff);
The new texture coordinate is the sum of the mouse position and the scaled direction vector:
myUV = u_mouse + normalize(diff) * u_radius * f(distance/u_radius);
For instance:
uniform float u_radius;
uniform vec2 u_mouse;
void main()
{
vec2 diff = myUV - u_mouse;
float distance = length(diff);
if (distance <= u_radius)
{
float scale = (1.0 - cos(distance/u_radius * PI * 0.5));
myUV = u_mouse + normalize(diff) * u_radius * scale;
}
vec3 tex = texture2D(tMap, myUV).rgb;
gl_FragColor = vec4(tex, 1.0);
}

Related

How to render a smooth ellipse?

I'm trying to render an ellipse where I can decide how hard the edge is.
(It should also be tileable, i.e. I must be able to split the rendering into multiple textures with a given offset)
I came up with this:
float inEllipseSmooth(vec2 pos, float width, float height, float smoothness, float tiling, vec2 offset)
{
pos = pos / iResolution.xy;
float smoothnessSqr = smoothness * tiling * smoothness * tiling;
vec2 center = -offset + tiling / 2.0;
pos -= center;
float x = (pos.x * pos.x + smoothnessSqr) / (width * width);
float y = (pos.y * pos.y + smoothnessSqr) / (height * height);
float result = (x + y);
return (tiling * tiling) - result;
}
See here (was updated after comment -> now it's how I needed it):
https://www.shadertoy.com/view/ssGBDK
But at the moment it is not possible to get a completely hard edge. It's also smooth if "smoothness" is set to 0.
One idea was "calculating the distance of the position to the center and comparing that to the corresponding radius", but I think there is probably a better solution.
I was not able to find anything online, maybe I'm just searching for the wrong keywords.
Any help would be appreciated.
I don't yet understand what you are trying to accomplish.
Anyway, I have been playing with shadertoy and I have created something that could help you.
I think that smoothstep GLSL function is what you need. And some inner and outer ratio to set the limits of the inner and border.
It is not optimized...
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
int tiling = 4;
float width = 0.5;
float height = 0.2;
float smoothness = 0.9;
float outerRatio = 1.0;
float innerRatio = 0.75;
vec2 offset = vec2(0.25, 0.75);
//offset = iMouse.xy / iResolution.xy;
vec2 center = vec2(0.5, 0.5);
vec2 axis = vec2(width, height);
vec2 pos = float(tiling) * (fragCoord.xy);
pos = mod(pos / iResolution.xy, 1.0);
pos = mod(pos - offset, 1.0);
pos = pos - center;
pos = (pos * pos) / (axis * axis);
float distance = pos.x + pos.y;
float alpha;
if ( distance > outerRatio ) { alpha = 0.0; }
else if ( distance < innerRatio ) { alpha = 1.0; }
else { alpha = smoothstep(outerRatio, innerRatio, distance); }
fragColor = vec4(vec3(alpha), 1.0);
}
Shadertoy multiple ellipses with soft edge and solid inner

glsl strange behavior with frag shader and player movement

im creating a 2d top down game in sfml where i would like the player to only be able to see things in their fov of 45 deg, currently my fragment shader looks like follows
uniform sampler2D texture;
uniform vec2 pos;
uniform vec2 screenSize;
uniform float in_angle;
void main()
{
vec2 fc = gl_FragCoord.xy/screenSize;
vec2 ndcCoords = vec2(0.0);
float fov = radians(45);
ndcCoords = (pos + (screenSize/2))/screenSize;
ndcCoords.y = abs(ndcCoords.y - 1.0);
float angle = radians(-angle+90+45);
float coT;
float siT;
vec2 adj = vec2(0.0);
coT = cos(angle);
siT = sin(angle);
adj.x = coT * ndcCoords.x - siT * ndcCoords.y;
adj.y = siT * ndcCoords.x + coT * ndcCoords.y;
vec2 diff = normalize(ndcCoords - fc);
float dist = acos(dot(diff, normalize(adj)));
vec3 color = vec3(0.0f);
if(dist < fov/2)
{
color = vec3(1.0);
}
gl_FragColor = vec4(color, 1.0) * texture2D(texture, gl_TexCoord[0].xy);
}
what this is doing is adjusting the playerPos vec2 and rotating it, so i can determine what fragcoords are within the players fov, however, when i move down without moving the mouse from directly above the player the fov shifts to the left / right without the player rotating at all, i've tried every solution i can think of but i can't seem to stop it, nor can i find a solution to this online. any suggestions would be appreciated
a solution has arisen, instead of trying to rotate the object to get its vector2 normalised direction, a simpler method is to calculate the angle between a frag and the playerPosition followed by creating a difference by subtracting the player rotation in radians as shown below. this can then be adjusted for the coordinate space and compared to the players fov
void main()
{
float fov = radians(45);
float pi = radians(180);
vec3 color = vec3(0.2);
vec2 st = gl_FragCoord.xy/screenSize.xy;
pos = (pos + (screenSize/2)) / screenSize;
float angleToObj = atan2(st.y - pos.y, st.x - pos.x);
float angleDiff = angleToObj - radians(-angle);
if(angleDiff > pi)
angleDiff -= 2.0f * pi;
if(angleDiff < -pi)
angleDiff += 2.0f * pi;
if(abs(angleDiff) < fov/2)
color = vec3(1.0f);
gl_FragColor = vec4(color, 1.0) * texture2D(texture, gl_TexCoord[0].xy);
}

I need help converting this 2D sky shader to 3D

I found this shader function on github and managed to get it working in GameMaker Studio 2, my current programming suite of choice. However this is a 2D effect that doesn't take into account the camera up vector, nor fov. Is there anyway that can be added into this? I'm only intermediate skill level when it comes to shaders so I'm not sure exactly what route to take, or whether it would even be considered worth it at this point, or if I should start with a different example.
uniform vec3 u_sunPosition;
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
varying vec3 v_vPosition;
#define PI 3.141592
#define iSteps 16
#define jSteps 8
vec2 rsi(vec3 r0, vec3 rd, float sr) {
// ray-sphere intersection that assumes
// the sphere is centered at the origin.
// No intersection when result.x > result.y
float a = dot(rd, rd);
float b = 2.0 * dot(rd, r0);
float c = dot(r0, r0) - (sr * sr);
float d = (b*b) - 4.0*a*c;
if (d < 0.0) return vec2(1e5,-1e5);
return vec2(
(-b - sqrt(d))/(2.0*a),
(-b + sqrt(d))/(2.0*a)
);
}
vec3 atmosphere(vec3 r, vec3 r0, vec3 pSun, float iSun, float rPlanet, float rAtmos, vec3 kRlh, float kMie, float shRlh, float shMie, float g) {
// Normalize the sun and view directions.
pSun = normalize(pSun);
r = normalize(r);
// Calculate the step size of the primary ray.
vec2 p = rsi(r0, r, rAtmos);
if (p.x > p.y) return vec3(0,0,0);
p.y = min(p.y, rsi(r0, r, rPlanet).x);
float iStepSize = (p.y - p.x) / float(iSteps);
// Initialize the primary ray time.
float iTime = 0.0;
// Initialize accumulators for Rayleigh and Mie scattering.
vec3 totalRlh = vec3(0,0,0);
vec3 totalMie = vec3(0,0,0);
// Initialize optical depth accumulators for the primary ray.
float iOdRlh = 0.0;
float iOdMie = 0.0;
// Calculate the Rayleigh and Mie phases.
float mu = dot(r, pSun);
float mumu = mu * mu;
float gg = g * g;
float pRlh = 3.0 / (16.0 * PI) * (1.0 + mumu);
float pp = 1.0 + gg - 2.0 * mu * g;
float pMie = 3.0 / (8.0 * PI) * ((1.0 - gg) * (mumu + 1.0)) / (sign(pp)*pow(abs(pp), 1.5) * (2.0 + gg));
// Sample the primary ray.
for (int i = 0; i < iSteps; i++) {
// Calculate the primary ray sample position.
vec3 iPos = r0 + r * (iTime + iStepSize * 0.5);
// Calculate the height of the sample.
float iHeight = length(iPos) - rPlanet;
// Calculate the optical depth of the Rayleigh and Mie scattering for this step.
float odStepRlh = exp(-iHeight / shRlh) * iStepSize;
float odStepMie = exp(-iHeight / shMie) * iStepSize;
// Accumulate optical depth.
iOdRlh += odStepRlh;
iOdMie += odStepMie;
// Calculate the step size of the secondary ray.
float jStepSize = rsi(iPos, pSun, rAtmos).y / float(jSteps);
// Initialize the secondary ray time.
float jTime = 0.0;
// Initialize optical depth accumulators for the secondary ray.
float jOdRlh = 0.0;
float jOdMie = 0.0;
// Sample the secondary ray.
for (int j = 0; j < jSteps; j++) {
// Calculate the secondary ray sample position.
vec3 jPos = iPos + pSun * (jTime + jStepSize * 0.5);
// Calculate the height of the sample.
float jHeight = length(jPos) - rPlanet;
// Accumulate the optical depth.
jOdRlh += exp(-jHeight / shRlh) * jStepSize;
jOdMie += exp(-jHeight / shMie) * jStepSize;
// Increment the secondary ray time.
jTime += jStepSize;
}
// Calculate attenuation.
vec3 attn = exp(-(kMie * (iOdMie + jOdMie) + kRlh * (iOdRlh + jOdRlh)));
// Accumulate scattering.
totalRlh += odStepRlh * attn;
totalMie += odStepMie * attn;
// Increment the primary ray time.
iTime += iStepSize;
}
// Calculate and return the final color.
return iSun * (pRlh * kRlh * totalRlh + pMie * kMie * totalMie);
}
vec3 ACESFilm( vec3 x )
{
float tA = 2.51;
float tB = 0.03;
float tC = 2.43;
float tD = 0.59;
float tE = 0.14;
return clamp((x*(tA*x+tB))/(x*(tC*x+tD)+tE),0.0,1.0);
}
void main() {
vec3 color = atmosphere(
normalize( v_vPosition ), // normalized ray direction
vec3(0,6372e3,0), // ray origin
u_sunPosition, // position of the sun
22.0, // intensity of the sun
6371e3, // radius of the planet in meters
6471e3, // radius of the atmosphere in meters
vec3(5.5e-6, 13.0e-6, 22.4e-6), // Rayleigh scattering coefficient
21e-6, // Mie scattering coefficient
8e3, // Rayleigh scale height
1.2e3, // Mie scale height
0.758 // Mie preferred scattering direction
);
// Apply exposure.
color = ACESFilm( color );
gl_FragColor = vec4(color, 1.0);
}
However this is a 2D effect that doesn't take into account the camera up vector, nor fov.
If you want to draw a sky in 3D, then you have to draw the on the back plane of the normalized device space. The normalized device space is is a cube with the left, bottom near of (-1, -1, -1) and the right, top, f ar of (1, 1, 1).
The back plane is the quad with:
bottom left: -1, -1, 1
bottom right: 1, -1, 1
top right: -1, -1, 1
top left: -1, -1, 1
Render this quad. Note, the vertex coordinates have not to be transformed by any matrix, because the are normalized device space coordinates. But you have to transform the ray which is used for the sky (the direction which is passed to atmosphere).
This ray has to be a direction in world space, from the camera position to the the sky. By the vertex coordinate of the quad you can get a ray in normalized device space. You have tor transform this ray to world space. The inverse projection matrix (MATRIX_PROJECTION) transforms from normalized devices space to view space and the inverse view matrix (MATRIX_VIEW) transforms form view space to world space. Use this matrices in the vertex shader:
attribute vec3 in_Position;
varying vec3 v_world_ray;
void main()
{
gl_Position = vec4(inPos, 1.0);
vec3 proj_ray = vec3(inverse(gm_Matrices[MATRIX_PROJECTION]) * vec4(inPos.xyz, 1.0));
v_world_ray = vec3(inverse(gm_Matrices[MATRIX_VIEW]) * vec4(proj_ray.xyz, 0.0));
}
In the fragment shader you have to rotate the ray by 90° around the x axis, but that is just caused by the way the ray is interpreted by function atmosphere:
varying vec3 v_world_ray;
// [...]
void main() {
vec3 world_ray = vec3(v_world_ray.x, v_world_ray.z, -v_world_ray.y);
vec3 color = atmosphere(
normalize( world_ray.xyz ), // normalized ray direction
vec3(0,6372e3,0), // ray origin
u_sunPosition, // position of the sun
22.0, // intensity of the sun
6371e3, // radius of the planet in meters
6471e3, // radius of the atmosphere in meters
vec3(5.5e-6, 13.0e-6, 22.4e-6), // Rayleigh scattering coefficient
21e-6, // Mie scattering coefficient
8e3, // Rayleigh scale height
1.2e3, // Mie scale height
0.758 // Mie preferred scattering direction
);
// Apply exposure.
color = ACESFilm( color );
fragColor = vec4(color.rgb, 1.0);
}

World To Screenspace for Mode7 effect (fragment shader)

I have a fragment shader that transforms the view into something resembling mode7.
I want to know the Screen-Space x,y coordinates given a world position.
As the transformation happens in the fragment shader, I can't simply inverse a matrix. This is the fragment shader code:
uniform float Fov; //1.4
uniform float Horizon; //0.6
uniform float Scaling; //0.8
void main() {
vec2 pos = uv.xy - vec2(0.5, Horizon);
vec3 p = vec3(pos.x, pos.y, pos.y + Fov);
vec2 s = vec2(p.x/p.z, p.y/p.z) * Scaling;
s.x += 0.5;
s.y += screenRatio;
gl_FragColor = texture2D(ColorTexture, s);
}
It transforms pixels in a pseudo 3d way:
-
What I want to do is get a screen-space coordinate for a given world position (in normal code, not shaders).
How do I reverse the order of operations above?
This is what I have right now:
(GAME_WIDTH and GAME_HEIGHT are constants and hold pixel values, e.g. 320x240)
vec2 WorldToScreenspace(float x, float y) {
// normalize coordinates 0..1, as x,y are in pixels
x = x/GAME_WIDTH - 0.5;
y = y/GAME_HEIGHT - Horizon;
// as z depends on a y value I have yet to calculate, how can I calc it?
float z = ??;
// invert: vec2 s = vec2(p.x/p.z, p.y/p.z) * Scaling;
float sx = x*z / Scaling;
float sy = y*z / Scaling;
// invert: pos = uv.xy - vec2(0.5, Horizon);
sx += 0.5;
sy += screenRatio;
// convert back to screen space
return new vec2(sx * GAME_WIDTH, sy * GAME_HEIGHT);
}

Procedural Texturing Checkerboard OpenGL

I encounter some difficulties to implement a procedural texture of checkerboard. Here is what I need to get:
Here is what i get:
It's close but my texture is kind of rotated in respect of what I need to get.
Here is the code of my shader:
#version 330
in vec2 uv;
out vec3 color;
uniform sampler1D colormap;
void main() {
float sx = sin(10*3.14*uv.x)/2 + 0.5;
float sy = sin(10*3.14*uv.y)/2 + 0.5;
float s = (sx + sy)/2;
if(true){
color = texture(colormap,s).rgb;
}
colormap is a mapping from 0 to 1, where 0 correspond to red, 1 to green.
I think the problem is coming from the formula i use, (sx+sy)/2 . I need to get the square not rotated but aligned with the border of the big square.
If someone has an idea to get the good formula.
Thanks.
You can add a rotation operation to "uv":
mat2 R(float degrees){
mat2 R = mat2(1);
float alpha = radians(degrees);
R[0][0] = cos(alpha);
R[0][1] = sin(alpha);
R[1][0] = -sin(alpha);
R[1][1] = cos(alpha);
return R;
}
void main(){
ver2 new_uv = R(45) * uv;
float sx = sin(10*3.14*new_uv.x)/2 + 0.5;
float sy = sin(10*3.14*new_uv.y)/2 + 0.5;
float s = (sx + sy)/2;
color = texture(colormap,s).rgb;
}
Maybe something like this:
float sx = sin(10.0 * M_PI * uv.x);
float sy = sin(10.0 * M_PI * uv.y);
float s = sx * sy / 2.0 + 0.5;
Example (without texture):
https://www.shadertoy.com/view/4sdSzn