Detailed Shading Techniques
Colors and Gradients
Before diving deep into color handling in shaders, let's first establish a fundamental understanding of how colors work in computer graphics.
Basic Color Representation
In Makepad shaders, we use vec4
(RGBA vector) to represent colors:
- Red:
0.0
to 1.0
- Green:
0.0
to 1.0
- Blue:
0.0
to 1.0
- Alpha (transparency):
0.0
(completely transparent) to 1.0
(completely opaque)
1// Basic solid color shader
2fn pixel(self) -> vec4 {
3 // Format: vec4(red, green, blue, alpha)
4 return vec4(1.0, 0.0, 0.0, 1.0); // Pure red
5}
Linear Gradients
Linear gradients create smooth transitions between two colors. Here's an implementation of a horizontal gradient:
1fn pixel(self) -> vec4 {
2 // self.pos.x provides horizontal position from 0.0 to 1.0
3 let mix_factor = self.pos.x;
4
5 let color1 = vec4(1.0, 0.0, 0.0, 1.0); // Red
6 let color2 = vec4(0.0, 0.0, 1.0, 1.0); // Blue
7
8 // Linear interpolation between two colors
9 return mix(color1, color2, mix_factor);
10}
Visual representation:
1Red [================>] Blue
2 <- mix factor ->
For diagonal gradients, we can implement it like this:
1fn pixel(self) -> vec4 {
2 // Combine x and y coordinates for diagonal direction
3 let mix_factor = (self.pos.x + self.pos.y) * 0.5;
4
5 let color1 = vec4(1.0, 0.0, 0.0, 1.0); // Red
6 let color2 = vec4(0.0, 0.0, 1.0, 1.0); // Blue
7
8 return mix(color1, color2, mix_factor);
9}
Radial Gradients
Radial gradients create color transitions that spread outward from a center point:
1fn pixel(self) -> vec4 {
2 // Calculate distance from center point
3 let center = vec2(0.5, 0.5);
4 let dist = length(self.pos - center);
5
6 // Convert distance to mix factor
7 let mix_factor = clamp(dist * 2.0, 0.0, 1.0);
8
9 let inner_color = vec4(1.0, 1.0, 1.0, 1.0); // White at center
10 let outer_color = vec4(0.0, 0.0, 0.0, 1.0); // Black at edge
11
12 return mix(inner_color, outer_color, mix_factor);
13}
Texture Mapping
Texture mapping is the technique of applying 2D images to geometric surfaces.
Basic Texture Sampling
1DrawTexture = {{DrawTexture}} {
2 texture tex: texture2d // Declare texture uniform
3
4 fn pixel(self) -> vec4 {
5 // Sample texture at current position
6 return sample2d(self.tex, self.pos);
7 }
8}
UV Coordinate System
UV coordinates are used to map texture pixels to geometry:
1(0,0) +-----------+ (1,0)
2 | |
3 | Texture |
4 | |
5(0,1) +-----------+ (1,1)
1fn vertex(self) -> vec4 {
2 // Transform UV coordinates
3 self.uv = self.geom_pos * self.tex_scale + self.tex_offset;
4
5 // Regular vertex transformation
6 let clip_pos = self.geom_pos * self.rect_size + self.rect_pos;
7 return self.camera_projection * vec4(clip_pos, 0.0, 1.0);
8}
9
10fn pixel(self) -> vec4 {
11 // Use transformed UV for sampling
12 return sample2d(self.tex, self.uv);
13}
Special Effects
Gaussian Blur
Here's an implementation of a simple Gaussian blur:
1fn blur(self) -> vec4 {
2 let blur_radius = 2.0; // Blur radius
3 let mut color = vec4(0.0);
4 let mut total_weight = 0.0;
5
6 // Sample in a 5x5 grid around current pixel
7 for i in -2..3 {
8 for j in -2..3 {
9 let offset = vec2(float(i), float(j)) * blur_radius;
10 // Calculate Gaussian weight
11 let weight = exp(-(offset.x * offset.x + offset.y * offset.y));
12
13 color += sample2d(self.tex, self.pos + offset) * weight;
14 total_weight += weight;
15 }
16 }
17
18 return color / total_weight; // Normalize result
19}
Glow Effect
1fn glow(self) -> vec4 {
2 let base_color = sample2d(self.tex, self.pos); // Original color
3 let blur_color = self.blur(); // Blurred color
4
5 // Overlay blurred version to create glow effect
6 return base_color + blur_color * self.glow_strength;
7}
Shadow Effect
1// Using Makepad's built-in GaussShadow to implement shadow effect
2fn draw_shadow(self) -> vec4 {
3 let shadow_color = vec4(0.0, 0.0, 0.0, 0.5);
4 let shadow_offset = vec2(5.0, 5.0);
5
6 // Calculate shadow area
7 let shadow = GaussShadow::box_shadow(
8 self.rect_pos + shadow_offset,
9 self.rect_size,
10 self.pos,
11 10.0 // Shadow blur radius
12 );
13
14 return shadow_color * shadow;
15}