mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-16 15:29:55 +00:00
fix persistent buffers and glsl_version
This commit is contained in:
parent
e67135a517
commit
9a5fa9b8d6
9 changed files with 172 additions and 58 deletions
|
@ -238,10 +238,35 @@ namespace MWRender
|
|||
|
||||
if (pass.mRenderTarget)
|
||||
{
|
||||
if (mDirtyAttachments)
|
||||
{
|
||||
const auto [w, h]
|
||||
= pass.mSize.get(mTextureScene->getTextureWidth(), mTextureScene->getTextureHeight());
|
||||
|
||||
pass.mRenderTexture->setTextureSize(w, h);
|
||||
if (pass.mMipMap)
|
||||
pass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
|
||||
pass.mRenderTexture->dirtyTextureObject();
|
||||
|
||||
// Custom render targets must be shared between frame ids, so it's impossible to double buffer
|
||||
// without expensive copies. That means the only thread-safe place to resize is in the draw
|
||||
// thread.
|
||||
osg::Texture2D* texture = const_cast<osg::Texture2D*>(dynamic_cast<const osg::Texture2D*>(
|
||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
.getTexture()));
|
||||
|
||||
texture->setTextureSize(w, h);
|
||||
texture->setNumMipmapLevels(pass.mRenderTexture->getNumMipmapLevels());
|
||||
texture->dirtyTextureObject();
|
||||
|
||||
mDirtyAttachments = false;
|
||||
}
|
||||
|
||||
pass.mRenderTarget->apply(state, osg::FrameBufferObject::DRAW_FRAMEBUFFER);
|
||||
|
||||
if (pass.mRenderTexture->getNumMipmapLevels() > 0)
|
||||
{
|
||||
|
||||
state.setActiveTextureUnit(0);
|
||||
state.applyTextureAttribute(0,
|
||||
pass.mRenderTarget->getAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0)
|
||||
|
|
|
@ -30,6 +30,8 @@ namespace MWRender
|
|||
|
||||
void dirty() { mDirty = true; }
|
||||
|
||||
void resizeRenderTargets() { mDirtyAttachments = true; }
|
||||
|
||||
const fx::DispatchArray& getPasses() { return mPasses; }
|
||||
|
||||
void setPasses(fx::DispatchArray&& passes);
|
||||
|
@ -65,6 +67,7 @@ namespace MWRender
|
|||
osg::ref_ptr<osg::Texture> mTextureNormals;
|
||||
|
||||
mutable bool mDirty = false;
|
||||
mutable bool mDirtyAttachments = false;
|
||||
mutable osg::ref_ptr<osg::Viewport> mRenderViewport;
|
||||
mutable osg::ref_ptr<osg::FrameBufferObject> mMultiviewResolveFramebuffer;
|
||||
mutable osg::ref_ptr<osg::FrameBufferObject> mDestinationFBO;
|
||||
|
|
|
@ -211,25 +211,14 @@ namespace MWRender
|
|||
if (Stereo::getStereo())
|
||||
Stereo::Manager::instance().screenResolutionChanged();
|
||||
|
||||
auto width = renderWidth();
|
||||
auto height = renderHeight();
|
||||
for (auto& technique : mTechniques)
|
||||
{
|
||||
for (auto& [name, rt] : technique->getRenderTargetsMap())
|
||||
{
|
||||
const auto [w, h] = rt.mSize.get(width, height);
|
||||
rt.mTarget->setTextureSize(w, h);
|
||||
}
|
||||
}
|
||||
|
||||
size_t frameId = frame() % 2;
|
||||
|
||||
createObjectsForFrame(frameId);
|
||||
|
||||
mRendering.updateProjectionMatrix();
|
||||
mRendering.setScreenRes(width, height);
|
||||
mRendering.setScreenRes(renderWidth(), renderHeight());
|
||||
|
||||
dirtyTechniques();
|
||||
dirtyTechniques(true);
|
||||
|
||||
mDirty = true;
|
||||
mDirtyFrameId = !frameId;
|
||||
|
@ -534,7 +523,7 @@ namespace MWRender
|
|||
mCanvases[frameId]->dirty();
|
||||
}
|
||||
|
||||
void PostProcessor::dirtyTechniques()
|
||||
void PostProcessor::dirtyTechniques(bool dirtyAttachments)
|
||||
{
|
||||
size_t frameId = frame() % 2;
|
||||
|
||||
|
@ -613,8 +602,6 @@ namespace MWRender
|
|||
uniform->mName.c_str(), *type, uniform->getNumElements()));
|
||||
}
|
||||
|
||||
std::unordered_map<osg::Texture2D*, osg::Texture2D*> renderTargetCache;
|
||||
|
||||
for (const auto& pass : technique->getPasses())
|
||||
{
|
||||
int subTexUnit = texUnit;
|
||||
|
@ -626,32 +613,27 @@ namespace MWRender
|
|||
|
||||
if (!pass->getTarget().empty())
|
||||
{
|
||||
const auto& rt = technique->getRenderTargetsMap()[pass->getTarget()];
|
||||
|
||||
const auto [w, h] = rt.mSize.get(renderWidth(), renderHeight());
|
||||
|
||||
subPass.mRenderTexture = new osg::Texture2D(*rt.mTarget);
|
||||
renderTargetCache[rt.mTarget] = subPass.mRenderTexture;
|
||||
subPass.mRenderTexture->setTextureSize(w, h);
|
||||
subPass.mRenderTexture->setName(std::string(pass->getTarget()));
|
||||
|
||||
if (rt.mMipMap)
|
||||
subPass.mRenderTexture->setNumMipmapLevels(osg::Image::computeNumberOfMipmapLevels(w, h));
|
||||
const auto& renderTarget = technique->getRenderTargetsMap()[pass->getTarget()];
|
||||
subPass.mSize = renderTarget.mSize;
|
||||
subPass.mRenderTexture = renderTarget.mTarget;
|
||||
subPass.mMipMap = renderTarget.mMipMap;
|
||||
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(
|
||||
0, 0, subPass.mRenderTexture->getTextureWidth(), subPass.mRenderTexture->getTextureHeight()));
|
||||
|
||||
subPass.mRenderTarget = new osg::FrameBufferObject;
|
||||
subPass.mRenderTarget->setAttachment(osg::FrameBufferObject::BufferComponent::COLOR_BUFFER0,
|
||||
osg::FrameBufferAttachment(subPass.mRenderTexture));
|
||||
|
||||
const auto [w, h] = renderTarget.mSize.get(renderWidth(), renderHeight());
|
||||
subPass.mStateSet->setAttributeAndModes(new osg::Viewport(0, 0, w, h));
|
||||
}
|
||||
|
||||
for (const auto& whitelist : pass->getRenderTargets())
|
||||
for (const auto& name : pass->getRenderTargets())
|
||||
{
|
||||
auto it = technique->getRenderTargetsMap().find(whitelist);
|
||||
if (it != technique->getRenderTargetsMap().end() && renderTargetCache[it->second.mTarget])
|
||||
{
|
||||
subPass.mStateSet->setTextureAttribute(subTexUnit, renderTargetCache[it->second.mTarget]);
|
||||
subPass.mStateSet->addUniform(new osg::Uniform(std::string(it->first).c_str(), subTexUnit++));
|
||||
}
|
||||
subPass.mStateSet->setTextureAttribute(subTexUnit, technique->getRenderTargetsMap()[name].mTarget);
|
||||
subPass.mStateSet->addUniform(new osg::Uniform(name.c_str(), subTexUnit));
|
||||
|
||||
subTexUnit++;
|
||||
}
|
||||
|
||||
node.mPasses.emplace_back(std::move(subPass));
|
||||
|
@ -668,6 +650,9 @@ namespace MWRender
|
|||
hud->updateTechniques();
|
||||
|
||||
mRendering.getSkyManager()->setSunglare(sunglare);
|
||||
|
||||
if (dirtyAttachments)
|
||||
mCanvases[frameId]->resizeRenderTargets();
|
||||
}
|
||||
|
||||
PostProcessor::Status PostProcessor::enableTechnique(
|
||||
|
@ -774,7 +759,7 @@ namespace MWRender
|
|||
for (auto& technique : mTemplates)
|
||||
technique->compile();
|
||||
|
||||
dirtyTechniques();
|
||||
dirtyTechniques(true);
|
||||
}
|
||||
|
||||
void PostProcessor::disableDynamicShaders()
|
||||
|
|
|
@ -204,7 +204,7 @@ namespace MWRender
|
|||
|
||||
void createObjectsForFrame(size_t frameId);
|
||||
|
||||
void dirtyTechniques();
|
||||
void dirtyTechniques(bool dirtyAttachments = false);
|
||||
|
||||
void update(size_t frameId);
|
||||
|
||||
|
|
|
@ -339,7 +339,7 @@ float omw_EstimateFogCoverageFromUV(vec2 uv)
|
|||
if (mCompiled)
|
||||
return;
|
||||
|
||||
mLegacyGLSL = technique.getGLSLVersion() != 330;
|
||||
mLegacyGLSL = technique.getGLSLVersion() < 330;
|
||||
|
||||
if (mType == Type::Pixel)
|
||||
{
|
||||
|
|
|
@ -279,6 +279,7 @@ namespace fx
|
|||
rt.mTarget->setSourceType(GL_UNSIGNED_BYTE);
|
||||
rt.mTarget->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
rt.mTarget->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
rt.mTarget->setName(std::string(mBlockName));
|
||||
|
||||
while (!isNext<Lexer::Close_bracket>() && !isNext<Lexer::Eof>())
|
||||
{
|
||||
|
|
|
@ -54,10 +54,14 @@ namespace fx
|
|||
osg::ref_ptr<osg::FrameBufferObject> mRenderTarget;
|
||||
osg::ref_ptr<osg::Texture2D> mRenderTexture;
|
||||
bool mResolve = false;
|
||||
Types::SizeProxy mSize;
|
||||
bool mMipMap;
|
||||
|
||||
SubPass(const SubPass& other, const osg::CopyOp& copyOp = osg::CopyOp::SHALLOW_COPY)
|
||||
: mStateSet(new osg::StateSet(*other.mStateSet, copyOp))
|
||||
, mResolve(other.mResolve)
|
||||
, mSize(other.mSize)
|
||||
, mMipMap(other.mMipMap)
|
||||
{
|
||||
if (other.mRenderTarget)
|
||||
mRenderTarget = new osg::FrameBufferObject(*other.mRenderTarget, copyOp);
|
||||
|
|
|
@ -29,6 +29,16 @@ namespace fx
|
|||
std::optional<int> mWidth;
|
||||
std::optional<int> mHeight;
|
||||
|
||||
SizeProxy() = default;
|
||||
|
||||
SizeProxy(const SizeProxy& other)
|
||||
: mWidthRatio(other.mWidthRatio)
|
||||
, mHeightRatio(other.mHeightRatio)
|
||||
, mWidth(other.mWidth)
|
||||
, mHeight(other.mHeight)
|
||||
{
|
||||
}
|
||||
|
||||
std::tuple<int, int> get(int width, int height) const
|
||||
{
|
||||
int scaledWidth = width;
|
||||
|
|
|
@ -529,48 +529,134 @@ is not wanted and you want a custom render target.
|
|||
| mipmaps | boolean | Whether mipmaps should be generated every frame |
|
||||
+------------------+---------------------+-----------------------------------------------------------------------------+
|
||||
|
||||
To use the render target a pass must be assigned to it, along with any optional clear or blend modes.
|
||||
To use the render target a pass must be assigned to it, along with any optional blend modes.
|
||||
As a restriction, only three render targets can be bound per pass with ``rt1``, ``rt2``, ``rt3``, respectively.
|
||||
|
||||
In the code snippet below a rendertarget is used to draw the red channel of a scene at half resolution, then a quarter. As a restriction,
|
||||
only three render targets can be bound per pass with ``rt1``, ``rt2``, ``rt3``, respectively.
|
||||
Blending modes can be useful at times. Below is a simple shader which, when activated, will slowly turn the screen pure red.
|
||||
Notice how we only ever write the value `.01` to the `RT_Red` buffer. Since we're using appropriate blending modes the
|
||||
color buffer will accumulate.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
render_target RT_Downsample {
|
||||
width_ratio = 0.5;
|
||||
height_ratio = 0.5;
|
||||
internal_format = r16f;
|
||||
render_target RT_Red {
|
||||
width = 4;
|
||||
height = 4;
|
||||
source_format = rgb;
|
||||
internal_format = rgb16f;
|
||||
source_type = float;
|
||||
source_format = red;
|
||||
}
|
||||
|
||||
render_target RT_Downsample4 {
|
||||
width_ratio = 0.25;
|
||||
height_ratio = 0.25;
|
||||
}
|
||||
|
||||
fragment downsample2x(target=RT_Downsample) {
|
||||
|
||||
fragment red(target=RT_Red,blend=(add, src_color, one), rt1=RT_Red) {
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
omw_FragColor.r = omw_GetLastShader(omw_TexCoord).r;
|
||||
omw_FragColor.rgb = vec3(0.01,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
fragment downsample4x(target=RT_Downsample4, rt1=RT_Downsample) {
|
||||
|
||||
fragment view(rt1=RT_Red) {
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
omw_FragColor = omw_Texture2D(RT_Downsample, omw_TexCoord);
|
||||
omw_FragColor = omw_Texture2D(RT_Red, omw_TexCoord);
|
||||
}
|
||||
}
|
||||
|
||||
Now, when the `downsample2x` pass runs it will write to the target buffer instead of the default
|
||||
one assigned by the engine.
|
||||
technique {
|
||||
author = "OpenMW";
|
||||
passes = red, view;
|
||||
}
|
||||
|
||||
|
||||
These custom render targets are persistent and ownership is given to the shader which defines them.
|
||||
This gives potential to implement temporal effects by storing previous frame data in these buffers.
|
||||
Below is an example which calculates a naive average scene luminance and transitions between values smoothly.
|
||||
|
||||
.. code-block:: none
|
||||
|
||||
render_target RT_Lum {
|
||||
width = 256;
|
||||
height = 256;
|
||||
mipmaps = true;
|
||||
source_format = rgb;
|
||||
internal_format = rgb16f;
|
||||
source_type = float;
|
||||
min_filter = linear_mipmap_linear;
|
||||
mag_filter = linear;
|
||||
}
|
||||
|
||||
render_target RT_LumAvg {
|
||||
source_type = float;
|
||||
source_format = rgb;
|
||||
internal_format = rgb16f;
|
||||
min_filter = nearest;
|
||||
mag_filter = nearest;
|
||||
}
|
||||
|
||||
render_target RT_LumAvgLastFrame {
|
||||
source_type = float;
|
||||
source_format = rgb;
|
||||
internal_format = rgb16f;
|
||||
min_filter = nearest;
|
||||
mag_filter = nearest;
|
||||
}
|
||||
|
||||
fragment calculateLum(target=RT_Lum) {
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 orgi = pow(omw_GetLastShader(omw_TexCoord), vec4(2.2)).rgb;
|
||||
omw_FragColor.rgb = orgi;
|
||||
}
|
||||
}
|
||||
|
||||
fragment fetchLumAvg(target=RT_LumAvg, rt1=RT_Lum, rt2=RT_LumAvgLastFrame) {
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 avgLumaCurrFrame = textureLod(RT_Lum, vec2(0.5, 0.5), 6).rgb;
|
||||
vec3 avgLumaLastFrame = omw_Texture2D(RT_LumAvgLastFrame, vec2(0.5, 0.5)).rgb;
|
||||
|
||||
const float speed = 0.9;
|
||||
|
||||
vec3 avgLuma = avgLumaLastFrame + (avgLumaCurrFrame - avgLumaLastFrame) * (1.0 - exp(-omw.deltaSimulationTime * speed));
|
||||
|
||||
omw_FragColor.rgb = avgLuma;
|
||||
}
|
||||
}
|
||||
|
||||
fragment adaptation(rt1=RT_LumAvg) {
|
||||
omw_In vec2 omw_TexCoord;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 avgLuma = omw_Texture2D(RT_LumAvg, vec2(0.5, 0.5)).rgb;
|
||||
|
||||
if (omw_TexCoord.y < 0.2)
|
||||
omw_FragColor = vec4(avgLuma, 1.0);
|
||||
else
|
||||
omw_FragColor = omw_GetLastShader(omw_TexCoord);
|
||||
}
|
||||
}
|
||||
|
||||
fragment store(target=RT_LumAvgLastFrame, rt1=RT_LumAvg) {
|
||||
void main()
|
||||
{
|
||||
vec3 avgLuma = omw_Texture2D(RT_LumAvg, vec2(0.5, 0.5)).rgb;
|
||||
omw_FragColor.rgb = avgLuma;
|
||||
}
|
||||
}
|
||||
|
||||
technique {
|
||||
author = "OpenMW";
|
||||
passes = calculateLum, fetchLumAvg, store, adaptation;
|
||||
glsl_version = 330;
|
||||
}
|
||||
|
||||
|
||||
Simple Example
|
||||
##############
|
||||
|
|
Loading…
Reference in a new issue