introduction

2 - Introduction to shaders

An introduction to GLSL shaders with a very basic example

The Canvas, The Colors, The Code

Welcome back, art-code aficionados! It's Walter Vikrus, your resident shader shaman, ready to guide you through the mystical realms of GLSL. In our last work, we set the stage for our shader art adventure. Today, we're getting our hands dirty with some actual code, focusing entirely on the fragment shaderβ€”the heart and soul of our artistic expression.

First, let's talk canvas. In the world of shader art, our canvas isn't a rectangle of fabric; it's a 2D plane in normalized device coordinates (NDC). This means our canvas stretches from (-1, -1) in the bottom-left to (1, 1) in the top-right. Every pixel on your screen maps to a point in this space. It's like graphing in math class πŸ“, but instead of plotting points, we're painting dreams! 🌈

Now, let's dive straight into our fragment shader, where all the magic happens:

             precision mediump float;
uniform vec2 u_resolution;
uniform float u_time;

void main() {
  vec2 v = gl_FragCoord.xy/u_resolution.xy;
  v.x *= u_resolution.x/u_resolution.y;
  v = v * 2.0 - 1.0;  // Map to NDC
  
  vec3 color = vec3(0.0);
  gl_FragColor = vec4(color, 1.0);
}

Don't worry if this looks like alien language now; we'll decipher it together:

precision mediump float;

We're telling the GPU, "Hey, we don't need super high precision for our floats." It's like saying we're painting with acrylics, not doing brain surgery 🎨🧠.

uniform vec2 u_resolution;

This is our canvas size. The GPU tells us how many pixels wide and tall our masterpiece will be πŸ“.

uniform float u_time;

An ever-increasing value. Great for animation! Think of it as our artistic heartbeat πŸ’“.

vec2 v = gl_FragCoord.xy/u_resolution.xy;

This normalizes our pixel's position to [0, 1]. Like saying "this pixel is 30% from the left and 70% from the bottom." πŸ“

v = v * 2.0 - 1.0;

We're mapping our space to NDC (-1 to 1). Our true artist's canvas! 🎨

v.x *= u_resolution.x/u_resolution.y;

Finally, we adjust for aspect ratio. No squished art here! πŸ–ΌοΈβ†”οΈ

vec3 color = vec3(0.0);

Our color, in RGB. (0.0, 0.0, 0.0) is black. We'll spice this up soon! ⬛

gl_FragColor = vec4(color, 1.0);

We're setting our pixel's color. The 1.0 is alpha (opacity) πŸ”’.

That's it! This compact block of code is your complete fragment shader. In GLSL, the fragment shader runs once for every pixel, deciding its color. It's like having millions of tiny artists πŸ‘¨β€πŸŽ¨πŸ‘©β€πŸŽ¨πŸ‘¨β€πŸŽ¨πŸ‘©β€πŸŽ¨πŸ‘¨β€πŸŽ¨, each responsible for painting a single dot of your masterpiece, all working in perfect synchronization.

Right now, our shader is the digital equivalent of a black canvas. Groundbreaking, I know. πŸ•ΆοΈ But fear not! This minimalist start is intentional. We're setting up our artistic workspace, ensuring our canvas is correctly scaled and oriented. It's like preparing a pristine black canvas, stretching it just right, before we unleash our creativity.

In our next post, we'll breathe life into this monochrome canvas with the vibrant pulse of shader artistry: color manipulation. We'll learn to mix, blend, and transition colorsβ€”creating sunsets πŸŒ…, ocean depths 🌊, and aurora skies πŸŒŒβ€”all using simple mathematical functions. We're not just coding; we're becoming digital colorists, each line of code a squeeze from our infinite paint tubes. πŸŽ¨πŸ”’βœ¨

And for those who love math as much as I do (there are dozens of us! DOZENS! πŸ€“), we'll delve into the enchanting world of gradients and alpha blending. These aren't just color ramps and transparency values; they're the very equations of light's journeyβ€”its birth in vibrant hues 🌈, its travel through mediums πŸ”­, and its gentle fading into shadow πŸŒ—. We'll turn oscillations into rainbows, distances into soft fades. Get ready to see math not just calculate color, but caress it. πŸ–ŒοΈπŸ“βœ¨

And if you can't wait, homework time! πŸ“š Play with our basic shader by yourself:

  1. Change vec3(0.0) to vec3(1.0, 0.0, 0.0). What happens? πŸ”΄

  2. Try vec3(v.x, 0.0, 0.0) or vec3(v.x, v.y, 0.0). Notice anything cool? 🌈

  3. Add u_time to the mix: vec3(sin(u_time), v.x, v.y). Groovy, right? πŸ•Ί

Each change you make to color transforms your entire canvas. That's the power of shaders: with a single line of code, you command millions of pixels. It's pointillism at the speed of light! 🎨⚑

Remember, in the realm of shader art, every line of code is a brushstroke, every function a technique. We're not just programmers or artists; we're digital alchemists, transforming raw mathematics into visual gold. Our fragment shader is our philosopher's stone, turning the lead of coordinate pairs into the gold of vivid imagery. πŸ§™β€β™‚οΈβœ¨

Next time: Shapes, Distance, and the Space Between. Get ready to see your code come alive with geometric poetry! We'll use our fragment shader to sculpt forms from pure math, painting not with strokes, but with equations. Until then, keep your vectors normalized and your gradients smooth. πŸŽ¨πŸ’»βœ¨


If you enjoyed this virtual work, please consider dropping me a line or making a donation to support my work. Your feedback and support mean a lot and help keep this project going. Thank you!

Reach out back

Featured

June 2024

Basic gradients

Basic examples of gradients

Hello, creative coders! πŸŽ¨πŸ’» In this virtual work, we're going to dive into the world of GLSL (OpenGL Shading Language) and explore how to create stunning gradients using this powerful language. Get ready to add some vibrant and dynamic flair to your shader art!

Before we get started, let's quickly review what GLSL is all about. GLSL is a high-level shading language used for programming graphics processors (GPUs). It allows you to write shaders, which are small programs that run on the GPU and determine how pixels are rendered on the screen. Shaders are essential for creating real-time graphics, visual effects, and procedural art.