# shader

> Write GPU shaders in OSL's syntax and turn them into GLSL for raylib windows.

This is the Markdown version of https://osl-lang.org/packages/shader. Every page of the OSL docs has one: add .md to its path. https://osl-lang.org/llms.txt lists them all.

```osl
import "std:shader"
```

A shader is a small program that runs on the graphics card once for every pixel. GPUs read a language called GLSL. `std:shader` lets you write the shader in OSL-style syntax (parentheses, `number`, no semicolons) and turns it into GLSL, with the usual Shadertoy variables already declared (`iTime`, `iResolution`, `iMouse` and friends).

It only translates text. To run the result you use [raylib](/packages/raylib.md), which compiles it and draws with it.

## A shader in OSL style

Write `def mainImage(fragCoord)` and return the colour of the pixel at `fragCoord`. `transpile` returns the GLSL. This prints the lines that changed:

```osl
import "std:shader"

string code = `def mainImage(fragCoord) (
  vec2 uv = fragCoord / iResolution.xy
  number t = iTime * 1.5
  return vec4(uv, 0.5 + 0.5 * sin(t), 1.0)
)`

string glsl = shader.transpile(code)
for line in glsl.split("\n") (
  if line.contains("vec2 uv") or line.contains("float t") or line.contains("fragColor = vec4") (
    log line.trim()
  )
)
```

Output:

```text
vec2 uv = fragCoord / iResolution.xy;
float t = iTime * 1.5;
fragColor = vec4(uv, 0.5 + 0.5 * sin(t), 1.0); return;
```

`number` became `float`, the blocks got braces, every line got its semicolon, and the `return` became an assignment to `fragColor`. Rename `t` to `time` and run it again. The output follows.

## Plain statements

A shader doesn't need a function at all. Write statements that set `fragColor`, and `uv` is there for you (the pixel's position from 0 to 1):

```osl
import "std:shader"

string glsl = shader.transpile("fragColor = vec4(uv, 0.0, 1.0)")
log glsl.startsWith("#version 330")
log glsl.contains("fragColor = vec4(uv, 0.0, 1.0);")
log glsl.contains("uniform float iTime;")
```

Output:

```text
true
true
true
```

## Real GLSL passes through

Code that already has `#version` and a `main` is returned unchanged, so you can mix: hand-written GLSL for the hard cases, OSL style for the rest.

```osl
import "std:shader"

string handwritten = "#version 330\nvoid main() {}"
log shader.transpile(handwritten) == handwritten
```

Output:

```text
true
```

## Vertex shaders and the built-ins

`shader.transpile(code, {stage: "vertex"})` makes a vertex shader. `shader.vertexDefault()` is raylib's standard one, which passes the texture coordinate and colour on. Two example fragment shaders are included:

```osl
import "std:shader"

log shader.vertexDefault().contains("gl_Position")
log shader.plasma().contains("mainImage")
log shader.gradient().contains("iTime")
```

Output:

```text
true
true
true
```

**Quiz.** What does `shader.transpile` do with the code you give it?

- [ ] Runs it on the CPU and returns an image.
- [x] Returns GLSL source text.
- [ ] Opens a window and draws it.

Answer: It's only a translator. Compiling and drawing happen in raylib.

## Drawing with it

Pass the code to `raylib.compileShader`. It accepts OSL style or GLSL, so you rarely call `transpile` yourself. `shader.use(draw)` runs the drawing calls inside it, and uniforms like `iTime` are set each frame. This needs a window, so it doesn't run here:

```osl
import "std:raylib"
import "std:shader"

raylib.initWindow(640, 360, "Plasma").unwrap()
raylib.setTargetFPS(60)

auto plasma = raylib.compileShader(shader.plasma()).unwrap()

while !raylib.windowShouldClose() (
  plasma.setUniform("iTime", raylib.elapsed() / 1000).unwrap()
  plasma.setUniform("iResolution", raylib.vec(640, 360)).unwrap()
  raylib.draw(def() (
    plasma.drawFullscreen().unwrap()
  ))
)
raylib.closeWindow()
```

Swap `shader.plasma()` for `shader.gradient()` and run it on your machine. A shader that the graphics driver rejects fails with a `ParseError`, and raylib prints the driver's message.

### Exercise: Check your shader

Translate this shader, then print whether the GLSL starts with `#version 330` and whether it contains `float`.

Starting code:

```osl
import "std:shader"

string code = "number glow = uv.x * uv.y\nfragColor = vec4(glow, glow, glow, 1.0)"

log code.len
```

It should print:

```text
true
true
```

Hint 1: `shader.transpile(code)` returns a string. Use `.startsWith` and `.contains`.

A solution:

```osl
import "std:shader"

string code = "number glow = uv.x * uv.y\nfragColor = vec4(glow, glow, glow, 1.0)"

string glsl = shader.transpile(code)
log glsl.startsWith("#version 330")
log glsl.contains("float")
```

> **Note**
>
> `shader.renderImage` and `shader.eval` are deprecated and always fail. Shaders only run on the GPU.

## API reference

## API reference

Every member of `std:shader`, generated from the source of OSL.

### Functions

#### `shader.eval(any expr, any ...args) result<number, error>`

**Deprecated.** Deprecated: shader expressions can't be evaluated on the CPU.

eval was meant to evaluate a shader expression, but it returned sin(t),
cos(t) or t depending on words in the text. It now fails with
UnsupportedError.

#### `shader.gradient() string`

gradient returns an example fragment shader in OSL style: a colour
gradient that cycles with iTime.

#### `shader.plasma() string`

plasma returns an example fragment shader in OSL style: animated plasma
bands driven by iTime.

#### `shader.renderImage(any code, any width, any height, any ...args) result<*img.Image, error>`

**Deprecated.** Deprecated: shaders only run on the GPU; use raylib.compileShader(code) and draw into a raylib.renderTexture.

renderImage was meant to run a shader on the CPU, but it drew one of three
built-in effects chosen by words in the code instead. It now fails with
UnsupportedError; run shaders on the GPU with raylib.compileShader.

#### `shader.toGLSL(any code, any ...args) string`

**Deprecated.** Deprecated: use shader.transpile(code, {stage: "vertex"}) instead.

toGLSL turns OSL-style shader code into GLSL; a second argument of "vert"
or "vertex" makes a vertex shader.

#### `shader.transpile(string code, object? options) string`

transpile turns OSL-style shader code into GLSL 330 for raylib. GLSL that
already has #version and main is returned unchanged. Options: stage
("fragment" or "vertex"). Defines mainImage or plain statements with
fragCoord, uv and fragColor, and declares the usual Shadertoy uniforms.
Throws ValueError for an unknown stage.

#### `shader.vertexDefault() string`

vertexDefault returns raylib's standard vertex shader as GLSL 330, which
passes fragTexCoord and fragColor to the fragment shader.
