GLSL in OpenGLContext

Every shader OpenGLContext ships is written in GLSL 330 and begins with #version 330 core. That is the version OpenGL 3.3 brings, which is what the engine asks for by default (core profile) and what every desktop driver of the last decade or so provides.

This page is for two readers: someone writing a shader for the engine, who wants to know what the engine will hand them; and someone with an older shader that no longer compiles, who wants to know what to change.

What the engine supplies

Vertex inputs

A geometry node writes its arrays to fixed attribute locations, declared once in OpenGLContext/scenegraph/vertexsemantics.py and listed under Fixed Attribute Locations. Spell an input with the engine's own name and it arrives with nothing declared on the node:

in vec3 aPosition;     // location 2
in vec3 aNormal;       // location 1
in vec2 aTexCoord;     // location 0
in vec4 aTangent;      // location 3
in vec4 aColor;        // location 4

To call an input something else, name the semantic it carries with a ShaderInput on the GLSLObject — see A Shape whose appearance brings its own shader.

Uniforms

The render pass sets these on any GLSLObject that declares one, so naming it is all that is needed:

UniformWhat it is
mat_modelviewmodel to eye
mat_projectioneye to clip
mat_modelprojthe two multiplied: model to clip
inv_, tps_, itp_ prefixes the inverse, the transpose, and the inverse-transpose of each

All are mat4. The normal matrix is the upper-left 3×3 of itp_modelview:

uniform mat4 itp_modelview;
...
vec3 eyeNormal = mat3( itp_modelview ) * aNormal;

A shader compiled and bound by hand — as the tutorials from shader_1 to shader_11 do — declares and uploads its own matrices instead, from the mode.matrix and mode.projection the pass hands to Render.

A fragment shader on its own

A core profile has no fixed-function vertex stage, so a GLSLObject carrying only a fragment shader has nothing to draw with. res://simpleshader_vert_txt is the vertex shader for that case: it transforms the position and passes on baseNormal, texCoord and vertexColor.

GLSLObject(
    shaders = [
        GLSLShader( url="res://simpleshader_vert_txt", type="VERTEX" ),
        GLSLShader( url="./my.frag", type="FRAGMENT" ),
    ],
)

tests/shaderobjects.py uses it for three of its shaders.

Moving a GLSL 1.20 shader to 330

The substitutions, in the order they usually bite:

GLSL 1.20GLSL 330
no #version, or #version 120 #version 330 core, on the first line
attribute vec3 x;in vec3 x;
varying vec4 x; out vec4 x; in the vertex shader, in vec4 x; in the fragment shader
gl_FragColor = c; out vec4 fragColor; … fragColor = c;
texture2D(s, uv)texture(s, uv)
gl_Vertexvec4( aPosition, 1.0 )
gl_NormalaNormal
gl_MultiTexCoord0aTexCoord
gl_ColoraColor, or a value the vertex shader passes on
gl_TexCoord[0]a value the vertex shader passes on
gl_ModelViewProjectionMatrixmat_modelproj
gl_ModelViewMatrixmat_modelview
gl_NormalMatrixmat3( itp_modelview )
ftransform()mat_modelproj * vec4( aPosition, 1.0 )
gl_LightSource[0].positiona uniform vec3 the application sets
gl_FrontMaterialuniforms the application sets, or a struct of them
gl_InstanceIDARBgl_InstanceID

Two of these are not substitutions but decisions. gl_TexCoord[0] and gl_Color were filled in by the fixed-function vertex stage, so a fragment shader reading them needs a vertex shader that writes them — either your own or res://simpleshader_vert_txt above. And the light and material built-ins were global GL state, so a shader reading them needs the application to say what the light and the material are; the engine's own lit shader (OpenGLContext/shaders/vrml97_lighting.*) shows one way, packing the lights into a uniform vec4 lights[] array.

Going the other way

To run one of these shaders on a driver that offers only GLSL 1.20, reverse the table: drop the #version line, turn each in in a vertex shader back into attribute and each in/ out pair between the stages back into varying, write to gl_FragColor instead of a declared output, call texture2D, and read gl_ModelViewProjectionMatrix and gl_NormalMatrix in place of the matrix uniforms. The context has to ask for the profile that still has them:

class MyContext( BaseContext ):
    profile = 'compatibility'

A vertex array object is still required by some drivers even under a compatibility profile, so leave the glGenVertexArrays / glBindVertexArray pair in place.

Where the shaders are

Two files in that resources directory stay at GLSL 1.20: legacy_lighting.vert.txt and lights.vert.txt. They reimplement the fixed-function lighting model in terms of gl_LightSource and gl_FrontMaterial, which is a description of that pipeline rather than a shader for this one; nothing loads them, and the engine's own vrml97_lighting.* is the lit shader it uses.