Browse Source

Support for mipmapped rendertargets & automatic mip regeneration after rendering. Renderer allocated screen buffers are not mipmapped, but a manually created rendertarget texture will be (like regular textures), unless disabled by calling SetNumLevels(1).

Lasse Öörni 9 years ago
parent
commit
1bb6d3d1ac

+ 1 - 0
Docs/Urho3D.dox

@@ -1052,6 +1052,7 @@ From 1.6 to master:
   Use the directory "Shaders/HLSL/Cache" to get the old behavior.
 - Signatures of UIElement virtual functions OnResize() and OnPositionSet() have changed.
 - UIElement::LoadChildXML() now returns the created element on success, instead of a boolean.
+- Rendertargets have gained the ability to have automatically regenerated mipmaps. Screen buffers received from the Renderer subsystem have mipmaps off, but for manually created rendertargets the default is mipmaps on, like for ordinary textures. Use SetNumLevels(1) to disable.
 */
 
 }

+ 1 - 0
Source/Urho3D/AngelScript/APITemplates.h

@@ -918,6 +918,7 @@ template <class T> void RegisterTexture(asIScriptEngine* engine, const char* cla
     engine->RegisterObjectMethod(className, "int get_multiSample() const", asMETHOD(T, GetMultiSample), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_autoResolve() const", asMETHOD(T, GetAutoResolve), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "bool get_resolveDirty() const", asMETHOD(T, IsResolveDirty), asCALL_THISCALL);
+    engine->RegisterObjectMethod(className, "bool get_levelsDirty() const", asMETHOD(T, GetLevelsDirty), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_backupTexture(Texture@+)", asMETHOD(T, SetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "Texture@+ get_backupTexture() const", asMETHOD(T, GetBackupTexture), asCALL_THISCALL);
     engine->RegisterObjectMethod(className, "void set_mipsToSkip(int, int)", asMETHOD(T, SetMipsToSkip), asCALL_THISCALL);

+ 7 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Graphics.cpp

@@ -1296,6 +1296,9 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
                     ResolveToTexture(static_cast<TextureCube*>(texture));
             }
         }
+
+        if (texture->GetLevelsDirty())
+            texture->RegenerateLevels();
     }
 
     if (texture && texture->GetParametersDirty())
@@ -1410,6 +1413,10 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                 parentTexture->SetResolveDirty(true);
                 renderTarget->SetResolveDirty(true);
             }
+
+            // If mipmapped, mark the levels needing regeneration
+            if (parentTexture->GetLevels() > 1)
+                parentTexture->SetLevelsDirty();
         }
     }
 }

+ 9 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture.cpp

@@ -192,4 +192,13 @@ unsigned Texture::GetSRGBFormat(unsigned format)
         return format;
 }
 
+void Texture::RegenerateLevels()
+{
+    if (!shaderResourceView_)
+        return;
+
+    graphics_->GetImpl()->GetDeviceContext()->GenerateMips((ID3D11ShaderResourceView*)shaderResourceView_);
+    levelsDirty_ = false;
+}
+
 }

+ 13 - 1
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2D.cpp

@@ -367,9 +367,17 @@ bool Texture2D::Create()
 
     D3D11_TEXTURE2D_DESC textureDesc;
     memset(&textureDesc, 0, sizeof textureDesc);
+
+    // Set mipmapping
+    if (usage_ == TEXTURE_DEPTHSTENCIL)
+        levels_ = 1;
+    else if (usage_ == TEXTURE_RENDERTARGET && levels_ != 1 && multiSample_ == 1)
+        textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+    
     textureDesc.Width = (UINT)width_;
     textureDesc.Height = (UINT)height_;
-    textureDesc.MipLevels = levels_;
+    // Disable mip levels from the multisample texture. Rather create them to the resolve texture
+    textureDesc.MipLevels = multiSample_ == 1 ? levels_ : 1;
     textureDesc.ArraySize = 1;
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
     textureDesc.SampleDesc.Count = (UINT)multiSample_;
@@ -393,8 +401,12 @@ bool Texture2D::Create()
     // Create resolve texture for multisampling if necessary
     if (multiSample_ > 1 && autoResolve_)
     {
+        textureDesc.MipLevels = levels_;
         textureDesc.SampleDesc.Count = 1;
         textureDesc.SampleDesc.Quality = 0;
+        if (levels_ != 1)
+            textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+
         HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&resolveTexture_);
         if (FAILED(hr))
         {

+ 5 - 0
Source/Urho3D/Graphics/Direct3D11/D3D11Texture2DArray.cpp

@@ -441,6 +441,11 @@ bool Texture2DArray::Create()
 
     D3D11_TEXTURE2D_DESC textureDesc;
     memset(&textureDesc, 0, sizeof textureDesc);
+
+    // Set mipmapping
+    if (usage_ == TEXTURE_RENDERTARGET && levels_ != 1)
+        textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+
     textureDesc.Width = (UINT)width_;
     textureDesc.Height = (UINT)height_;
     textureDesc.MipLevels = levels_;

+ 12 - 3
Source/Urho3D/Graphics/Direct3D11/D3D11TextureCube.cpp

@@ -434,9 +434,15 @@ bool TextureCube::Create()
 
     D3D11_TEXTURE2D_DESC textureDesc;
     memset(&textureDesc, 0, sizeof textureDesc);
+
+    // Set mipmapping
+    if (usage_ == TEXTURE_RENDERTARGET && levels_ != 1 && multiSample_ == 1)
+        textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+
     textureDesc.Width = (UINT)width_;
     textureDesc.Height = (UINT)height_;
-    textureDesc.MipLevels = levels_;
+    // Disable mip levels from the multisample texture. Rather create them to the resolve texture
+    textureDesc.MipLevels = multiSample_ == 1 ? levels_ : 1;
     textureDesc.ArraySize = MAX_CUBEMAP_FACES;
     textureDesc.Format = (DXGI_FORMAT)(sRGB_ ? GetSRGBFormat(format_) : format_);
     textureDesc.SampleDesc.Count = (UINT)multiSample_;
@@ -451,7 +457,7 @@ bool TextureCube::Create()
     // When multisample is specified, creating an actual cube texture will fail. Rather create as a 2D texture array
     // whose faces will be rendered to; only the resolve texture will be an actual cube texture
     if (multiSample_ < 2)
-        textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+        textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
 
     HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&object_.ptr_);
     if (FAILED(hr))
@@ -466,7 +472,10 @@ bool TextureCube::Create()
     {
         textureDesc.SampleDesc.Count = 1;
         textureDesc.SampleDesc.Quality = 0;
-        textureDesc.MiscFlags = D3D11_RESOURCE_MISC_TEXTURECUBE;
+        textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_TEXTURECUBE;
+        if (levels_ != 1)
+            textureDesc.MiscFlags |= D3D11_RESOURCE_MISC_GENERATE_MIPS;
+
         HRESULT hr = graphics_->GetImpl()->GetDevice()->CreateTexture2D(&textureDesc, 0, (ID3D11Texture2D**)&resolveTexture_);
         if (FAILED(hr))
         {

+ 6 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9Texture.cpp

@@ -104,4 +104,10 @@ unsigned Texture::GetRowDataSize(int width) const
     }
 }
 
+void Texture::RegenerateLevels()
+{
+    // No-op on Direct3D9
+    levelsDirty_ = false;
+}
+
 }

+ 15 - 1
Source/Urho3D/Graphics/Direct3D9/D3D9Texture2D.cpp

@@ -488,6 +488,8 @@ bool Texture2D::Create()
         autoResolve_ = true;
     }
 
+    GraphicsImpl* impl = graphics_->GetImpl();
+
     unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
     unsigned d3dUsage = 0;
 
@@ -498,9 +500,22 @@ bool Texture2D::Create()
         break;
     case TEXTURE_RENDERTARGET:
         d3dUsage |= D3DUSAGE_RENDERTARGET;
+        if (requestedLevels_ != 1)
+        {
+            // Check mipmap autogeneration support
+            if (impl->CheckFormatSupport((D3DFORMAT)format_, D3DUSAGE_AUTOGENMIPMAP, D3DRTYPE_TEXTURE))
+            {
+                requestedLevels_ = 0;
+                d3dUsage |= D3DUSAGE_AUTOGENMIPMAP;
+            }
+            else
+                requestedLevels_ = 1;
+        }
         break;
     case TEXTURE_DEPTHSTENCIL:
         d3dUsage |= D3DUSAGE_DEPTHSTENCIL;
+        // No mipmaps for depth-stencil textures
+        requestedLevels_ = 1;
         break;
     default:
         break;
@@ -509,7 +524,6 @@ bool Texture2D::Create()
     if (multiSample_ > 1)
     {
         // Fall back to non-multisampled if unsupported multisampling mode
-        GraphicsImpl* impl = graphics_->GetImpl();
         if (!impl->CheckMultiSampleSupport((D3DFORMAT)format_,  multiSample_))
         {
             multiSample_ = 1;

+ 13 - 0
Source/Urho3D/Graphics/Direct3D9/D3D9TextureCube.cpp

@@ -548,6 +548,8 @@ bool TextureCube::Create()
         return true;
     }
 
+    GraphicsImpl* impl = graphics_->GetImpl();
+
     unsigned pool = usage_ > TEXTURE_STATIC ? D3DPOOL_DEFAULT : D3DPOOL_MANAGED;
     unsigned d3dUsage = 0;
 
@@ -558,6 +560,17 @@ bool TextureCube::Create()
         break;
     case TEXTURE_RENDERTARGET:
         d3dUsage |= D3DUSAGE_RENDERTARGET;
+        if (requestedLevels_ != 1)
+        {
+            // Check mipmap autogeneration support
+            if (impl->CheckFormatSupport((D3DFORMAT)format_, D3DUSAGE_AUTOGENMIPMAP, D3DRTYPE_TEXTURE))
+            {
+                requestedLevels_ = 0;
+                d3dUsage |= D3DUSAGE_AUTOGENMIPMAP;
+            }
+            else
+                requestedLevels_ = 1;
+        }
         break;
     default:
         break;

+ 11 - 2
Source/Urho3D/Graphics/OpenGL/OGLGraphics.cpp

@@ -1563,6 +1563,8 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
 
             if (texture->GetParametersDirty())
                 texture->UpdateParameters();
+            if (texture->GetLevelsDirty())
+                texture->RegenerateLevels();
         }
         else if (impl_->textureTypes_[index])
         {
@@ -1574,7 +1576,7 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
     }
     else
     {
-        if (texture && texture->GetParametersDirty())
+        if (texture && (texture->GetParametersDirty() || texture->GetLevelsDirty()))
         {
             if (impl_->activeTexture_ != index)
             {
@@ -1583,7 +1585,10 @@ void Graphics::SetTexture(unsigned index, Texture* texture)
             }
 
             glBindTexture(texture->GetTarget(), texture->GetGPUObjectName());
-            texture->UpdateParameters();
+            if (texture->GetParametersDirty())
+                texture->UpdateParameters();
+            if (texture->GetLevelsDirty())
+                texture->RegenerateLevels();
         }
     }
 }
@@ -1681,6 +1686,10 @@ void Graphics::SetRenderTarget(unsigned index, RenderSurface* renderTarget)
                 parentTexture->SetResolveDirty(true);
                 renderTarget->SetResolveDirty(true);
             }
+
+            // If mipmapped, mark the levels needing regeneration
+            if (parentTexture->GetLevels() > 1)
+                parentTexture->SetLevelsDirty();
         }
 
         impl_->fboDirty_ = true;

+ 17 - 0
Source/Urho3D/Graphics/OpenGL/OGLTexture.cpp

@@ -316,4 +316,21 @@ unsigned Texture::GetSRGBFormat(unsigned format)
 #endif
 }
 
+void Texture::RegenerateLevels()
+{
+    if (!object_.name_)
+        return;
+
+#ifndef GL_ES_VERSION_2_0
+    if (Graphics::GetGL3Support())
+        glGenerateMipmap(target_);
+    else
+        glGenerateMipmapEXT(target_);
+#else
+    glGenerateMipmap(target_);
+#endif
+
+    levelsDirty_ = false;
+}
+
 }

+ 13 - 0
Source/Urho3D/Graphics/OpenGL/OGLTexture2D.cpp

@@ -444,6 +444,19 @@ bool Texture2D::Create()
     }
 
     // Set mipmapping
+    if (usage_ == TEXTURE_DEPTHSTENCIL)
+        requestedLevels_ = 1;
+    else if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        if (requestedLevels_ != 1)
+        {
+            // Generate levels for the first time now
+            RegenerateLevels();
+            // Determine max. levels automatically
+            requestedLevels_ = 0;
+        }
+    }
+
     levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
 #ifndef GL_ES_VERSION_2_0
     glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);

+ 13 - 0
Source/Urho3D/Graphics/OpenGL/OGLTexture2DArray.cpp

@@ -451,6 +451,19 @@ bool Texture2DArray::Create()
         URHO3D_LOGERROR("Failed to create texture array");
 
     // Set mipmapping
+    if (usage_ == TEXTURE_DEPTHSTENCIL)
+        requestedLevels_ = 1;
+    else if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        if (requestedLevels_ != 1)
+        {
+            // Generate levels for the first time now
+            RegenerateLevels();
+            // Determine max. levels automatically
+            requestedLevels_ = 0;
+        }
+    }
+
     levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
     glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);
     glTexParameteri(target_, GL_TEXTURE_MAX_LEVEL, levels_ - 1);

+ 13 - 0
Source/Urho3D/Graphics/OpenGL/OGLTextureCube.cpp

@@ -469,6 +469,19 @@ bool TextureCube::Create()
         URHO3D_LOGERROR("Failed to create texture");
 
     // Set mipmapping
+    if (usage_ == TEXTURE_DEPTHSTENCIL)
+        requestedLevels_ = 1;
+    else if (usage_ == TEXTURE_RENDERTARGET)
+    {
+        if (requestedLevels_ != 1)
+        {
+            // Generate levels for the first time now
+            RegenerateLevels();
+            // Determine max. levels automatically
+            requestedLevels_ = 0;
+        }
+    }
+
     levels_ = CheckMaxLevels(width_, height_, requestedLevels_);
 #ifndef GL_ES_VERSION_2_0
     glTexParameteri(target_, GL_TEXTURE_BASE_LEVEL, 0);

+ 7 - 0
Source/Urho3D/Graphics/Renderer.cpp

@@ -956,6 +956,9 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
     int retries = 3;
     unsigned dummyColorFormat = graphics_->GetDummyColorFormat();
 
+    // Disable mipmaps from the shadow map
+    newShadowMap->SetNumLevels(1);
+
     while (retries)
     {
         if (!newShadowMap->SetSize(width, height, shadowMapFormat, shadowMapUsage, multiSample))
@@ -984,6 +987,7 @@ Texture2D* Renderer::GetShadowMap(Light* light, Camera* camera, unsigned viewWid
                 if (!colorShadowMaps_.Contains(searchKey))
                 {
                     colorShadowMaps_[searchKey] = new Texture2D(context_);
+                    colorShadowMaps_[searchKey]->SetNumLevels(1);
                     colorShadowMaps_[searchKey]->SetSize(width, height, dummyColorFormat, TEXTURE_RENDERTARGET);
                 }
                 // Link the color rendertarget to the shadow map
@@ -1051,6 +1055,8 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, int m
         if (!cubemap)
         {
             SharedPtr<Texture2D> newTex2D(new Texture2D(context_));
+            /// \todo Mipmaps disabled for now. Allow to request mipmapped buffer?
+            newTex2D->SetNumLevels(1);
             newTex2D->SetSize(width, height, format, depthStencil ? TEXTURE_DEPTHSTENCIL : TEXTURE_RENDERTARGET, multiSample, autoResolve);
 
 #ifdef URHO3D_OPENGL
@@ -1072,6 +1078,7 @@ Texture* Renderer::GetScreenBuffer(int width, int height, unsigned format, int m
         else
         {
             SharedPtr<TextureCube> newTexCube(new TextureCube(context_));
+            newTexCube->SetNumLevels(1);
             newTexCube->SetSize(width, format, TEXTURE_RENDERTARGET, multiSample);
 
             newBuffer = newTexCube;

+ 8 - 1
Source/Urho3D/Graphics/Texture.cpp

@@ -74,7 +74,8 @@ Texture::Texture(Context* context) :
     sRGB_(false),
     parametersDirty_(true),
     autoResolve_(false),
-    resolveDirty_(false)
+    resolveDirty_(false),
+    levelsDirty_(false)
 {
     for (int i = 0; i < MAX_COORDS; ++i)
         addressMode_[i] = ADDRESS_WRAP;
@@ -256,6 +257,12 @@ void Texture::SetParametersDirty()
     parametersDirty_ = true;
 }
 
+void Texture::SetLevelsDirty()
+{
+    if (usage_ == TEXTURE_RENDERTARGET && levels_ > 1)
+        levelsDirty_ = true;
+}
+
 unsigned Texture::CheckMaxLevels(int width, int height, unsigned requestedLevels)
 {
     unsigned maxLevels = 1;

+ 14 - 0
Source/Urho3D/Graphics/Texture.h

@@ -45,6 +45,10 @@ public:
     virtual ~Texture();
 
     /// Set number of requested mip levels. Needs to be called before setting size.
+    /** The default value (0) allocates as many mip levels as necessary to reach 1x1 size. Set value 1 to disable mipmapping.
+        Note that rendertargets need to regenerate mips dynamically after rendering, which may cost performance. Screen buffers
+        and shadow maps allocated by Renderer will have mipmaps disabled.
+     */
     void SetNumLevels(unsigned levels);
     /// Set filtering mode.
     void SetFilterMode(TextureFilterMode filter);
@@ -107,6 +111,9 @@ public:
 
     /// Return whether multisampled texture needs resolve.
     bool IsResolveDirty() const { return resolveDirty_; }
+
+    /// Return whether rendertarget mipmap levels need regenration.
+    bool GetLevelsDirty() const { return levelsDirty_; }
     
     /// Return backup texture.
     Texture* GetBackupTexture() const { return backupTexture_; }
@@ -162,6 +169,11 @@ public:
     /// Set or clear the need resolve flag. Called internally by Graphics.
     void SetResolveDirty(bool enable) { resolveDirty_ = enable; }
 
+    /// Set the mipmap levels dirty flag. Called internally by Graphics.
+    void SetLevelsDirty();
+    /// Regenerate mipmap levels for a rendertarget after rendering and before sampling. Called internally by Graphics. No-op on Direct3D9. On OpenGL the texture must have been bound to work properly.
+    void RegenerateLevels();
+
     /// Check maximum allowed mip levels for a specific texture size.
     static unsigned CheckMaxLevels(int width, int height, unsigned requestedLevels);
     /// Check maximum allowed mip levels for a specific 3D texture size.
@@ -230,6 +242,8 @@ protected:
     bool autoResolve_;
     /// Multisampling resolve needed -flag.
     bool resolveDirty_;
+    /// Mipmap levels regeneration needed -flag.
+    bool levelsDirty_;
     /// Backup texture.
     SharedPtr<Texture> backupTexture_;
 };

+ 5 - 2
Source/Urho3D/Graphics/Texture2D.cpp

@@ -126,6 +126,10 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
         return false;
     }
 
+    // Disable mipmaps if multisample & custom resolve
+    if (multiSample > 1 && autoResolve == false)
+        requestedLevels_ = 1;
+
     // Delete the old rendersurface if any
     renderSurface_.Reset();
 
@@ -135,11 +139,10 @@ bool Texture2D::SetSize(int width, int height, unsigned format, TextureUsage usa
     {
         renderSurface_ = new RenderSurface(this);
 
-        // Clamp mode addressing by default, nearest filtering, and mipmaps disabled
+        // Clamp mode addressing by default and nearest filtering
         addressMode_[COORD_U] = ADDRESS_CLAMP;
         addressMode_[COORD_V] = ADDRESS_CLAMP;
         filterMode_ = FILTER_NEAREST;
-        requestedLevels_ = 1;
     }
 
     if (usage == TEXTURE_RENDERTARGET)

+ 1 - 2
Source/Urho3D/Graphics/Texture2DArray.cpp

@@ -175,9 +175,8 @@ bool Texture2DArray::SetSize(unsigned layers, int width, int height, unsigned fo
     {
         renderSurface_ = new RenderSurface(this);
 
-        // Nearest filtering and mipmaps disabled by default
+        // Nearest filtering by default
         filterMode_ = FILTER_NEAREST;
-        requestedLevels_ = 1;
     }
 
     if (usage == TEXTURE_RENDERTARGET)

+ 1 - 2
Source/Urho3D/Graphics/TextureCube.cpp

@@ -297,9 +297,8 @@ bool TextureCube::SetSize(int size, unsigned format, TextureUsage usage, int mul
 #endif
         }
 
-        // Nearest filtering and mipmaps disabled by default
+        // Nearest filtering by default
         filterMode_ = FILTER_NEAREST;
-        requestedLevels_ = 1;
     }
 
     if (usage == TEXTURE_RENDERTARGET)

+ 4 - 2
Source/Urho3D/LuaScript/pkgs/Graphics/Texture.pkg

@@ -22,8 +22,9 @@ class Texture : public Resource
     const Color& GetBorderColor() const;
     bool GetSRGB() const;
     int GetMultiSample() const;
-	bool GetAutoResolve() const;
-	bool IsResolveDirty() const;
+    bool GetAutoResolve() const;
+    bool IsResolveDirty() const;
+    bool GetLevelsDirty() const;
     Texture* GetBackupTexture() const;
     int GetMipsToSkip(int quality) const;
     int GetLevelWidth(unsigned level) const;
@@ -46,6 +47,7 @@ class Texture : public Resource
     tolua_readonly tolua_property__get_set int multiSample;
     tolua_readonly tolua_property__get_set bool autoResolve;
     tolua_readonly tolua_property__is_set bool resolveDirty;
+    tolua_readonly tolua_property__get_set bool levelsDirty;
     tolua_property__get_set Texture* backupTexture;
     tolua_readonly tolua_property__get_set TextureUsage usage;
 };

+ 4 - 0
Source/Urho3D/UI/View3D.cpp

@@ -49,6 +49,10 @@ View3D::View3D(Context* context) :
     depthTexture_ = new Texture2D(context_);
     viewport_ = new Viewport(context_);
 
+    // Disable mipmaps since the texel ratio should be 1:1
+    renderTexture_->SetNumLevels(1);
+    depthTexture_->SetNumLevels(1);
+
     SubscribeToEvent(E_RENDERSURFACEUPDATE, URHO3D_HANDLER(View3D, HandleRenderSurfaceUpdate));
 }