Shading Languages

HUD Project

A portfolio project that showcases proficiency in shaders, UI, and digital humans. 

shader-proj-hud-human.gif

Reference done in DCC: https://www.youtube.com/watch?v=r-l74gVYgM8

Getting Started

Lets ask Chatgpt by uploading an animated GIF of the radar done in a DCC to see if it can come up with the shader code itself.

hud-radar-perfect-loop-low-res.gif

Reference: Kinemaster Premium 3D Interface Radar 2 - Hologram Black Screen Ironman Hud Max Quality (4K) +Sound (youtube.com)

The following is the response on first try: 

hud-chatgpt-question.gif

RESPONSE:

To create a radar-like effect as seen in the GIF, we need to simulate several elements in ShaderToy:

  1. Rotating Radar Sweep: This is a rotating arc or line that moves around the radar.
  2. Circular Grid: We can use concentric circles and radial lines to build the structure of the radar.
  3. World Map Projection: We can omit the map for simplicity at first but simulate dots or blips on the radar.

Here's a basic approach to replicating this effect using ShaderToy:

Step-by-Step Breakdown:
  1. Circular Radar Grid: We will use mathematical functions to create circles and radial lines.
  2. Radar Sweep: A rotating arc that moves around the radar in sync with the iTime value in ShaderToy.
  3. Radar Dots/Blips: Simulated points that blink or pulse as the radar sweep passes over them.
// ShaderToy - Radar Simulation

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Convert fragCoord to normalized UV coordinates
    vec2 uv = fragCoord / iResolution.xy;
    uv = uv * 2.0 - 1.0;  // Center the UV coordinates (-1 to 1 range)
    uv.x *= iResolution.x / iResolution.y; // Correct for aspect ratio
    
    vec2 center = vec2(0.0, 0.0); // Radar center at (0, 0)
    float radius = length(uv); // Distance from center
    float angle = atan(uv.y, uv.x); // Angle in radians
    
    // Radar grid: Concentric circles
    float grid = smoothstep(0.02, 0.03, mod(radius * 10.0, 1.0));
    
    // Radar grid: Radial lines (create radial divisions)
    float radialLines = smoothstep(0.01, 0.015, abs(mod(angle + 3.14159, 3.14159 / 8.0) - (3.14159 / 16.0)));
    
    // Radar sweep (rotating arc)
    float sweepAngle = mod(iTime * 1.5, 6.28318); // Rotates with time
    float sweep = smoothstep(0.03, 0.0, abs(angle - sweepAngle)) * smoothstep(0.5, 0.3, radius); // Controls width
    
    // Radar blips: Simulated dots
    float blip = smoothstep(0.02, 0.0, length(uv - vec2(sin(iTime), cos(iTime)) * 0.5));
    
    // Combine the elements
    vec3 color = vec3(0.0);
    color += vec3(0.2, 0.8, 0.4) * (grid + radialLines); // Radar grid color
    color += vec3(0.0, 1.0, 0.5) * sweep; // Radar sweep color
    color += vec3(1.0, 0.2, 0.2) * blip; // Blip color
    
    // Output final color
    fragColor = vec4(color, 1.0);
}
Explanation:
Next Steps:

You can paste this code directly into ShaderToy to test and see the rotating radar sweep with blips and a grid.

hud-chatgpt-radar.gif

Fi

CURRENT WIP

https://www.shadertoy.com/view/43lyD8

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    // Set the center of the rings
    vec2 center = iResolution.xy * 0.5;

    // Define base radius and width for the big ring
    float bigRadius = 0.25 * iResolution.x;
    float bigRingWidth = 0.025 * iResolution.x;

    // Parameters for smaller rings
    float smallRadius = 0.26 * iResolution.x;
    float smallRingWidth = 0.005 * iResolution.x;

    // Parameters for the small white ring inside all rings
    float innerRadius = 0.12 * iResolution.x;
    float innerRingWidth = 0.005 * iResolution.x;

    // Calculate the distance from the current pixel to the center
    vec2 pos = fragCoord - center;
    float dist = length(pos);
    float angle = atan(pos.y, pos.x);

    // Normalize angle to [0, 1] range and divide into 12 segments
    angle = mod(angle + 2.0 * 3.14159265359, 2.0 * 3.14159265359) / (2.0 * 3.14159265359); // Normalize to [0, 1]
    float segment = angle * 12.0;  // 12 segments

    // Create transparent lines between the 12 segments
    float lineWidth = 0.03; // Width of the dividing lines
    float lines = smoothstep(0.0, lineWidth, abs(fract(segment) - 0.5));

    // Create the sequential blinking effect
    float blinkSpeed = 7.5; // Adjust speed of blinking
    float phaseShift = 1.0; // Time offset for sequential blinking

    // Blinking for the big ring (inner ring) starts first
    float bigRingBlink = 0.5 + 0.5 * sin(iTime * blinkSpeed);

    // Blinking for the smaller rings starts after the inner ring, with a phase shift
    float smallRing1Blink = 0.5 + 0.5 * sin(iTime * blinkSpeed - phaseShift);
    float smallRing2Blink = 0.5 + 0.5 * sin(iTime * blinkSpeed - 2.0 * phaseShift);
    float smallRing3Blink = 0.5 + 0.5 * sin(iTime * blinkSpeed - 3.0 * phaseShift);

    // Define the ring color (blinking between gray and white) for each ring
    vec3 bigRingColor = mix(vec3(0.5), vec3(1.0), bigRingBlink);
    vec3 smallRing1Color = mix(vec3(0.5), vec3(1.0), smallRing1Blink);
    vec3 smallRing2Color = mix(vec3(0.5), vec3(1.0), smallRing2Blink);
    vec3 smallRing3Color = mix(vec3(0.5), vec3(1.0), smallRing3Blink);

    // Define a static white color for the small inner ring
    vec3 innerRingColor = vec3(1.0, 1.0, 1.0);

    // Create the big central ring with hard edges and transparent lines
    float bigRing = step(bigRadius - bigRingWidth, dist) - step(bigRadius, dist);
    bigRing *= lines;

    // Create the three smaller rings with hard edges and transparent lines
    float smallRings = 0.0;

    // Small ring 1
    float smallDist = length(fragCoord - center);
    float smallRing1 = step(smallRadius - smallRingWidth, smallDist) - step(smallRadius, smallDist);
    smallRing1 *= lines;
    
    // Small ring 2
    float smallRing2 = step(0.27 * iResolution.x - smallRingWidth, smallDist) - step(0.27 * iResolution.x, smallDist);
    smallRing2 *= lines;

    // Small ring 3
    float smallRing3 = step(0.28 * iResolution.x - smallRingWidth, smallDist) - step(0.28 * iResolution.x, smallDist);
    smallRing3 *= lines;

    // Combine all small rings
    smallRings = smallRing1 + smallRing2 + smallRing3;

    // Create the small inner white ring
    float innerRing = step(innerRadius - innerRingWidth, dist) - step(innerRadius, dist);

    // Set the fragment color using the combined rings, with individual blinking for each ring
    vec3 finalColor = bigRingColor * bigRing + smallRing1Color * smallRing1 + smallRing2Color * smallRing2 + smallRing3Color * smallRing3 + innerRingColor * innerRing;
    fragColor = vec4(finalColor, 1.0);
}

hud-radar-perfect-loop-outside-shadertoy.gif

Additional Reference

Checked to see other HUDS on Shadertoy, this is the only other good one. 

hud-aircraft-shadertoy-sample-code.gif

float getBox(vec2 st, float left, float bottom, float width, float height) {
    float sm = 0.002;
    float x_range = smoothstep(left - sm, left, st.x) - smoothstep(left + width, left + width + sm, st.x);
    float y_range = smoothstep(bottom - sm, bottom, st.y) - smoothstep(bottom + height,bottom + height + sm, st.y);
    
    return x_range * y_range;
}

float getCircle(vec2 st, vec2 center, float radius, float thickness, float sm) {
    float distance = distance(st, center);
    return smoothstep(radius, radius + sm, distance) - smoothstep(radius + thickness, radius + thickness + sm, distance);
}

float getDottedCircle(vec2 st, vec2 center, float radius, float thickness, float sm) {
    float distance = distance(st, center);
    float circle = smoothstep(radius, radius + sm, distance) - smoothstep(radius + thickness, radius + thickness + sm, distance);
    
    vec2 vector = center - st;
    float angle = atan(vector.y, vector.x);
    angle = ((angle * 0.5) + (3.14 * 0.5)) / 3.14;
    circle *= step(8., mod(floor(angle / 0.001), 10.0));
    
    return circle;
}

mat2 rotate(in float angle) {
    return mat2(
                cos(angle), -sin(angle), 
                sin(angle), cos(angle)
    		);
}

vec2 getRotation(vec2 st, vec2 origin, float angle) {
    vec2 rotatedCoord = st - vec2(origin);
    rotatedCoord = rotate(angle) * rotatedCoord;
    rotatedCoord += vec2(origin);
    
    return rotatedCoord;
}

mat2 scale(in float x, in float y) {
    return mat2(
        		x, 0.0, 
                0.0, y
        	);
}

vec2 getScaling(vec2 st, vec2 origin, float x, float y) {
    vec2 scaledCoord = st - vec2(origin);
    scaledCoord = scale(x, y) * scaledCoord;
    scaledCoord += vec2(origin);
    
    return scaledCoord;
}

float getInnetDial(vec2 st, float center, float radius) {
 	float lineThickness = 0.002;
    float sm = 0.003;
    
    float circle2 = getCircle(st, vec2(center), radius - 0.015, lineThickness, sm);
    circle2 *= step(st.x, -0.254);
    
    float circle2dash1 = getBox(st, -0.254, 0.134, 0.100, lineThickness);
    circle2 += circle2dash1;
    float circle2dash1_2 = getBox(st, -0.204, 0.134, 0.052, lineThickness * 2.);
    circle2 += circle2dash1_2;
    float circle2dash2 = getBox(st, -0.288, 0.068, 0.031, lineThickness);
    circle2 += circle2dash2;
    float circle2dash2_2 = getBox(st, -0.247, 0.068, 0.096, lineThickness);
    circle2 += circle2dash2_2;
    float circle2dash3 = getBox(st, -0.350, 0.0, 0.215, lineThickness);
    circle2 += circle2dash3;
    float circle2dash3_2 = getBox(st, -0.19, 0.0, 0.055, lineThickness * 2.);
    circle2 += circle2dash3_2;
    float circle2dash3_3 = getBox(st, -0.36, 0.0, lineThickness * 2., lineThickness * 2.);
    circle2 += circle2dash3_3;
    float circle2dash4 = getBox(st, -0.288, -0.068, 0.031, lineThickness);
    circle2 += circle2dash4;
    float circle2dash4_2 = getBox(st, -0.247, -0.068, 0.096, lineThickness);
    circle2 += circle2dash4_2;
    
    float circle2dash5 = getBox(st, -0.13, 0.0, 0.01, lineThickness);
    circle2 += circle2dash5;
    float circle2dash5_1 = getBox(st, -0.12, -0.03, lineThickness, 0.06);
    circle2 += circle2dash5_1;
    float circle2dash5_2 = getBox(st, -0.118, 0.03, 0.01, lineThickness);
    circle2 += circle2dash5_2;
    float circle2dash5_3 = getBox(st, -0.118, -0.03, 0.01, lineThickness);
    circle2 += circle2dash5_3;
    
    return circle2;
}

float getSideLine(vec2 st, float left, float top, float lineThickness) {
    float lineEdgeLength = 0.008;
    float sideLine = getBox(st, left, top, lineThickness, 0.236);
    sideLine += getBox(st, left + lineThickness, top + 0.234, lineEdgeLength, lineThickness);
    sideLine += getBox(st, left + lineThickness, top, lineEdgeLength, lineThickness);
    sideLine += getBox(st, left + lineThickness + 0.006, 0.0, lineThickness, lineThickness);
    
    return sideLine;
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
	vec2 st = fragCoord.xy / iResolution.xy;
    st -= 0.5;
    st.x *= iResolution.x/iResolution.y;
	fragColor = vec4(st,0.5+0.5*sin(iTime),1.0);
    
    float center = 0.0;
    float radius = 0.3;
    float sm = 0.003;
    float lineThickness = 0.002;
    float angle = atan(st.y, st.x);
    angle = (angle + 3.1416) / (2.0 * 3.1416);

    vec3 color = vec3(0.);
    
    st = getRotation(st, vec2(center), -iTime);
    float circle1 = getCircle(st, vec2(0.), radius, lineThickness, sm);
    circle1 *= (step(st.x, -0.25) + step(0.25, st.x));
    st = getRotation(st, vec2(center), iTime);
    
    
    
    float dottedCircle = getDottedCircle(st, vec2(center), radius + 0.015, lineThickness + 0.002, sm);
    dottedCircle *= (
        (step(0.02, angle) - step(0.08, angle)) +
        (step(0.09, angle) - step(0.16, angle)) +
        (step(0.32, angle) - step(0.4, angle)) +
        (step(0.41, angle) - step(0.48, angle)) +
        
        (step(0.52, angle) - step(0.59, angle)) +
        (step(0.6, angle) - step(0.66, angle)) +
        (step(0.83, angle) - step(0.89, angle)) +
        (step(0.9, angle) - step(0.98, angle))
    );
    
    float timeFactor = 3.14 * 0.2 * cos(iTime * 0.5);
    st = getRotation(st, vec2(center), timeFactor);
    float dial1 = getDottedCircle(st, vec2(center), radius + 0.04, lineThickness + 0.01, sm);
    
    float dial2 = getDottedCircle(st, vec2(center), radius + -0.04, lineThickness + 0.01, sm);
    dial2 += getDottedCircle(st, vec2(center), radius + -0.02, lineThickness + 0.001, sm);
    st = getRotation(st, vec2(center), -timeFactor);
    
    
    dial1 *= (step(0.662, angle) - step(0.84, angle));
    dial2 *= (step(0.682, angle) - step(0.82, angle));
    
    
    float sideLine = getSideLine(st, -0.4, -0.116, 0.002);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    sideLine += getSideLine(st, -0.4, -0.116, 0.002);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    
    st = getRotation(st, vec2(center), 3.14 * 0.1 * sin(iTime));
    
    float innerDial = getInnetDial(st, center, radius);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    innerDial += getInnetDial(st, center, radius);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    
    
    st = getRotation(st, vec2(center), 3.14 / 4.);
    float box = getBox(st, -0.068, -0.057, 0.004, 0.115);
    box += getBox(st, 0.066, -0.057, 0.004, 0.115);
    box += getBox(st, -0.057, 0.070, 0.115, 0.004);
    box += getBox(st, -0.057, -0.070, 0.115, 0.004);
    box -= getBox(st, -0.036, -0.036, 0.072, 0.072);
    
    box += getBox(st, -0.05, -0.05, 0.006, 0.006);
    box += getBox(st, 0.048, -0.05, 0.006, 0.006);
    box += getBox(st, 0.048, 0.046, 0.006, 0.006);
    box += getBox(st, -0.05, 0.046, 0.006, 0.006);
    
    float innerBox = getBox(st, -0.04, -0.04, 0.08, 0.08);
    innerBox -= getBox(st, -0.036, -0.036, 0.072, 0.072);
    innerBox += getBox(st, -0.07, -0.07, 0.006, 0.006);
    innerBox += getBox(st, 0.066, -0.07, 0.006, 0.006);
    innerBox += getBox(st, 0.066, 0.066, 0.006, 0.006);
    innerBox += getBox(st, -0.07, 0.066, 0.006, 0.006);
    st = getRotation(st, vec2(center), -3.14 / 4.);
    
    st = getRotation(st, vec2(center), -3.14 * 0.1 * sin(iTime));
    
    float sideMarks = step(18., mod(floor((st.y + 0.1 * sin(iTime)) / 0.002), 20.0));
    sideMarks *= (
        (step(-0.44, st.x) - step(-0.415, st.x)) +
        (step(0.415, st.x) - step(0.44, st.x))
    );
    sideMarks *= (step(-0.12, st.y) - step(0.12, st.y));
    
    float sideMarksBox = getBox(st, -0.45, -0.015, 0.04, 0.03);
    sideMarksBox -= getBox(st, -0.448, -0.013, 0.036, 0.026);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    sideMarksBox += getBox(st, -0.45, -0.015, 0.04, 0.03);
    sideMarksBox -= getBox(st, -0.448, -0.013, 0.036, 0.026);
    st = getScaling(st, vec2(0.0), -1.0, 1.0);
    
    sideMarksBox += getBox(st, -0.0025, 0.33, 0.005, 0.005);
    sideMarksBox += getBox(st, -0.0025, 0.25, 0.005, 0.005);
    
    box *= (0.2 + 0.8 * pow(abs(sin(iTime * 4.)), 2.));

    color += vec3(1.000,0.345,0.287) * circle1;
    color += vec3(0.39,0.61,0.65) * dottedCircle;
    color += vec3(0.39,0.61,0.65) * dial1;
    color += vec3(0.39,0.61,0.65) * dial2;
    color += vec3(0.39,0.61,0.65) * innerDial;
    color += vec3(0.39,0.61,0.65) * sideLine;
    color += vec3(0.39,0.61,0.65) * sideMarks;
    color += vec3(0.39,0.61,0.65) * sideMarksBox;
    color += vec3(0.995,0.425,0.003) * box;
    color += vec3(0.96, 0.98, 0.8) * innerBox;
    
    fragColor = vec4(color,1.0);
}

Tools

Shader Playground (timjones.io) - Decompiles shader code to Assembly 
SHADERed - Free and open source shader editor

OpenGL Shading Language (GLSL)

OpenGL Shading Language (GLSL) is a high-level shading language with a syntax based on the C programming language. It runs natively on OpenGL but can also run on Vulkan by using Spir-V

OpenGL Shading Language (GLSL)

Core C Programming for GLSL

Writing GLSL (OpenGL Shading Language) code does not require extensive knowledge of C programming, but familiarity with some core C concepts can be very helpful. GLSL shares many similarities with C, as it is a C-like language, but there are important differences as well.

Here's a breakdown of how much C programming you need to know to effectively write GLSL shaders:

### Core C Concepts Useful for GLSL:
1. **Data Types and Variables**:
   - GLSL has many data types similar to C, such as `int`, `float`, and `bool`, but it also includes types specific to graphics, such as `vec2`, `vec3`, `vec4` (vectors), and `mat4` (matrices).
   - Knowing how to declare variables and use basic data types from C is useful.
   ```c
   // C:
   int x = 5;
   float y = 3.14;
   
   // GLSL:
   int x = 5;
   float y = 3.14;
   vec3 color = vec3(1.0, 0.5, 0.0); // A 3D vector for color
   ```

2. **Control Structures**:
   - C-like control structures, such as `if`, `else`, `for`, and `while`, are available in GLSL.
   - Basic knowledge of how to write loops and conditional statements in C will help in GLSL.
   ```c
   // C:
   for (int i = 0; i < 10; i++) {
       if (i % 2 == 0) {
           // Do something
       }
   }
   
   // GLSL:
   for (int i = 0; i < 10; i++) {
       if (i % 2 == 0) {
           // Do something, like color a pixel
       }
   }
   ```

3. **Functions**:
   - GLSL, like C, allows you to write functions that take parameters, return values, and encapsulate logic.
   - Knowledge of defining and calling functions in C will help in GLSL.
   ```c
   // C:
   int add(int a, int b) {
       return a + b;
   }
   
   // GLSL:
   float add(float a, float b) {
       return a + b;
   }
   ```

4. **Arrays**:
   - Similar to C, GLSL supports arrays. Knowing how to declare and use arrays in C translates directly into GLSL.
   ```c
   // C:
   int arr[3] = {1, 2, 3};
   
   // GLSL:
   float arr[3] = float[3](1.0, 2.0, 3.0);
   ```

5. **Mathematical Operators**:
   - Like C, GLSL uses standard arithmetic (`+`, `-`, `*`, `/`) and relational operators (`<`, `>`, `==`). Knowing basic arithmetic and logic operators from C will help in writing shader code.
   - In addition, GLSL has built-in functions like `sin()`, `cos()`, `dot()`, `cross()`, etc., for vector and matrix math, which are unique to graphics programming.

6. **Type Casting**:
   - Understanding type casting in C helps, although GLSL type casting is simpler. For instance, casting between different numeric types or vector types is common in GLSL.
   ```c
   // C:
   float f = (float)5;
   
   // GLSL:
   float f = float(5); // Casts int to float
   ```

### GLSL-Specific Concepts (Beyond C):
While C programming fundamentals are useful, there are several key areas where GLSL differs from C, especially since it is designed specifically for graphics.

1. **Vectors and Matrices**:
   - GLSL has built-in types for vectors (`vec2`, `vec3`, `vec4`) and matrices (`mat3`, `mat4`) to handle graphics-specific tasks like transformations, lighting, and colors. You won’t find these in C, so you’ll need to learn how to work with these types in GLSL.
   - GLSL allows vector arithmetic and component-wise operations, which are not natively supported in C.
   ```glsl
   vec3 position = vec3(1.0, 2.0, 3.0);
   vec3 direction = normalize(position);  // Normalize vector
   mat4 transform = mat4(1.0);  // 4x4 identity matrix
   ```

2. **Shader Stages**:
   - Unlike C, GLSL is executed in the context of a **graphics pipeline**, with specific shader stages like **vertex shaders**, **fragment shaders**, **geometry shaders**, etc. Each stage has a specific role (e.g., vertex shaders handle vertex transformations, fragment shaders handle per-pixel color calculations).
   - Knowing how data flows through the pipeline and between shaders is essential for writing GLSL, but this isn't part of C programming.

3. **Uniforms and Inputs/Outputs**:
   - In GLSL, shaders use **uniforms** (global variables passed from the CPU to the GPU) and input/output variables to communicate between different shader stages. These don’t exist in C, but learning to use them is crucial for GLSL programming.
   ```glsl
   uniform mat4 modelViewMatrix;  // Passed from the application
   in vec3 vertexPosition;        // Input from vertex data
   out vec4 fragColor;            // Output to fragment shader
   ```

4. **Built-in Functions**:
   - GLSL has a large set of built-in functions for graphics-specific operations, like texture sampling (`texture()`), lighting calculations, and geometric functions (`dot()`, `cross()`, etc.). These are not part of C, and you’ll need to learn how to use them effectively.
   
5. **No Pointers**:
   - Unlike C, GLSL does not support **pointers** or **manual memory management**. This simplifies the language somewhat compared to C, but it also means you can’t use some of the lower-level features that C provides.

### Summary: How Much C Do You Need?
- **Basic Knowledge** of C is sufficient for learning GLSL. Understanding variables, control structures (loops, conditionals), functions, and arrays from C will help you write shader code.
- **Advanced C Concepts** like pointers, manual memory management, and structs are not needed in GLSL.
- However, you’ll need to learn **GLSL-specific features** like vectors, matrices, built-in functions, and the graphics pipeline, which are beyond the scope of C.

In short, **if you are comfortable with basic C programming**, you’ll find it relatively easy to pick up GLSL with some additional learning of its graphics-specific features.

Let me know if you need further clarification or examples!

OpenGL Shading Language (GLSL)

Fragment Shaders

Fragment shader code is executed entirely on the GPU (Graphics Processing Unit). 

  1. Parallelism: The GPU is designed to handle many operations at once, and since fragment shaders need to process every pixel on the screen, the GPU's architecture allows these operations to be run in parallel. Each fragment (which corresponds to a pixel or a portion of a pixel) is processed independently by the fragment shader

  2. Efficiency: Fragment shaders handle tasks like lighting, color blending, texturing, and other pixel-level effects. These operations are computationally intensive but relatively simple when done for each pixel. The CPU would be far less efficient at handling millions of pixels in real-time.

Process Overview:

When rendering a scene:

  1. Vertices are processed in the vertex shader.
  2. Rasterization occurs, converting geometric shapes (like triangles) into fragments (potential pixels).
  3. The fragment shader then runs on each fragment to compute its color, texture, and other attributes, often involving texture sampling and lighting calculations.
  4. After the fragment shader, the final color values are written to the framebuffer, creating the final image.
Example:
void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    // Compute UV coordinates
    vec2 uv = fragCoord.xy / iResolution.xy;

    // Set the output color
    fragColor = vec4(uv, 0.0, 1.0);  // Sets a color based on UV
}

 

Color Space

In GLSL (and other graphics APIs), colors are typically represented using RGB (Red, Green, Blue) values. These values can exist in two spaces: the original space (or "integer space") and the normalized space (or "floating-point space"). Here’s how these two spaces differ:

1. Integer Space
  • Range: Each color component (R, G, B) is usually represented as an integer between 0 and 255.
  • Representation: The RGB values are discrete integers, where 0 represents no intensity (black) and 255 represents full intensity (maximum brightness) for each color component.
    • Black: (0, 0, 0) (all components are at their minimum)
    • White: (255, 255, 255) (all components are at their maximum)
    • Red: (255, 0, 0) (only the red component is at maximum)
    • Green: (0, 255, 0) (only the green component is at maximum)
    • Blue: (0, 0, 255) (only the blue component is at maximum)

Example:

  • A pixel with the color (128, 64, 255) in integer space would have:
    • Red: 128 (half brightness)
    • Green: 64 (quarter brightness)
    • Blue: 255 (full brightness)
2. Normalized Space (Floating-Point Space)
  • Range: Each color component (R, G, B) is represented as a floating-point number between 0.0 and 1.0.
  • Representation: In this space, 0.0 represents no intensity (black), and 1.0 represents full intensity (maximum brightness). Each integer value from the original space is divided by 255 to convert it into normalized space.
    • Black: (0.0, 0.0, 0.0) (all components are at their minimum)
    • White: (1.0, 1.0, 1.0) (all components are at their maximum)
    • Red: (1.0, 0.0, 0.0) (only the red component is at maximum)
    • Green: (0.0, 1.0, 0.0) (only the green component is at maximum)
    • Blue: (0.0, 0.0, 1.0) (only the blue component is at maximum)
Conversion Formula:

To convert from the original space to normalized space: normalized value=original value255\text{normalized value} = \frac{\text{original value}}{255}

For example:

  • A pixel with the color (128, 64, 255) in integer space would be represented as:
    • Red: 128255≈0.502\frac{128}{255} \approx 0.502
    • Green: 64255≈0.251\frac{64}{255} \approx 0.251
    • Blue: 255255=1.0\frac{255}{255} = 1.0
    So, the color in normalized space would be approximately (0.502, 0.251, 1.0).
Relationship Between Spaces:
  • The conversion between the two spaces is linear. Integer values in the range [0, 255] are mapped to floating-point values in the range [0.0, 1.0].
  • Integer space is typically used for storing pixel data in images and textures, while normalized space is used when performing operations in shaders.
Example in GLSL:

In GLSL, colors are usually represented in normalized space. For example:

glsl
vec4 color = vec4(1.0, 0.0, 0.0, 1.0); // Red in normalized space

Here, the red channel is set to 1.0, which corresponds to 255 in integer space, and the green and blue channels are 0.0, corresponding to 0 in integer space.

Practical Use in Shaders:

When working with textures or colors in shaders:

  • Textures sampled using the texture() function usually return colors in normalized space. For instance, a texture value might be returned as vec4(0.5, 0.3, 0.8, 1.0), where each component is in the [0.0, 1.0] range.
  • If you want to manipulate colors in integer space, you would need to scale them up by 255 when necessary.
Summary:
  • Original Space: RGB values range from 0 to 255.
  • Normalized Space: RGB values range from 0.0 to 1.0.
  • Conversion between the two spaces involves dividing or multiplying by 255.

Would you like more details on any specific use of color spaces in shaders?




Common GLSL Variables and Functions:

These functions could be specific to Shadertoy or OpenGL ES 

Getting Started 

When you're working in shaders, the pixel coordinates from fragCoord.xy are typically in pixel units (e.g., ranging from (0,0) to (1920,1080) for an HD screen). If you want to make your shader work consistently across different resolutions, you need to normalize these coordinates.

By dividing fragCoord.xy by iResolution.xy, you map the pixel coordinates to a normalized space, which usually ranges from [0, 1].

INCLUDE CODE HERE

1. fragCoord.xy:
2. iResolution.xy:

The length function in GLSL is a useful tool for calculating the magnitude (or Euclidean distance) of a vector. It's often used in shaders to compute distances between points or to create effects like radial gradients, circular shapes, and more.

Definition:

OpenGL Shading Language (GLSL)

Converting ShaderToy to GLSL

ShaderToy is a platform for creating and sharing shaders in GLSL, but it includes some unique functions and features that are specific to its environment. If you want to port ShaderToy shaders to plain GLSL or another environment, you’ll need to understand these specific functions and how to replace or adapt them.

Here are some common ShaderToy-specific functions and their equivalents or alternatives in standard GLSL:

  1. iGlobalTime: This variable represents the time elapsed since the shader started running.

    • In GLSL: You’ll need to pass the elapsed time as a uniform variable. For example:
      glsl
      uniform float uTime; // Use this uniform in your shader
  2. iResolution: This variable holds the resolution of the output image.

    • In GLSL: You can pass the resolution as a uniform variable:
      glsl
      uniform vec2 uResolution; // Use this uniform in your shader
  3. iMouse: This variable contains the position and state of the mouse.

    • In GLSL: You’ll need to pass the mouse position as a uniform variable:
      glsl
      uniform vec4 uMouse; // x, y, z, and w can represent mouse position and state
  4. iChannel0, iChannel1, etc.: These variables represent textures that you can sample from.

    • In GLSL: You’ll need to pass these textures as uniform sampler2D variables:
      glsl
      uniform sampler2D uTexture0; // Use this uniform in your shader
  5. mainImage(out vec4 fragColor, in vec2 fragCoord): This is the main function in ShaderToy shaders, where fragColor is the output color and fragCoord is the coordinate of the current fragment.

    • In GLSL: In a typical GLSL setup, you might use a similar fragment shader function, but the signature and setup might vary depending on your framework. For example, in a typical OpenGL setup, the fragment shader might look like this:
      glsl
      void main() { // Your code here }
  6. fragCoord.xy: This variable provides the coordinates of the current fragment.

    • In GLSL: You can pass these coordinates as a varying variable or compute them based on the screen dimensions:
      glsl
      vec2 fragCoord = gl_FragCoord.xy; // In a GLSL shader

When porting shaders, make sure to adapt any dependencies on the ShaderToy environment to the specifics of your target environment. This usually involves setting up appropriate uniform variables, handling texture inputs and outputs, and ensuring that your fragment shader logic aligns with the rendering pipeline you're using.