In this project, I implemented several vertex/fragment shaders and post-processing shaders in OpenGL pipeline.
Surface Shaders
Below are some examples of effects implemented in vertex/fragment shader .
Matcap Reflection Shader
The basic idea of this shader is to map the (x, y) coordinates of the surface normal of a fragment to a point in the circular matcap texture. So, if the (x, y) is (-1, 0), that would map to the leftmost point of the circle.
The uv coordinate mapping is computed in vertex shader.
Color Gradient (Iridescent) Shader
We can shade the model using a prodedurally-generated color palette based on cosine curves with different periods. In this example I use four colors as a palette. Then, map this palette to the surface of the provided models by using the Lambertian dot product as the t value in this formula: color(t) = a + b * cos(2 * PI * (c * t + d)). In this case, the interpolator t is N_dot_L.
Custom vertex deformation shader & Toon shading
The current position of each vertex is the interpolation of the model’s original vertex position and a sphere. To get the position of the sphere, we can simply normalize the vertex position in model space so their distance to the center pivot of the model is equal to 1, which is a sphere.
In vertex shader:
t = smoothstep(0.0, 0.7, cos(t) * 0.5 + 0.5);
vec4 newPos = mix(vs_Pos, vec4(normalize(vs_Pos.xyz) * 3.0, 1.0), t * t);
In the fragment shader I also used a gradient shading. To achieve the toon effect, which is basically distinctive edges between two palette colors, the floor() fucntion is used. A simple outline determination is used, which is to check the angle between the eye direction and normal direction.
Post-process Shaders
All post-process shaders are gragment shaders. They takes in the rendered scene as a texture and modify their pixel colors.
Greyscale and vignette shader
For greyscale effect, we need to get a grey value based on the rgb value of the original pixel color. The green color should be a little bit more weighted because human eyes are more sensitive to the green color.
The traditional way was to only compute one gray value and set the final color as (0, grey, 0), but I would like to tilt it to a yellowish tone so the scene will have a western vibe.
To achieve this, I slightly increase the r channel value.
float red = 0.21 * ori_color.r;
float green = 0.72 * ori_color.g;
float blue = 0.07 * ori_color.b;
For vignette effect, all we have to do is substracting some value from the final value so it will appear more black.
The final fragment shader will look like this: (with many magic numbers…)
vec4 ori_color = texture2D(u_RenderedTexture, fs_UV);
float red = 0.21 * ori_color.r;
float green = 0.72 * ori_color.g;
float blue = 0.07 * ori_color.b;
float distance = distance(fs_UV, vec2(0.5, 0.5));
float grey = red + green + blue - vignette(distance);
color.r = grey + 0.2;
color.g = grey + 0.1;
color.b = grey;
Gaussian Blur Shader
Use an 11*11 kernel to
Sobel Filter Shader
Fake Bloom Shader
noise-based post-process shader