Shaders in 1.81

For test results, bug reports, announcements of new builds etc.

Moderators: winston, another_commander, Getafix

Post Reply
User avatar
kanthoney
Commodore
Commodore
Posts: 281
Joined: Thu Nov 07, 2013 10:21 pm

Shaders in 1.81

Post by kanthoney »

You may have noticed that Lave has been looking a bit flat recently, like this:

Image

It's meant to look like this:

Image

but on some graphics cards, our planet shader didn't work properly and produced the top image instead. The good news is, this is fixed in tonight's build. The bad news is that the same problem may be affecting other shaders, so I thought I'd explain what the problem was and how to get around it. If you're not a shader programmer you can ignore the rest of this post.

OpenGL defines a few built in uniforms that you can use in shaders, such as gl_ModelViewMatrix and gl_ProjectionMatrix. Unfortunately, use of these matrices is deprecated, which leads to some graphics cards taking shortcuts. The problem with the planet shader was that some cards don't bother calculating the uniform gl_NormalMatrix, which in our case affects the normal mapping, erasing the bumps (the problem originally caused the razor sharp day-night terminator, if you remember that. But we tweaked it to affect the normal mapping instead.) If your shader uses this uniform it probably won't work on affected cards either.

The way round this (from tonight's build, of course) is to replace the built in gl_NormalMatrix uniform with the new ooliteNormalMatrix uniform. There are two simple steps to this. Firstly, declare the uniform at the top of your shader:

Code: Select all

uniform mat3 ooliteNormalMatrix;
Then replace all occurences of gl_NormalMatrix with ooliteNormalMatrix throughout the shader. I've also created similar replacements for most of the other gl_* matrices:

Code: Select all

ooliteModelView
ooliteProjection
ooliteModelViewProjection
ooliteNormalMatrix
ooliteModelViewInverse
ooliteProjectionInverse
ooliteModelViewProjectionInverse
ooliteModelViewTranspose
ooliteProjectionTranspose
ooliteModelViewProjectionTranspose
ooliteModelViewInverseTranspose
ooliteProjectionInverseTranspose
ooliteModelViewProjectionInverseTranspose
These are all type mat4 except the ooliteNormalMatrix which is mat3. If you replace the corresponding gl_* matrices in your shader with these it should work on all cards and be nicely future-proofed.

As an example, here's the original planet vertex shader:

Code: Select all

// No vNormal, because normal is always 0,0,1 in tangent space.
varying vec3 vEyeVector;
varying vec2 vTexCoords;
varying vec3 vLight1Vector;
varying vec3 vCoords;

void main(void)
{
    vCoords = gl_Vertex.xyz;

    // Build tangent basis.
    vec3 normal = normalize(gl_NormalMatrix * gl_Normal);
    vec3 binormal = cross(normal, gl_NormalMatrix * vec3(0, 1, 0));
    vec3 tangent = -cross(normal, binormal);

    mat3 TBN = mat3(tangent, binormal, normal);

    vec3 eyeVector = -vec3(gl_ModelViewMatrix * gl_Vertex);
    vEyeVector = eyeVector * TBN;

    vec3 light1Vector = gl_LightSource[1].position.xyz + eyeVector;
    vLight1Vector = light1Vector * TBN;

    vTexCoords = gl_MultiTexCoord0.st;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
And here's the new one, where I've replaced gl_NormalMatrix, gl_ModelViewMatrix and gl_ModelViewProjectionMatrix:

Code: Select all

// No vNormal, because normal is always 0,0,1 in tangent space.
uniform mat3 ooliteNormalMatrix;
uniform mat4 ooliteModelView;
uniform mat4 ooliteModelViewProjection;
varying vec3 vEyeVector;
varying vec2 vTexCoords;
varying vec3 vLight1Vector;
varying vec3 vCoords;

void main(void)
{
    vCoords = gl_Vertex.xyz;

    // Build tangent basis.
    vec3 normal = normalize(ooliteNormalMatrix * gl_Normal);
    vec3 binormal = normalize(cross(normal, ooliteNormalMatrix * vec3(0, 1, 0)));  // ignore the extra normalize() here - that's another fix
    vec3 tangent = -cross(normal, binormal);
    
    mat3 TBN = mat3(tangent, binormal, normal);

    vec3 eyeVector = -vec3(ooliteModelView * gl_Vertex);
    vEyeVector = eyeVector * TBN;

    vec3 light1Vector = gl_LightSource[1].position.xyz + eyeVector;
    vLight1Vector = light1Vector * TBN;

    vTexCoords = gl_MultiTexCoord0.st;
    gl_Position = ooliteModelViewProjection * gl_Vertex;
}
User avatar
Cody
Sharp Shooter Spam Assassin
Sharp Shooter Spam Assassin
Posts: 16081
Joined: Sat Jul 04, 2009 9:31 pm
Location: The Lizard's Claw
Contact:

Re: Shaders in 1.81

Post by Cody »

For reference, this is how Lave displays on my machine in 1.80 Deployment and the latest 1.81 nightly (both lit by a farsun).
I would advise stilts for the quagmires, and camels for the snowy hills
And any survivors, their debts I will certainly pay. There's always a way!
User avatar
Griff
Oolite 2 Art Director
Oolite 2 Art Director
Posts: 2483
Joined: Fri Jul 14, 2006 12:29 pm
Location: Probably hugging his Air Fryer

Re: Shaders in 1.81

Post by Griff »

Wow, awesome stuff, thanks for the guide on what needs updating, i think this is going to affect the vertex shader in all my ship OXP's so i better get cracking on it :)
edit: ignore all this below, there's a tangent space vertex shader i should have used instead which works perfectly :oops:
I had a go at transplanting the bits of code from the planet vertex shader above into the vertex shader in my cobraIII oxp, it works really well apart from one weird lighting glitch that seems to affect just a few polygons on the ship (it looks like it's ones that have normals which point either directly up or down), there's one long strip at the top of the cobra and two small areas underneath that don't seem to get lit in direct sunlight in game, and on the 'load savegame' screen, in the rotating ship preview these same polygons flicker weirdly as the ship rotates, here's a screenshot showing the ship in game and a grab of the ship wireframe showing their position on the ship and their normals (the dotty yellow line)
Image

This is most likely a modelling error on my part, but i did a bit of experimenting and if i change this line

Code: Select all

vec3 binormal = normalize(cross(normal, ooliteNormalMatrix * vec3(0, 1, 0)));  // ignore the extra normalize() here - that's another fix
to

Code: Select all

vec3 binormal = normalize(cross(gl_Normal, ooliteNormalMatrix * vec3(0, 1, 0)));  // ignore the extra normalize() here - that's another fix
basically just changing 'normal' to 'gl_Normal ' it works OK, is this an OK thing to do? I'm really out of my depth with this stuff! :lol: I'm not sure what change it makes to the lighting on the ship in game - it's perhaps not quite as fab looking in gl_Normal version.
edit: Nope, just tried this out on a different model (an asteroid) and my change makes the lighting go all screwy, i'll have a look at my cobraIII model and see what's going on with it as I think that's probably where there's a problem, in the geometry not in the shader
Here's the shader i've been using (with the gl_Normal change)

Code: Select all

uniform mat3 ooliteNormalMatrix;
uniform mat4 ooliteModelView;
uniform mat4 ooliteModelViewProjection;

varying vec2         vTexCoord;
varying vec3         vEyeVector;   // These are all in tangent space
varying vec3         vLight1Vector;

void main()
{
	// Build tangent basis
	   vec3 normal = normalize(ooliteNormalMatrix * gl_Normal);
	   vec3 binormal = normalize(cross(gl_Normal, ooliteNormalMatrix * vec3(0.0, 1.0, 0.0))); // switched normal for gl_Normal on this line!
	   vec3 tangent = -cross(normal, binormal);
	   
	   mat3 TBN = mat3(tangent, binormal, normal);
	 
	   vec3 eyeVector = -vec3(ooliteModelView * gl_Vertex);
	   vEyeVector = eyeVector * TBN;

	   vec3 light1Vector = gl_LightSource[1].position.xyz + eyeVector;
	   vLight1Vector = light1Vector * TBN;
	   vTexCoord = gl_MultiTexCoord0.st;
	   
	   gl_Position = ooliteModelViewProjection * gl_Vertex;
}
Post Reply