Edge shader using GLSL-ES - graphics

I'm trying to do an edge shader but I have places where the edges are not shown at all. The behaviour is shown in the following image.
As you see the way I look at the model, doesn't show edges, but the other places are very sharp and looks good.
here is my attempt
uniform mat4 projection_matrix;
varying vec3 normFrag;
void main()
{
vec4 pos_transformed = modelViewProjectionMatrix * vertex;
vec3 normalizedNormal = normalize(normal);
vec3 norm = mat3(normalMatrix)* normalizedNormal ;
norm.y *=projection_matrix[2][3];
norm.x *= projection_matrix[3][2];
//norm *=-1.0;
norm = normalize(norm);
pos_transformed.xy -= pos_transformed.z*norm.xy*0.005;
gl_Position = vec4(pos_transformed);
}

Related

How to make elevated parts of a steep plane seem darker than the lower surface?

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!

pixi.js: how to draw outline of container while keeping its content transparent

I have a container with several graphics containing circles. I would like to only render this container's outline, without the graphics themselves.
I managed to draw the outlines using OutlineFilter, and I managed to make the container transparent using AlphaFilter, but not both at the same time, no matter in which order I added the filters.
That is technically not possible like you intend to do it. One shader (pixi.js filter) doesn't know about the previous shader, such as where the outline was painted or what is the original texture alpha.
Alternatively you can create a new filter with a new shader that achieves that effect. I'm basing this on the OutlineFilter:
varying vec2 vTextureCoord;
uniform sampler2D uSampler;
uniform vec2 thickness;
uniform vec4 outlineColor;
uniform vec4 filterClamp;
const float DOUBLE_PI = 3.14159265358979323846264 * 2.;
void main(void) {
vec4 ownColor = texture2D(uSampler, vTextureCoord);
vec4 curColor;
float maxAlpha = 0.;
vec2 displaced;
for (float angle = 0.; angle <= DOUBLE_PI; angle += 0.1) {
displaced.x = vTextureCoord.x + thickness.x * cos(angle);
displaced.y = vTextureCoord.y + thickness.y * sin(angle);
curColor = texture2D(uSampler, clamp(displaced, filterClamp.xy, filterClamp.zw));
maxAlpha = max(maxAlpha, curColor.a);
}
float resultAlpha = maxAlpha * step(ownColor.a, 0.0) > 0. ? 1. : 0.0;
gl_FragColor = vec4(outlineColor.rgb * resultAlpha, resultAlpha);
}
Example result as in the pixi-filters demos:

Compute Shader Corrupting Vertex Buffer

I'm making a tutorial for computing tangents and bitangents in a WGPU (Vulkan GLSL) compute shader. I'm creating the vertex buffer on the CPU from a .obj I made in blender.
Here's the code for the compute shader.
#version 450
#define VERTICES_PER_TRIANGLE 3
layout(local_size_x = VERTICES_PER_TRIANGLE) in;
// Should match the struct in model.rs
struct ModelVertex {
vec3 position;
vec2 tex_coords;
vec3 normal;
vec3 tangent;
vec3 bitangent;
};
layout(std140, set=0, binding=0) buffer SrcVertexBuffer {
ModelVertex srcVertices[];
};
layout(std140, set=0, binding=1) buffer DstVertexBuffer {
ModelVertex dstVertices[];
};
layout(std140, set=0, binding=2) buffer IndexBuffer {
uint Indices[];
};
void main() {
uint index = gl_GlobalInvocationID.x;
// Grab the indices for the triangle
uint i0 = Indices[index];
uint i1 = Indices[index + 1];
uint i2 = Indices[index + 2];
// Grab the vertices for the triangle
ModelVertex v0 = srcVertices[i0];
ModelVertex v1 = srcVertices[i1];
ModelVertex v2 = srcVertices[i2];
// Grab the position and uv components of the vertices
vec3 pos0 = v0.position;
vec3 pos1 = v1.position;
vec3 pos2 = v2.position;
vec2 uv0 = v0.tex_coords;
vec2 uv1 = v1.tex_coords;
vec2 uv2 = v2.tex_coords;
// Calculate the edges of the triangle
vec3 delta_pos1 = pos1 - pos0;
vec3 delta_pos2 = pos2 - pos0;
// This will give us a direction to calculate the
// tangent and bitangent
vec2 delta_uv1 = uv1 - uv0;
vec2 delta_uv2 = uv2 - uv0;
// Solving the following system of equations will
// give us the tangent and bitangent.
// delta_pos1 = delta_uv1.x * T + delta_u.y * B
// delta_pos2 = delta_uv2.x * T + delta_uv2.y * B
// Luckily, the place I found this equation provided
// the solution!
float r = 1.0 / (delta_uv1.x * delta_uv2.y - delta_uv1.y * delta_uv2.x);
vec3 tangent = (delta_pos1 * delta_uv2.y - delta_pos2 * delta_uv1.y) * r;
vec3 bitangent = (delta_pos2 * delta_uv1.x - delta_pos1 * delta_uv2.x) * r;
// We'll use the same tangent/bitangent for each vertex in the triangle
dstVertices[i0].tangent = tangent;
dstVertices[i1].tangent = tangent;
dstVertices[i2].tangent = tangent;
dstVertices[i0].bitangent = bitangent;
dstVertices[i1].bitangent = bitangent;
dstVertices[i2].bitangent = bitangent;
}
This leads to an image like the following.
The problem occurs in the last six lines.
dstVertices[i0].tangent = tangent;
dstVertices[i1].tangent = tangent;
dstVertices[i2].tangent = tangent;
dstVertices[i0].bitangent = bitangent;
dstVertices[i1].bitangent = bitangent;
dstVertices[i2].bitangent = bitangent;
If I delete these lines, the output is fine (albeit the lightings all wrong due to the tangent and bitangent being a 0 vector).
Why is modifying the tangent and bitangent messing with the position of the vertices?
Here's the rest of the code for context. https://github.com/sotrh/learn-wgpu/tree/compute/code/intermediate/tutorial14-compute
EDIT:
Here's the code where I'm calling the compute shader.
let src_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::STORAGE,
});
let dst_vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Vertex Buffer", m.name)),
contents: bytemuck::cast_slice(&vertices),
// UPDATED!
usage: wgpu::BufferUsage::VERTEX | wgpu::BufferUsage::STORAGE,
});
let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some(&format!("{:?} Index Buffer", m.name)),
contents: bytemuck::cast_slice(&m.mesh.indices),
// UPDATED!
usage: wgpu::BufferUsage::INDEX | wgpu::BufferUsage::STORAGE,
});
let binding = BitangentComputeBinding {
dst_vertex_buffer,
src_vertex_buffer,
index_buffer,
num_elements: m.mesh.indices.len() as u32,
};
// Calculate the tangents and bitangents
let calc_bind_group = self.binder.create_bind_group(
&binding,
device,
Some("Mesh BindGroup")
);
let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
label: Some("Tangent and Bitangent Calc"),
});
{
let mut pass = encoder.begin_compute_pass();
pass.set_pipeline(&self.pipeline);
pass.set_bind_group(0, &calc_bind_group, &[]);
pass.dispatch(binding.num_elements as u32 / 3, 1, 1);
}
queue.submit(std::iter::once(encoder.finish()));
device.poll(wgpu::Maintain::Wait);
The shader is supposed to loop through all the triangles in the mesh and compute the tangent and bitangent using the positon, and uv coordinates of the vertices of that triangle. I'm guessing that the vertices that are shared with multiple triangles are getting written to at the same time, causing this memory corruption.
I don't think it's a problem with shaders elsewhere, as I'm using the same model for the light, and the vertex shader responsible for that doesn't use the tangent and bitangent at all.
#version 450
layout(location=0) in vec3 a_position;
layout(location=0) out vec3 v_color;
layout(set=0, binding=0)
uniform Uniforms {
vec3 u_view_position;
mat4 u_view_proj;
};
layout(set=1, binding=0)
uniform Light {
vec3 u_position;
vec3 u_color;
};
// Let's keep our light smaller than our other objects
float scale = 0.25;
void main() {
vec3 v_position = a_position * scale + u_position;
gl_Position = u_view_proj * vec4(v_position, 1);
v_color = u_color;
}
Looking at the vertex data in Render Doc shows that they position data is getting messed up.
Also here's what the cubes look like if I set the tangent and bitangent to vec3(0, 1, 0).
My only guess is that storage buffers have a byte alignment rule that I'm unaware of. I know that's the case for uniform buffers, but I'm using storage buffers for my instancing code, and that doesn't seem to have any issues.
Turns out Vulkan style GLSL aligns to the largest field in the struct when using std430.
https://github.com/KhronosGroup/glslang/issues/264
In my case it's vec3. The vec2 tex_coord is throwing it off causing the shader to pull data from the wrong parts of the vertex buffer.
The fix was to change the struct in model_load.comp to specify the individual components instead.
struct ModelVertex {
float x; float y; float z;
float uv; float uw;
float nx; float ny; float nz;
float tx; float ty; float tz;
float bx; float by; float bz;
};
Now the base alignment is a float (4 bytes), and the shader reads the vertex buffer data properly.
I'm aware there's a packed layout, but shaderc doesn't allow me to use that for reasons beyond me. Honestly I think this is quite annoying, and cumbersome, but it works.
There's still a flaw in the result. There's some banding on the edge faces of the cube. My guess is that it's do a single vertex sharing multiple triangles, but that's another problem that I'll have to look into later.

Is there a faked antialiasing algorithm using the depth buffer?

Lately I implemented the FXAA algorithm into my OpenGL application. I haven't understand this algorithm completely by now but I know that it uses contrast data of the final image to selectively apply blurring. As a post processing effect that makes sense. B since I use deferred shading in my application I already have a depth texture of the scene. Using that it might be much easier and more precise to find edges for applying blur there.
So is there a known antialiasing algorithm using the depth texture instead of the final image to find the edges? By fakes I mean an antialiasing algorithm based on a pixel basis instead of a vertex basis.
After some research I found out that my idea is widely used already in deferred renderers. I decided to post this answer because I came up with my own implementation which I want to share with the community.
Based on the gradient changes of the depth and the angle changes of the normals, there is blurring applied to the pixel.
// GLSL fragment shader
#version 330
in vec2 coord;
out vec4 image;
uniform sampler2D image_tex;
uniform sampler2D position_tex;
uniform sampler2D normal_tex;
uniform vec2 frameBufSize;
void depth(out float value, in vec2 offset)
{
value = texture2D(position_tex, coord + offset / frameBufSize).z / 1000.0f;
}
void normal(out vec3 value, in vec2 offset)
{
value = texture2D(normal_tex, coord + offset / frameBufSize).xyz;
}
void main()
{
// depth
float dc, dn, ds, de, dw;
depth(dc, vec2( 0, 0));
depth(dn, vec2( 0, +1));
depth(ds, vec2( 0, -1));
depth(de, vec2(+1, 0));
depth(dw, vec2(-1, 0));
float dvertical = abs(dc - ((dn + ds) / 2));
float dhorizontal = abs(dc - ((de + dw) / 2));
float damount = 1000 * (dvertical + dhorizontal);
// normals
vec3 nc, nn, ns, ne, nw;
normal(nc, vec2( 0, 0));
normal(nn, vec2( 0, +1));
normal(ns, vec2( 0, -1));
normal(ne, vec2(+1, 0));
normal(nw, vec2(-1, 0));
float nvertical = dot(vec3(1), abs(nc - ((nn + ns) / 2.0)));
float nhorizontal = dot(vec3(1), abs(nc - ((ne + nw) / 2.0)));
float namount = 50 * (nvertical + nhorizontal);
// blur
const int radius = 1;
vec3 blur = vec3(0);
int n = 0;
for(float u = -radius; u <= +radius; ++u)
for(float v = -radius; v <= +radius; ++v)
{
blur += texture2D(image_tex, coord + vec2(u, v) / frameBufSize).rgb;
n++;
}
blur /= n;
// result
float amount = mix(damount, namount, 0.5);
vec3 color = texture2D(image_tex, coord).rgb;
image = vec4(mix(color, blur, min(amount, 0.75)), 1.0);
}
For comparison, this is the scene without any anti-aliasing.
This is the result with anti-aliasing applied.
You may need to view the images at their full resolution to judge the effect. In my view the result is adequate for the simple implementation. The best thing is that there are nearly no jagged artifacts when the camera moves.

How do you calculate the angle between two normals in glsl?

How do you calculate the angle between two normals in glsl? I am trying to add the fresnel effect to the outer edges of an object (combining that effect with phong shading), and I think that the angle is the only thing I am missing.
Fragment Shader:
varying vec3 N;
varying vec3 v;
void main(void) {
v = vec3(gl_ModelViewMatrix * gl_Vertex);
N = normalize(gl_NormalMatrix * gl_Normal);
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Vertex Shader:
varying vec3 N;
varying vec3 v;
void main(void) {
vec3 L = normalize(gl_LightSource[0].position.xyz - v);
vec3 E = normalize(-v);
vec3 R = normalize(-reflect(L,N));
vec4 Iamb = gl_FrontLightProduct[0].ambient
vec4 Idiff = gl_FrontLightProduct[0].diffuse * max(dot(N,L), 0.0);
vec4 Ispec = gl_FrontLightProduct[0].specular * pow(max(dot(R,E),0.0), gl_FrontMaterial.shininess);
vec4 Itot = gl_FrontLightModelProduct.sceneColor + Iamb + Idiff + Ispec;
vec3 A = //calculate the angle between the lighting direction and the normal//
float F = 0.33 + 0.67*(1-cos(A))*(1-cos(A))*(1-cos(A))*(1-cos(A))*(1-cos(A));
vec4 white = {1.0, 1.0, 1.0, 1.0};
gl_FragColor = F*white + (1.0-F)*Itot;
}
varying vec3
dot product between two vectors will return the cosine of the angle (in GLSL it's dot(a,b)). Taking arc-cosine of that will return angle in radians (in GLSL it's acos(x)).
Dot product is very cheap, arc-cosine is quite expensive.
However, Fresnel effect does not really need the angle. Just having dot result between the vectors is enough. There are many approximations for the Fresnel effect, one of the cheapest is just using the dot directly. Or squaring it (x*x), or raising to some other power.
In your shader above, it looks like you just want to raise dot to 5th power. Something like:
float oneMinusDot = 1.0 - dot(L, N);
float F = pow(oneMinusDot, 5.0);
From the dot product of two vectors you can get the cosine of the angle between them
cos A = DotProduct(v1, v2) / (Length(v1) * Length(v2))
Using this, you don't need to calculate the cosine when calculating F. Since your vectors are unit vectors, e.g., have length one, you can even avoid the division.

Resources