Blur frame buffer content - opengl-es

Can I blur not a texture but the frame buffer? The following shader blurs a texture:
#ifdef GL_ES
precision mediump float;
#endif
varying vec4 v_fragmentColor;
varying vec2 v_texCoord;
uniform vec2 resolution;
uniform float blurRadius;
uniform float sampleNum;
vec3 blur(vec2);
void main(void)
{
vec3 col = blur(v_texCoord);
gl_FragColor = vec4(col, 1.0) * v_fragmentColor;
}
vec3 blur(vec2 p)
{
if (blurRadius > 0.0 && sampleNum > 1.0)
{
vec3 col = vec3(0);
vec2 unit = 1.0 / resolution.xy;
float r = blurRadius;
float sampleStep = r / sampleNum;
float count = 0.0;
for(float x = -r; x < r; x += sampleStep)
{
for(float y = -r; y < r; y += sampleStep)
{
float weight = (r - abs(x)) * (r - abs(y));
col += texture2D(CC_Texture0, p + vec2(x * unit.x, y * unit.y)).rgb * weight;
count += weight;
}
}
return col / count;
}
return texture2D(CC_Texture0, p).rgb;
}
How can I blur not a texture pixels, but Frame Buffer pixels that is already drawn?

You can apply that blur shader to texture that you've attached to the framebuffer as drawing to the framebuffer writes the pixel data to the selected colour attachment.
This page provides info on one way that it can be done by drawing the framebuffers colour attachment to the screen with a shader. It's in opengl 3.3 though but all the functions used should exist in opengl es 2.0.

Related

Color a set of fragments in fragment shader

My goal is to color a set of fragments determined by the interpolation between two points. Below the code I've written, It didn't work.!.
I also added some comments, Probably there are some mistakes I made or something that I misunderstood.
Thank you for your help.
#ifdef GL_ES
precision highp float;
#endif
uniform vec2 u_resolution;
void main(){
vec2 position = gl_FragCoord.xy / u_resolution;
vec4 color = vec4(0.97, 0.1, 0.53, 1.0);
// center (hopefully)
vec2 P1 = vec2(0.0,0.0);
// top right
vec2 P2 = vec2(1.0,1.0);
// generate 100 points between P1...P2
for(float i = 0.0; i < 1.0; i+=0.01) {
float lerpX = mix(P1.x, P2.x, i);
float lerpY = mix(P1.y, P2.y, i);
vec2 interpolatedPoint = vec2(lerpX, lerpY);
// check if current fragment is one of the
// interpolated points and color it
if (position.x == interpolatedPoint.x) {
gl_FragColor = color;
} else {
discard;
}
}
}
WORKING SOLUTION
#ifdef GL_ES
precision highp float;
#endif
uniform vec2 u_resolution;
void main(){
vec2 position = gl_FragCoord.xy / u_resolution;
vec4 color = vec4(0.97, 0.1, 0.53, 1.0);
// center (hopefully)
vec2 P1 = vec2(0.0,0.0);
// top right
vec2 P2 = vec2(1.0,1.0);
// generate 100 points between P1...P2
for(float i = 0.0; i < 1.0; i+=0.01) {
float lerpX = mix(P1.x, P2.x, i);
float lerpY = mix(P1.y, P2.y, i);
vec2 interpolatedPoint = vec2(lerpX, lerpY);
// check if current fragment is one of the
// interpolated points and color it
if (distance(position, interpolatedPoint) <= 0.01) {
gl_FragColor = color;
}
}
}
position.x == interpolatedPoint.x is a floating point comparison and is almost never evaluated as true. Your code discards all fragments. Implement a floating point comparison. Calculate the absolute value of the difference between the two values and compare it with an epsilon:
if (distance(position, interpolatedPoint) <= 0.01) {
gl_FragColor = color;
}

Change color in GLSL shaders

I am trying to modify the color of a 3d model (three.js) which uses GLSL shaders (frag and vert files). To be honest I am not experienced at all with shader language.
.frag file
precision highp float;
uniform sampler2D uTexture;
varying vec2 vPUv;
varying vec2 vUv;
void main() {
vec4 color = vec4(0.0);
vec2 uv = vUv;
vec2 puv = vPUv;
// pixel color
vec4 colA = texture2D(uTexture, puv);
// greyscale
float grey = colA.r * 0.31 + colA.g * 0.71 + colA.b * 0.07;
vec4 colB = vec4(grey, grey, grey, 1.0);
// circle
float border = 0.3;
float radius = 0.5;
float dist = radius - distance(uv, vec2(0.5));
float t = smoothstep(0.0, border, dist);
// final color
color = colB;
color.a = t;
gl_FragColor = color;
}
.vert file
precision highp float;
attribute float pindex;
attribute vec3 position;
attribute vec3 offset;
attribute vec2 uv;
attribute float angle;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform float uTime;
uniform float uRandom;
uniform float uDepth;
uniform float uSize;
uniform vec2 uTextureSize;
uniform sampler2D uTexture;
uniform sampler2D uTouch;
varying vec2 vPUv;
varying vec2 vUv;
#pragma glslify: snoise2 = require(glsl-noise/simplex/2d)
float random(float n) {
return fract(sin(n) * 43758.5453123);
}
void main() {
vUv = uv;
// particle uv
vec2 puv = offset.xy / uTextureSize;
vPUv = puv;
// pixel color
vec4 colA = texture2D(uTexture, puv);
float grey = colA.r * 0.21 + colA.g * 0.71 + colA.b * 0.07;
// displacement
vec3 displaced = offset;
// randomise
displaced.xy += vec2(random(pindex) - 0.5, random(offset.x + pindex) - 0.5) * uRandom;
float rndz = (random(pindex) + snoise_1_2(vec2(pindex * 0.1, uTime * 0.1)));
displaced.z += rndz * (random(pindex) * 2.0 * uDepth);
// center
displaced.xy -= uTextureSize * 0.5;
// touch
float t = texture2D(uTouch, puv).r;
displaced.z += t * 20.0 * rndz;
displaced.x += cos(angle) * t * 20.0 * rndz;
displaced.y += sin(angle) * t * 20.0 * rndz;
// particle size
float psize = (snoise_1_2(vec2(uTime, pindex) * 0.5) + 2.0);
psize *= max(grey, 0.2);
psize *= uSize;
// final position
vec4 mvPosition = modelViewMatrix * vec4(displaced, 1.0);
mvPosition.xyz += position * psize;
vec4 finalPosition = projectionMatrix * mvPosition;
gl_Position = finalPosition;
}
This creates particles from a very dark grey tone up to white like you see in the example. I would like to change only the color of the very dark tones to match the background color. I've played with some color values I've found here but unfortunately the results are not what I expected.
Maybe somebody has a quick hint for me?
I would like to change only the color of the very dark tones to match the background color.
Actually you create a gray scale color. But what you actually want is that the "dark tones to match the background" and the light tones are white. Hence you want a gradient from the background to white.
Either blend a white color with the back ground dependent on the grayscale:
void main()
{
vec4 color = vec4(0.0);
// [...]
// final color
color.rgb = vec3(1.0);
color.a = t * grey;
gl_FragColor = color;
}
You have to mix the back ground color and white dependent on the gray scale. For this you have to know the background color in the fragment shader:
void main()
{
vec4 color = vec4(0.0);
vec3 backgroundColor = vec3(42.0, 67.0, 101.0) / 255.0;
// [...]
// final color
color.rgb = mix(backgroundColor.rgb, vec3(1.0), gray);
color.a = t;
gl_FragColor = color;
}

How can I implement the Thin Plate Spline algorithm in a GLSL vertex shader?

I would like to make a GLSL shader for warping an image/texture using the TPS algorithm. How would I write the GLSL vertex shader for that?
The answer was that I needed to make a vertex shader, not a fragment shader.
What I needed was to actually warp an image with GLSL and it seems this article describes how to do it: https://testdrive-archive.azurewebsites.net/Graphics/Warp/Default.html
attribute vec2 aPosition;
varying vec2 vTexCoord;
#define MAXPOINTS 9
uniform vec2 p1[MAXPOINTS]; // where the reference points
uniform vec2 p2[MAXPOINTS]; // where the warp points
void main() {
vTexCoord = aPosition;
vec2 position = aPosition * 2.0 - 1.0; // convert 0 - 1 range to -1 to +1 range
for (int i = 0; i < MAXPOINTS; i++)
{
float dragdistance = distance(p1[i], p2[i]);
float mydistance = distance(p1[i], position);
if (mydistance < dragdistance)
{
vec2 maxdistort = (p2[i] - p1[i]) / 4.0;
float normalizeddistance = mydistance / dragdistance;
float normalizedimpact = (cos(normalizeddistance*3.14159265359)+1.0)/2.0;
position += (maxdistort * normalizedimpact);
}
}
//gl_Position = vec4(aPosition * 2.0 - 1.0, 0.0, 1.0);
gl_Position = vec4(position, 0.0, 1.0);
}

Drawing a circle in fragment shader

I am a complete noob when it comes to creating shaders. Or better said, I just learned about it yesterday.
I am trying to create a really simple circle. I thouht I finally figured it out but it turns out to be to large. It should match the DisplayObject size where the filter is applied to.
The fragment shader:
precision mediump float;
varying vec2 vTextureCoord;
vec2 resolution = vec2(1.0, 1.0);
void main() {
vec2 uv = vTextureCoord.xy / resolution.xy;
uv -= 0.5;
uv.x *= resolution.x / resolution.y;
float r = 0.5;
float d = length(uv);
float c = smoothstep(d,d+0.003,r);
gl_FragColor = vec4(vec3(c,0.5,0.0),1.0);
}
Example using Pixi.js:
var app = new PIXI.Application();
document.body.appendChild(app.view);
var background = PIXI.Sprite.fromImage("required/assets/bkg-grass.jpg");
background.width = 200;
background.height = 200;
app.stage.addChild(background);
var vertexShader = `
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat3 projectionMatrix;
varying vec2 vTextureCoord;
void main(void)
{
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
vTextureCoord = aTextureCoord;
}
`;
var fragShader = `
precision mediump float;
varying vec2 vTextureCoord;
vec2 resolution = vec2(1.0, 1.0);
void main() {
vec2 uv = vTextureCoord.xy / resolution.xy;
uv -= 0.5;
uv.x *= resolution.x / resolution.y;
float r = 0.5;
float d = length(uv);
float c = smoothstep(d,d+0.003,r);
gl_FragColor = vec4(vec3(c,0.5,0.),1.0);
}
`;
var filter = new PIXI.Filter(vertexShader, fragShader);
filter.padding = 0;
background.filters = [filter];
body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.2/pixi.js"></script>
Pixi.js's vTextureCoord do not go from 0 to 1.
From the docs
V4 filters differ from V3. You can't just add in the shader and assume that texture coordinates are in the [0,1] range.
...
Note: vTextureCoord multiplied by filterArea.xy is the real size of bounding box.
If you want to get the pixel coordinates, use uniform filterArea, it will be passed to the filter automatically.
uniform vec4 filterArea;
...
vec2 pixelCoord = vTextureCoord * filterArea.xy;
They are in pixels. That won't work if we want something like "fill the ellipse into a bounding box". So, lets pass dimensions too! PIXI doesnt do it automatically, we need a manual fix:
filter.apply = function(filterManager, input, output)
{
this.uniforms.dimensions[0] = input.sourceFrame.width
this.uniforms.dimensions[1] = input.sourceFrame.height
// draw the filter...
filterManager.applyFilter(this, input, output);
}
Lets combine it in shader!
uniform vec4 filterArea;
uniform vec2 dimensions;
...
vec2 pixelCoord = vTextureCoord * filterArea.xy;
vec2 normalizedCoord = pixelCoord / dimensions;
Here's your snippet updated.
var app = new PIXI.Application();
document.body.appendChild(app.view);
var background = PIXI.Sprite.fromImage("required/assets/bkg-grass.jpg");
background.width = 200;
background.height = 200;
app.stage.addChild(background);
var vertexShader = `
attribute vec2 aVertexPosition;
attribute vec2 aTextureCoord;
uniform mat3 projectionMatrix;
varying vec2 vTextureCoord;
void main(void)
{
gl_Position = vec4((projectionMatrix * vec3(aVertexPosition, 1.0)).xy, 0.0, 1.0);
vTextureCoord = aTextureCoord;
}
`;
var fragShader = `
precision mediump float;
varying vec2 vTextureCoord;
uniform vec2 dimensions;
uniform vec4 filterArea;
void main() {
vec2 pixelCoord = vTextureCoord * filterArea.xy;
vec2 uv = pixelCoord / dimensions;
uv -= 0.5;
float r = 0.5;
float d = length(uv);
float c = smoothstep(d,d+0.003,r);
gl_FragColor = vec4(vec3(c,0.5,0.),1.0);
}
`;
var filter = new PIXI.Filter(vertexShader, fragShader);
filter.apply = function(filterManager, input, output)
{
this.uniforms.dimensions[0] = input.sourceFrame.width
this.uniforms.dimensions[1] = input.sourceFrame.height
// draw the filter...
filterManager.applyFilter(this, input, output);
}
filter.padding = 0;
background.filters = [filter];
body { margin: 0; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/pixi.js/4.5.2/pixi.js"></script>
It seems you've stumbled upon weird floating point precision problems: texture coordinates (vTextureCoord) in your fragment shader aren't strictly in (0, 1) range. Here's what I've got when I've added line gl_FragColor = vec4(vTextureCoord, 0, 1):
It seems good, but if we inspect it closely, lower right pixel should be (1, 1, 0), but it isn't:
The problem goes away if instead of setting size to 500 by 500 we use power-of-two size (say, 512 by 512), the problem goes away:
The other possible way to mitigate the problem would be to try to circumvent Pixi's code that computes projection matrix and provide your own that transforms smaller quad into desired screen position.

GLSL Check texture alpha between 2 vectors

I'm trying to learn how to make shaders, and a little while ago, I posted a question here : GLSL Shader - Shadow between 2 textures on a plane
So, the answer gave me the right direction to take, but I have some trouble for checking if there is a fragment that is not transparent between the current fragment and the light position.
So here is the code :
Vertex Shader :
attribute vec3 position;
attribute vec3 normal;
attribute vec2 uv;
varying vec2 uvVarying;
varying vec3 normalVarying;
varying vec3 posVarying;
uniform vec4 uvBounds0;
uniform mat4 agk_World;
uniform mat4 agk_ViewProj;
uniform mat3 agk_WorldNormal;
void main()
{
vec4 pos = agk_World * vec4(position,1);
gl_Position = agk_ViewProj * pos;
vec3 norm = agk_WorldNormal * normal;
posVarying = pos.xyz;
normalVarying = norm;
uvVarying = uv * uvBounds0.xy + uvBounds0.zw;
}
And the fragment shader :
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
uniform sampler2D texture0;
uniform sampler2D texture1;
varying vec2 uvVarying;
varying vec3 normalVarying;
varying vec3 posVarying;
uniform vec4 uvBounds0;
uniform vec2 playerPos;
uniform vec2 agk_resolution;
uniform vec4 agk_PLightPos;
uniform vec4 agk_PLightColor;
uniform vec4 agk_ObjColor;
void main (void)
{
vec4 lightPos = agk_PLightPos;
lightPos.x = playerPos.x;
lightPos.y = -playerPos.y;
vec3 dir = vec3(lightPos.x - posVarying.x, lightPos.y - posVarying.y, lightPos.z - posVarying.z);
vec3 norm = normalize(normalVarying);
float atten = dot(dir,dir);
atten = clamp(lightPos.w/atten,0.0,1.0);
float intensity = dot(normalize(dir),norm);
intensity = clamp(intensity,0.0,1.0);
vec3 lightColor = agk_PLightColor.rgb * intensity * atten;
vec3 shadowColor = agk_PLightColor.rgb * 0;
bool inTheShadow = false;
if (intensity * atten > 0.05) {
float distanceToLight = length(posVarying.xy - lightPos.xy);
for (float i = distanceToLight; i > 0.0; i -= 0.1) {
vec2 uvShadow = ???
if (texture2D(texture0, uvShadow).a > 0) {
inTheShadow = true;
break;
}
}
}
if (texture2D(texture0, uvVarying).a == 0) {
if (inTheShadow == true) {
gl_FragColor = texture2D(texture1, uvVarying) * vec4(shadowColor, 1) * agk_ObjColor;
}
else {
gl_FragColor = texture2D(texture1, uvVarying) * vec4(lightColor, 1) * agk_ObjColor;
}
}
else {
gl_FragColor = texture2D(texture0, uvVarying) * agk_ObjColor;
}
}
So, this is the part where I have some troubles :
bool inTheShadow = false;
if (intensity * atten > 0.05) {
float distanceToLight = length(posVarying.xy - lightPos.xy);
for (float i = distanceToLight; i > 0.0; i -= 0.1) {
vec2 uvShadow = ???
if (texture2D(texture0, uvShadow).a > 0) {
inTheShadow = true;
break;
}
}
}
I first check if I'm in the light radius with intensity * atten > 0.05
Then I get the distance from the current fragment to the light position.
And then, I make a for loop, to check each fragment between the current fragment and the light position. I tried some calculations to get the current fragment, but with no success.
So, any idea on how I can calculate the uvShadow in my loop ?
I hope I'm using the good variables too, cause in the last part of my code, where I use gl_FragColor, I'm using uvVarying to get the current fragment (If i'm not mistaken), but to get the light distance, I had to calculate the length between posVarying and lightPos and not between uvVarying and lightPos (I made a test, where the further I was from the light, the more red it became, and with posVarying, it made me a circle with gradient around my player (lightPos) but when I used uvVarying, the circle was only one color, and it was more or less red, when I was approaching my player to the center of the screen).
Thanks and best regards,
Max
When you access a texture through texture2D() you use normalised coordinates. I.e. numbers that go from (0.0, 0.0) to (1.0, 1.0). So you need to convert your world positions to this normalised space. So something like:
vec2 uvShadow = posVarying.xy + ((distanceToLight / 0.1) * i * (posVarying.xy - lightPos.xy));
// Take uvShadow from world space to view space, this is -1.0 to 1.0
uvShadow *= mat2(inverse(agk_View)); // This could be optimized if you are using orthographic projection
// Now take it to texture space
uvShadow += 0.5;
uvShadow *= 0.5;

Resources