Split world & interface shaders

This commit is contained in:
Kae 2023-06-30 05:55:41 +10:00
parent d5fbd2001b
commit fd5247321f
11 changed files with 180 additions and 65 deletions

View File

@ -0,0 +1,9 @@
{
"effectParameters" : {},
"effectTextures" : {},
"effectShaders" : {
"vertex" : "default.vert",
"fragment" : "default.frag"
}
}

View File

@ -0,0 +1,27 @@
#version 110
uniform sampler2D texture0;
uniform sampler2D texture1;
uniform sampler2D texture2;
uniform sampler2D texture3;
varying vec2 fragmentTextureCoordinate;
varying float fragmentTextureIndex;
varying vec4 fragmentColor;
void main() {
vec4 texColor;
if (fragmentTextureIndex > 2.9) {
texColor = texture2D(texture3, fragmentTextureCoordinate);
} else if (fragmentTextureIndex > 1.9) {
texColor = texture2D(texture2, fragmentTextureCoordinate);
} else if (fragmentTextureIndex > 0.9) {
texColor = texture2D(texture1, fragmentTextureCoordinate);
} else {
texColor = texture2D(texture0, fragmentTextureCoordinate);
}
if (texColor.a <= 0.0)
discard;
gl_FragColor = texColor * fragmentColor;
}

View File

@ -0,0 +1,35 @@
#version 110
uniform vec2 textureSize0;
uniform vec2 textureSize1;
uniform vec2 textureSize2;
uniform vec2 textureSize3;
uniform vec2 screenSize;
uniform mat3 vertexTransform;
attribute vec2 vertexPosition;
attribute vec2 vertexTextureCoordinate;
attribute float vertexTextureIndex;
attribute vec4 vertexColor;
attribute float vertexParam1;
varying vec2 fragmentTextureCoordinate;
varying float fragmentTextureIndex;
varying vec4 fragmentColor;
void main() {
vec2 screenPosition = (vertexTransform * vec3(vertexPosition, 1.0)).xy;
if (vertexTextureIndex > 2.9) {
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize3;
} else if (vertexTextureIndex > 1.9) {
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize2;
} else if (vertexTextureIndex > 0.9) {
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize1;
} else {
fragmentTextureCoordinate = vertexTextureCoordinate / textureSize0;
}
fragmentTextureIndex = vertexTextureIndex;
fragmentColor = vertexColor;
gl_Position = vec4(screenPosition / screenSize * 2.0 - 1.0, 0.0, 1.0);
}

View File

@ -2,7 +2,7 @@
"effectParameters" : { "effectParameters" : {
"lightMapEnabled" : { "lightMapEnabled" : {
"type" : "bool", "type" : "bool",
"default" : false, "default" : true,
"uniform" : "lightMapEnabled" "uniform" : "lightMapEnabled"
}, },
"lightMapScale" : { "lightMapScale" : {
@ -38,7 +38,7 @@
}, },
"effectShaders" : { "effectShaders" : {
"vertex" : "opengl20.vert", "vertex" : "world.vert",
"fragment" : "opengl20.frag" "fragment" : "world.frag"
} }
} }

View File

@ -77,7 +77,9 @@ void main() {
vec4 finalColor = texColor * fragmentColor; vec4 finalColor = texColor * fragmentColor;
float finalLightMapMultiplier = fragmentLightMapMultiplier * lightMapMultiplier; float finalLightMapMultiplier = fragmentLightMapMultiplier * lightMapMultiplier;
if (lightMapEnabled && finalLightMapMultiplier > 0.0) if (texColor.a == 0.99607843137)
finalColor.a = fragmentColor.a;
else if (lightMapEnabled && finalLightMapMultiplier > 0.0)
finalColor.rgb *= sampleLightMap(fragmentLightMapCoordinate, 1.0 / lightMapSize).rgb * finalLightMapMultiplier; finalColor.rgb *= sampleLightMap(fragmentLightMapCoordinate, 1.0 / lightMapSize).rgb * finalLightMapMultiplier;
gl_FragColor = finalColor; gl_FragColor = finalColor;
} }

View File

@ -135,13 +135,15 @@ public:
// specific to each type of renderer, so it will be necessary to key the // specific to each type of renderer, so it will be necessary to key the
// configuration off of the renderId string. This should not be called every // configuration off of the renderId string. This should not be called every
// frame, because it will result in a recompile of the underlying shader set. // frame, because it will result in a recompile of the underlying shader set.
virtual void setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) = 0; virtual void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) = 0;
// The effect config will specify named parameters and textures which can be // The effect config will specify named parameters and textures which can be
// set here. // set here.
virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0; virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 0;
virtual void setEffectTexture(String const& textureName, Image const& image) = 0; virtual void setEffectTexture(String const& textureName, Image const& image) = 0;
virtual bool switchEffectConfig(String const& name) = 0;
// Any further rendering will be scissored based on this rect, specified in // Any further rendering will be scissored based on this rect, specified in
// pixels // pixels
virtual void setScissorRect(Maybe<RectI> const& scissorRect) = 0; virtual void setScissorRect(Maybe<RectI> const& scissorRect) = 0;

View File

@ -93,7 +93,7 @@ OpenGl20Renderer::OpenGl20Renderer() {
TextureFiltering::Nearest); TextureFiltering::Nearest);
m_immediateRenderBuffer = createGlRenderBuffer(); m_immediateRenderBuffer = createGlRenderBuffer();
setEffectConfig(JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}}); loadEffectConfig("internal", JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}});
m_limitTextureGroupSize = false; m_limitTextureGroupSize = false;
m_useMultiTexturing = true; m_useMultiTexturing = true;
@ -102,7 +102,8 @@ OpenGl20Renderer::OpenGl20Renderer() {
} }
OpenGl20Renderer::~OpenGl20Renderer() { OpenGl20Renderer::~OpenGl20Renderer() {
glDeleteProgram(m_program); for (auto& effect : m_effects)
glDeleteProgram(effect.second.program);
logGlErrorSummary("OpenGL errors during shutdown"); logGlErrorSummary("OpenGL errors during shutdown");
} }
@ -115,8 +116,13 @@ Vec2U OpenGl20Renderer::screenSize() const {
return m_screenSize; return m_screenSize;
} }
void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) { void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) {
flushImmediatePrimitives(); if (m_effects.contains(name)) {
Logger::warn("OpenGL effect {} already exists", name);
switchEffectConfig(name);
return;
}
GLint status = 0; GLint status = 0;
char logBuffer[1024]; char logBuffer[1024];
@ -161,32 +167,11 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
throw RendererException(strf("Failed to link program: {}\n", logBuffer)); throw RendererException(strf("Failed to link program: {}\n", logBuffer));
} }
if (m_program != 0) glUseProgram(m_program = program);
glDeleteProgram(m_program); setupGlUniforms(m_program);
m_program = program;
glUseProgram(m_program);
m_positionAttribute = glGetAttribLocation(m_program, "vertexPosition"); auto& effect = m_effects.emplace(name, Effect{ program, effectConfig, {}, {} }).first->second;
m_texCoordAttribute = glGetAttribLocation(m_program, "vertexTextureCoordinate"); m_currentEffect = &effect;
m_texIndexAttribute = glGetAttribLocation(m_program, "vertexTextureIndex");
m_colorAttribute = glGetAttribLocation(m_program, "vertexColor");
m_param1Attribute = glGetAttribLocation(m_program, "vertexParam1");
m_textureUniforms.clear();
m_textureSizeUniforms.clear();
for (size_t i = 0; i < MultiTextureCount; ++i) {
m_textureUniforms.append(glGetUniformLocation(m_program, strf("texture{}", i).c_str()));
m_textureSizeUniforms.append(glGetUniformLocation(m_program, strf("textureSize{}", i).c_str()));
}
m_screenSizeUniform = glGetUniformLocation(m_program, "screenSize");
m_vertexTransformUniform = glGetUniformLocation(m_program, "vertexTransform");
for (size_t i = 0; i < MultiTextureCount; ++i) {
glUniform1i(m_textureUniforms[i], i);
}
glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]);
m_effectParameters.clear();
for (auto const& p : effectConfig.getObject("effectParameters", {})) { for (auto const& p : effectConfig.getObject("effectParameters", {})) {
EffectParameter effectParameter; EffectParameter effectParameter;
@ -212,7 +197,7 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
throw RendererException::format("Unrecognized effect parameter type '{}'", type); throw RendererException::format("Unrecognized effect parameter type '{}'", type);
} }
m_effectParameters[p.first] = effectParameter; effect.parameters[p.first] = effectParameter;
if (Json def = p.second.get("default", {})) { if (Json def = p.second.get("default", {})) {
if (type == "bool") { if (type == "bool") {
@ -232,8 +217,6 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
} }
} }
m_effectTextures.clear();
// Assign each texture parameter a texture unit starting with MultiTextureCount, the first // Assign each texture parameter a texture unit starting with MultiTextureCount, the first
// few texture units are used by the primary textures being drawn. Currently, // few texture units are used by the primary textures being drawn. Currently,
// maximum texture units are not checked. // maximum texture units are not checked.
@ -256,7 +239,7 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
Logger::warn("OpenGL20 effect parameter '{}' has textureSizeUniform '{}' with no associated uniform", p.first, *tsu); Logger::warn("OpenGL20 effect parameter '{}' has textureSizeUniform '{}' with no associated uniform", p.first, *tsu);
} }
m_effectTextures[p.first] = effectTexture; effect.textures[p.first] = effectTexture;
} }
} }
@ -265,7 +248,7 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
} }
void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEffectParameter const& value) { void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEffectParameter const& value) {
auto ptr = m_effectParameters.ptr(parameterName); auto ptr = m_currentEffect->parameters.ptr(parameterName);
if (!ptr || (ptr->parameterValue && *ptr->parameterValue == value)) if (!ptr || (ptr->parameterValue && *ptr->parameterValue == value))
return; return;
@ -291,7 +274,7 @@ void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEff
} }
void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& image) { void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& image) {
auto ptr = m_effectTextures.ptr(textureName); auto ptr = m_currentEffect->textures.ptr(textureName);
if (!ptr) if (!ptr)
return; return;
@ -311,6 +294,21 @@ void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const&
} }
} }
bool OpenGl20Renderer::switchEffectConfig(String const& name) {
flushImmediatePrimitives();
auto find = m_effects.find(name);
if (find == m_effects.end())
return false;
Effect& effect = find->second;
glUseProgram(m_program = effect.program);
setupGlUniforms(m_program);
m_currentEffect = &effect;
return true;
}
void OpenGl20Renderer::setScissorRect(Maybe<RectI> const& scissorRect) { void OpenGl20Renderer::setScissorRect(Maybe<RectI> const& scissorRect) {
if (scissorRect == m_scissorRect) if (scissorRect == m_scissorRect)
return; return;
@ -798,7 +796,7 @@ void OpenGl20Renderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F
glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture); glBindTexture(GL_TEXTURE_2D, vb.textures[i].texture);
} }
for (auto const& p : m_effectTextures) { for (auto const& p : m_currentEffect->textures) {
if (p.second.textureValue) { if (p.second.textureValue) {
glActiveTexture(GL_TEXTURE0 + p.second.textureUnit); glActiveTexture(GL_TEXTURE0 + p.second.textureUnit);
glBindTexture(GL_TEXTURE_2D, p.second.textureValue->textureId); glBindTexture(GL_TEXTURE_2D, p.second.textureValue->textureId);
@ -823,4 +821,26 @@ void OpenGl20Renderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F
} }
} }
void OpenGl20Renderer::setupGlUniforms(GLuint program) {
m_positionAttribute = glGetAttribLocation(program, "vertexPosition");
m_texCoordAttribute = glGetAttribLocation(program, "vertexTextureCoordinate");
m_texIndexAttribute = glGetAttribLocation(program, "vertexTextureIndex");
m_colorAttribute = glGetAttribLocation(program, "vertexColor");
m_param1Attribute = glGetAttribLocation(program, "vertexParam1");
m_textureUniforms.clear();
m_textureSizeUniforms.clear();
for (size_t i = 0; i < MultiTextureCount; ++i) {
m_textureUniforms.append(glGetUniformLocation(program, strf("texture{}", i).c_str()));
m_textureSizeUniforms.append(glGetUniformLocation(program, strf("textureSize{}", i).c_str()));
}
m_screenSizeUniform = glGetUniformLocation(program, "screenSize");
m_vertexTransformUniform = glGetUniformLocation(program, "vertexTransform");
for (size_t i = 0; i < MultiTextureCount; ++i)
glUniform1i(m_textureUniforms[i], i);
glUniform2f(m_screenSizeUniform, m_screenSize[0], m_screenSize[1]);
}
} }

View File

@ -20,12 +20,15 @@ public:
String rendererId() const override; String rendererId() const override;
Vec2U screenSize() const override; Vec2U screenSize() const override;
void setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) override; void loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) override;
void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override; void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) override;
void setEffectTexture(String const& textureName, Image const& image) override; void setEffectTexture(String const& textureName, Image const& image) override;
void setScissorRect(Maybe<RectI> const& scissorRect) override; void setScissorRect(Maybe<RectI> const& scissorRect) override;
bool switchEffectConfig(String const& name) override;
TexturePtr createTexture(Image const& texture, TextureAddressing addressing, TextureFiltering filtering) override; TexturePtr createTexture(Image const& texture, TextureAddressing addressing, TextureFiltering filtering) override;
void setSizeLimitEnabled(bool enabled) override; void setSizeLimitEnabled(bool enabled) override;
void setMultiTexturingEnabled(bool enabled) override; void setMultiTexturingEnabled(bool enabled) override;
@ -155,6 +158,13 @@ private:
RefPtr<GlLoneTexture> textureValue; RefPtr<GlLoneTexture> textureValue;
}; };
struct Effect {
GLuint program;
Json config;
StringMap<EffectParameter> parameters;
StringMap<EffectTexture> textures;
};
static void logGlErrorSummary(String prefix); static void logGlErrorSummary(String prefix);
static void uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data); static void uploadTextureImage(PixelFormat pixelFormat, Vec2U size, uint8_t const* data);
@ -166,6 +176,8 @@ private:
void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation); void renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F const& transformation);
void setupGlUniforms(GLuint program);
Vec2U m_screenSize; Vec2U m_screenSize;
GLuint m_program = 0; GLuint m_program = 0;
@ -181,10 +193,11 @@ private:
GLint m_screenSizeUniform = -1; GLint m_screenSizeUniform = -1;
GLint m_vertexTransformUniform = -1; GLint m_vertexTransformUniform = -1;
StringMap<Effect> m_effects;
Effect* m_currentEffect;
RefPtr<GlTexture> m_whiteTexture; RefPtr<GlTexture> m_whiteTexture;
StringMap<EffectParameter> m_effectParameters;
StringMap<EffectTexture> m_effectTextures;
Maybe<RectI> m_scissorRect; Maybe<RectI> m_scissorRect;
bool m_limitTextureGroupSize; bool m_limitTextureGroupSize;

View File

@ -207,25 +207,31 @@ void ClientApplication::renderInit(RendererPtr renderer) {
Application::renderInit(renderer); Application::renderInit(renderer);
auto assets = m_root->assets(); auto assets = m_root->assets();
String rendererConfig = strf("/rendering/{}.config", renderer->rendererId()); auto loadConfig = [&](String const& name) {
if (assets->assetExists(rendererConfig)) { String path = strf("/rendering/{}.config", name);
StringMap<String> shaders; if (assets->assetExists(path)) {
auto config = assets->json(rendererConfig); StringMap<String> shaders;
auto shaderConfig = config.getObject("effectShaders"); auto config = assets->json(path);
for (auto& entry : shaderConfig) { auto shaderConfig = config.getObject("effectShaders");
if (entry.second.isType(Json::Type::String)) { for (auto& entry : shaderConfig) {
String shader = entry.second.toString(); if (entry.second.isType(Json::Type::String)) {
if (!shader.hasChar('\n')) { String shader = entry.second.toString();
auto shaderBytes = assets->bytes(AssetPath::relativeTo(rendererConfig, shader)); if (!shader.hasChar('\n')) {
shader = std::string(shaderBytes->ptr(), shaderBytes->size()); auto shaderBytes = assets->bytes(AssetPath::relativeTo(path, shader));
shader = std::string(shaderBytes->ptr(), shaderBytes->size());
}
shaders[entry.first] = shader;
} }
shaders[entry.first] = shader;
} }
renderer->loadEffectConfig(name, config, shaders);
} }
renderer->setEffectConfig(config, shaders); else
} Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId());
else };
Logger::warn("No rendering config found for renderer with id '{}'", renderer->rendererId());
loadConfig("world");
loadConfig("default");
if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false)) if (m_root->configuration()->get("limitTextureAtlasSize").optBool().value(false))
renderer->setSizeLimitEnabled(true); renderer->setSizeLimitEnabled(true);
@ -365,13 +371,13 @@ void ClientApplication::render() {
RendererPtr renderer = Application::renderer(); RendererPtr renderer = Application::renderer();
if (worldClient) { if (worldClient) {
int64_t start = Time::monotonicMilliseconds(); int64_t start = Time::monotonicMilliseconds();
if (renderer)
renderer->setEffectParameter("lightMapEnabled", true); renderer->switchEffectConfig("world");
worldClient->render(m_renderData, TilePainter::BorderTileSize); worldClient->render(m_renderData, TilePainter::BorderTileSize);
m_worldPainter->render(m_renderData, [&]() { worldClient->waitForLighting(); }); m_worldPainter->render(m_renderData, [&]() { worldClient->waitForLighting(); });
m_mainInterface->renderInWorldElements(); m_mainInterface->renderInWorldElements();
if (renderer) renderer->switchEffectConfig("default");
renderer->setEffectParameter("lightMapEnabled", false);
LogMap::set("render_world", strf("{}ms", Time::monotonicMilliseconds() - start)); LogMap::set("render_world", strf("{}ms", Time::monotonicMilliseconds() - start));
} }
int64_t start = Time::monotonicMilliseconds(); int64_t start = Time::monotonicMilliseconds();

View File

@ -231,6 +231,7 @@ void EnvironmentPainter::renderSky(Vec2F const& screenSize, SkyRenderData const&
m_renderer->flush(); m_renderer->flush();
} }
// TODO: Fix this to work with decimal zoom levels. Currently, the clouds shake rapidly when interpolating between zoom levels.
void EnvironmentPainter::renderParallaxLayers( void EnvironmentPainter::renderParallaxLayers(
Vec2F parallaxWorldPosition, WorldCamera const& camera, ParallaxLayers const& layers, SkyRenderData const& sky) { Vec2F parallaxWorldPosition, WorldCamera const& camera, ParallaxLayers const& layers, SkyRenderData const& sky) {