I'm trying to draw pixel-perfect lines in OpenGL using a fragment shader. I have the following fragment shader which I got from this question. However the result is 2-pixels in size rather than 1 pixel.
#version 330
layout(location=0) out vec4 frag_colour;
in vec4 color;
uniform vec2 positionA;
uniform vec2 positionB;
uniform int thickness;
float lineSegment(vec2 p, vec2 a, vec2 b, float thickness)
{
vec2 pa = p - a;
vec2 ba = b - a;
float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
float k = length(pa - ba*h);
return smoothstep(0.0, thickness, k);
}
void main()
{
vec2 uv = gl_FragCoord.xy;
frag_colour = mix(color,vec4(0),lineSegment(uv,positionA,positionB,thickness));
}
As I understand it, this is because wherever the "line" is, the pixels to either side are technically the same distance away, so if I try to remove any kind of anti-aliasing, they are both "on the line" and so my 1-pixel wide line ends up being 2 pixels.
You can see how the yellow horizontal line is 2-pixels wide (compare it to the individual pixels in the text).
I've tried some other fragment code here and there from the internet and they all feature this same problem.
How can I draw a pixel-perfect, aliased (not anti-aliased), line in GLSL?
EDIT 1
After adjusting the positions and thickness by 0.5f, so move to the centre of the pixels, the diagonal lines have anti-aliasing on each sort of "step".
Diagonal Line:
Perhaps this would be helpful (tested in shadertoy):
bool lineSegment(vec2 p, vec2 a, vec2 b, float thickness)
{
vec2 pa = p - a;
vec2 ba = b - a;
float len = length(ba);
vec2 dir = ba / len;
float t = dot(pa, dir);
vec2 n = vec2(-dir.y, dir.x);
float factor = max(abs(n.x), abs(n.y));
float distThreshold = (thickness - 1.0 + factor)*0.5;
float proj = dot(n, pa);
return (t > 0.0) && (t < len) && (proj <= distThreshold) && (proj > -distThreshold);
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 positionA = iResolution.xy * 0.5;
vec2 positionB = iMouse.xy;
float thickness = 1.0;
fragColor = mix(vec4(0), vec4(1), float(lineSegment(fragCoord,positionA,positionB,thickness)));
}
Just in case, here's a related question on math.stackexchange.
Related
I want to draw a smooth circle in GLSL but with a border of variable width, in a separate colour. Potentially, the interior of the circle could be transparent.
My original non-smooth shader:
#version 330
layout(location=0) out vec4 frag_colour;
in vec4 color;
uniform float radius;
uniform vec2 position;
uniform vec4 borderColor;
uniform float borderThickness;
void main()
{
float distanceX = abs(gl_FragCoord.x - position.x);
float distanceY = abs(gl_FragCoord.y - position.y);
if(sqrt(distanceX * distanceX + distanceY * distanceY) > radius)
discard;
else if(sqrt(distanceX * distanceX + distanceY * distanceY) <= radius &&
sqrt(distanceX * distanceX + distanceY * distanceY) >= radius-borderThickness)
frag_colour = borderColor;
else
frag_colour = color;
}
This works, but is not smooth. I can draw smooth circles:
#version 330
layout(location=0) out vec4 frag_colour;
in vec4 color;
uniform float radius;
uniform vec2 position;
uniform vec4 borderColor;
uniform float borderThickness;
void main()
{
vec2 uv = gl_FragCoord.xy - position;
float d = sqrt(dot(uv,uv));
float t = 1.0 - smoothstep(radius-borderThickness,radius, d);
frag_colour = vec4(color.rgb,color.a*t);
}
But I can't work out how to add my border to the above.
You have to compute the absolut value of the difference between the radius and the distance and interpolate between 0.0 and borderThickness:
float t = 1.0 - smoothstep(0.0, borderThickness, abs(radius-d));
If you want to fill the circle, then you need 2 gradients. 1 for the transition between the inner circle and the border and a 2nd one for the alpha channel on the outline. mix the colors by the former and set the alpha channel by the later:
float t1 = 1.0 - smoothstep(radius-borderThickness, radius, d);
float t2 = 1.0 - smoothstep(radius, radius+borderThickness, d);
frag_colour = vec4(mix(color.rgb, baseColor.rgb, t1), t2);
I have coded a fragment shader in vizard IDE and its not working. The code is free of compilation errors except for one which says, " ERROR: 0:? : 'variable' : is not available in current GLSL version gl_TexCoord."
FYI the gl_TexCoord is the output of the vertex shader which is in build to vizard. Can someone help me to fix it. here is the code:
#version 440
// All uniforms as provided by Vizard
uniform sampler2D vizpp_InputDepthTex; // Depth texture
uniform sampler2D vizpp_InputTex; // Color texture
uniform ivec2 vizpp_InputSize; // Render size of screen in pixels
uniform ivec2 vizpp_InputPixelSize; // Pixel size (1.0/vizpp_InputSize)
uniform mat4 osg_ViewMatrix; // View matrix of camera
uniform mat4 osg_ViewMatrixInverse; // Inverse of view matrix
// Your own uniforms
//uniform sampler2D u_texture;
//uniform sampler2D u_normalTexture;
uniform sampler2D g_FinalSSAO;
const bool onlyAO = false; //Only show AO pass for debugging
const bool externalBlur = false; //Store AO in alpha slot for a later blur
struct ASSAOConstants
{
vec2 ViewportPixelSize; // .zw == 1.0 / ViewportSize.xy
vec2 HalfViewportPixelSize; // .zw == 1.0 / ViewportHalfSize.xy
vec2 DepthUnpackConsts;
vec2 CameraTanHalfFOV;
vec2 NDCToViewMul;
vec2 NDCToViewAdd;
ivec2 PerPassFullResCoordOffset;
vec2 PerPassFullResUVOffset;
vec2 Viewport2xPixelSize;
vec2 Viewport2xPixelSize_x_025; // Viewport2xPixelSize * 0.25 (for fusing add+mul into mad)
float EffectRadius; // world (viewspace) maximum size of the shadow
float EffectShadowStrength; // global strength of the effect (0 - 5)
float EffectShadowPow;
float EffectShadowClamp;
float EffectFadeOutMul; // effect fade out from distance (ex. 25)
float EffectFadeOutAdd; // effect fade out to distance (ex. 100)
float EffectHorizonAngleThreshold; // limit errors on slopes and caused by insufficient geometry tessellation (0.05 to 0.5)
float EffectSamplingRadiusNearLimitRec; // if viewspace pixel closer than this, don't enlarge shadow sampling radius anymore (makes no sense to grow beyond some distance, not enough samples to cover everything, so just limit the shadow growth; could be SSAOSettingsFadeOutFrom * 0.1 or less)
float DepthPrecisionOffsetMod;
float NegRecEffectRadius; // -1.0 / EffectRadius
float LoadCounterAvgDiv; // 1.0 / ( halfDepthMip[SSAO_DEPTH_MIP_LEVELS-1].sizeX * halfDepthMip[SSAO_DEPTH_MIP_LEVELS-1].sizeY )
float AdaptiveSampleCountLimit;
float InvSharpness;
int PassIndex;
vec2 QuarterResPixelSize; // used for importance map only
vec4 PatternRotScaleMatrices[5];
float NormalsUnpackMul;
float NormalsUnpackAdd;
float DetailAOStrength;
float Dummy0;
mat4 NormalsWorldToViewspaceMatrix;
};
uniform ASSAOConstants g_ASSAOConsts;
float PSApply( in vec4 inPos, in vec2 inUV)
{ //inPos = gl_FragCoord;
float ao;
uvec2 pixPos = uvec2(inPos.xy);
uvec2 pixPosHalf = pixPos / uvec2(2, 2);
// calculate index in the four deinterleaved source array texture
int mx = int (pixPos.x % 2);
int my = int (pixPos.y % 2);
int ic = mx + my * 2; // center index
int ih = (1-mx) + my * 2; // neighbouring, horizontal
int iv = mx + (1-my) * 2; // neighbouring, vertical
int id = (1-mx) + (1-my)*2; // diagonal
vec2 centerVal = texelFetchOffset( g_FinalSSAO, ivec2(pixPosHalf), 0, ivec2(ic, 0 ) ).xy;
ao = centerVal.x;
if (true){ // change to 0 if you want to disable last pass high-res blur (for debugging purposes, etc.)
vec4 edgesLRTB = UnpackEdges( centerVal.y );
// convert index shifts to sampling offsets
float fmx = mx;
float fmy = my;
// in case of an edge, push sampling offsets away from the edge (towards pixel center)
float fmxe = (edgesLRTB.y - edgesLRTB.x);
float fmye = (edgesLRTB.w - edgesLRTB.z);
// calculate final sampling offsets and sample using bilinear filter
vec2 uvH = (inPos.xy + vec2( fmx + fmxe - 0.5, 0.5 - fmy ) ) * 0.5 * g_ASSAOConsts.HalfViewportPixelSize;
float aoH = textureLodOffset( g_FinalSSAO, uvH, 0, ivec2(ih , 0) ).x;
vec2 uvV = (inPos.xy + vec2( 0.5 - fmx, fmy - 0.5 + fmye ) ) * 0.5 * g_ASSAOConsts.HalfViewportPixelSize;
float aoV = textureLodOffset( g_FinalSSAO, uvV, 0, ivec2( iv , 0) ).x;
vec2 uvD = (inPos.xy + vec2( fmx - 0.5 + fmxe, fmy - 0.5 + fmye ) ) * 0.5 * g_ASSAOConsts.HalfViewportPixelSize;
float aoD = textureLodOffset( g_FinalSSAO, uvD, 0, ivec2( id , 0) ).x;
// reduce weight for samples near edge - if the edge is on both sides, weight goes to 0
vec4 blendWeights;
blendWeights.x = 1.0;
blendWeights.y = (edgesLRTB.x + edgesLRTB.y) * 0.5;
blendWeights.z = (edgesLRTB.z + edgesLRTB.w) * 0.5;
blendWeights.w = (blendWeights.y + blendWeights.z) * 0.5;
// calculate weighted average
float blendWeightsSum = dot( blendWeights, vec4( 1.0, 1.0, 1.0, 1.0 ) );
ao = dot( vec4( ao, aoH, aoV, aoD ), blendWeights ) / blendWeightsSum;
}
return ao;
}
void main(void)
{
// Get base values
vec2 texCoord = gl_TexCoord[0].st;
vec4 color = texture2D(vizpp_InputTex,texCoord);
float depth = texture2D(vizpp_InputDepthTex,texCoord).x;
// Do not calculate if nothing visible (for VR for instance)
if (depth>=1.0)
{
gl_FragColor = color;
return;
}
float ao = PSApply(gl_FragCoord, texCoord);
// Output the result
if(externalBlur) {
gl_FragColor.rgb = color.rgb;
gl_FragColor.a = ao;
}
else if(onlyAO) {
gl_FragColor.rgb = vec3(ao,ao,ao);
gl_FragColor.a = 1.0;
}
else {
gl_FragColor.rgb = ao*color.rgb;
gl_FragColor.a = 1.0;
}
}
gl_TexCoord is a deprecated Compatibility Profile Built-In Language Variables and is removed after GLSL Version 1.20.
If you want to use gl_TexCoord then you would have to use GLSL version 1.20 (#version 120).
But, you don't need the deprecated compatibility profile built-in language variable at all. Define a Vertex shader output texCoord and use this output rather than gl_TexCoord:
#version 440
out vec2 texCoord;
void main()
{
texCoord = ...;
// [...]
}
Specify a corresponding input in the fragment shader:
#version 440
in vec2 texCoord;
void main()
{
vec4 color = texture2D(vizpp_InputTex, texCoord.st);
// [...]
}
I'd like my faded lighting (based on distance from a point) to be a perfect circle no matter the resolution. Currently, the light is only a circle if the height and width of the window are equal.
This is what it looks like right now:
My fragment shader looks like this:
precision mediump float;
#endif
#define MAX_LIGHTS 10
// varying input variables from our vertex shader
varying vec4 v_color;
varying vec2 v_texCoords;
// a special uniform for textures
uniform sampler2D u_texture;
uniform vec2 u_resolution;
uniform float u_time;
uniform vec2 lightsPos[MAX_LIGHTS];
uniform vec3 lightsColor[MAX_LIGHTS];
uniform float lightsSize[MAX_LIGHTS];
uniform vec2 cam;
uniform vec2 randPos;
uniform bool dark;
void main()
{
vec4 lights = vec4(0.0,0.0,0.0,1.0);
float ratio = u_resolution.x/u_resolution.y;
vec2 st = gl_FragCoord.xy/u_resolution;
vec2 loc = vec2(.5 + randPos.x, 0.5 + randPos.y);
for(int i = 0; i < MAX_LIGHTS; i++)
{
if(lightsSize[i] != 0.0)
{
// trying to reshape the light
// vec2 st2 = st;
// st2.x *= ratio;
float size = 2.0/lightsSize[i];
float dist = max(0.0, distance(lightsPos[i], st)); // st here was replaced with st2 when experimenting
lights = lights + vec4(max(0.0, lightsColor[i].x - size * dist), max(0.0, lightsColor[i].y - size * dist), max(0.0, lightsColor[i].z - size * dist), 0.0);
}
}
if(dark)
{
lights.r = max(lights.r, 0.075);
lights.g = max(lights.g, 0.075);
lights.b = max(lights.b, 0.075);
}
else
{
lights.r += 1.0;
lights.g += 1.0;
lights.b += 1.0;
}
gl_FragColor = texture2D(u_texture, v_texCoords) * lights;
}
I tried reshaping the light by multiplying the x value of the pixel by the ratio of the screen width to the screen height but that caused the lights to be out of place. I couldn't figure out anything that would put them back in their correct place while maintaining their shape.
EDIT: the displacement is determined by my camera's position in my libgdx scene.
what you need is to rescale the difference between light position and fragment position
vec2 dr = st-lightsPos[i];
dr.x*=ratio;
float dist = length(dr);
Try normalizing st like that
vec2 st = (gl_FragCoord.xy - .5*u_resolution.xy) / min(u_resolution.x, u_resolution.y);
So that coordinates of your fragments are in range [-1; 1] for y and [-ratio/2; ratio/2] where ratio = u_resolution.x/u_resolution.y
You can also make it [0; 1] for y and [0; ratio] for x by doing
vec2 st = gl_FragCoord.xy / min(u_resolution.x, u_resolution.y);
But the former is more convenient in many cases
I have some code I wrote in the book of shaders editor:
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution;
uniform vec2 u_mouse;
uniform float u_time;
float remap(float a, float b, float c, float d, float t) {
return ((t - a) / (b - a)) * (d-c) + c;
}
float outline(vec2 st) {
return smoothstep(0.99, 1.0, st.y) + smoothstep(0.99, 1.0, st.x) + smoothstep(0.01, 0.0, st.y) + smoothstep(0.01, 0.0, st.x);
}
float mouseFoo(vec2 scaledSt, vec2 u_mouse, float scaleVal) {
vec2 scaledMouse = u_mouse * scaleVal;
if(scaledSt.x < ceil(scaledMouse.x) && scaledSt.x > floor(scaledMouse.x) && scaledSt.y < ceil(scaledMouse.y) && scaledSt.y > floor(scaledMouse.y)) {
// if(u_mouse.x < 100.0) {
return 1.0;
} else {
return 0.0;
}
}
void main(){
vec2 st = gl_FragCoord.xy/u_resolution.xy;
vec3 color = vec3(0.03,0.07,0.15);
vec3 redColor = vec3(1.0, 0.0, 0.0);
vec3 outlineColor = vec3(1.0);
float floorSt;
float scaleVal = 5.0;
vec2 scaledSt = st * scaleVal;
// tile
st *= scaleVal;
floorSt = floor(st.x);
st = fract(st);
// inner color
color = mix(color, redColor, mouseFoo(scaledSt, u_mouse/u_resolution.xy, scaleVal));
// outline
color = mix(color, outlineColor, outline(st));
gl_FragColor = vec4(color, 1.0 );
}
I'm wondering if it's possible to have the red color tween to the blue color when a box is hovered off? I think I might have an idea of how to do it if I were to write data to a texture and look that up, but even then I'm not entirely sure.
Use mix
Use mix to interpolate between red and blue. You need another variable that transitions from 0-1 to do the blend, which is mix's 3rd parameter.
ShaderToy example:
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = fragCoord/iResolution.xy;
vec3 red = vec3(1,0,0);
vec3 blue = vec3(0,0,1);
// Output to screen
fragColor = vec4(mix(red, blue, uv.x),1.0);
}
which produces:
In your case, you'll want the 3rd parameter (the alpha or lerp parameter) to be driven over some time (say, .2 seconds) after the mouse entered the hover area. You'll need to do one of the following:
Detect the hover entered at a higher level and then pass the mouse down time in as a uniform
Drive the 3rd parameter directly from a uniform
I'm searching for a way to interpolate / smooth a texture which is red from a texture atlas.
That problem is that if i set the MIN_FILTER to GL_LINEAR i'll get bleeding at the edges. (the pixel of the next texture are used for interpolation)
I tried solving this by writing a shader which does the interpolation manually but to detect which pixels it has to ignore, the shader has to know the texCoords of the vertices so that it can calculate if the pixel used is in or outside of the triangle. To get the texCoords into the shader i tried to use uniform but i use DisplayLists (i considered switching to VBOs, but my first implemetation used them and it was extremly slow) which makes it very complicated.
How can i smooth my textures without bleeding?
Another problem is that the method that checks if a point is in a triangle doesn't seem to work.
Vertex Shader:
#version 130
void main()
{
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
gl_FrontColor = gl_Color;
gl_Position = ftransform();
}
Fragement Shader:
#version 130
uniform sampler2D tex;
uniform vec2 texCoord[4];
float textureSize;
float texelSize;
bool pointInTriangle(vec3 P, vec3 A, vec3 B, vec3 C)
{
vec3 u = B - A;
vec3 v = C - A;
vec3 w = P - A;
vec3 vCrossW = cross(v, w);
vec3 vCrossU = cross(v, u);
if(dot(vCrossW, vCrossU) < 0)
{
return false;
}
vec3 uCrossW = cross(u, w);
vec3 uCrossV = cross(u, v);
if(dot(uCrossW, uCrossV) < 0)
{
return false;
}
float denom = length(uCrossV);
float r = length(vCrossW);
float t = length(uCrossW);
return (r + t <= 1);
}
vec4 texture2DBilinear(sampler2D textureSampler, vec2 uv)
{
vec4 tl = texture2D(textureSampler, uv);
vec4 tr = texture2D(textureSampler, uv + vec2(texelSize, 0));
vec4 bl = texture2D(textureSampler, uv + vec2(0, texelSize));
vec4 br = texture2D(textureSampler, uv + vec2(texelSize , texelSize));
vec2 f = fract( uv.xy * textureSize );
vec4 tA = mix( tl, tr, f.x );
vec4 tB = mix( bl, br, f.x );
return mix( tA, tB, f.y );
}
void main()
{
ivec2 textureSize2d = textureSize(tex,0);
textureSize = float(textureSize2d.x);
texelSize = 1.0 / textureSize;
//texture coordinate:
vec2 texCoord = (gl_TexCoord[0].st);
//vec4 color = texture2D(tex,gl_TexCoord[0].st);
gl_FragColor = texture2DBilinear(tex, texCoord) * gl_Color;
//TEST IF METHOD WORKS
if(pointInTriangle(vec3(1,1,0),vec3(0,0,0),vec3(3,0,0),vec3(0,3,0)))
{
//pointInTriangle DOES NOT WORK!
gl_FragColor = gl_Color;
}
}