A real-time shader viewer built with Qt and OpenGL. The same 3D character and backdrop can be drawn with different surface shaders and post-process shaders, chosen from a control panel while the scene runs. Everything visual is written in GLSL: how vertices move, how surfaces take their color, and how the finished image is filtered.
The first pass draws the 3D scene into an off-screen framebuffer instead of the screen. The second pass draws a full-screen quad, using that image as a texture. A post-process shader runs on every pixel of the quad, so it can read any part of the frame and change where each pixel samples from.
Blinn-Phong. Adds a specular highlight to Lambert shading. With view direction \(\mathbf{V}\), light direction \(\mathbf{L}\), and normal \(\mathbf{N}\), the highlight depends on how close the normal is to the halfway vector:
\[ \mathbf{H} = \frac{\mathbf{V} + \mathbf{L}}{\|\mathbf{V} + \mathbf{L}\|} \]
\[ I_s = \max(\mathbf{H} \cdot \mathbf{N},\, 0)^{35} \]
Matcap. A single image of a shaded sphere stands in for the whole material. The normal is moved into camera space, and its \(x\) and \(y\) become the coordinates used to read the image. Swapping the image changes the material without any lighting calculation.
\[ (u, v) = \frac{1}{2}(n_x, n_y) + \frac{1}{2} \]
Vertex deformation. Dividing a vertex by its largest coordinate pushes it onto the surface of a cube. The vertex shader blends between the original position and that cube over time, reaching 80% at its peak:
\[ \mathbf{p}_{\text{cube}} = \frac{4\,\mathbf{p}}{\max(|p_x|, |p_y|, |p_z|)} \]
\[ s(t) = \frac{1}{2}\sin t + \frac{1}{2} \]
\[ k(t) = 0.8\,\text{smoothstep}(0, 1, s(t)) \]
\[ \mathbf{p}' = (1 - k)\,\mathbf{p} + k\,\mathbf{p}_{\text{cube}} \]
The fragment shader mixes the model’s texture with a cosine color palette, offset by position along \(x\) and by time, so bands of color travel across the body.
Sobel. Two kernels estimate how quickly the color changes horizontally and vertically around each pixel. Their combined magnitude is bright at edges and black in flat areas.
\[ K_x = \begin{bmatrix} 3 & 0 & -3 \\ 10 & 0 & -10 \\ 3 & 0 & -3 \end{bmatrix} \]
\[ K_y = \begin{bmatrix} 3 & 10 & 3 \\ 0 & 0 & 0 \\ -3 & -10 & -3 \end{bmatrix} \]
\[ G = \sqrt{G_x^2 + G_y^2} \]
Gaussian blur. Each output pixel is a weighted average of the 11×11 block around it, with coordinates clamped at the image border.
Worley glass. Worley noise scatters one random point in each grid cell, with 20 cells per unit of screen height, and returns the distance \(d\) to the nearest point. A central difference gives the slope of that distance field. The slope bends the texture lookup, like light passing through rippled glass, and \(d\) adds a soft shading to each cell:
\[ \mathbf{uv}' = \mathbf{uv} + 0.0007\,\nabla d \]
\[ C = \text{image}(\mathbf{uv}')\,(0.9 + 0.3\,d) \]
Warp fog (extra credit). Fractal Brownian motion \(f\) sums six octaves of interpolated noise, doubling the frequency and halving the amplitude each time. Two copies of \(f\) form a 2D offset \(w\). Feeding one offset into the next, known as domain warping, turns the noise into curling, smoke-like shapes:
\[ w(\mathbf{p}) = 2\big(f(\mathbf{p}), f(\mathbf{p} + (4, 1))\big) - 1 \]
\[ \mathbf{q} = w(\mathbf{p} + t\,(0.1, 0.3)) \]
\[ \mathbf{r} = w(\mathbf{p} + \mathbf{q}) \]
The offset \(\mathbf{r}\) also displaces the image, so the scene appears to waver behind the fog. Fog density comes from the warped noise itself, and it blends the image toward a color running from dark to acid green. The sampling point \(\mathbf{p}\) moves with a constant wind, which keeps the fog drifting across the frame.
\[ \rho = \text{smoothstep}(0.2, 0.6, f(\mathbf{p}+\mathbf{r})^2) \]
| Tool | Used for |
|---|---|
| GLSL 3.30 | All vertex, fragment, and post-process shaders |
| C++, OpenGL | Camera, framebuffer setup, and the two render passes |
| Qt | Window, shader menus, and mouse input |
| Qt Creator | Building and running the project |
Built on the course’s Qt framework, which provides the interface, the Lambert shader, the Sobel and Gaussian weights, and the model and texture assets. Source code is not public, as this is course work.