Split world & interface shaders
This commit is contained in:
parent
d5fbd2001b
commit
fd5247321f
9
assets/opensb/rendering/default.config
Normal file
9
assets/opensb/rendering/default.config
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"effectParameters" : {},
|
||||
"effectTextures" : {},
|
||||
|
||||
"effectShaders" : {
|
||||
"vertex" : "default.vert",
|
||||
"fragment" : "default.frag"
|
||||
}
|
||||
}
|
27
assets/opensb/rendering/default.frag
Normal file
27
assets/opensb/rendering/default.frag
Normal 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;
|
||||
}
|
35
assets/opensb/rendering/default.vert
Normal file
35
assets/opensb/rendering/default.vert
Normal 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);
|
||||
}
|
@ -2,7 +2,7 @@
|
||||
"effectParameters" : {
|
||||
"lightMapEnabled" : {
|
||||
"type" : "bool",
|
||||
"default" : false,
|
||||
"default" : true,
|
||||
"uniform" : "lightMapEnabled"
|
||||
},
|
||||
"lightMapScale" : {
|
||||
@ -38,7 +38,7 @@
|
||||
},
|
||||
|
||||
"effectShaders" : {
|
||||
"vertex" : "opengl20.vert",
|
||||
"fragment" : "opengl20.frag"
|
||||
"vertex" : "world.vert",
|
||||
"fragment" : "world.frag"
|
||||
}
|
||||
}
|
@ -77,7 +77,9 @@ void main() {
|
||||
|
||||
vec4 finalColor = texColor * fragmentColor;
|
||||
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;
|
||||
gl_FragColor = finalColor;
|
||||
}
|
@ -135,13 +135,15 @@ public:
|
||||
// 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
|
||||
// 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
|
||||
// set here.
|
||||
virtual void setEffectParameter(String const& parameterName, RenderEffectParameter const& parameter) = 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
|
||||
// pixels
|
||||
virtual void setScissorRect(Maybe<RectI> const& scissorRect) = 0;
|
||||
|
@ -93,7 +93,7 @@ OpenGl20Renderer::OpenGl20Renderer() {
|
||||
TextureFiltering::Nearest);
|
||||
m_immediateRenderBuffer = createGlRenderBuffer();
|
||||
|
||||
setEffectConfig(JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}});
|
||||
loadEffectConfig("internal", JsonObject(), {{"vertex", DefaultVertexShader}, {"fragment", DefaultFragmentShader}});
|
||||
|
||||
m_limitTextureGroupSize = false;
|
||||
m_useMultiTexturing = true;
|
||||
@ -102,7 +102,8 @@ OpenGl20Renderer::OpenGl20Renderer() {
|
||||
}
|
||||
|
||||
OpenGl20Renderer::~OpenGl20Renderer() {
|
||||
glDeleteProgram(m_program);
|
||||
for (auto& effect : m_effects)
|
||||
glDeleteProgram(effect.second.program);
|
||||
|
||||
logGlErrorSummary("OpenGL errors during shutdown");
|
||||
}
|
||||
@ -115,8 +116,13 @@ Vec2U OpenGl20Renderer::screenSize() const {
|
||||
return m_screenSize;
|
||||
}
|
||||
|
||||
void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<String> const& shaders) {
|
||||
flushImmediatePrimitives();
|
||||
void OpenGl20Renderer::loadEffectConfig(String const& name, Json const& effectConfig, StringMap<String> const& shaders) {
|
||||
if (m_effects.contains(name)) {
|
||||
Logger::warn("OpenGL effect {} already exists", name);
|
||||
switchEffectConfig(name);
|
||||
return;
|
||||
}
|
||||
|
||||
GLint status = 0;
|
||||
char logBuffer[1024];
|
||||
|
||||
@ -161,32 +167,11 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
|
||||
throw RendererException(strf("Failed to link program: {}\n", logBuffer));
|
||||
}
|
||||
|
||||
if (m_program != 0)
|
||||
glDeleteProgram(m_program);
|
||||
m_program = program;
|
||||
glUseProgram(m_program);
|
||||
glUseProgram(m_program = program);
|
||||
setupGlUniforms(m_program);
|
||||
|
||||
m_positionAttribute = glGetAttribLocation(m_program, "vertexPosition");
|
||||
m_texCoordAttribute = glGetAttribLocation(m_program, "vertexTextureCoordinate");
|
||||
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();
|
||||
auto& effect = m_effects.emplace(name, Effect{ program, effectConfig, {}, {} }).first->second;
|
||||
m_currentEffect = &effect;
|
||||
|
||||
for (auto const& p : effectConfig.getObject("effectParameters", {})) {
|
||||
EffectParameter effectParameter;
|
||||
@ -212,7 +197,7 @@ void OpenGl20Renderer::setEffectConfig(Json const& effectConfig, StringMap<Strin
|
||||
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 (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
|
||||
// few texture units are used by the primary textures being drawn. Currently,
|
||||
// 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);
|
||||
}
|
||||
|
||||
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) {
|
||||
auto ptr = m_effectParameters.ptr(parameterName);
|
||||
auto ptr = m_currentEffect->parameters.ptr(parameterName);
|
||||
if (!ptr || (ptr->parameterValue && *ptr->parameterValue == value))
|
||||
return;
|
||||
|
||||
@ -291,7 +274,7 @@ void OpenGl20Renderer::setEffectParameter(String const& parameterName, RenderEff
|
||||
}
|
||||
|
||||
void OpenGl20Renderer::setEffectTexture(String const& textureName, Image const& image) {
|
||||
auto ptr = m_effectTextures.ptr(textureName);
|
||||
auto ptr = m_currentEffect->textures.ptr(textureName);
|
||||
if (!ptr)
|
||||
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) {
|
||||
if (scissorRect == m_scissorRect)
|
||||
return;
|
||||
@ -798,7 +796,7 @@ void OpenGl20Renderer::renderGlBuffer(GlRenderBuffer const& renderBuffer, Mat3F
|
||||
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) {
|
||||
glActiveTexture(GL_TEXTURE0 + p.second.textureUnit);
|
||||
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]);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,12 +20,15 @@ public:
|
||||
String rendererId() 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 setEffectTexture(String const& textureName, Image const& image) override;
|
||||
|
||||
void setScissorRect(Maybe<RectI> const& scissorRect) override;
|
||||
|
||||
bool switchEffectConfig(String const& name) override;
|
||||
|
||||
TexturePtr createTexture(Image const& texture, TextureAddressing addressing, TextureFiltering filtering) override;
|
||||
void setSizeLimitEnabled(bool enabled) override;
|
||||
void setMultiTexturingEnabled(bool enabled) override;
|
||||
@ -155,6 +158,13 @@ private:
|
||||
RefPtr<GlLoneTexture> textureValue;
|
||||
};
|
||||
|
||||
struct Effect {
|
||||
GLuint program;
|
||||
Json config;
|
||||
StringMap<EffectParameter> parameters;
|
||||
StringMap<EffectTexture> textures;
|
||||
};
|
||||
|
||||
static void logGlErrorSummary(String prefix);
|
||||
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 setupGlUniforms(GLuint program);
|
||||
|
||||
Vec2U m_screenSize;
|
||||
|
||||
GLuint m_program = 0;
|
||||
@ -181,10 +193,11 @@ private:
|
||||
GLint m_screenSizeUniform = -1;
|
||||
GLint m_vertexTransformUniform = -1;
|
||||
|
||||
StringMap<Effect> m_effects;
|
||||
Effect* m_currentEffect;
|
||||
|
||||
RefPtr<GlTexture> m_whiteTexture;
|
||||
|
||||
StringMap<EffectParameter> m_effectParameters;
|
||||
StringMap<EffectTexture> m_effectTextures;
|
||||
Maybe<RectI> m_scissorRect;
|
||||
|
||||
bool m_limitTextureGroupSize;
|
||||
|
@ -207,25 +207,31 @@ void ClientApplication::renderInit(RendererPtr renderer) {
|
||||
Application::renderInit(renderer);
|
||||
auto assets = m_root->assets();
|
||||
|
||||
String rendererConfig = strf("/rendering/{}.config", renderer->rendererId());
|
||||
if (assets->assetExists(rendererConfig)) {
|
||||
StringMap<String> shaders;
|
||||
auto config = assets->json(rendererConfig);
|
||||
auto shaderConfig = config.getObject("effectShaders");
|
||||
for (auto& entry : shaderConfig) {
|
||||
if (entry.second.isType(Json::Type::String)) {
|
||||
String shader = entry.second.toString();
|
||||
if (!shader.hasChar('\n')) {
|
||||
auto shaderBytes = assets->bytes(AssetPath::relativeTo(rendererConfig, shader));
|
||||
shader = std::string(shaderBytes->ptr(), shaderBytes->size());
|
||||
auto loadConfig = [&](String const& name) {
|
||||
String path = strf("/rendering/{}.config", name);
|
||||
if (assets->assetExists(path)) {
|
||||
StringMap<String> shaders;
|
||||
auto config = assets->json(path);
|
||||
auto shaderConfig = config.getObject("effectShaders");
|
||||
for (auto& entry : shaderConfig) {
|
||||
if (entry.second.isType(Json::Type::String)) {
|
||||
String shader = entry.second.toString();
|
||||
if (!shader.hasChar('\n')) {
|
||||
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))
|
||||
renderer->setSizeLimitEnabled(true);
|
||||
@ -365,13 +371,13 @@ void ClientApplication::render() {
|
||||
RendererPtr renderer = Application::renderer();
|
||||
if (worldClient) {
|
||||
int64_t start = Time::monotonicMilliseconds();
|
||||
if (renderer)
|
||||
renderer->setEffectParameter("lightMapEnabled", true);
|
||||
|
||||
renderer->switchEffectConfig("world");
|
||||
worldClient->render(m_renderData, TilePainter::BorderTileSize);
|
||||
m_worldPainter->render(m_renderData, [&]() { worldClient->waitForLighting(); });
|
||||
m_mainInterface->renderInWorldElements();
|
||||
if (renderer)
|
||||
renderer->setEffectParameter("lightMapEnabled", false);
|
||||
renderer->switchEffectConfig("default");
|
||||
|
||||
LogMap::set("render_world", strf("{}ms", Time::monotonicMilliseconds() - start));
|
||||
}
|
||||
int64_t start = Time::monotonicMilliseconds();
|
||||
|
@ -231,6 +231,7 @@ void EnvironmentPainter::renderSky(Vec2F const& screenSize, SkyRenderData const&
|
||||
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(
|
||||
Vec2F parallaxWorldPosition, WorldCamera const& camera, ParallaxLayers const& layers, SkyRenderData const& sky) {
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user