Shady can compile a subset of Nim into OpenGL Shader Language
used by the GPU. This allows you to test your shader code with echo
statements on the CPU and then run the exact same code on the GPU.
Shady has two main goals:
- Write vertex and fragment/pixel shaders for games and 3d applications.
- Write compute shaders for offline processing and number crunching.
Shady uses:
pixie
library for image operations.vmath
library for vector and matrix operations.chroma
library for color conversions and operations.bumpy
library for collisions and intersections.
API reference: https://nimdocs.com/treeform/shady/shady.html
# both CPU and GPU code:
proc circleSmooth(gl_FragColor: var Color, uv: Vec2) =
var a = 0.0
for x in 0 ..< 8:
for y in 0 ..< 8:
if (uv + vec2(x.float32 - 4.0, y.float32 - 4.0) / 8.0).length < 400.0:
a += 1
a = a / (8 * 8)
gl_FragColor = color(a, a, a, 1)
# test on the CPU:
var testColor: Color
circleSmooth(testColor, vec2(100, 100))
echo testColor
# compile to a GPU shader:
var shader = toShader(circleSmooth)
echo shader
Nim vertex shader:
proc basicVert(
gl_Position: var Vec4,
MVP: Uniform[Mat4],
vCol: Attribute[Vec3],
vPos: Attribute[Vec3],
fragColor: var Vec3
) =
gl_Position = MVP * vec4(vPos.x, vPos.y, 0.0, 1.0)
fragColor = vCol
GLSL output:
#version 410
precision highp float;
uniform mat4 MVP;
attribute vec3 vCol;
attribute vec3 vPos;
out vec3 fragColor;
void main() {
gl_Position = MVP * vec4(vPos.x, vPos.y, 0.0, 1.0);
fragColor = vCol;
}
Nim fragment shader:
proc basicFrag(gl_FragColor: var Color, fragColor: Vec3) =
gl_FragColor = color(fragColor.x, fragColor.y, fragColor.z, 1.0)
GLSL output:
#version 410
precision highp float;
in vec3 fragColor;
void main() {
gl_FragColor = vec4(fragColor.x, fragColor.y, fragColor.z, 1.0);
}
Shady can be used to write compute shaders. Compute shaders allow more general purpose code execution work in parallel and hence faster then the CPU.
# Setup the uniforms.
var inputCommandBuffer*: Uniform[SamplerBuffer]
var outputImageBuffer*: UniformWriteOnly[UImageBuffer]
var dimensions*: Uniform[IVec4] # ivec4(width, height, 0, 0)
# The shader itself.
proc commandsToImage() =
var pos = gl_GlobalInvocationID
for x in 0 ..< dimensions.x:
pos.x = x.uint32
let value = uint32(texelFetch(inputCommandBuffer, int32(pos.x)).x)
#echo pos.x, " ", value
let colorValue = uvec4(
128,
0,
value,
255
)
imageStore(outputImageBuffer, int32(pos.y * uint32(dimensions.x) + pos.x), colorValue)