Vincent Kwok

Starting GLSL language

I have been looking into several Youtube rabbit holes recently and found a few interesting videos on shader related coding languages, I found a few quick tutorials on the topic. Looking deeper into shaders and rendering, I’ve remembered that although I have a graphics card in my desktop computer, I have not really used it yet for anything besides gaming and some small machine learning training processes. Although I assume it will take a long period of practice before I would put any meaningful load onto my GPU, I think it would be cool to say that I have experience in computer generated graphics. I often see computer generated images on the internet and can see many applications for them. Visual synthesizers for music and entertainment purposes could be a start. Perhaps getting into 3-D rendering and lighting though it looks far out of my scope of knowledge for now. Anyways, combining this with some electrical engineering principals on making music and maybe I can make something interesting in the future. Hear’s what imagine, an entire concert played by my instruments and visuals. Sounds cool.

Small GLSL ^One of my final products!

I found a really simple and intuitive website to get started on GLSL (OpenGL shading language), seems pretty similar syntactically to Java and with a heavy focus on using arithmetic parameter manipulation to change how images are generated. I found a website that give code templates for you to make a shape such as a circle. I have been challenging myself by incrementally increasing the difficulty of the graphics I am making, at the time of writing this, I have already learned a few mathematical principles that I honestly should have known before but are great to know now.

Firstly, I created a circle and challenged myself to make a circle with a red background. Essentially, this requires the code to have a distance function from a predetermined point to create a circle. It appears that using step functions is a strong way to prevent making gradient colors in GLSL, having a step function makes a clear circle to be formed, where all values inside can be multiplied to make a circle of a certain color. Since I wanted to manipulate the background instead of the circle, I realized I had to horizontally flip the step function, which ended up in me learning that all you have to do to flip any function is to make all it’s inputs negative, from a programming perspective, this is very easy to implement as well!

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    // center is considered as (0, 0), and top right corner is (1,1)
    vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
       uv.x *= iResolution.x/iResolution.y; // makes sure the images we create seem like they are on a square canvas even when they are actually on a rectangle
     
    float d = length(uv);
    d -= 0.5;
    //d = abs(d);
    d = step( 0.1, d);
    // Output to screen
    fragColor = vec4(1.0, d, d, 1.0);
}

imageimage

^ Left/Upper image (Japanese Flag) is created with the code provided, setting both step() parameters negative causes right/lower image to show!

After this I decided to create some sort of temporal motion, following the idea of circles, making concentric circles seems doable, combining the smoothstep() and sin() functions allows me to make concentric circles on a blurry gradient that is not as defined as a normal step function. abs() creates hollow circles. By adding a time variable into the sine function and some small scaling, I was able to make a sort of hypnotizing animation, this is getting really interesting!

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    // center is considered as (0, 0), and top right corner is (1,1)
    vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
       uv.x *= iResolution.x/iResolution.y; // makes sure the images we create seem like they are on a square canvas even when they are actually on a rectangle
     
    float d = length(uv);
    d = sin(d*7.0 + iTime)/7.0; // here You can multipy the d inside sin to create concentric circles, dividing by 7 to create more gradients
    d = abs(d);
    d = smoothstep( 0.0, 0.1, d);
    // Output to screen
    fragColor = vec4(1.0, d, d, 1.0);
}

^ red hypnotizing animation!

After doing this, I fooled around more with the code, using a rational function instead of a smooth step, a smaller denominator allows for the shown colors to seem more vibrant and visible, a larger denominator like 1 will cause an all white screen, as 1/1 is still 1 which in terms of rgb, creates a purely solid coloration. If I cast all the colors through a filter as seen in col, the colors become more “neon” and tinted. As long as all the parameters are given values that are relatively close in magnitude to each other like 1.5, 1.0, and 3.0, it seems colors are neon due to the fact that if all red, green, and blue are combined white is created, giving a natural bright visual. I thought I would stick to this as it looks quite nice and cool for applications that I touched on previously at the start of this post.

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    // center is considered as (0, 0), and top right corner is (1,1)
    vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
       uv.x *= iResolution.x/iResolution.y; // makes sure the images we create seem like they are on a square canvas even when they are actually on a rectangle
     
    float d = length(uv);
    
    vec3 col = vec3(1.5, 1.0, 3.0); // interestingly, the glow only occurs when r, g, and b are all given values
    d = sin(d*10.0 + iTime)/7.0; // here You can multipy the d inside sin to create concentric circles, dividing by 7 to create more gradients
    d = abs(d);
    d = 0.02 / d;
    col *= d;
    // Output to screen
    fragColor = vec4(col, 1.0);
}

^ neon hypnotizing animation!

Next I added some color gradients to the code. Using some examples I found online and this very helpful website explaining how to use linear cosine transformations to create color gradients that are present for the animation.

The equation is in the form: \(color(t) = a + b * cos(2\pi(c*d+t))\)

Using this equation with the parameters set in a, b, c, d allowed me to create my own color gradient, I attempted to customize a gradient using this website but it did not come out as expected, perhaps I simply dont know what I am doing. I talk about my process in the video attached and you can see the gradient in action! I also fool around with the code and show how easy it is to locate values in the code that have a very fascinating inputs. This small side project has become really mind boggling! Apologies for the low audio and bad screen recording.

vec3 palette( float t) {
    vec3 a = vec3(0.468, 0.528, 0.500);
    vec3 b = vec3(0.468, 0.528, 0.500);
    vec3 c = vec3(0.468, 0.528, 0.500);
    vec3 d = vec3(0.468, 0.528, 0.500);
    
    return a + b * cos (6.28318 * (c*t+d));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Normalized pixel coordinates (from 0 to 1)
    // center is considered as (0, 0), and top right corner is (1,1)
    vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
       uv.x *= iResolution.x/iResolution.y; // makes sure the images we create seem like they are on a square canvas even when they are actually on a rectangle
     
    float d = length(uv);
    
    vec3 col = palette(d); // interestingly, the glow only occurs when r, g, and b are all given values
    d = sin(d*10.0 + iTime)/6.0; // here You can multipy the d inside sin to create concentric circles, dividing by 7 to create more gradients
    d = abs(d);
    d = 0.02 / d;
    col *= d;
    // Output to screen
    fragColor = vec4(col, 1.0);
}

To end this tiny project, I have decided to rapidly increase in complexity of the code. I added many things which I explain a little more in the video provided below. This code first uses the fract() function which takes the result of the function and splits it into identical copies onto the screen, these copies can be moved and offset as I have done in the code. I wanted to change the behavior of these fractals based on their position, By using uv0, the changing distance from the center of the canvas causes the behavior of the fractal to change, this can cause really interesting behaviors that are starting to become very unpredictable. I noticed that many forms of example code seem to prefer either the exp() of log() functions to add interesting patterns in their graphics so i did the same, taking uv0 in as a parameter. Using this website, I messed around with putting different functions into my code, I noticed functions that run through the small positive numbers in (x,y) coordinates were very interesting. I found success in log, exp, asinh, and acosh. I then added a for loop, in my video i demonstarte that incresing the number of loops also increases the density/complexity of the graphics, which seems to be consistent with computer sciences idea of added complexity as more loops are added. I then incorporated i into my code, causing even more temporal variablility and here we have something beautiful! I thought the blackhole effect I created by including i into the for loop was amazing. I really do recomend taking my code to the shader toy website I linked before and messing with some values like I did in the video. It was really awesome!

Small GLSL

vec3 palette( float t) {
    vec3 a = vec3(0.468, 0.528, 0.500);
    vec3 b = vec3(0.968, 0.928, 0.900);
    vec3 c = vec3(0.468, 0.368, 0.500);
    vec3 d = vec3(0.168, 0.128, 0.100);
    
    return  a + b * cos ( 6.28318 * (c*t+d));
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 uv = fragCoord/iResolution.xy * 2.0 - 1.0;
       uv.x *= iResolution.x/iResolution.y; // makes sure the images we create seem like they are on a square canvas even when they are actually on a rectangle
    vec2 uv0 = uv;
    vec3 finalColor = vec3(0.0);
    
    for (float i = 0.0; i < 3.0; i++) {
    
    uv = fract(uv * 2.0) - 0.5;
    float d = length(uv)* log(length (uv0));
    
    vec3 col = palette(length(uv0) + iTime * i * 0.3);
    d = cos(d * 5.0 * i + iTime)/7.0;
    d = abs(d);
    d = pow(0.025 / d, 2.0);
    finalColor += d * d * col;
    
    }
    
    fragColor = vec4(finalColor, 1.0);
}

Link to all the code for this post