Skip to content

Commit

Permalink
Renderer|Refactor: Moved general purpose shader functions to GLSL files
Browse files Browse the repository at this point in the history
In the "net.dengine.client.renderer" pack, shaders/include/ contains
GLSL files with functions useful for many shaders. This allows more
easily creating new custom shaders for instance for models.

Uses the include feature in GLShaderBank.
  • Loading branch information
skyjake committed Oct 29, 2015
1 parent fbe5f62 commit 7698a97
Show file tree
Hide file tree
Showing 5 changed files with 195 additions and 85 deletions.
@@ -0,0 +1,95 @@
/*
* The Doomsday Engine Project
* Common OpenGL Shaders: Lighting
*
* Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

uniform highp float uEmission; // factor for emissive light

uniform highp vec4 uAmbientLight;
uniform highp vec4 uLightIntensities[4];
uniform highp vec3 uLightDirs[4];

varying highp vec3 vLightDirs[4]; // tangent space
varying highp vec3 vEyeDir; // tangent space

highp vec4 diffuseLightContrib(int index, highp vec3 normal)
{
if(uLightIntensities[index].a <= 0.001)
{
return vec4(0.0); // too dim
}
highp float d = dot(normal, normalize(vLightDirs[index]));
return max(d * uLightIntensities[index], vec4(0.0));
}

highp vec4 diffuseLight(highp vec3 normal)
{
return (uAmbientLight +
diffuseLightContrib(0, normal) +
diffuseLightContrib(1, normal) +
diffuseLightContrib(2, normal) +
diffuseLightContrib(3, normal));
}

#ifdef DENG_HAVE_UTEX

highp vec4 specularLightContrib(highp vec2 specularUV, int index, highp vec3 normal)
{
if(uLightIntensities[index].a <= 0.001)
{
return vec4(0.0); // too dim
}

// Is the surface facing the light direction?
highp float facing = smoothstep(0.0, 0.2, dot(vLightDirs[index], normal));
if(facing <= 0.0)
{
return vec4(0.0); // wrong way
}

highp vec3 reflected = reflect(-vLightDirs[index], normal);

// Check the specular texture for parameters.
highp vec4 specular = texture2D(uTex, specularUV);
highp float shininess = max(1.0, specular.a * 7.0);

highp float d =
facing *
dot(normalize(vEyeDir), reflected) *
shininess - max(0.0, shininess - 1.0);
return max(0.0, d) *
uLightIntensities[index] *
vec4(specular.rgb * 2.0, max(max(specular.r, specular.g), specular.b));
}

highp vec4 specularLight(highp vec2 specularUV, highp vec3 normal)
{
return specularLightContrib(specularUV, 0, normal) +
specularLightContrib(specularUV, 1, normal) +
specularLightContrib(specularUV, 2, normal) +
specularLightContrib(specularUV, 3, normal);
}

highp vec4 emittedLight(highp vec2 emissiveUV)
{
highp vec4 emission = uEmission * texture2D(uTex, emissiveUV);
emission.a = 0.0; // Does not contribute to alpha.
return emission;
}

#endif // DENG_HAVE_UTEX
@@ -0,0 +1,36 @@
/*
* The Doomsday Engine Project
* Common OpenGL Shaders: Skeletal animation (vertex shader)
*
* Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

uniform highp mat4 uBoneMatrices[64];

attribute highp vec4 aBoneIDs;
attribute highp vec4 aBoneWeights;

/*
* Calculates the bone matrix for the current vertex. Bones and their weights
* are determined by vertex attributes.
*/
highp mat4 vertexBoneTransform()
{
return uBoneMatrices[int(aBoneIDs.x + 0.5)] * aBoneWeights.x +
uBoneMatrices[int(aBoneIDs.y + 0.5)] * aBoneWeights.y +
uBoneMatrices[int(aBoneIDs.z + 0.5)] * aBoneWeights.z +
uBoneMatrices[int(aBoneIDs.w + 0.5)] * aBoneWeights.w;
}
@@ -0,0 +1,36 @@
/*
* The Doomsday Engine Project
* Common OpenGL Shaders: Texture manipulation
*
* Copyright (c) 2015 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program is distributed in the hope that it
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#define DENG_HAVE_UTEX

uniform sampler2D uTex; // texture atlas

/*
* Maps the normalized @a uv to the rectangle defined by @a bounds.
*/
highp vec2 mapToBounds(highp vec2 uv, highp vec4 bounds)
{
return bounds.xy + uv * bounds.zw;
}

highp vec3 normalVector(highp vec2 uv)
{
return normalize((texture2D(uTex, uv).xyz * 2.0) - 1.0);
}
Expand Up @@ -5,12 +5,14 @@ model {
variable uAlpha { value = 1 }
variable uAlphaLimit { value = 0 }
variable uEmission { value = 0 }

# Mapping when used with ModelDrawable.
textureMapping <diffuse, normals, specular, emission>

include.vertex <include/skeletal.glsl,
include/lighting.glsl>
vertex = "
uniform highp mat4 uMvpMatrix;
uniform highp mat4 uBoneMatrices[64];
uniform highp vec3 uLightDirs[4];
uniform highp vec3 uEyePos;

attribute highp vec4 aVertex;
Expand All @@ -22,13 +24,9 @@ model {
attribute highp vec4 aBounds2; // normal map
attribute highp vec4 aBounds3; // specular map
attribute highp vec4 aBounds4; // emission map
attribute highp vec4 aBoneIDs;
attribute highp vec4 aBoneWeights;

varying highp vec2 vUV;
varying highp vec4 vUVBounds[4];
varying highp vec3 vLightDirs[4]; // tangent space
varying highp vec3 vEyeDir; // tangent space

highp vec3 transformVector(highp vec3 dir, highp mat4 matrix)
{
Expand All @@ -37,12 +35,7 @@ model {

void main(void)
{
// Bone transformation.
highp mat4 bone =
uBoneMatrices[int(aBoneIDs.x + 0.5)] * aBoneWeights.x +
uBoneMatrices[int(aBoneIDs.y + 0.5)] * aBoneWeights.y +
uBoneMatrices[int(aBoneIDs.z + 0.5)] * aBoneWeights.z +
uBoneMatrices[int(aBoneIDs.w + 0.5)] * aBoneWeights.w;
highp mat4 bone = vertexBoneTransform();

// Vertex position.
highp vec4 modelPos = bone * aVertex;
Expand All @@ -69,92 +62,38 @@ model {
vUVBounds[2] = aBounds3;
vUVBounds[3] = aBounds4;
}"

include.fragment <include/texture.glsl,
include/lighting.glsl>
fragment = "
uniform sampler2D uTex;
uniform highp vec4 uAmbientLight;
uniform highp vec4 uLightIntensities[4];
uniform highp float uEmission;
uniform highp float uAlpha;
uniform highp float uAlphaLimit; // alpha test to discard fragments

varying highp vec2 vUV;
varying highp vec4 vUVBounds[4];
varying highp vec3 vLightDirs[4]; // tangent space
varying highp vec3 vEyeDir; // tangent space

highp vec3 normalVector(highp vec2 uv)
{
return normalize((texture2D(uTex, uv).xyz * 2.0) - 1.0);
}

highp vec4 diffuseLight(int index, highp vec3 normal)
{
if(uLightIntensities[index].a <= 0.001)
{
return vec4(0.0); // too dim
}
highp float d = dot(normal, normalize(vLightDirs[index]));
return max(d * uLightIntensities[index], vec4(0.0));
}

highp vec4 specularLight(highp vec2 specularUV, int index, highp vec3 normal)
{
if(uLightIntensities[index].a <= 0.001)
{
return vec4(0.0); // too dim
}

// Is the surface facing the light direction?
highp float facing = smoothstep(0.0, 0.2, dot(vLightDirs[index], normal));
if(facing <= 0.0)
{
return vec4(0.0); // wrong way
}

highp vec3 reflected = reflect(-vLightDirs[index], normal);

// Check the specular texture for parameters.
highp vec4 specular = texture2D(uTex, specularUV);
highp float shininess = max(1.0, specular.a * 7.0);

highp float d =
facing *
dot(normalize(vEyeDir), reflected) *
shininess - max(0.0, shininess - 1.0);
return max(0.0, d) *
uLightIntensities[index] *
vec4(specular.rgb * 2.0, max(max(specular.r, specular.g), specular.b));
}

void main(void)
{
// Calculate UV at the fragment (wrapped inside the bounds).
highp vec2 wrappedUV = fract(vUV);
highp vec2 uv = vUVBounds[0].xy + wrappedUV * vUVBounds[0].zw;
highp vec2 normalUV = vUVBounds[1].xy + wrappedUV * vUVBounds[1].zw;
highp vec2 specularUV = vUVBounds[2].xy + wrappedUV * vUVBounds[2].zw;
highp vec2 emissiveUV = vUVBounds[3].xy + wrappedUV * vUVBounds[3].zw;
highp vec2 uv = mapToBounds(wrappedUV, vUVBounds[0]);
highp vec2 normalUV = mapToBounds(wrappedUV, vUVBounds[1]);
highp vec2 specularUV = mapToBounds(wrappedUV, vUVBounds[2]);
highp vec2 emissiveUV = mapToBounds(wrappedUV, vUVBounds[3]);

highp vec3 normal = normalVector(normalUV);

highp vec4 diffuse = texture2D(uTex, uv);
diffuse.a *= uAlpha;

gl_FragColor = diffuse *
(uAmbientLight +
diffuseLight(0, normal) +
diffuseLight(1, normal) +
diffuseLight(2, normal) +
diffuseLight(3, normal));
gl_FragColor = diffuse * diffuseLight(normal);

highp vec4 specular =
specularLight(specularUV, 0, normal) +
specularLight(specularUV, 1, normal) +
specularLight(specularUV, 2, normal) +
specularLight(specularUV, 3, normal);

highp vec4 emission = uEmission * texture2D(uTex, emissiveUV);

gl_FragColor.rgb += specular.rgb + emission.rgb;
highp vec4 specular = specularLight(specularUV, normal);
gl_FragColor.rgb += specular.rgb;

gl_FragColor += emittedLight(emissiveUV);

// Specular reflections show up on transparent surfaces.
gl_FragColor.a = min(1.0, diffuse.a + specular.a);

if(gl_FragColor.a < uAlphaLimit) discard;
Expand Down
12 changes: 8 additions & 4 deletions doomsday/sdk/libgui/src/graphics/glshaderbank.cpp
Expand Up @@ -246,16 +246,20 @@ Bank::ISource *GLShaderBank::newSourceFromInfo(String const &id)
// Additional shaders to append to the main source.
if(def.has("include.vertex"))
{
DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, def["include.vertex"].value().as<ArrayValue>().elements())
// Including in reverse to retain order -- each one is prepended.
auto const &incs = def["include.vertex"].value().as<ArrayValue>().elements();
for(int i = incs.size() - 1; i >= 0; --i)
{
vtx.insertFromFile(relativeToPath(def) / (*i)->asText());
vtx.insertFromFile(relativeToPath(def) / incs.at(i)->asText());
}
}
if(def.has("include.fragment"))
{
DENG2_FOR_EACH_CONST(ArrayValue::Elements, i, def["include.fragment"].value().as<ArrayValue>().elements())
// Including in reverse to retain order -- each one is prepended.
auto const &incs = def["include.fragment"].value().as<ArrayValue>().elements();
for(int i = incs.size() - 1; i >= 0; --i)
{
frag.insertFromFile(relativeToPath(def) / (*i)->asText());
frag.insertFromFile(relativeToPath(def) / incs.at(i)->asText());
}
}

Expand Down

0 comments on commit 7698a97

Please sign in to comment.