GLSL
- Made for graphics
- Running on the GPU
- C-like syntax
- Isolated small programs
Structure
cpp
#version 330 core
in type in_name;
out type out_name;
uniform type uniform_name;
int main() {
...
}
Data Types
- scalar:
float
,double
,int
,uint
,bool
- container:
n = 1/2/3/4
- vector:
vecn
,dvecn
,ivecn
,uvecn
,bvecn
- matrix:
matn
,dmatn
,mat2x3
,mat4x2
, ...
- aggregate:
struct
,array
- opaque:
samplerXXX
,imageXXX
,atomic_xxx
... - void
Alias
glsl
vec4 v;
v.xyzw; // coord
v.rgba; // color
v.stpq; // texture
Swizzling
glsl
vec2 v;
vec4 v1 = v.xyxx;
vec3 v2 = v1.rgb;
Constructor
glsl
vec2 v = vec2(1.0); // = vec2(1.0, 1.0)
vec3 v1 = vec3(v, 1.0);
vec4 v2 = vec4(v, v);
vec4 v3 = vec4(1.0, v2.rgb);
Ins and Outs
Vertex shader:
glsl
#version 330 core
layout (location = 0) in vec3 position;
out vec4 vertexColor;
void main() {
gl_Position = vec4(position, 1.0);
vertexColor = vec4(0.5f, 0.0f, 0.0f, 1.0f);
}
gl_PointSize
is a builtin variable.
Fragment shader:
glsl
#version 330 core
in vec4 vertexColor; // Same name as vertex shader
out vec4 color; // Single vec4 as output color
void main()
{
color = vertexColor;
}
Interface Blocks
glsl
in VS_OUT
{
vec2 TexCoords;
} fs_in;
Uniform
- Global data from the main program
- Read-only in shaders
Example (OpenGL):
glsl
#version 330 core
uniform vec4 ourColor;
cpp
// C++: For each frame:
GLfloat timeValue = glfwGetTime();
GLfloat greenValue = (sin(timeValue) / 2) + 0.5;
GLint location = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(location, 0.0f, greenValue, 0.0f, 1.0f);
Uniform Buffer Objects
- Only the size and alignment of members are important.
- Allows batch updating and management of uniform data.
- Reduces API calls.
- May use
std140
layout, which aligns members:
glsl
layout (std140) uniform ExampleBlock
{
float value;
...
};
Builtin Operations
- Operators:
+-*/&|
... - Functions:
sin
,length
,normalize
,inverse
, ... - Accelerated by hardware.
Other Shading Languages
This section mainly refers to https://alain.xyz/blog/a-review-of-shader-languages#language-features.
Shader → IR → Machine code
Abbreviation | Language | Official Graphics APIs | Transpiling |
---|---|---|---|
HLSL | High Level S.L. | DirectX | HLSL → SPIR-V via dxc and glslang |
GLSL | OpenGL S.L. | Vulkan / OpenGL | GLSL → SPIR-V → HLSL |
MSL | Metal S.L. | Metal | SPIR-V → MSL via SPIR-V Cross |
WGSL | WebGPU S.L. | WebGPU | WGSL → SPIR-V via naga or tint |
- All similar to C
- Sharing most of builtin function names.
HLSL
- "High level"
- Namespaces, template generics, operator overloading,
#include
, ... - Semantic labels for struct fields.
float4
for vector,float4x4
for matrix.
Compute shader:
hlsl
struct Constants
{
float time : packoffset(c0);
};
ConstantBuffer<Constants> constants : register(b0);
Texture2D<float4> albedoTex : register(t1);
Texture2D<float4> pbrLutTex : register(t2);
SamplerState gSampler : register(s0);
RWTexture2D<float4> tOutput : register(u0);
groupshared float groupData[4];
namespace random
{
float foo()
{
return 16.0;
}
}
[numthreads(8, 4, 1)]
void main(uint3 groupThreadID : SV_GroupThreadID,
uint3 groupID : SV_GroupID,
uint groupIndex : SV_GroupIndex,
uint3 dispatchThreadID: SV_DispatchThreadID)
{
tOutput[dispatchThreadID.xy] = ...;
}
MSL
- Clearer input/output model.
- Vector and matrix name same as HLSL.
- Fields also have labels.
kernel
keyword for compute shaders.
WGSL
- More "verbose" vector and matrix type:
mat4x4<f32>
@builtin
/@location
/... as labels- Compute shader is similar:
wgsl
struct Constants
{
time: f32
};
@group(0) @binding(0) var<uniform> constants: Constants;
@group(0) @binding(1) var tOutput: texture_storage_2d<rgba8unorm,write>;
fn foo() -> f32
{
return 4.0;
}
@compute @workgroup_size(8, 4, 1)
fn main(@builtin(local_invocation_id) localInvocationID: vec3<u32>,
@builtin(workgroup_id) workgroupID: vec3<u32>,
@builtin(local_invocation_index) localInvocationIndex: u32,
@builtin(global_invocation_id) globalInvocationID: vec3<u32>) {
var color = vec4<f32>(...);
textureStore(tOutput, vec2<i32>(globalInvocationID.xy), color);
}
OpenCL
- Compute kernel only.
- Incredibly similar to both Apple Metal and OpenGL.
cpp
struct Constants
{
float time;
uint width;
uint height;
};
float foo()
{
return 16.0;
}
__kernel void main
(
__constant Constants constants,
__global float4* tOutput
)
{
uint localId = get_local_id(0);
uint workgroupId = get_group_id(0);
uint globalId = get_global_id(0);
int x = globalId % constants.width;
int y = globalId / constants.width;
if (x >= constants.width || y >= constants.height)
{
return;
}
tOutput[y * constants.width + x] = ...;
}
How to Ship Shaders
- Offline: compiled to target IR, and stored as bytecode in lib/exe.
- Online: WGSL or older GLSL. May minify/obfuscate the code.
- Sort of like AOT vs JIT.
meow
- DSL can be necessary and nice.
- I love the idea of field alias and swizzling. I haven't seen these anywhere else before myself.
- Semantic labels. ATM I love WGSL's way - clear and extensible.
- IR is useful as always.
- How C-like syntax is widely accepted.
- I love the look of input (including uniform buffer) as args and output as return value of
main
- consistency. But this makes them only invisible to subroutines - less flexible. vec4
orfloat4
orvec4<f32>
?