I'm drawing a rounded rectangle with a GLSL shader using the following pixel shader:
float roundedBoxSDF(vec2 center, vec2 size, float radius) {
return length(max(abs(center) - size + radius, 0.0)) - radius;
}
void main() {
float edgeSoftness = 1.0;
float distance = roundedBoxSDF(v_pos.xy - v_loc - (v_size / 2.0), v_size / 2.0, v_border_radius);
float smoothing = smoothstep(0.0, edgeSoftness * 2.0, distance);
outputColor = mix(vec4(0), v_color, 1.0 - smoothing);
}
and the corner looks like this at the moment:
Notice the black streak. I think it has to do with the vec4(0) in the final line, because if I change that to vec4(1, 1, 1, 0), then I get the following result:
Even more unsightly. What am I doing wrong? If I change edgeSoftness to 0.0, then it looks decent:
But it doesn't have any anti-aliasing. Is there any way to have an anti-aliased rounded rectangle without the unsightly edges? I'm not sure what I'm doing wrong.
The issue you see is because you are interpolating from solid base color to transparent black. Now, for the pixels in-between the interpolation, that are not yet fully transparent, the base color will still be slightly shifted towards black and you will get the results you see. What you likely want to do is keep the RGB values for the base color, and only change the alpha channel.
outputColor = vec4(v_color.rgb, mix(v_color.a, 0., smoothing));
Related
I'm trying to create a shader which can crop or expand a texture (along the x axis)
so far this is what I've come up with on the cropping part:
shader_type canvas_item;
uniform float start_x:hint_range(0.0, 1.0, 0.001) = 0.0;
uniform float end_x:hint_range(0.0, 1.0, 0.001) = 1.0;
void fragment()
{
if(UV.x>start_x && UV.x<end_x)
COLOR = texture(TEXTURE,UV);
else
COLOR = vec4(0.0,0.0,0.0,0.0);
}
But I have no clue how work on the expand part
What do I mean by expanding?
I'm actually trying to apply this shader to the AnimatedSprite node to create a pseudo
"Region" effect like in a Sprite node
So by expanding I mean the sprite equivalent of setting the texture on mirrored repeat and increasing it's region_rect
So if I'd give the shader parameters start_x=0.0 & end_x=1.5 it would display the entire animatedsprite + 1/2 (as in the above diagram but with animated sprites instead)
What you need to do is transform your UVs from their default range (0 to 1) to your desired range (start_x to end_x).
You can do that in the shader with something like the following:
UV.x = (UV.x * (end_x - start_x)) + start_x;
I made a plane in THREEjs using Mesh, PlaneGeometry and ShaderMaterial. It's a very simple/basic form.
I applied a simple phormula to make the plain more steep. Now I'm trying to make the lower surface darker than the higher surface. Here is what I tried.
Vertex shader:
varying vec3 test;
void main(void) {
float amp = 2.5;
float z = amp * sin(position.x*0.2) * cos(position.y*0.5); //this makes the surface steeper
test = vec3(1, 1, -z); //this goes to fragment shader
//test = vec3(698.0, 400.0, -z); I have tried this. first coordenates here are to normalize the vector
gl_Position = projectionMatrix * modelViewMatrix * vec4(position.x, position.y, z, 1.0);
}
Fragment shader:
precision mediump float;
varying vec3 test;
void main(void) {
vec3 st = gl_FragCoord.xyz/test;
gl_FragColor = vec4(st.xyz, 1.0);
}
Result:
This result is not desirable, since the contrast between top and down is too aggressive and I'd like the lower surface less white. What do I have to change to accomplish this?
If you want to create a brightness based on the height of the waves, then you'll need to only use the test.z value, since test.xy aren't really doing anything. The problem is that brightness needs a value between [0, 1] and due to the amplitude multiplication, you're getting a value between [-2.5, 2.5] range.
precision mediump float;
varying vec3 test;
void main(void) {
float amp = 2.5;
// Extract brightness from test.z
float brightness = test.z;
// Convert brightness from [-2.5, 2.5] to [0.0, 1.0] range
brightness = (brightness / amp) * 0.5 + 0.5;
vec3 yellow = vec3(1.0, 1.0, 0.0);
// Multiply final color by brigthness (0 brightness = black)
vec3 st = yellow * brightness;
gl_FragColor = vec4(st.xyz, 1.0);
}
That should give you a smoother transition from full yellow to black.
As an aside, to help me visualize the values I'm getting from GLSL functions, I like to use the Graphtoy tool. I recommend you give it a shot to help you write shaders!
I'm trying to get into Shaders and decided to init a project using Rust and Bevy, the objective is to reproduce a raymarching shader just to confirm that the environment is ok, i was able to reproduce the "fragCoord" by using:
var fragCoord: vec2<f32> = vec2<f32>((input.uv.x+1.0) * iResolution.res.x, (input.uv.y+1.0) * iResolution.res.y);
//iResolution.res is the screen res in pixels
up to this point everything is ok, when trying to reproduce BigWing's example i notice a difference in the result imagine when passing only the follow line:
vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y;
//where fragCoord is the pixel position of the frag and the iResolution is the screen size in pixels
shader result image
I suspected about fragCoord, but after a check, it give the same result as shadertoy's version, but after trying to check iResolution I noticed a big difference, then did a test with fixed output color values and got this, as you can see, the color is not the same:
Result of using the same values for the shader
I used different browser too but got the same result :(, i suspect now of my camera/mesh code:
//camera
fn spawn_camera(mut commands: Commands) {
let mut camera = OrthographicCameraBundle::new_2d();
camera.orthographic_projection.right = 0.0;
camera.orthographic_projection.left = 1.0 ;
camera.orthographic_projection.top = 0.0;
camera.orthographic_projection.bottom = 1.0;
camera.orthographic_projection.scaling_mode = ScalingMode::None;
commands.spawn_bundle(camera);
}
//mesh to display the frag shader
let ZOOM = 1.0;
let vertices = [
([-1.0,-1.0,0.0] /*pos*/, [0.0,0.0,0.0] /*normal*/, [1.0 / ZOOM, 1.0 / ZOOM] /*uv*/), //bottom left
([-1.0,1.0,0.0], [0.0, 0.0, 0.0], [1.0 / ZOOM, -1.0 / ZOOM]), //top left
([1.0,1.0,0.0], [0.0, 0.0, 0.0], [-1.0 / ZOOM, -1.0 / ZOOM]), //top right
([1.0,-1.0,0.0], [0.0, 0.0, 0.0], [-1.0 / ZOOM, 1.0 / ZOOM]), //bottom right];
let indices = Indices::U32(vec![ 0, 3, 2,0, 2, 1]);
My main question here is, how i can reproduce the exact environment of shadertoy using Rust and Bevy? If it's not possible please show me an alternative.
I'm just trying to use the fragment shader, I don't need to show anything besides the actual fragment shader result.
Kevin Reid is correct. The default color space for is sRGB:
How to specify color space for canvas in JavaScript?
You can get the expected result by transforming your colors from linear color space to sRGB, like it is posted here:
https://www.shadertoy.com/view/Wd2yRt
Which will make your code look like this:
vec3 lin2srgb( vec3 cl )
{
vec3 c_lo = 12.92 * cl;
vec3 c_hi = 1.055 * pow(cl,vec3(0.41666)) - 0.055;
vec3 s = step( vec3(0.0031308), cl);
return mix( c_lo, c_hi, s );
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (fragCoord-.5*iResolution.xy)/iResolution.y;
vec3 c = vec3( lin2srgb( vec3(uv.xy, 0.0) ) );
fragColor = vec4(c,1.0);
}
Making you end up with:
So, to finally answer your question:
To reproduce the shadertoy's environment, you need to use the sRGB color space in Rust.
Alternative: Just use the transformation to sRGB in shadertoy.
I have a Three js scene that contains a 100x100 plane centred at the origin (ie. min coord: (-50,-50), max coord: (50,50)). I am trying to have the plane appear as a colour wheel by using the x and z coords in a custom glsl shader. Using this guide (see HSB in polar coordinates, towards the bottom of the page) I have gotten my
Shader Code with Three.js Scene
but it is not quite right.
I have played around tweaking all the variables that make sense to me, but as you can see in the screenshot the colours change twice as often as what they should. My math intuition says just divide the angle by 2 but when I tried that it was completely incorrect.
I know the solution is very simple but I have tried for a couple hours and I haven't got it.
How do I turn my shader that I currently have into one that makes exactly 1 full colour rotation in 2pi radians?
EDIT: here is the relevant shader code in plain text
varying vec3 vColor;
const float PI = 3.1415926535897932384626433832795;
uniform float delta;
uniform float scale;
uniform float size;
vec3 hsb2rgb( in vec3 c ){
vec3 rgb = clamp(abs(mod(c.x*6.0+vec3(0.0,4.0,2.0),
6.0)-3.0)-1.0,
0.0,
1.0 );
rgb = rgb*rgb*(3.0-2.0*rgb);
return c.z * mix( vec3(1.0), rgb, c.y);
}
void main()
{
vec4 worldPosition = modelMatrix * vec4(position, 1.0);
float r = 0.875;
float g = 0.875;
float b = 0.875;
if (worldPosition.y > 0.06 || worldPosition.y < -0.06) {
vec2 toCenter = vec2(0.5) - vec2((worldPosition.z+50.0)/100.0, (worldPosition.x+50.0)/100.0);
float angle = atan(worldPosition.z/worldPosition.x);
float radius = length(toCenter) * 2.0;
vColor = hsb2rgb(vec3((angle/(PI))+0.5,radius,1.0));
} else {
vColor = vec3(r,g,b);
}
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
gl_PointSize = size * (scale/length(mvPosition.xyz));
gl_Position = projectionMatrix * mvPosition;
}
I have discovered that the guide I was following was incorrect. I wasn't thinking about my math properly but I now know what the problem was.
atan has a range from -PI/2 to PI/2 which only accounts for half of a circle. When worldPosition.x is negative atan will not return the correct angle since it is out of range of the function. The angle needs to be adjusted based on what quadrant it is in the plane.
Q1: do nothing
Q2: add PI to the angle
Q3: add PI to the angle
Q4: add 2PI to the angle
After this normalize the angle (divide by 2PI) then pass it to the hsb2rgb function.
I am drawing a circle using two triangles and the fragment shader. I can get a nice looking circle to draw in the middle of the square formed by the triangles, but for some reason the circle is surrounded by the remaining white triangles and is not transparent. I want the edges to be transparent, so that two circles would overlay each other nicely.
Here is my fragment shader code:
void main() {
float diff = 1.0 / vscale.x;
float dist = distance(center, origin);
float step = smoothstep(radius - diff, radius + diff, dist);
gl_FragColor = mix(vec4(1.0, 0.0, 0.0, 1.0), vec4(1.0, 0.0, 0.0, 0.0), step);
}
as #andon-m-coleman said, i had a lack of blending between fragments. i didnt know i had to set a blendfunc. thanks!
EDIT: that is to say, i did not set a blend function, like this:
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);