Module luminance::shader[][src]

Expand description

Shader stages, programs and uniforms.

This module contains everything related to shaders. Shader programs — shaders, for short — are GPU binaries that run to perform a series of transformation. Typically run when a draw command is issued, they are responsible for:

  • Transforming vertex data. This is done in a vertex shader. Vertex data, such as the positions, colors, UV coordinates, bi-tangents, etc. of each vertices will go through the vertex shader and get transformed based on the code provided inside the stage. For example, vertices can be projected on the screen with a perspective and view matrices.
  • Tessellating primitive patches. This is done in tessellation shaders.
  • Filtering, transforming again or even generating new vertices and primitives. This is done by the geometry shader.
  • And finally, outputting a color for each fragment covered by the objects you render. This is done by the fragment shader.

Shader stages

Right now, five shader stages — Stage — are supported, ordered by usage in the graphics pipeline:

  1. StageType::VertexShader.
  2. StageType::TessellationControlShader.
  3. StageType::TessellationEvaluationShader.
  4. StageType::GeometryShader.
  5. StageType::FragmentShader.

Those are not all mandatory: only the vertex stage and fragment stages are mandatory. If you want tessellation shaders, you have to provide both of them.

Shader stages — Stage — are compiled independently at runtime by your GPU driver, and then linked into a shader program. The creation of a Stage implies using an input string, representing the source code of the stage. This is an opaque String that must represent a GLSL stage. The backend will transform the string into its own representation if needed.

For this version of the crate, the GLSL string must be at least 330-compliant. It is possible that this changes in the future to be more flexible, but right now GLSL 150, for instance, is not allowed.

Shader program

A shader program — Program is akin to a binary program, but runs on GPU. It is invoked when you issue draw commands. It will run each stages you’ve put in it to transform vertices and rasterize fragments inside a framebuffer. Once this is done, the framebuffer will contain altered fragments by the final stage (fragment shader). If the shader program outputs several properties, we call that situation MRT (Multiple Render Target) and the framebuffer must be configured to be able to receive those outputs — basically, it means that its color slots and/or depth slots must adapt to the output of the shader program.

Creating shader programs is done by gathering the Stage you want and linking them. Some helper methods allow to create a shader Program directly from the string source for each stage, removing the need to build each stage individually.

Shader programs are typed with three important piece of information:

Vertex semantics

When a shader program runs, it first executes the mandatory _vertex stage on a set of vertices. Those vertices have a given format — that is described by the Vertex trait. Because running a shader on an incompatible vertex would yield wrong results, both the vertices and the shader program must be tagged with a type which must implement the Semantics trait. More on that on the documentation of Semantics.

Render target outputs

A shader program, in its final mandatory fragment stage, will write values into the currently in-use framebuffer. The number of “channels” to write to represents the render targets. Typically, simple renders will simply write the color of a pixel — so only one render target. In that case, the type of the output of the shader program must match the color slot of the framebuffer it is used with.

However, it is possible to write more data. For instance, deferred shading is a technique that requires to write several data to a framebuffer, called G-buffer (for geometry buffer): space coordinates, normals, tangents, bi-tangents, etc. In that case, your framebuffer must have a type matching the outputs of the fragment shader, too.

Shader customization

A shader Program represents some code, in a binary form, that transform data. If you consider such code, it can adapt to the kind of data it receives, but the behavior is static. That means that it shouldn’t be possible to ask the program to do something else — shader programs don’t have a state as they must be spawned in parallel for your vertices, pixels, etc. However, there is a way to dynamically change what happens inside a shader program. That way

The concept is similar to environment variables: you can declare, in your shader stages, environment variables that will receive values from the host (i.e. on the Rust side). It is not possible to change those values while a draw command is issued: you have to change them in between draw commands. For this reason, those environment variables are often called constant buffers, uniform, uniform buffers, etc. by several graphics libraries. In our case, right now, we call them Uniform.

Uniforms

A Uniform is parametric type that accepts the type of the value it will be able to change. For instance, Uniform<f32> represents a f32 that can be changed in a shader program. That value can be set by the Rust program when the shader program is not currently in use — no draw commands.

A Uniform is a single variable that allows the most basic form of customization. It’s very similar to environment variables. You can declare several ones as you would declare several environment variables. More on that on the documentation of Uniform.

Shader data

Another way to pass data to shader is to use a ShaderData. This kind of object allows to shared data between shaders: set the data once, it will be available to any shader you pass the ShaderData to.

Most implementation also allows much more data via this mechanism, allowing to pass huge amount of data to implement various techniques, such as geometry instancing for instance.

Uniform interfaces

As with vertex semantics and render targets, the uniforms that can be used with a shader program are part of its type, too, and represented by a single type that must implement UniformInterface. That type can contain anything, but it is advised to just put Uniform fields in it. More on the UniformInterface documentation.

Modules

Shader type wrappers.

Structs

A Program uniform adaptation that has failed.

A built program with potential warnings.

A shader program.

Interact with the UniformInterface carried by a Program and/or perform dynamic uniform lookup.

Shader data.

A shader stage.

Tessellation stages.

A GPU shader program environment variable.

A builder of Uniform.

Enums

Errors that a Program can generate.

Program warnings, not necessarily considered blocking errors.

Possible errors that can occur with shader data.

Errors that shader stages can emit.

A shader stage type.

Type of a uniform.

Warnings related to uniform issues.

Warnings related to vertex attributes issues.

Traits