I am trying to get a fluid simulation to work using WebGL using http://meatfighter.com/fluiddynamics/GPU_Gems_Chapter_38.pdf as a resource. I have implemented everything but I feel like there are multiple things that aren't working correctly. I added boundaries but it seems like they are having no effect, which makes me suspicious about how much pressure and advection are working. I displayed the divergence and I get very little around where I am moving the object around as well as when the velocity hits the edge (boundary), but the pressure that I get is completely empty. I calculate pressure using the diffusion shader as described in the linked resource.
I know the code I am posting is al little confusing due to the nature of what it is about. I can supply any pictures/links to the simulation if that would help.
--EDIT--
After some more investigation I believe the problem is related to my advection function. or at least a problem. I am unsure how to fix it though.
Instead of posting all of my code, the general process I follow is:
advect velocity
diffuse velocity
add velocity
calculate divergence
compute pressure
subtract gradient
for diffusing velocity and computing pressure I am only do 10 iterations because thats all my computer can handle with my implementation (I will optimize once I get it working), but I feel like the computing pressure and subtracting gradient are not having any effect.
here are the shaders I am using:
//advection
uniform vec2 res;//The width and height of our screen
uniform sampler2D velocity;//input velocity
uniform sampler2D quantity;//quantity to advect
void main() {
vec2 pixel = gl_FragCoord.xy / res.xy;
float i0, j0, i1, j1;
float x, y, s0, s1, t0, t1, dxt0, dyt0;
float dt = 1.0/60.0;
float Nx = res.x -1.0;
float Ny = res.y -1.0;
float i = pixel.x;
float j = pixel.y;
dxt0 = dt ;
dyt0 = dt ;
x = gl_FragCoord.x - dxt0 * (texture2D(velocity, pixel).x );
y = gl_FragCoord.y - dyt0 * (texture2D(velocity, pixel).y );
i0=x-0.5;
i1=x+0.5;
j0=y-0.5;
j1=y+0.5;
s1 = x-i0;
s0 = 1.0-s1;
t1 = y-j0;
t0 = 1.0-t1;
float p1 = (t0 * texture2D(quantity, vec2(i0,j0)/res.xy).r);
float p2 = (t1 * texture2D(quantity, vec2(i0,j1)/res.xy).r);
float p3 = (t0 * texture2D(quantity, vec2(i1,j0)/res.xy).r);
float p4 = (t1 * texture2D(quantity, vec2(i1,j1)/res.xy).r);
float total1 = s0 * (p1 + p2);
float total2 = s1 * (p3 + p4);
gl_FragColor.r = total1 + total2;
p1 = (t0 * texture2D(quantity, vec2(i0,j0)/res.xy).g);
p2 = (t1 * texture2D(quantity, vec2(i0,j1)/res.xy).g);
p3 = (t0 * texture2D(quantity, vec2(i1,j0)/res.xy).g);
p4 = (t1 * texture2D(quantity, vec2(i1,j1)/res.xy).g);
total1 = s0 * (p1 + p2);
total2 = s1 * (p3 + p4);
gl_FragColor.g = total1 + total2;
}
//diffusion shader starts here
uniform vec2 res;//The width and height of our screen
uniform sampler2D x;//Our input texture
uniform sampler2D b;
uniform float alpha;
uniform float rBeta;
void main() {
float xPixel = 1.0/res.x;
float yPixel = 1.0/res.y;
vec2 pixel = gl_FragCoord.xy / res.xy;
gl_FragColor = texture2D( b, pixel );
vec4 leftColor = texture2D(x,vec2(pixel.x-xPixel,pixel.y));
vec4 rightColor = texture2D(x,vec2(pixel.x+xPixel,pixel.y));
vec4 upColor = texture2D(x,vec2(pixel.x,pixel.y-yPixel));
vec4 downColor = texture2D(x,vec2(pixel.x,pixel.y+yPixel));
gl_FragColor.r = (gl_FragColor.r * alpha +leftColor.r + rightColor.r + upColor.r + downColor.r) * rBeta;
gl_FragColor.g = (gl_FragColor.g * alpha +leftColor.g + rightColor.g + upColor.g + downColor.g)* rBeta;
gl_FragColor.b = (gl_FragColor.b * alpha +leftColor.b + rightColor.b + upColor.b + downColor.b)* rBeta;
}
//gradient
uniform vec2 res;//The width and height of our screen
uniform sampler2D velocity;//Our input velocity
uniform sampler2D pressure;//Our input pressure
void main() {
float xPixel = 1.0/res.x;
float yPixel = 1.0/res.y;
vec2 pixel = gl_FragCoord.xy / res.xy;
vec4 leftColor = texture2D(pressure, vec2(pixel.x-xPixel,pixel.y));
vec4 rightColor = texture2D(pressure, vec2(pixel.x+xPixel,pixel.y));
vec4 upColor = texture2D(pressure, vec2(pixel.x,pixel.y-yPixel));
vec4 downColor = texture2D(pressure, vec2(pixel.x,pixel.y+yPixel));
vec2 gradient = xPixel/2.0 * vec2((rightColor.x - leftColor.x), (upColor.y - downColor.y));
//Diffuse equation
gl_FragColor = texture2D(velocity, pixel) ;
gl_FragColor.xy -= gradient;
}
uniform vec2 res;//The width and height of our screen
uniform sampler2D velocity;//Our input texture
void main() {
float xPixel = 1.0/res.x;
float yPixel = 1.0/res.y;
vec2 pixel = gl_FragCoord.xy / res.xy;
vec4 leftColor = texture2D(velocity, vec2(pixel.x-xPixel,pixel.y));
vec4 rightColor = texture2D(velocity, vec2(pixel.x+xPixel,pixel.y));
vec4 upColor = texture2D(velocity, vec2(pixel.x,pixel.y-yPixel));
vec4 downColor = texture2D(velocity, vec2(pixel.x,pixel.y+yPixel));
float div = xPixel/2.0 * ((rightColor.x - leftColor.x) + (upColor.y - downColor.y));
//Diffuse equation
gl_FragColor = vec4(div);
}
Related
I would like to convert the following fragment Shader written for glsl to Metal Shader.
const float PI = 3.14159265359;
mat2 rotate2d (float _angle) {
return mat2 (cos (_angle), -sin (_angle),
sin (_angle), cos (_angle));
}
void main (void) {
vec2 st = (gl_FragCoord.xy * 2.0 --resolution) /min(resolution.x,resolution.y);
float p = 0.0;
st = rotate2d (sin (time) * PI) * st;
vec2 c = max (abs (st) --0.2,0.0);
p = length (c);
p = ceil (p);
vec3 color = vec3 (1.0-p);
gl_FragColor = vec4 (color, 1.0);
}
At that time, I understand that there is no problem if vec2 etc. is set to float2 etc.
How should I write it?
It's hard to convert this shader without having any informartion about your current render pipeline:
#include <metal_stdlib>
float2x2 rotate2d(float angle)
{
return float2x2(float2(cos(angle), -sin(angle)),
float2(sin(angle), cos(angle)));
}
fragment float4 fragmentShader(RasterizerData in [[stage_in]],
constant simd::float2 &resolution [[ buffer(0) ]],
constant float &time [[ buffer(1) ]])
{
float2 st = (in.textureCoordinate.xy * 2.0 - resolution) / min(resolution.x, resolution.y);
float p = 0.0;
st = rotate2d(sin(time) * M_PI_F) *st;
float2 c = max(abs(st) - 0.2, 0.0);
p = length(c);
p = ceil(p);
float3 color = float3(1.0 - p);
return float4(color, 1);
}
I'm trying to convert this particular shader to CIKernel Code.
https://www.shadertoy.com/view/4scBRH
I've got this soo far,
kernel vec4 thresholdFilter(__sample image, float time)
{
vec2 uv = destCoord();
float amount = sin(time) * 0.1;
amount *= 0.3;
float split = 1. - fract(time / 2.0);
float scanOffset = 0.01;
vec2 uv1 = vec2(uv.x + amount, uv.y);
vec2 uv2 = vec2(uv.x, uv.y + amount);
if (uv.y > split) {
uv.x += scanOffset;
uv1.x += scanOffset;
uv2.x += scanOffset;
}
float r = sample(image, uv1).r;
float g = sample(image, uv).g;
float b = sample(image, uv2).b;
float a = 1.0;
vec3 outPutPixel = sample(image, samplerTransform(image, uv)).rgb;
return vec4(outPutPixel, 1.0);
}
The output of this code is not even close to the shaderToy output.
I am trying to use GLSL to implement a Harris Corner Detection. But it does not work properly(I am guessing). First off it doesn't detect all corners and it detects alot of points that are not corners, another big problem is that it the threshold is very specific for each image. Maybe that is normal with a Harris detector?
All help is appreciated.
Shader Passes:
1st: Standard passthrough.
2nd: I turn the image into a grayscale image.
3rd: Sobel filtering the image and passing the x, y gradient intensitys and the product of the xy intensitys.
uniform sampler2D texture;
varying vec2 vUV;
void main() {
vec2 uv = vUV;
// Offset used to get access to neighbours
float w = 1.0/800.0;
float h = 1.0/600.0;
vec3 temp;
vec3 sum = vec3(0.0);
// Sobel - Edge Detection
// y gradient
vec3 texel0 = texture2D(texture, uv + vec2(-w, h)).xyz;
vec3 texel1 = texture2D(texture, uv + vec2(-w, 0)).xyz;
vec3 texel2 = texture2D(texture, uv + vec2(-w, -h)).xyz;
vec3 texel6 = texture2D(texture, uv + vec2(w, h)).xyz;
vec3 texel7 = texture2D(texture, uv + vec2(w, 0)).xyz;
vec3 texel8 = texture2D(texture, uv + vec2(w, -h)).xyz;
vec3 vertEdge = 1.0 * texel0 + (2.0*texel1) + 1.0 * texel2 -
(1.0 * texel6 + (2.0*texel7) + 1.0 * texel8);
// x gradient
vec3 texe0 = texture2D(texture, uv + vec2(-w,h)).xyz;
vec3 texe1 = texture2D(texture, uv + vec2(0, h)).xyz;
vec3 texe2 = texture2D(texture, uv + vec2(w, h)).xyz;
vec3 texe6 = texture2D(texture, uv + vec2(-w,-h)).xyz;
vec3 texe7 = texture2D(texture, uv + vec2(0,-h)).xyz;
vec3 texe8 = texture2D(texture, uv + vec2(w,-h)).xyz;
vec3 horizEdge = 1.0 * texe0 + (2.0*texe1) + 1.0 * texe2 -
(1.0 * texe6 + (2.0*texe7) + 1.0 * texe8);
// Gradient intensity values
float iy = (vertEdge.r + vertEdge.g + vertEdge.b) /3.0;
float ix = (horizEdge.r + horizEdge.g + horizEdge.b) /3.0;
// Absolute to get negative values
iy = abs(iy);
ix = abs(ix);
float gradProcduct = ix * iy;
gl_FragColor = vec4(ix,iy,gradProcduct, 0.0);
Not the best looking code - just want it to work for now
4th and 5th: Standard Gaussian Blur
6th: Calculating Harris Response.
If it is a corner i paint that pixel in magenta.
void main() {
vec2 uv = vUV;
float w = 1.0/800.0;
float h = 1.0/600.0;
float threshold = 0.05;
vec4 gradientInfo = texture2D(texture, uv);
/************** Harris Reponse **********************
R is calculated as R = det(M)- K(Trace(M)) which leads to
R = Ix^2*Ix^y - Ixy^2-K(ix^2+iy^2)^2
Ix = X-gradient intesity
Iy = Y-gradient intesity
Ixy = product of the X- and Y-gradient intensities
*********************************************************/
float R = pow(gradientInfo.r,2.0)*pow(gradientInfo.g,2.0)
- pow(gradientInfo.b,2.0)
- threshold * pow((pow(gradientInfo.r,2.0)+pow(gradientInfo.g,2.0)),2.0);
vec4 test;
//if(R > 0.000000000005)
if(R > 0.0000000000750){
// Extremley small values, ugly soloution for now to be able to use R in maxSupress
test = vec4(1.0, 0.0, 1.0, R*1000000000.0);
}
else
test = vec4(vec3(gradientInfo.xyz),0.0);
gl_FragColor = vec4( test);
}
Results
Result on a simple square
Result on a more complex figure with the same R and Threshold
And the result when the response check is mulitplied by a 1000. Doesn't really seem to work.
Below is the code for the maximum supression.
void main() {
vec2 uv = vUV;
float vOffset = 1.0/800.0;
float hOffset = 1.0/600.0;
vec4 neighbourPixels[9];
vec3 result;
int check = 0;
vec3 previous = texture2D(texture2, uv).xyz;
vec4 current = texture2D(texture, uv);
float temp = current.a;
vec4 neighbourArray[25];
if(current.a > 0.0){
for(int i = -2; i<3;i++){
for(int j = -2; j<3;j++){
if(temp < texture2D(texture, vUV.xy+vec2(i,j)*vec2(vOffset,hOffset)).a ){
//result = vec3(1.0,0.0,1.0);
check = 1;
break;
}
}
if(check==1){
break;
}
}
if(check==1){
result = vec3(1.0,0.0,0.0);
}
else{
result = vec3(0.0,1.0,1.0);
}
}
else{
result = previous.xyz;
}
gl_FragColor = vec4( result, 0.0);
}
You need to look for local maxima in the Harris response, rather than just thresholding. Dilate the response with a 3x3 or 5x5 box, then find pixels whether original response and dilated version are equal.
I know to perform this in OpenGL with the code below:
glDisable(GL_LIGHTING);
glEnable(GL_COLOR_MATERIAL);
how to simulate that in WebGL?
Here is a blinn-phong shader which emulates the OpenGL fixed function pipeline for per vertex lighting. By default this is equivalent to having glEnable(GL_COLOR_MATERIAL) and glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE) enabled. You can emulate glColorMaterial by setting the uniforms to 1.0 which will cause material parameters track the current color instead of those set by glMaterial.
#version 120
////////////////////////////////////////////////////////////////////////////////
// http://www.glprogramming.com/red/chapter05.html //
// //
// color = (matEmission + globalAmbient * matAmbient) + //
// AttenuationFactor( 1.0 / ( Kc + Kl*d + Kq*d^2 ) ) * //
// [ (lightAmbient * matAmbient) + //
// (max(N.L,0) * lightDiffuse * matDiffuse) + //
// (max(N.H,0)^matShininess * lightSpecular * matSpecular) ] //
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
// Uniforms //
////////////////////////////////////////////////////////////////////////////////
uniform float uColorMaterialAmbient = 1.0;
uniform float uColorMaterialDiffuse = 1.0;
uniform float uColorMaterialEmission = 0.0;
uniform float uColorMaterialSpecular = 0.0;
////////////////////////////////////////////////////////////////////////////////
// Main //
////////////////////////////////////////////////////////////////////////////////
void main(void)
{
vec4 matAmbient = mix(gl_FrontMaterial.ambient, gl_Color, uColorMaterialAmbient);
vec4 matDiffuse = mix(gl_FrontMaterial.diffuse, gl_Color, uColorMaterialDiffuse);
vec4 matEmission = mix(gl_FrontMaterial.emission, gl_Color, uColorMaterialEmission);
vec4 matSpecular = mix(gl_FrontMaterial.specular, gl_Color, uColorMaterialSpecular);
// Transform normal into eye space. gl_NormalMatrix is the transpose of the
// inverse of the upper leftmost 3x3 of gl_ModelViewMatrix.
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
// Calculate emission and global ambient light
vec4 emissionAmbient = matEmission + (gl_LightModel.ambient * matAmbient);
// Calculate ambient
vec4 lightAmbient = gl_LightSource[0].ambient * matAmbient;
// Transform the vertex into eye space
vec4 eyeVertex = gl_ModelViewMatrix * gl_Vertex;
vec3 eyeLightDir = gl_LightSource[0].position.xyz - eyeVertex.xyz;
float dist = length(eyeLightDir);
eyeLightDir = normalize(eyeLightDir);
// No attenuation for a directional light
float attenuationFactor = 1.0 / (gl_LightSource[0].constantAttenuation
+ gl_LightSource[0].linearAttenuation * dist
+ gl_LightSource[0].quadraticAttenuation * dist * dist);
// Calculate lambert term
float NdotL = max(dot(eyeNormal, eyeLightDir), 0.0);
// Calculate diffuse
vec4 lightDiffuse = NdotL * (gl_LightSource[0].diffuse * matDiffuse);
// Calculate specular
vec4 lightSpecular = vec4(0.0);
if ( NdotL > 0.0 )
{
float NdotHV = max(dot(eyeNormal, gl_LightSource[0].halfVector.xyz), 0.0);
lightSpecular = pow(NdotHV, gl_FrontMaterial.shininess) * (gl_LightSource[0].specular * matSpecular);
}
gl_FrontColor = emissionAmbient + attenuationFactor * (lightAmbient + lightDiffuse + lightSpecular);
gl_Position = ftransform();
}
How can I perform the following image processing tasks using OpenGL ES 2.0 shaders?
Colorspace transform ( RGB/YUV/HSL/Lab )
Swirling of the image
Converting to a sketch
Converting to an oil painting
I just added filters to my open source GPUImage framework that perform three of the four processing tasks you describe (swirling, sketch filtering, and converting to an oil painting). While I don't yet have colorspace transforms as filters, I do have the ability to apply a matrix to transform colors.
As examples of these filters in action, here is a sepia tone color conversion:
a swirl distortion:
a sketch filter:
and finally, an oil painting conversion:
Note that all of these filters were done on live video frames, and all but the last filter can be run in real time on video from iOS device cameras. The last filter is pretty computationally intensive, so even as a shader it takes ~1 second or so to render on an iPad 2.
The sepia tone filter is based on the following color matrix fragment shader:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform lowp mat4 colorMatrix;
uniform lowp float intensity;
void main()
{
lowp vec4 textureColor = texture2D(inputImageTexture, textureCoordinate);
lowp vec4 outputColor = textureColor * colorMatrix;
gl_FragColor = (intensity * outputColor) + ((1.0 - intensity) * textureColor);
}
with a matrix of
self.colorMatrix = (GPUMatrix4x4){
{0.3588, 0.7044, 0.1368, 0},
{0.2990, 0.5870, 0.1140, 0},
{0.2392, 0.4696, 0.0912 ,0},
{0,0,0,0},
};
The swirl fragment shader is based on this Geeks 3D example and has the following code:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform highp vec2 center;
uniform highp float radius;
uniform highp float angle;
void main()
{
highp vec2 textureCoordinateToUse = textureCoordinate;
highp float dist = distance(center, textureCoordinate);
textureCoordinateToUse -= center;
if (dist < radius)
{
highp float percent = (radius - dist) / radius;
highp float theta = percent * percent * angle * 8.0;
highp float s = sin(theta);
highp float c = cos(theta);
textureCoordinateToUse = vec2(dot(textureCoordinateToUse, vec2(c, -s)), dot(textureCoordinateToUse, vec2(s, c)));
}
textureCoordinateToUse += center;
gl_FragColor = texture2D(inputImageTexture, textureCoordinateToUse );
}
The sketch filter is generated using Sobel edge detection, with edges shown in varying grey shades. The shader for this is as follows:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform mediump float intensity;
uniform mediump float imageWidthFactor;
uniform mediump float imageHeightFactor;
const mediump vec3 W = vec3(0.2125, 0.7154, 0.0721);
void main()
{
mediump vec3 textureColor = texture2D(inputImageTexture, textureCoordinate).rgb;
mediump vec2 stp0 = vec2(1.0 / imageWidthFactor, 0.0);
mediump vec2 st0p = vec2(0.0, 1.0 / imageHeightFactor);
mediump vec2 stpp = vec2(1.0 / imageWidthFactor, 1.0 / imageHeightFactor);
mediump vec2 stpm = vec2(1.0 / imageWidthFactor, -1.0 / imageHeightFactor);
mediump float i00 = dot( textureColor, W);
mediump float im1m1 = dot( texture2D(inputImageTexture, textureCoordinate - stpp).rgb, W);
mediump float ip1p1 = dot( texture2D(inputImageTexture, textureCoordinate + stpp).rgb, W);
mediump float im1p1 = dot( texture2D(inputImageTexture, textureCoordinate - stpm).rgb, W);
mediump float ip1m1 = dot( texture2D(inputImageTexture, textureCoordinate + stpm).rgb, W);
mediump float im10 = dot( texture2D(inputImageTexture, textureCoordinate - stp0).rgb, W);
mediump float ip10 = dot( texture2D(inputImageTexture, textureCoordinate + stp0).rgb, W);
mediump float i0m1 = dot( texture2D(inputImageTexture, textureCoordinate - st0p).rgb, W);
mediump float i0p1 = dot( texture2D(inputImageTexture, textureCoordinate + st0p).rgb, W);
mediump float h = -im1p1 - 2.0 * i0p1 - ip1p1 + im1m1 + 2.0 * i0m1 + ip1m1;
mediump float v = -im1m1 - 2.0 * im10 - im1p1 + ip1m1 + 2.0 * ip10 + ip1p1;
mediump float mag = 1.0 - length(vec2(h, v));
mediump vec3 target = vec3(mag);
gl_FragColor = vec4(mix(textureColor, target, intensity), 1.0);
}
Finally, the oil painting look is generated using a Kuwahara filter. This particular filter is from the outstanding work of Jan Eric Kyprianidis and his fellow researchers, as described in the article "Anisotropic Kuwahara Filtering on the GPU" within the GPU Pro book. The shader code from that is as follows:
varying highp vec2 textureCoordinate;
uniform sampler2D inputImageTexture;
uniform int radius;
precision highp float;
const vec2 src_size = vec2 (768.0, 1024.0);
void main (void)
{
vec2 uv = textureCoordinate;
float n = float((radius + 1) * (radius + 1));
vec3 m[4];
vec3 s[4];
for (int k = 0; k < 4; ++k) {
m[k] = vec3(0.0);
s[k] = vec3(0.0);
}
for (int j = -radius; j <= 0; ++j) {
for (int i = -radius; i <= 0; ++i) {
vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
m[0] += c;
s[0] += c * c;
}
}
for (int j = -radius; j <= 0; ++j) {
for (int i = 0; i <= radius; ++i) {
vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
m[1] += c;
s[1] += c * c;
}
}
for (int j = 0; j <= radius; ++j) {
for (int i = 0; i <= radius; ++i) {
vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
m[2] += c;
s[2] += c * c;
}
}
for (int j = 0; j <= radius; ++j) {
for (int i = -radius; i <= 0; ++i) {
vec3 c = texture2D(inputImageTexture, uv + vec2(i,j) / src_size).rgb;
m[3] += c;
s[3] += c * c;
}
}
float min_sigma2 = 1e+2;
for (int k = 0; k < 4; ++k) {
m[k] /= n;
s[k] = abs(s[k] / n - m[k] * m[k]);
float sigma2 = s[k].r + s[k].g + s[k].b;
if (sigma2 < min_sigma2) {
min_sigma2 = sigma2;
gl_FragColor = vec4(m[k], 1.0);
}
}
}
Again, these are all built-in filters within GPUImage, so you can just drop that framework into your application and start using them on images, video, and movies without having to touch any OpenGL ES. All the code for the framework is available under a BSD license, if you'd like to see how it works or tweak it.
You could start by checking out this list of shaders here. If you want to dig in a bit more, I'd recommend you check out the orange book found here.