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 #endif
#if LIGHT_COUNT #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 /* 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 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. specified as 12 + LIGHT_COUNT because that requires ARB_enhanced_layouts.
@ -202,8 +212,7 @@ layout(std140
MaterialUniform materials[MATERIAL_COUNT]; MaterialUniform materials[MATERIAL_COUNT];
}; };
/* Keep in sync with Phong.vert. Can't "outsource" to a common file because #if LIGHT_COUNT
the #extension directive needs to be always before any code. */
struct LightUniform { struct LightUniform {
highp vec4 position; highp vec4 position;
lowp vec3 colorReserved; lowp vec3 colorReserved;
@ -214,7 +223,6 @@ struct LightUniform {
#define light_range rangeReservedReservedReserved.x #define light_range rangeReservedReservedReserved.x
}; };
#if LIGHT_COUNT
layout(std140 layout(std140
#ifdef EXPLICIT_BINDING #ifdef EXPLICIT_BINDING
, binding = 5 , binding = 5
@ -293,8 +301,7 @@ in mediump vec3 transformedTangent;
in mediump vec3 transformedBitangent; in mediump vec3 transformedBitangent;
#endif #endif
#endif #endif
in highp vec4 lightDirections[LIGHT_COUNT]; in highp vec3 transformedPosition;
in highp vec3 cameraDirection;
#endif #endif
#if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE) #if defined(AMBIENT_TEXTURE) || defined(DIFFUSE_TEXTURE) || defined(SPECULAR_TEXTURE) || defined(NORMAL_TEXTURE)
@ -441,17 +448,26 @@ void main() {
#endif #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 /* Attenuation. Directional lights have the .w component set to 0, use
that to make the distance zero -- which will then ensure the that to make the distance zero -- which will then ensure the
attenuation is always 1.0 */ 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 /* 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 avoid a NaN when dist is 0 as well (which is the case for
directional lights). */ directional lights). */
highp float attenuation = clamp(1.0 - pow(dist/max(lightRange, 0.0001), 4.0), 0.0, 1.0); 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); 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; lowp float intensity = max(0.0, dot(normalizedTransformedNormal, normalizedLightDirection))*attenuation;
fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float( fragmentColor += vec4(finalDiffuseColor.rgb*lightColor*intensity, finalDiffuseColor.a/float(
#ifndef UNIFORM_BUFFERS #ifndef UNIFORM_BUFFERS
@ -465,7 +481,7 @@ void main() {
if(intensity > 0.001) { if(intensity > 0.001) {
highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal); highp vec3 reflection = reflect(-normalizedLightDirection, normalizedTransformedNormal);
/* Use attenuation for the specularity as well */ /* 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); fragmentColor += vec4(finalSpecularColor.rgb*lightSpecularColor.rgb*specularity, finalSpecularColor.a);
} }
} }

76
src/Magnum/Shaders/Phong.vert

@ -69,7 +69,7 @@ uniform highp mat4 projectionMatrix
#endif #endif
; ;
#if LIGHT_COUNT #ifdef HAS_LIGHTS
#ifdef EXPLICIT_UNIFORM_LOCATION #ifdef EXPLICIT_UNIFORM_LOCATION
layout(location = 2) layout(location = 2)
#endif #endif
@ -99,18 +99,6 @@ layout(location = 4)
uniform highp uint textureLayer; /* defaults to zero */ uniform highp uint textureLayer; /* defaults to zero */
#endif #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 */ /* Uniform buffers */
#else #else
@ -191,28 +179,6 @@ layout(std140
TextureTransformationUniform textureTransformations[DRAW_COUNT]; TextureTransformationUniform textureTransformations[DRAW_COUNT];
}; };
#endif #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 #endif
/* Inputs */ /* Inputs */
@ -222,7 +188,7 @@ layout(location = POSITION_ATTRIBUTE_LOCATION)
#endif #endif
in highp vec4 position; in highp vec4 position;
#if LIGHT_COUNT #ifdef HAS_LIGHTS
#ifdef EXPLICIT_ATTRIB_LOCATION #ifdef EXPLICIT_ATTRIB_LOCATION
layout(location = NORMAL_ATTRIBUTE_LOCATION) layout(location = NORMAL_ATTRIBUTE_LOCATION)
#endif #endif
@ -315,7 +281,7 @@ out lowp vec4 interpolatedVertexColor;
flat out highp uint interpolatedInstanceObjectId; flat out highp uint interpolatedInstanceObjectId;
#endif #endif
#if LIGHT_COUNT #ifdef HAS_LIGHTS
out mediump vec3 transformedNormal; out mediump vec3 transformedNormal;
#ifdef NORMAL_TEXTURE #ifdef NORMAL_TEXTURE
#ifndef BITANGENT #ifndef BITANGENT
@ -325,8 +291,7 @@ out mediump vec3 transformedTangent;
out mediump vec3 transformedBitangent; out mediump vec3 transformedBitangent;
#endif #endif
#endif #endif
out highp vec4 lightDirections[LIGHT_COUNT]; out highp vec3 transformedPosition;
out highp vec3 cameraDirection;
#endif #endif
#ifdef MULTI_DRAW #ifdef MULTI_DRAW
@ -348,7 +313,7 @@ void main() {
#endif #endif
highp const mat4 transformationMatrix = transformationMatrices[drawId]; highp const mat4 transformationMatrix = transformationMatrices[drawId];
#if LIGHT_COUNT #ifdef HAS_LIGHTS
mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix); mediump const mat3 normalMatrix = mat3(draws[drawId].normalMatrix);
#endif #endif
#ifdef TEXTURE_TRANSFORMATION #ifdef TEXTURE_TRANSFORMATION
@ -357,9 +322,6 @@ void main() {
highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer); highp const uint textureLayer = floatBitsToUint(textureTransformations[drawId].textureTransformation_layer);
#endif #endif
#endif #endif
#if LIGHT_COUNT
mediump const uint lightOffset = draws[drawId].draw_lightOffset;
#endif
#endif #endif
/* Transformed vertex position */ /* Transformed vertex position */
@ -368,9 +330,12 @@ void main() {
instancedTransformationMatrix* instancedTransformationMatrix*
#endif #endif
position; 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 */ /* Transformed normal and tangent vector */
transformedNormal = normalMatrix* transformedNormal = normalMatrix*
#ifdef INSTANCED_TRANSFORMATION #ifdef INSTANCED_TRANSFORMATION
@ -397,27 +362,6 @@ void main() {
bitangent; bitangent;
#endif #endif
#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 #endif
/* Transform the position */ /* 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); GL::Shader frag = Implementation::createCompatibilityShader(rs, version, GL::Shader::Type::Fragment);
#ifndef MAGNUM_TARGET_GLES #ifndef MAGNUM_TARGET_GLES
std::string lightInitializerVertex, lightInitializerFragment; std::string lightInitializer;
if(!(flags >= Flag::UniformBuffers) && lightCount) { if(!(flags >= Flag::UniformBuffers) && lightCount) {
using namespace Containers::Literals; 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 lightColorInitializerItem = "vec3(1.0), "_s;
constexpr Containers::StringView lightRangeInitializerItem = "1.0/0.0, "_s; constexpr Containers::StringView lightRangeInitializerItem = "1.0/0.0, "_s;
lightInitializerVertex.reserve( lightInitializer.reserve(
lightPositionInitializerPreamble.size() + 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) 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 */ /* Drop the last comma and add a newline at the end */
lightInitializerVertex[lightInitializerVertex.size() - 2] = '\n'; lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializerVertex.resize(lightInitializerVertex.size() - 1); lightInitializer.resize(lightInitializer.size() - 1);
lightInitializerFragment.reserve(
lightColorInitializerPreamble.size() +
lightRangeInitializerPreamble.size() +
lightCount*(lightColorInitializerItem.size() +
lightRangeInitializerItem.size()));
lightInitializerFragment.append(lightColorInitializerPreamble); lightInitializer.append(lightColorInitializerPreamble);
for(std::size_t i = 0; i != lightCount; ++i) 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 */ /* Drop the last comma and add a newline at the end */
lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializerFragment.resize(lightInitializerFragment.size() - 1); lightInitializer.resize(lightInitializer.size() - 1);
lightInitializerFragment.append(lightRangeInitializerPreamble); lightInitializer.append(lightRangeInitializerPreamble);
for(std::size_t i = 0; i != lightCount; ++i) 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 */ /* Drop the last comma and add a newline at the end */
lightInitializerFragment[lightInitializerFragment.size() - 2] = '\n'; lightInitializer[lightInitializer.size() - 2] = '\n';
lightInitializerFragment.resize(lightInitializerFragment.size() - 1); lightInitializer.resize(lightInitializer.size() - 1);
} }
#endif #endif
@ -211,7 +209,7 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
.addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "") .addSource(flags & Flag::TextureArrays ? "#define TEXTURE_ARRAYS\n" : "")
#endif #endif
.addSource(Utility::formatString("#define LIGHT_COUNT {}\n", lightCount)) .addSource(lightCount ? "#define HAS_LIGHTS\n" : "")
#ifndef MAGNUM_TARGET_GLES2 #ifndef MAGNUM_TARGET_GLES2
.addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "") .addSource(flags >= Flag::InstancedObjectId ? "#define INSTANCED_OBJECT_ID\n" : "")
#endif #endif
@ -221,17 +219,12 @@ PhongGL::PhongGL(const Flags flags, const UnsignedInt lightCount
if(flags >= Flag::UniformBuffers) { if(flags >= Flag::UniformBuffers) {
vert.addSource(Utility::formatString( vert.addSource(Utility::formatString(
"#define UNIFORM_BUFFERS\n" "#define UNIFORM_BUFFERS\n"
"#define DRAW_COUNT {}\n" "#define DRAW_COUNT {}\n",
"#define LIGHT_COUNT {}\n",
drawCount, drawCount,
lightCount)); lightCount));
vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : ""); vert.addSource(flags >= Flag::MultiDraw ? "#define MULTI_DRAW\n" : "");
} }
#endif #endif
#ifndef MAGNUM_TARGET_GLES
if(!(flags >= Flag::UniformBuffers) && lightCount)
vert.addSource(std::move(lightInitializerVertex));
#endif
vert.addSource(rs.get("generic.glsl")) vert.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.vert")); .addSource(rs.get("Phong.vert"));
frag.addSource(flags & Flag::AmbientTexture ? "#define AMBIENT_TEXTURE\n" : "") 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 #ifndef MAGNUM_TARGET_GLES
if(!(flags >= Flag::UniformBuffers) && lightCount) if(!(flags >= Flag::UniformBuffers) && lightCount)
frag.addSource(std::move(lightInitializerFragment)); frag.addSource(std::move(lightInitializer));
#endif #endif
frag.addSource(rs.get("generic.glsl")) frag.addSource(rs.get("generic.glsl"))
.addSource(rs.get("Phong.frag")); .addSource(rs.get("Phong.frag"));

Loading…
Cancel
Save