From 9bdd83771e0e081ecf3be1384c7b1bb197ad4165 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Kapu=C5=9Bci=C5=84ski?= Date: Mon, 2 Jan 2017 16:35:40 +0100 Subject: [PATCH] Optimizations and changes in OpenGL 2.1 device. * Limited number of lights to 4 * Only directional lights * Per-pixel lighting * Improved dynamic shadows a bit --- src/graphics/opengl/gl21device.cpp | 149 ++++++++++-------- src/graphics/opengl/gl21device.h | 10 +- src/graphics/opengl/glutil.h | 2 - .../opengl/shaders/gl21/fs_normal.glsl | 81 ++++++++-- .../opengl/shaders/gl21/vs_normal.glsl | 95 +---------- 5 files changed, 164 insertions(+), 173 deletions(-) diff --git a/src/graphics/opengl/gl21device.cpp b/src/graphics/opengl/gl21device.cpp index b38f6387..48467668 100644 --- a/src/graphics/opengl/gl21device.cpp +++ b/src/graphics/opengl/gl21device.cpp @@ -242,7 +242,7 @@ bool CGL21Device::Create() glViewport(0, 0, m_config.size.x, m_config.size.y); // this is set in shader - int numLights = 8; + int numLights = 4; m_lights = std::vector(numLights, Light()); m_lightsEnabled = std::vector (numLights, false); @@ -408,7 +408,7 @@ bool CGL21Device::Create() uni.fogColor = glGetUniformLocation(m_normalProgram, "uni_FogColor"); uni.shadowColor = glGetUniformLocation(m_normalProgram, "uni_ShadowColor"); - uni.lightingEnabled = glGetUniformLocation(m_normalProgram, "uni_LightingEnabled"); + uni.lightCount = glGetUniformLocation(m_normalProgram, "uni_LightCount"); uni.ambientColor = glGetUniformLocation(m_normalProgram, "uni_Material.ambient"); uni.diffuseColor = glGetUniformLocation(m_normalProgram, "uni_Material.diffuse"); @@ -417,12 +417,6 @@ bool CGL21Device::Create() GLchar name[64]; for (int i = 0; i < 8; i++) { - sprintf(name, "uni_Light[%d].Enabled", i); - uni.lights[i].enabled = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Type", i); - uni.lights[i].type = glGetUniformLocation(m_normalProgram, name); - sprintf(name, "uni_Light[%d].Position", i); uni.lights[i].position = glGetUniformLocation(m_normalProgram, name); @@ -434,18 +428,6 @@ bool CGL21Device::Create() sprintf(name, "uni_Light[%d].Specular", i); uni.lights[i].specular = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Attenuation", i); - uni.lights[i].attenuation = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].SpotDirection", i); - uni.lights[i].spotDirection = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].Exponent", i); - uni.lights[i].spotExponent = glGetUniformLocation(m_normalProgram, name); - - sprintf(name, "uni_Light[%d].SpotCutoff", i); - uni.lights[i].spotCutoff = glGetUniformLocation(m_normalProgram, name); } // Set default uniform values @@ -476,10 +458,7 @@ bool CGL21Device::Create() glUniform1f(uni.shadowColor, 0.5f); - glUniform1i(uni.lightingEnabled, 0); - - for (int i = 0; i < 8; i++) - glUniform1i(uni.lights[i].enabled, 0); + glUniform1i(uni.lightCount, 0); } // Obtain uniform locations from interface rendering program and initialize them @@ -594,6 +573,7 @@ void CGL21Device::ConfigChanged(const DeviceConfig& newConfig) // Reset state m_lighting = false; + m_updateLights = true; glViewport(0, 0, m_config.size.x, m_config.size.y); @@ -721,36 +701,7 @@ void CGL21Device::SetLight(int index, const Light &light) m_lights[index] = light; - LightLocations &loc = m_uniforms[m_mode].lights[index]; - - glUniform4fv(loc.ambient, 1, light.ambient.Array()); - glUniform4fv(loc.diffuse, 1, light.diffuse.Array()); - glUniform4fv(loc.specular, 1, light.specular.Array()); - glUniform3f(loc.attenuation, light.attenuation0, light.attenuation1, light.attenuation2); - - if (light.type == LIGHT_DIRECTIONAL) - { - glUniform1i(loc.type, 1); - glUniform4f(loc.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); - } - else if (light.type == LIGHT_POINT) - { - glUniform1i(loc.type, 2); - glUniform4f(loc.position, light.position.x, light.position.y, light.position.z, 1.0f); - - glUniform3f(loc.spotDirection, 0.0f, 1.0f, 0.0f); - glUniform1f(loc.spotCutoff, -1.0f); - glUniform1f(loc.spotExponent, 1.0f); - } - else if (light.type == LIGHT_SPOT) - { - glUniform1i(loc.type, 3); - glUniform4f(loc.position, light.position.x, light.position.y, light.position.z, 1.0f); - - glUniform3f(loc.spotDirection, -light.direction.x, -light.direction.y, -light.direction.z); - glUniform1f(loc.spotCutoff, std::cos(light.spotAngle)); - glUniform1f(loc.spotExponent, light.spotIntensity); - } + m_updateLights = true; } void CGL21Device::SetLightEnabled(int index, bool enabled) @@ -760,7 +711,7 @@ void CGL21Device::SetLightEnabled(int index, bool enabled) m_lightsEnabled[index] = enabled; - glUniform1i(m_uniforms[m_mode].lights[index].enabled, enabled ? 1 : 0); + m_updateLights = true; } /** If image is invalid, returns invalid texture. @@ -798,12 +749,13 @@ Texture CGL21Device::CreateTexture(ImageData *data, const TextureCreateParams &p result.originalSize = result.size; - glActiveTexture(GL_TEXTURE0); - glEnable(GL_TEXTURE_2D); - glGenTextures(1, &result.id); + + glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, result.id); + glEnable(GL_TEXTURE_2D); + // Set texture parameters GLint minF = GL_NEAREST, magF = GL_NEAREST; int mipmapLevel = 1; @@ -991,7 +943,7 @@ void CGL21Device::SetTexture(int index, const Texture &texture) glBindTexture(GL_TEXTURE_2D, texture.id); // Params need to be updated for the new bound texture - UpdateTextureStatus(); + UpdateTextureState(index); UpdateTextureParams(index); } @@ -1009,7 +961,7 @@ void CGL21Device::SetTexture(int index, unsigned int textureId) glBindTexture(GL_TEXTURE_2D, textureId); // Params need to be updated for the new bound texture - UpdateTextureStatus(); + UpdateTextureState(index); UpdateTextureParams(index); } @@ -1024,15 +976,54 @@ void CGL21Device::SetTextureEnabled(int index, bool enabled) if (same) return; // nothing to do - UpdateTextureStatus(); + UpdateTextureState(index); } -void CGL21Device::UpdateTextureStatus() +void CGL21Device::UpdateTextureState(int index) { - for (int i = 0; i < 3; i++) + bool enabled = m_texturesEnabled[index] && (m_currentTextures[index].id != 0); + glUniform1i(m_uniforms[m_mode].textureEnabled[index], enabled ? 1 : 0); +} + +void CGL21Device::UpdateLights() +{ + m_updateLights = false; + + // If not in normal rendering mode, return immediately + if (m_mode != 0) return; + + // Lighting enabled + if (m_lighting) { - bool enabled = m_texturesEnabled[i] && (m_currentTextures[i].id != 0); - glUniform1i(m_uniforms[m_mode].textureEnabled[i], enabled ? 1 : 0); + int index = 0; + + // Iterate all lights + for (unsigned int i = 0; i < m_lights.size(); i++) + { + // If disabled, ignore and continue + if (!m_lightsEnabled[i]) continue; + + // If not directional, ignore and continue + if (m_lights[i].type != LIGHT_DIRECTIONAL) continue; + + Light &light = m_lights[i]; + LightLocations &uni = m_uniforms[m_mode].lights[index]; + + glUniform4fv(uni.ambient, 1, light.ambient.Array()); + glUniform4fv(uni.diffuse, 1, light.diffuse.Array()); + glUniform4fv(uni.specular, 1, light.specular.Array()); + + glUniform4f(uni.position, -light.direction.x, -light.direction.y, -light.direction.z, 0.0f); + + index++; + } + + glUniform1i(m_uniforms[m_mode].lightCount, index); + } + // Lighting disabled + else + { + glUniform1i(m_uniforms[m_mode].lightCount, 0); } } @@ -1044,6 +1035,12 @@ inline void CGL21Device::BindVBO(GLuint vbo) m_currentVBO = vbo; } +inline void CGL21Device::BindTexture(int index, GLuint texture) +{ + glActiveTexture(GL_TEXTURE0 + index); + glBindTexture(GL_TEXTURE_2D, texture); +} + /** Sets the texture parameters for the given texture stage. If the given texture was not set (bound) yet, nothing happens. @@ -1121,6 +1118,8 @@ void CGL21Device::SetTextureStageWrap(int index, TexWrapMode wrapS, TexWrapMode void CGL21Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); Vertex* vs = const_cast(vertices); @@ -1148,6 +1147,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const Vertex *vertices, int void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, int vertexCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexTex2* vs = const_cast(vertices); @@ -1180,6 +1181,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexTex2 *vertices, void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, int vertexCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexCol* vs = const_cast(vertices); @@ -1199,6 +1202,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const VertexCol *vertices, i void CGL21Device::DrawPrimitive(PrimitiveType type, const void *vertices, int size, const VertexFormat &format, int vertexCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); const char *ptr = reinterpret_cast(vertices); @@ -1277,6 +1282,8 @@ void CGL21Device::DrawPrimitive(PrimitiveType type, const void *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const void *vertices, int size, const VertexFormat &format, int first[], int count[], int drawCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); const char *ptr = reinterpret_cast(vertices); @@ -1355,6 +1362,8 @@ void CGL21Device::DrawPrimitives(PrimitiveType type, const void *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); Vertex* vs = const_cast(vertices); @@ -1381,6 +1390,8 @@ void CGL21Device::DrawPrimitives(PrimitiveType type, const Vertex *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, int first[], int count[], int drawCount, Color color) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexTex2* vs = const_cast(vertices); @@ -1414,6 +1425,8 @@ void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexTex2 *vertices, void CGL21Device::DrawPrimitives(PrimitiveType type, const VertexCol *vertices, int first[], int count[], int drawCount) { + if (m_updateLights) UpdateLights(); + BindVBO(0); VertexCol* vs = const_cast(vertices); @@ -1574,6 +1587,8 @@ void CGL21Device::DrawStaticBuffer(unsigned int bufferId) if (it == m_vboObjects.end()) return; + if (m_updateLights) UpdateLights(); + BindVBO((*it).second.bufferId); if ((*it).second.vertexType == VERTEX_TYPE_NORMAL) @@ -1741,9 +1756,11 @@ void CGL21Device::SetRenderState(RenderState state, bool enabled) } else if (state == RENDER_STATE_LIGHTING) { + if (m_lighting == enabled) return; + m_lighting = enabled; - glUniform1i(m_uniforms[m_mode].lightingEnabled, enabled ? 1 : 0); + m_updateLights = true; return; } diff --git a/src/graphics/opengl/gl21device.h b/src/graphics/opengl/gl21device.h index e70d66c6..9a2bea7e 100644 --- a/src/graphics/opengl/gl21device.h +++ b/src/graphics/opengl/gl21device.h @@ -183,10 +183,14 @@ public: private: //! Updates the texture params for given texture stage void UpdateTextureParams(int index); - //! Updates texture status - void UpdateTextureStatus(); + //! Updates texture state + void UpdateTextureState(int index); + //! Update light parameters + void UpdateLights(); //! Binds VBO inline void BindVBO(GLuint vbo); + //! Binds texture + inline void BindTexture(int index, GLuint texture); private: //! Current config @@ -208,6 +212,8 @@ private: //! Whether lighting is enabled bool m_lighting = false; + //! true means that lights need to be updated + bool m_updateLights = false; //! Current lights std::vector m_lights; //! Current lights enable status diff --git a/src/graphics/opengl/glutil.h b/src/graphics/opengl/glutil.h index 4b66d50b..f6b0f16f 100644 --- a/src/graphics/opengl/glutil.h +++ b/src/graphics/opengl/glutil.h @@ -194,8 +194,6 @@ struct UniformLocations //! Shadow color GLint shadowColor = -1; - //! true enables lighting - GLint lightingEnabled = -1; // Number of enabled lights GLint lightCount = -1; //! Ambient color diff --git a/src/graphics/opengl/shaders/gl21/fs_normal.glsl b/src/graphics/opengl/shaders/gl21/fs_normal.glsl index fdf59ac2..d21bc9cf 100644 --- a/src/graphics/opengl/shaders/gl21/fs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/fs_normal.glsl @@ -19,6 +19,8 @@ // FRAGMENT SHADER - NORMAL MODE #version 120 +#define CONFIG_QUALITY_SHADOWS 1 + uniform sampler2D uni_PrimaryTexture; uniform sampler2D uni_SecondaryTexture; uniform sampler2DShadow uni_ShadowTexture; @@ -34,6 +36,26 @@ uniform vec4 uni_FogColor; uniform float uni_ShadowColor; +struct LightParams +{ + vec4 Position; + vec4 Ambient; + vec4 Diffuse; + vec4 Specular; +}; + +struct Material +{ + vec4 ambient; + vec4 diffuse; + vec4 specular; +}; + +uniform Material uni_Material; + +uniform int uni_LightCount; +uniform LightParams uni_Light[4]; + varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -47,6 +69,55 @@ void main() { vec4 color = pass_Color; + if (uni_LightCount > 0) + { + vec4 ambient = vec4(0.0f); + vec4 diffuse = vec4(0.0f); + vec4 specular = vec4(0.0f); + + vec3 normal = normalize(pass_Normal); + + for (int i = 0; i < uni_LightCount; i++) + { + LightParams light = uni_Light[i]; + + vec3 lightDirection = light.Position.xyz; + vec3 reflectDirection = -reflect(lightDirection, normal); + + float diffuseComponent = clamp(dot(normal, lightDirection), 0.0f, 1.0f); + float specularComponent = clamp(pow(dot(normal, lightDirection + reflectDirection), 10.0f), 0.0f, 1.0f); + + ambient += light.Ambient; + diffuse += diffuseComponent * light.Diffuse; + specular += specularComponent * light.Specular; + } + + float shadow = 1.0f; + + if (uni_TextureEnabled[2]) + { +#ifdef CONFIG_QUALITY_SHADOWS + float offset = 0.00025f; + + float value = (1.0f / 5.0f) * (shadow2D(uni_ShadowTexture, pass_TexCoord2).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( offset, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3(-offset, 0.0f, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, offset, 0.0f)).x + + shadow2D(uni_ShadowTexture, pass_TexCoord2 + vec3( 0.0f, -offset, 0.0f)).x); + + shadow = mix(uni_ShadowColor, 1.0f, value); +#else + shadow = mix(uni_ShadowColor, 1.0f, shadow2D(uni_ShadowTexture, pass_TexCoord2).x); +#endif + } + + vec4 result = ambient * uni_Material.ambient + + diffuse * uni_Material.diffuse * shadow + + specular * uni_Material.specular * shadow; + + color = clamp(vec4(result.rgb, 1.0f), 0.0f, 1.0f); + } + if (uni_TextureEnabled[0]) { color = color * texture2D(uni_PrimaryTexture, pass_TexCoord0); @@ -57,16 +128,6 @@ void main() color = color * texture2D(uni_SecondaryTexture, pass_TexCoord1); } - if (uni_TextureEnabled[2]) - { - vec3 normal = pass_Normal * (2.0f * gl_Color.x - 1.0f); - - if (dot(normal, const_LightDirection) < 0.0f) - color.rgb *= uni_ShadowColor; - else - color.rgb *= mix(uni_ShadowColor, 1.0f, shadow2D(uni_ShadowTexture, pass_TexCoord2).x); - } - if (uni_FogEnabled) { float interpolate = (pass_Distance - uni_FogRange.x) / (uni_FogRange.y - uni_FogRange.x); diff --git a/src/graphics/opengl/shaders/gl21/vs_normal.glsl b/src/graphics/opengl/shaders/gl21/vs_normal.glsl index 117f09b5..9195cd20 100644 --- a/src/graphics/opengl/shaders/gl21/vs_normal.glsl +++ b/src/graphics/opengl/shaders/gl21/vs_normal.glsl @@ -25,33 +25,6 @@ uniform mat4 uni_ModelMatrix; uniform mat4 uni_ShadowMatrix; uniform mat4 uni_NormalMatrix; -struct LightParams -{ - bool Enabled; - int Type; - vec4 Position; - vec4 Ambient; - vec4 Diffuse; - vec4 Specular; - float Shininess; - vec3 Attenuation; - vec3 SpotDirection; - float SpotCutoff; - float SpotExponent; -}; - -struct Material -{ - vec4 ambient; - vec4 diffuse; - vec4 specular; -}; - -uniform Material uni_Material; - -uniform bool uni_LightingEnabled; -uniform LightParams uni_Light[8]; - varying float pass_Distance; varying vec4 pass_Color; varying vec3 pass_Normal; @@ -65,75 +38,11 @@ void main() vec4 eyeSpace = uni_ViewMatrix * position; vec4 shadowCoord = uni_ShadowMatrix * position; - vec4 color = gl_Color; - - vec3 normal = normalize((uni_NormalMatrix * vec4(gl_Normal, 0.0f)).xyz); - - if (uni_LightingEnabled) - { - vec4 ambient = vec4(0.0f); - vec4 diffuse = vec4(0.0f); - vec4 specular = vec4(0.0f); - - for (int i = 0; i < 8; i++) - { - if (uni_Light[i].Enabled) - { - LightParams light = uni_Light[i]; - - vec3 lightDirection = light.Position.xyz; - float atten = 1.0f; - - if (light.Position.w > 0.5f) - { - float dist = distance(light.Position.xyz, position.xyz); - - float atten = 1.0f / dot(light.Attenuation, - vec3(1.0f, dist, dist * dist)); - - lightDirection = normalize(light.Position.xyz - position.xyz); - } - - float spot = 1.0f; - - if (light.SpotCutoff > 0.0f) - { - float cone = dot(light.SpotDirection, lightDirection); - - if (cone > light.SpotCutoff) - { - spot = pow(cone, light.SpotExponent); - } - else - { - continue; - } - } - - vec3 reflectDirection = -reflect(lightDirection, normal); - - float component = atten * spot; - float diffuseComponent = clamp(dot(normal, lightDirection), 0.0f, 1.0f); - float specularComponent = clamp(pow(dot(normal, lightDirection + reflectDirection), light.Shininess), 0.0f, 1.0f); - - ambient += component * light.Ambient * uni_Material.ambient; - diffuse += component * diffuseComponent * light.Diffuse * uni_Material.diffuse; - specular += component * specularComponent * light.Specular * uni_Material.specular; - } - } - - vec4 result = ambient + diffuse + specular; - - color = clamp(vec4(result.rgb, uni_Material.diffuse), 0.0f, 1.0f); - } - gl_Position = uni_ProjectionMatrix * eyeSpace; - gl_FrontColor = vec4(1.0f); - gl_BackColor = vec4(0.0f); + pass_Color = gl_Color; + pass_Normal = normalize((uni_NormalMatrix * vec4(gl_Normal, 0.0f)).xyz); pass_Distance = abs(eyeSpace.z / eyeSpace.w); - pass_Color = color; - pass_Normal = normal; pass_TexCoord0 = gl_MultiTexCoord0.st; pass_TexCoord1 = gl_MultiTexCoord1.st; pass_TexCoord2 = shadowCoord.xyz / shadowCoord.w;