WebGL: Interpolate between old and new value - glsl

I am struggling with this for the second day now and it seems like such a simple task, but I can not find the right solution.
With p5.js I am creating a GL instance and sending uniforms to the vertex shader.
Now I want to change the global variable that is the uniform and have the shader interpolate between the old value to the new value.
In the example below, after the button is clicked, it should not change the color instantly, but do small increments to get to the new color.
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 iResolution;
uniform float iTime;
uniform vec3 uSky;
void main()
{
// Normalized pixel coordinates (from 0 to 1)
vec2 uv = gl_FragCoord.xy/iResolution.xy;
uv -= 0.5;
uv.x *= iResolution.x/iResolution.y;
vec3 sky = uSky;
//sky = mix(sky, uSky, 0.001);
// ^- This needs to be increment until 1 every time the uniform changes
// Output to screen
gl_FragColor = vec4(sky, 1.0);
}
https://glitch.com/~shader-prb

As Rabbid76 commented. I can not change the uniform value in the shader.
The solution was to lerp from the host.

Related

Banding Problem in Multi Step Shader with Ping Pong Buffers, does not happen in ShaderToy

I am trying to implement a Streak shader, which is described here:
http://www.chrisoat.com/papers/Oat-SteerableStreakFilter.pdf
Short explanation: Samples a point with a 1d kernel in a given direction. The kernel size grows exponentially in each step. Color values are weighted based on distance to sampled point and summed. The result is a smooth tail/smear/light streak effect on that direction. Here is the frag shader:
precision highp float;
uniform sampler2D u_texture;
varying vec2 v_texCoord;
uniform float u_Pass;
const float kernelSize = 4.0;
const float atten = 0.95;
vec4 streak(in float pass, in vec2 texCoord, in vec2 dir, in vec2 pixelStep) {
float kernelStep = pow(kernelSize, pass - 1.0);
vec4 color = vec4(0.0);
for(int i = 0; i < 4; i++) {
float sampleNum = float(i);
float weight = pow(atten, kernelStep * sampleNum);
vec2 sampleTexCoord = texCoord + ((sampleNum * kernelStep) * (dir * pixelStep));
vec4 texColor = texture2D(u_texture, sampleTexCoord) * weight;
color += texColor;
}
return color;
}
void main() {
vec2 iResolution = vec2(512.0, 512.0);
vec2 pixelStep = vec2(1.0, 1.0) / iResolution.xy;
vec2 dir = vec2(1.0, 0.0);
float pass = u_Pass;
vec4 streakColor = streak(pass, v_texCoord, dir, pixelStep);
gl_FragColor = vec4(streakColor.rgb, 1.0);
}
It was going to be used for a starfield type of effect. And here is the implementation on ShaderToy which works fine:
https://www.shadertoy.com/view/ll2BRG
(Note: Disregard the first shader in Buffer A, it just filters out the dim colors in the input texture to emulate a star field since afaik ShaderToy doesn't allow uploading custom textures)
But when I use the same shader in my own code and render using ping-pong FrameBuffers, it looks different. Here is my own implementation ported over to WebGL:
https://jsfiddle.net/1b68eLdr/87755/
I basically create 2 512x512 buffers, ping-pong the shader 4 times increasing kernel size at each iteration according to the algorithm and render the final iteration on the screen.
The problem is visible banding, and my streaks/tails seem to be losing brightness a lot faster: (Note: the image is somewhat inaccurate, the lengths of the streaks are same/correct, its color values that are wrong)
I have been struggling with this for a while in Desktop OpenGl / LWJGL, I ported it over to WebGL/Javascript and uploaded on JSFiddle in hopes someone can spot what the problem is. I suspect it's either about texture coordinates or FrameBuffer configuration since shaders are exactly the same.
The reason it works on Shadertoys is because it uses a floating-point render target.
Simply use gl.FLOAT as the type of your framebuffer texture and the issue is fixed (I could verify it with the said modification on your JSFiddle).
So do this in your createBackingTexture():
// Just request the extension (MUST be done).
gl.getExtension('OES_texture_float');
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this._width, this._height, 0, gl.RGBA, gl.FLOAT, null);

GLSL - per-fragment lighting

I started lighting with several light sources. All the manuals that I saw without taking into account the distance between the light source and the object (for example https://learnopengl.com/Lighting/Basic-Lighting). So I wrote my shader, but I'm not sure about its correctness. Please, analyze this shader, and tell me what's wrong / not correct in it. I will be very grateful for any help! Below I bring the shader itself, and the results of its work for different values of n and k.
Fragment shader:
#version 130
precision mediump float; // Set the default precision to medium. We don't need as high of a
// precision in the fragment shader.
#define MAX_LAMPS_COUNT 8 // Max lamps count.
uniform vec3 u_LampsPos[MAX_LAMPS_COUNT]; // The position of lamps in eye space.
uniform vec3 u_LampsColors[MAX_LAMPS_COUNT];
uniform vec3 u_AmbientColor = vec3(1, 1, 1);
uniform sampler2D u_TextureUnit;
uniform float u_DiffuseIntensivity = 12;
uniform float ambientStrength = 0.1;
uniform int u_LampsCount;
varying vec3 v_Position; // Interpolated position for this fragment.
varying vec3 v_Normal; // Interpolated normal for this fragment.
varying vec2 v_Texture; // Texture coordinates.
// The entry point for our fragment shader.
void main() {
float n = 2;
float k = 2;
float finalDiffuse = 0;
vec3 finalColor = vec3(0, 0, 0);
for (int i = 0; i<u_LampsCount; i++) {
// Will be used for attenuation.
float distance = length(u_LampsPos[i] - v_Position);
// Get a lighting direction vector from the light to the vertex.
vec3 lightVector = normalize(u_LampsPos[i] - v_Position);
// Calculate the dot product of the light vector and vertex normal. If the normal and light vector are
// pointing in the same direction then it will get max illumination.
float diffuse = max(dot(v_Normal, lightVector), 0.1);
// Add attenuation.
diffuse = diffuse / (1 + pow(distance, n));
// Calculate final diffuse for fragment
finalDiffuse += diffuse;
// Calculate final light color
finalColor += u_LampsColors[i] / (1 + pow(distance, k));
}
finalColor /= u_LampsCount;
vec3 ambient = ambientStrength * u_AmbientColor;
vec3 diffuse = finalDiffuse * finalColor * u_DiffuseIntensivity;
gl_FragColor = vec4(ambient + diffuse, 1) * texture2D(u_TextureUnit, v_Texture);
}
Vertex shader:
#version 130
uniform mat4 u_MVPMatrix; // A constant representing the combined model/view/projection matrix.
uniform mat4 u_MVMatrix; // A constant representing the combined model/view matrix.
attribute vec4 a_Position; // Per-vertex position information we will pass in.
attribute vec3 a_Normal; // Per-vertex normal information we will pass in.
attribute vec2 a_Texture; // Per-vertex texture information we will pass in.
varying vec3 v_Position; // This will be passed into the fragment shader.
varying vec3 v_Normal; // This will be passed into the fragment shader.
varying vec2 v_Texture; // This will be passed into the fragment shader.
void main() {
// Transform the vertex into eye space.
v_Position = vec3(u_MVMatrix * a_Position);
// Pass through the texture.
v_Texture = a_Texture;
// Transform the normal's orientation into eye space.
v_Normal = vec3(u_MVMatrix * vec4(a_Normal, 0.0));
// gl_Position is a special variable used to store the final position.
// Multiply the vertex by the matrix to get the final point in normalized screen coordinates.
gl_Position = u_MVPMatrix * a_Position;
}
n=2 k=2
n=1 k=3
n=3 k=1
n=3 k=3
And if my shader is correct, then how do I name these parameters (n, k)?
By "correct" I assume you mean is the code working as well as it should. These lighting calculations are not by any means physically accurate. Unless you are going for full compatibility with old devices, I would recommend you use a higher glsl version which allows you to use in and out and some other useful glsl features. The current is version 450 and you are still using 130. The vertex shader looks ok, as it is only passing through values to the fragment shader.
As for the fragment shader there are is one optimisation you could make.
The calculation u_LampsPos[i] - v_Position doesn't have to be repeated twice. Do it once and do the length and normalize on the same result from one calculation.
The code is quite small so there is not much to go wrong glsl wise however I was wondering why you did: finalColor /= u_LampsCount;?
This didn't make sense to me.

How to create noise inside WEBGL shader?

I'm finding the way to create some noise in WEBGL shaders. Some noise for vertex displacement or colors randomization inside shader itself. I want to have it inside a shader in order to not consider what i'm rendering. Just to make current fragment color randomized or vertex position moved a bit.
You can see my shaders code here:
https://github.com/rantiev/webgl-learning/blob/master/stars/index.html
It's plain simple shaders:
<script id="shader-fs" type="x-shader/x-fragment">
precision mediump float;
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec3 uColor;
void main(void) {
vec4 textureColor = texture2D(uSampler, vec2(vTextureCoord.s, vTextureCoord.t));
gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
}
</script>
Here is one way to do it:
First, we calculate a screen space uv by dividing gl_Position.xy by gl_Position.ww and remap it from (-1, +1) to (0 to 1).
vec4 projCoords = projection * view * model * attr_pos;
vec2 screenUV = projCoords.xy/projCoords.ww; // (from -1 to 1)
screenUV = screenUV * 0.5 + 0.5; // remap to (0 to 1)
Then with this uv we can apply a screen space noise via either a random texture or some math calculation in the shader. Assuming that you generated a random texture, then noise is simply:
vec4 noise = texture2D(u_randomTexture, screenUV);
If you want to use calculations in the shader, look into implementing something like perlin2D noise or perhaps search in shadertoy.

LibGDX Overlapping 2D Shadows

I'm working on shadows for a 2D overhead game. Right now, the shadows are just sprites with the color (0,0,0,0.1) drawn on a layer above the tiles.
The problem: When many entities or trees get clumped together, the shadows overlap, forming unnatural-looking dark areas.
I've tried drawing the shadows to a framebuffer and using a simple shader to prevent overlapping, but that lead to other problems, including layering issues.
Is it possible to enable a certain blend function for the shadows that prevents "stacking", or a better way to use a shader?
If you don't want to deal with sorting issues, I think you could do this with a shader. But every object will have to be either affected by shadow or not. So tall trees could be marked as not shadow receiving, while the ground, grass, and characters would be shadow receiving.
First make a frame buffer with clear color white. Draw all your shadows on it as pure black.
Then make a shadow mapping shader to draw everything in your world. This relies on you not needing all four channels of the sprite's color, because we need one of those channels to mark each sprite as shadow receiving or not. For example, if you aren't using RGB to tint your sprites, we could use the R channel. Or if you aren't fading them in and out, we could use A. I'll assume the latter here:
Vertex shader:
attribute vec4 a_position;
attribute vec4 a_color;
attribute vec2 a_texCoord0;
varying vec2 v_texCoords;
varying vec2 v_texCoordsShadowmap;
varying vec4 v_color;
uniform mat4 u_projTrans;
void main()
{
v_texCoords = a_texCoord0;
v_color = a_color;
v_color.a = v_color.a * (255.0/254.0); //this is a correction due to color float precision (see SpriteBatch's default shader)
vec3 screenPosition = u_projTrans * a_position;
v_texCoordsShadowmap = (screenPosition.xy * 0.5) + 0.5;
gl_Position = screenPosition;
}
Fragment shader:
#ifdef GL_ES
precision mediump float;
#endif
varying vec2 v_texCoords;
varying vec2 v_texCoordsShadowmap;
varying vec4 v_color;
uniform sampler2D u_texture;
uniform sampler2D u_textureShadowmap;
void main()
{
vec4 textureColor = texture2D(u_texture, v_texCoords);
float shadowColor = texture2D(u_textureShadowmap, v_texCoordsShadowmap).r;
shadowColor = mix(shadowColor, 1.0, v_color.a);
textureColor.rgb *= shadowColor * v_color.rgb;
gl_FragColor = textureColor;
}
These are completely untested and probably have bugs. Make sure you assign the frame buffer's color texture to "u_textureShadowmap". And for all your sprites, set their color's alpha based on how much shadow you want them to have cast on them, which will generally always be 0 or 0.1 (based on the brightness you were using before).
Draw your shadows to fbo with disabled blending.
Draw background e.g. grass
Draw shadows texture from fbo
Draw all other sprites

GLSL mixing base texture with decal texture at needed place

Lets say we texturing quad (two triangles). I think what this question is similiar to texture splatting like in next example
precision lowp float;
uniform sampler2D Terrain;
uniform sampler2D Grass;
uniform sampler2D Stone;
uniform sampler2D Rock;
varying vec2 tex_coord;
void main(void)
{
vec4 terrain = texture2D(Terrain, tex_coord);
vec4 tex0 = texture2D(Grass, tex_coord * 4.0); // Tile
vec4 tex1 = texture2D(Rock, tex_coord * 4.0); // Tile
vec4 tex2 = texture2D(Stone, tex_coord * 4.0); // Tile
tex0 *= terrain.r; // Red channel - puts grass
tex1 = mix( tex0, tex1, terrain.g ); // Green channel - puts rock and mix with grass
vec4 outColor = mix( tex1, tex2, terrain.b ); // Blue channel - puts stone and mix with others
gl_FragColor = outColor; //final color
}
But i want to just place a 1 decal on base quad texture in desired place.
Algorithm is just the same, but i think we don't need extra texture with 1 filled layer to hold positions(e.g. where red layer != 0) of decal, some how we must generate our own "terrain.r"(is this float?) variable and mix base texture and decal texture with it.
precision lowp float;
uniform sampler2D base;
uniform sampler2D decal;
uniform vec2 decal_location; //where we want place decal (e.g. 0.5, 0.5 is center of quad)
varying vec2 base_tex_coord;
varying vec2 decal_tex_coord;
void main(void)
{
vec4 v_base = texture2D(base, base_tex_coord);
vec4 v_decal = texture2D(Grass, decal_tex_coord);
float decal_layer = /*somehow get our decal_layer based on decal_position*/
gl_FragColor = mix(v_base, v_decal, decal_layer);
}
How achieve such thing?
Or i may just generate splat texture on opengl side and pass it to first shader? This will give me up to 4 various decals on quad but will be slow for frequent updates (e.g. machine gun hits wall)
float decal_layer = /*somehow get our decal_layer based on decal_position*/
Well, it's up to you, how you interpret decal_position. I think a simple distance metric would suffice. but this also requires the size of the quad. Let's assume you provide this through an additional uniform decal_radius. Then we can use
decal_layer = clamp(length(decal_position - vec2(0.5, 0.5)) / decal_radius, 0., 1.);
Yes, decal_layer is a float as you've described. Its range is 0 to 1. But you don't have quite enough info, here you've specified decal_location but no size for the decal. You also don't know where this fragment falls in the quad, you'll need a varying vec2 quad_coord; or similar input from the vertex shader if you want to know where this fragment is relative to the quad being rendered.
But let's try a different approach. Edit the top of your 2nd example to include these uniforms:
uniform vec2 decal_location; // Location of decal relative to base_tex_coord
uniform float decal_size; // Size of decal relative to base_tex_coord
Now, in main(), you should be able to compute decal_layer with something like this:
float decal_layer = 1.0 - smoothstep(decal_size - 0.01, decal_size, max(abs(decal_location.x - base_tex_coord.x), abs(decal_location.y - base_tex_coord.y)));
Basically you're trying to get decal_layer to be 1.0 within the decal, and 0.0 outside the decal. I've added a 0.01 fuzzy edge at the boundary that you can play with. Good luck!