Browse Source

Shaders: calculate Phong light directions in the fragment shader.

Hm, I wonder why I did it this way, the operation isn't really heavy to
benefit from the hardware interpolators, and with a lot of lights we'd
hit the maximum output count in the vertex shader.

With classic uniforms this seems to be ~5% faster on both Intel and AMD
cards. With UBOs this is ~15% (!) faster on AMD (I guess because the
constant UBO access overhead is moved to just one stage instead of
both?) but slower on Intel (of course, sigh... I assume due to UBO reads
being slow and so when done for every fragment instead of every vertex it
costs more?). Since this benefits the *real* GPUs while the card that
was already awful is more awful I don't think that's a big deal. This
change stays.
pull/518/head
Vladimír Vondruš 5 years ago
parent
commit
df75ab926a
  1. 32
      src/Magnum/Shaders/Phong.frag
  2. 76
      src/Magnum/Shaders/Phong.vert
  3. 51
      src/Magnum/Shaders/PhongGL.cpp

32
src/Magnum/Shaders/Phong.frag

@ -113,6 +113,16 @@ uniform highp uint objectId; /* defaults to zero */
#endif
#if LIGHT_COUNT
/* Needs to be last because it uses locations 12 to 12 + LIGHT_COUNT - 1 */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 12)
#endif
uniform highp vec4 lightPositions[LIGHT_COUNT]
#ifndef GL_ES
= vec4[](LIGHT_POSITION_INITIALIZER)
#endif
;
/* Needs to be last because it uses locations 12 + LIGHT_COUNT to
12 + 2*LIGHT_COUNT - 1. Location 12 is lightPositions. Also it can't be
specified as 12 + LIGHT_COUNT because that requires ARB_enhanced_layouts.
@ -202,8 +212,7 @@ layout(std140
MaterialUniform materials[MATERIAL_COUNT];
};
/* Keep in sync with Phong.vert. Can't "outsource" to a common file because
the #extension directive needs to be always before any code. */
#if LIGHT_COUNT
struct LightUniform {
highp vec4 position;
lowp vec3 colorReserved;
@ -214,7 +223,6 @@ struct LightUniform {
#define light_range rangeReservedReservedReserved.x
};
#if LIGHT_COUNT
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 5
@ -293,8 +301,7 @@ in mediump vec3 transformedTangent;
in mediump vec3 transformedBitangent;
#endif
#endif
in highp vec4 lightDirections[LIGHT_COUNT];
in highp vec3 cameraDirection;
in highp vec3 transformedPosition;
#endif
#if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE)
@ -441,17 +448,26 @@ void main() {
#endif
;
highp const vec4 lightPosition =
#ifndef UNIFORM_BUFFERS
lightPositions[i]
#else
lights[lightOffset + i].position
#endif
;
highp const vec4 lightDirection = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w);
/* Attenuation. Directional lights have the .w component set to 0, use
that to make the distance zero -- which will then ensure the
attenuation is always 1.0 */
highp float dist = length(lightDirections[i].xyz)*lightDirections[i].w;
highp float dist = length(lightDirection.xyz)*lightDirection.w;
/* If range is 0 for whatever reason, clamp it to a small value to
avoid a NaN when dist is 0 as well (which is the case for
directional lights). */
highp float attenuation = clamp(1.0 - pow(dist/max(lightRange, 0.0001), 4.0), 0.0, 1.0);
attenuation = attenuation*attenuation/(1.0 + dist*dist);
highp vec3 normalizedLightDirection = normalize(lightDirections[i].xyz);
highp vec3 normalizedLightDirection = normalize(lightDirection.xyz);
lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation;
fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float(
#ifndef UNIFORM_BUFFERS
@ -465,7 +481,7 @@ void main() {
if(intensity > 0.001) {
highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal);
/* Use attenuation for the specularity as well */
mediump float specularity = clamp(pow(max(0.0, dot(normalize(cameraDirection), reflection)), shininess), 0.0, 1.0)*attenuation;
mediump float specularity = clamp(pow(max(0.0, dot(normalize(-transformedPosition), reflection)), shininess), 0.0, 1.0)*attenuation;
fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a);
}
}

76
src/Magnum/Shaders/Phong.vert

@ -69,7 +69,7 @@ uniform highp mat4 projectionMatrix
#endif
;
#if LIGHT_COUNT
#ifdef HAS_LIGHTS
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2)
#endif
@ -99,18 +99,6 @@ layout(location = 4)
uniform highp uint textureLayer; /* defaults to zero */
#endif
#if LIGHT_COUNT
/* Needs to be last because it uses locations 11 to 11 + LIGHT_COUNT - 1 */
#ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 12)
#endif
uniform highp vec4 lightPositions[LIGHT_COUNT]
#ifndef GL_ES
= vec4[](LIGHT_POSITION_INITIALIZER)
#endif
;
#endif
/* Uniform buffers */
#else
@ -191,28 +179,6 @@ layout(std140
TextureTransformationUniform textureTransformations[DRAW_COUNT];
};
#endif
#if LIGHT_COUNT
/* Keep in sync with Phong.frag. Can't "outsource" to a common file because
the #extension directive needs to be always before any code. */
struct LightUniform {
highp vec4 position;
lowp vec3 colorReserved;
#define light_color colorReserved.xyz
lowp vec4 specularColorReserved;
#define light_specularColor specularColorReserved.xyz
lowp vec4 rangeReservedReservedReserved;
#define light_range rangeReservedReservedReserved.x
};
layout(std140
#ifdef EXPLICIT_BINDING
, binding = 5
#endif
) uniform Light {
LightUniform lights[LIGHT_COUNT];
};
#endif
#endif
/* Inputs */
@ -222,7 +188,7 @@ layout(location = POSITION_ATTRIBUTE_LOCATION)
#endif
in highp vec4 position;
#if LIGHT_COUNT
#ifdef HAS_LIGHTS
#ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = NORMAL_ATTRIBUTE_LOCATION)
#endif
@ -315,7 +281,7 @@ out lowp vec4 interpolatedVertexColor;
flat out highp uint interpolatedInstanceObjectId;
#endif
#if LIGHT_COUNT
#ifdef HAS_LIGHTS
out mediump vec3 transformedNormal;
#ifdef NORMAL_TEXTURE
#ifndef BITANGENT
@ -325,8 +291,7 @@ out mediump vec3 transformedTangent;
out mediump vec3 transformedBitangent;
#endif
#endif
out highp vec4 lightDirections[LIGHT_COUNT];
out highp vec3 cameraDirection;
out highp vec3 transformedPosition;
#endif
#ifdef MULTI_DRAW
@ -348,7 +313,7 @@ void main() {
#endif
highp const mat4 transformationMatrix = transformationMatrices[drawId];
#if LIGHT_COUNT
#ifdef HAS_LIGHTS
mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix);
#endif
#ifdef TEXTURE_TRANSFORMATION
@ -357,9 +322,6 @@ void main() {
highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer);
#endif
#endif
#if LIGHT_COUNT
mediump const uint lightOffset = draws[drawId].draw_lightOffset;
#endif
#endif
/* Transformed vertex position */
@ -368,9 +330,12 @@ void main() {
instancedTransformationMatrix*
#endif
position;
highp vec3 transformedPosition = transformedPosition4.xyz/transformedPosition4.w;
#ifndef HAS_LIGHTS
highp vec3
#endif
transformedPosition = transformedPosition4.xyz/transformedPosition4.w;
#if LIGHT_COUNT
#ifdef HAS_LIGHTS
/* Transformed normal and tangent vector */
transformedNormal = normalMatrix*
#ifdef INSTANCED_TRANSFORMATION
@ -397,27 +362,6 @@ void main() {
bitangent;
#endif
#endif
/* Direction to the light. Directional lights have the last component set
to 0, which gets used to ignore the transformed position. */
#ifndef UNIFORM_BUFFERS
for(int i = 0; i < LIGHT_COUNT; ++i)
#else
for(uint i = 0u, actualLightCount = min(uint(LIGHT_COUNT), draws[drawId].draw_lightCount); i < actualLightCount; ++i)
#endif
{
highp const vec4 lightPosition =
#ifndef UNIFORM_BUFFERS
lightPositions[i]
#else
lights[lightOffset + i].position
#endif
;
lightDirections[i] = vec4(lightPosition.xyz - transformedPosition*lightPosition.w, lightPosition.w);
}
/* Direction to the camera */
cameraDirection = -transformedPosition;
#endif
/* Transform the position */

51
src/Magnum/Shaders/PhongGL.cpp

@ -153,7 +153,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
#ifndef MAGNUM_TARGET_GLES
std::string lightInitializerVertex, lightInitializerFragment;
std::string lightInitializer;
if(!(flags >= Flag::UniformBuffers) && lightCount) {
using namespace Containers::Literals;
@ -167,39 +167,37 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
constexpr Containers::StringView lightColorInitializerItem = "vec3(1.0), "_s;
constexpr Containers::StringView lightRangeInitializerItem = "1.0/0.0, "_s;
lightInitializerVertex.reserve(
lightInitializer.reserve(
lightPositionInitializerPreamble.size() +
lightCount*(lightPositionInitializerItem.size()));
lightColorInitializerPreamble.size() +
lightRangeInitializerPreamble.size() +
lightCount*(lightPositionInitializerItem.size() +
lightColorInitializerItem.size() +
lightRangeInitializerItem.size()));
lightInitializerVertex.append(lightPositionInitializerPreamble);
lightInitializer.append(lightPositionInitializerPreamble);
for(std::size_t i = 0; i != lightCount; ++i)
lightInitializerVertex.append(lightPositionInitializerItem);
lightInitializer.append(lightPositionInitializerItem);
/* Drop the last comma and add a newline at the end */
lightInitializerVertex[lightInitializerVertex.size() - 2] = '\n';
lightInitializerVertex.resize(lightInitializerVertex.size() - 1);
lightInitializerFragment.reserve(
lightColorInitializerPreamble.size() +
lightRangeInitializerPreamble.size() +
lightCount*(lightColorInitializerItem.size() +
lightRangeInitializerItem.size()));
lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializer.resize(lightInitializer.size() - 1);
lightInitializerFragment.append(lightColorInitializerPreamble);
lightInitializer.append(lightColorInitializerPreamble);
for(std::size_t i = 0; i != lightCount; ++i)
lightInitializerFragment.append(lightColorInitializerItem);
lightInitializer.append(lightColorInitializerItem);
/* Drop the last comma and add a newline at the end */
lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n';
lightInitializerFragment.resize(lightInitializerFragment.size() - 1);
lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializer.resize(lightInitializer.size() - 1);
lightInitializerFragment.append(lightRangeInitializerPreamble);
lightInitializer.append(lightRangeInitializerPreamble);
for(std::size_t i = 0; i != lightCount; ++i)
lightInitializerFragment.append(lightRangeInitializerItem);
lightInitializer.append(lightRangeInitializerItem);
/* Drop the last comma and add a newline at the end */
lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n';
lightInitializerFragment.resize(lightInitializerFragment.size() - 1);
lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializer.resize(lightInitializer.size() - 1);
}
#endif
@ -211,7 +209,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
#ifndef MAGNUM_TARGET_GLES2
.addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "")
#endif
.addSource(Utility::formatString("#define LIGHT_COUNT {}\n", lightCount))
.addSource(lightCount ? "#define HAS_LIGHTS\n" : "")
#ifndef MAGNUM_TARGET_GLES2
.addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "")
#endif
@ -221,17 +219,12 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
if(flags >= Flag::UniformBuffers) {
vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n"
"#define DRAW_COUNT {}\n"
"#define LIGHT_COUNT {}\n",
"#define DRAW_COUNT {}\n",
drawCount,
lightCount));
vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "");
}
#endif
#ifndef MAGNUM_TARGET_GLES
if(!(flags >= Flag::UniformBuffers) && lightCount)
vert.addSource(std::move(lightInitializerVertex));
#endif
vert.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.vert"));
frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "")
@ -275,7 +268,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
}
#ifndef MAGNUM_TARGET_GLES
if(!(flags >= Flag::UniformBuffers) && lightCount)
frag.addSource(std::move(lightInitializerFragment));
frag.addSource(std::move(lightInitializer));
#endif
frag.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.frag"));

Loading…
Cancel
Save