diff --git a/examples/render-to-texture.cpp b/examples/render-to-texture.cpp index c58157c0..e4fc03c9 100644 --- a/examples/render-to-texture.cpp +++ b/examples/render-to-texture.cpp @@ -24,7 +24,7 @@ class RenderToTextureExample { texture = Texture::create().withRGBData(nullptr, 1024,1024).build(); - framebuffer = Framebuffer::create().withTexture(texture).build(); + framebuffer = Framebuffer::create().withColorTexture(texture).build(); materialOffscreen = Shader::getStandardBlinnPhong()->createMaterial(); materialOffscreen->setSpecularity({1,1,1,120}); diff --git a/imgui.ini b/imgui.ini index 357f85e2..08bf84ca 100644 --- a/imgui.ini +++ b/imgui.ini @@ -9,12 +9,12 @@ Size=266,600 Collapsed=0 [Window][SRE Renderer] -Pos=7,58 +Pos=549,271 Size=744,811 Collapsed=0 [Window][Standard] -Pos=68,309 +Pos=199,170 Size=680,544 Collapsed=0 @@ -23,3 +23,8 @@ Pos=60,60 Size=913,610 Collapsed=0 +[Window][StandardBlinnPhong] +Pos=668,175 +Size=819,638 +Collapsed=0 + diff --git a/include/sre/Framebuffer.hpp b/include/sre/Framebuffer.hpp index 2783d7cf..10589258 100644 --- a/include/sre/Framebuffer.hpp +++ b/include/sre/Framebuffer.hpp @@ -11,6 +11,7 @@ #include #include "glm/glm.hpp" #include "Texture.hpp" +#include "impl/CPPShim.hpp" namespace sre { @@ -29,11 +30,16 @@ namespace sre { public: class FrameBufferBuilder { public: + DEPRECATED("Use withColorTexture() instead") FrameBufferBuilder& withTexture(std::shared_ptr texture); + + FrameBufferBuilder& withColorTexture(std::shared_ptr texture); + FrameBufferBuilder& withDepthTexture(std::shared_ptr texture); FrameBufferBuilder& withName(std::string name); std::shared_ptr build(); private: std::vector> textures; + std::shared_ptr depthTexture; glm::uvec2 size; std::string name; FrameBufferBuilder() = default; @@ -47,7 +53,10 @@ namespace sre { static int getMaximumColorAttachments(); + DEPRECATED("Use setColorTexture() instead") void setTexture(std::shared_ptr tex, int index = 0); + void setColorTexture(std::shared_ptr tex, int index = 0); + void setDepthTexture(std::shared_ptr tex); const std::string& getName(); @@ -56,6 +65,7 @@ namespace sre { bool dirty = true; explicit Framebuffer(std::string name); std::vector> textures; + std::shared_ptr depthTexture; unsigned int frameBufferObjectId; uint32_t renderBufferDepth = 0; std::string name; @@ -64,6 +74,7 @@ namespace sre { }; + } diff --git a/include/sre/Mesh.hpp b/include/sre/Mesh.hpp index 335dc14d..2ce56531 100755 --- a/include/sre/Mesh.hpp +++ b/include/sre/Mesh.hpp @@ -47,7 +47,7 @@ namespace sre { // primitives MeshBuilder& withSphere(int stacks = 16, int slices = 32, float radius = 1); // Creates a sphere mesh including UV coordinates, positions and normals MeshBuilder& withCube(float length = 1); // Creates a cube including UV coordinates, positions and normals - MeshBuilder& withQuad(float size=1); // Creates a quad z,y = [-size;size] and z=0, UV=[0;1], normals=(0,0,1) + MeshBuilder& withQuad(float size=1); // Creates a quad x,y = [-size;size] and z=0, UV=[0;1], normals=(0,0,1) MeshBuilder& withTorus(int segmentsC = 24, int segmentsA = 24, float radiusC = 1, float radiusA = .25); // Creates a torus in xy plane. C is in the outer (large) circle, A is the sweeping circle. // raw data @@ -112,6 +112,7 @@ namespace sre { std::vector getAttributeNames(); // Names of the vertex attributes std::array getBoundsMinMax(); // get the local axis aligned bounding box (AABB) + void setBoundsMinMax(const std::array& minMax);// set the local axis aligned bounding box (AABB) const std::string& getName(); // Return the mesh name diff --git a/include/sre/RenderPass.hpp b/include/sre/RenderPass.hpp index 325065de..9d1b7f76 100755 --- a/include/sre/RenderPass.hpp +++ b/include/sre/RenderPass.hpp @@ -102,6 +102,12 @@ namespace sre { void draw(std::shared_ptr&& spriteBatch, // Draws a spriteBatch using modelTransform glm::mat4 modelTransform = glm::mat4(1)); // using a model-to-world transformation + void blit(std::shared_ptr texture, // Render texture to screen + glm::mat4 transformation = glm::mat4(1.0f)); + + void blit(std::shared_ptr material, // Render material to screen + glm::mat4 transformation = glm::mat4(1.0f)); + std::vector readPixels(unsigned int x, // Reads pixel(s) from the current framebuffer unsigned int y, // The defined rectangle must be within the size of the current framebuffer unsigned int width = 1, // This function must be called after finish has been explicit called on the renderPass @@ -124,7 +130,6 @@ namespace sre { void drawInstance(RenderQueueObj& rqObj); // perform the actual rendering - RenderPass::RenderPassBuilder builder; explicit RenderPass(RenderPass::RenderPassBuilder& builder); @@ -140,6 +145,4 @@ namespace sre { friend class Renderer; }; - - } diff --git a/include/sre/Renderer.hpp b/include/sre/Renderer.hpp index ff64410a..6c025bf1 100755 --- a/include/sre/Renderer.hpp +++ b/include/sre/Renderer.hpp @@ -65,7 +65,7 @@ namespace sre { ~Renderer(); static constexpr int sre_version_major = 1; static constexpr int sre_version_minor = 0; - static constexpr int sre_version_point = 2; + static constexpr int sre_version_point = 3; glm::ivec2 getWindowSize(); // Return the current size of the window diff --git a/include/sre/Shader.hpp b/include/sre/Shader.hpp index 205cf329..e35de3b4 100755 --- a/include/sre/Shader.hpp +++ b/include/sre/Shader.hpp @@ -212,6 +212,10 @@ namespace sre { // "uv" vec4 (note: xy is lower left corner, z is size and w is rotation in radians) // Expects a mesh with topology = Points + static std::shared_ptr getBlit(); // Shader used for blitting + // Uniforms + // "tex" shared_ptr (default white texture) + static ShaderBuilder create(); ShaderBuilder update(); // Update the shader using the builder pattern. (Must end with build()). diff --git a/include/sre/Texture.hpp b/include/sre/Texture.hpp index 666d97fd..3c6b2515 100755 --- a/include/sre/Texture.hpp +++ b/include/sre/Texture.hpp @@ -16,6 +16,10 @@ #include "sre/Framebuffer.hpp" #include "sre/Shader.hpp" +#ifdef None // Fix Linux compile issue +#undef None +#endif + class SDL_Surface; namespace sre{ @@ -47,25 +51,36 @@ class DllExport Texture : public std::enable_shared_from_this { NegativeZ }; - enum class SamplerColorspace { Linear, // Convert values from gamma space to linear space, when gamma correction is enabled. Default behavior. Gamma // Sampler performs no gamma convertions. This is useful for e.g. normal textures where no gamma correction must be performed }; + enum class DepthPrecision { + I16, // 16 bit integer + I24, // 24 bit integer + I32, // 32 bit integer + F32, // 32 bit float + I24_STENCIL8, // 24 bit integer 8 bit stencil + F32_STENCIL8, // 32 bit float 8 bit stencil + STENCIL8, // 8 bit stencil + None + }; + class DllExport TextureBuilder { public: ~TextureBuilder(); TextureBuilder& withGenerateMipmaps(bool enable); TextureBuilder& withFilterSampling(bool enable); // if true texture sampling is filtered (bi-linear or tri-linear sampling) otherwise use point sampling. TextureBuilder& withWrappedTextureCoordinates(bool enable); - TextureBuilder& withFileCubemap(std::string filename, CubemapSide side); // Must define a cubemap for each side + TextureBuilder& withFileCubemap(std::string filename, CubemapSide side); // Must define a cubemap for each side TextureBuilder& withFile(std::string filename); // Currently only PNG files supported TextureBuilder& withRGBData(const char* data, int width, int height); // data may be null (for a uninitialized texture) TextureBuilder& withRGBAData(const char* data, int width, int height); // data may be null (for a uninitialized texture) TextureBuilder& withWhiteData(int width=2, int height=2); TextureBuilder& withSamplerColorspace(SamplerColorspace samplerColorspace); TextureBuilder& withWhiteCubemapData(int width=2, int height=2); + TextureBuilder& withDepth(int width, int height, DepthPrecision precision=DepthPrecision::I16); // Creates a depth texture. TextureBuilder& withName(const std::string& name); TextureBuilder& withDumpDebug(); // Output debug info on build std::shared_ptr build(); @@ -83,7 +98,7 @@ class DllExport Texture : public std::enable_shared_from_this { std::vector data; void dumpDebug(); }; - + DepthPrecision depthPrecision = DepthPrecision::None; std::string name; bool transparent; bool generateMipmaps = false; @@ -121,6 +136,8 @@ class DllExport Texture : public std::enable_shared_from_this { const std::string& getName(); // name of the string int getDataSize(); // get size of the texture in bytes on GPU + bool isDepthTexture(); + DepthPrecision getDepthPrecision(); private: Texture(unsigned int textureId, int width, int height, uint32_t target, std::string string); void updateTextureSampler(bool filterSampling, bool wrapTextureCoordinates); @@ -132,6 +149,7 @@ class DllExport Texture : public std::enable_shared_from_this { uint32_t target; bool generateMipmap; bool transparent; + DepthPrecision depthPrecision = DepthPrecision::None; std::string name; SamplerColorspace samplerColorspace; bool filterSampling = true; // true = linear/trilinear sampling, false = point sampling diff --git a/include/sre/impl/GL.hpp b/include/sre/impl/GL.hpp index a2b510c8..291cdd5d 100755 --- a/include/sre/impl/GL.hpp +++ b/include/sre/impl/GL.hpp @@ -23,7 +23,7 @@ #include // For internal debugging of gl errors -inline void checkGLError(); +inline void checkGLError(const char* title = nullptr); inline bool hasExtension(std::string extensionName); inline std::vector listExtension(); diff --git a/include/sre/impl/GL.inl b/include/sre/impl/GL.inl index 5ca27f26..0688806a 100755 --- a/include/sre/impl/GL.inl +++ b/include/sre/impl/GL.inl @@ -8,12 +8,17 @@ #include #include -void checkGLError() { +void checkGLError(const char* title) { for(GLenum err; (err = glGetError()) != GL_NO_ERROR;) { + if (err != GL_NONE) + { + if (title) std::cerr << title << std::endl; + } //Process/log the error. switch (err){ case GL_INVALID_ENUM: + std::cerr << "GL_INVALID_ENUM"< #include #include @@ -674,6 +674,37 @@ void main(void) { vColor = color; #endif })"), +std::make_pair("blit_frag.glsl",R"(#version 140 +out vec4 fragColor; +in vec2 vUV; + +uniform sampler2D tex; + +#pragma include "sre_utils_incl.glsl" + +void main(void) +{ + fragColor = toLinear(texture(tex, vUV)); + fragColor = toOutput(fragColor); +})"), +std::make_pair("blit_vert.glsl",R"(#version 140 +in vec3 position; +in vec3 normal; +#ifdef S_VERTEX_COLOR +in vec4 color; +out vec4 vColor; +#endif +in vec4 uv; +out vec2 vUV; + +uniform mat4 g_model; +uniform mat4 g_view; +uniform mat4 g_projection; + +void main(void) { + gl_Position = g_model * vec4(position,1.0); + vUV = uv.xy; +})"), std::make_pair("unlit_frag.glsl",R"(#version 140 out vec4 fragColor; in vec2 vUV; diff --git a/src/embedded_deps/blit_frag.glsl b/src/embedded_deps/blit_frag.glsl new file mode 100644 index 00000000..9d3e2039 --- /dev/null +++ b/src/embedded_deps/blit_frag.glsl @@ -0,0 +1,13 @@ +#version 140 +out vec4 fragColor; +in vec2 vUV; + +uniform sampler2D tex; + +#pragma include "sre_utils_incl.glsl" + +void main(void) +{ + fragColor = toLinear(texture(tex, vUV)); + fragColor = toOutput(fragColor); +} \ No newline at end of file diff --git a/src/embedded_deps/blit_vert.glsl b/src/embedded_deps/blit_vert.glsl new file mode 100644 index 00000000..948f1393 --- /dev/null +++ b/src/embedded_deps/blit_vert.glsl @@ -0,0 +1,18 @@ +#version 140 +in vec3 position; +in vec3 normal; +#ifdef S_VERTEX_COLOR +in vec4 color; +out vec4 vColor; +#endif +in vec4 uv; +out vec2 vUV; + +uniform mat4 g_model; +uniform mat4 g_view; +uniform mat4 g_projection; + +void main(void) { + gl_Position = g_model * vec4(position,1.0); + vUV = uv.xy; +} \ No newline at end of file diff --git a/src/sre/Framebuffer.cpp b/src/sre/Framebuffer.cpp index 8e56aaef..ea2eeee9 100644 --- a/src/sre/Framebuffer.cpp +++ b/src/sre/Framebuffer.cpp @@ -10,14 +10,37 @@ #include "sre/impl/GL.hpp" #include "sre/Log.hpp" +#include namespace sre{ - Framebuffer::FrameBufferBuilder & Framebuffer::FrameBufferBuilder::withTexture(std::shared_ptr texture) { - this->size = {texture->getWidth(), texture->getHeight()}; + Framebuffer::FrameBufferBuilder& Framebuffer::FrameBufferBuilder::withTexture(std::shared_ptr texture) { + return withColorTexture(texture); + } + + Framebuffer::FrameBufferBuilder& Framebuffer::FrameBufferBuilder::withColorTexture(std::shared_ptr texture) { + assert(!texture->isDepthTexture()); + auto s = glm::uvec2{texture->getWidth(), texture->getHeight()}; + if (!textures.empty() || depthTexture.get()){ + assert(this->size == s); + } else { + this->size = s; + } textures.push_back(texture); return *this; } + Framebuffer::FrameBufferBuilder& Framebuffer::FrameBufferBuilder::withDepthTexture(std::shared_ptr texture) { + assert(texture->isDepthTexture()); + auto s = glm::uvec2{texture->getWidth(), texture->getHeight()}; + if (!textures.empty() || depthTexture.get()){ + assert(this->size == s); + } else { + this->size = s; + } + depthTexture = texture; + return *this; + } + Framebuffer::FrameBufferBuilder &Framebuffer::FrameBufferBuilder::withName(std::string name) { this->name = name; return *this; @@ -40,7 +63,15 @@ namespace sre{ } int Framebuffer::getMaximumColorAttachments() { +#ifdef EMSCRIPTEN return 1; +#else + GLint maxAttach = 0; + glGetIntegerv(GL_MAX_COLOR_ATTACHMENTS, &maxAttach); + GLint maxDrawBuf = 0; + glGetIntegerv(GL_MAX_DRAW_BUFFERS, &maxDrawBuf); + return std::min(maxAttach, maxDrawBuf); +#endif } const std::string& Framebuffer::getName() { @@ -48,10 +79,23 @@ namespace sre{ } void Framebuffer::setTexture(std::shared_ptr tex, int index) { + setColorTexture(tex, index); + } + + void Framebuffer::setColorTexture(std::shared_ptr tex, int index) { + assert(textures.size() > index && index >= 0); + assert(!tex->isDepthTexture()); textures[index] = tex; dirty = true; } + void Framebuffer::setDepthTexture(std::shared_ptr tex) { + assert(tex->isDepthTexture()); + depthTexture = tex; + dirty = true; + } + + void Framebuffer::bind() { glBindFramebuffer(GL_FRAMEBUFFER, frameBufferObjectId); if (dirty){ @@ -60,6 +104,9 @@ namespace sre{ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0+i, GL_TEXTURE_2D, textures[i]->textureId, 0); } } + if (depthTexture){ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture->textureId, 0); + } dirty = false; } } @@ -117,42 +164,46 @@ namespace sre{ } auto framebuffer = new Framebuffer(name); framebuffer->size = size; - glGenRenderbuffers(1,&framebuffer->renderBufferDepth); // Create a renderbuffer object - - glBindRenderbuffer(GL_RENDERBUFFER, framebuffer->renderBufferDepth); - glRenderbufferStorage(GL_RENDERBUFFER, -#ifdef GL_ES_VERSION_2_0 - GL_DEPTH_COMPONENT16 -#else - GL_DEPTH_COMPONENT32 -#endif - , size.x, size.y); - - glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, - size.x, size.y); - glBindRenderbuffer(GL_RENDERBUFFER, 0); glGenFramebuffers(1, &(framebuffer->frameBufferObjectId)); glBindFramebuffer(GL_FRAMEBUFFER, framebuffer->frameBufferObjectId); + std::vector drawBuffers; for (unsigned i=0;itextureId, 0); + drawBuffers.push_back(GL_COLOR_ATTACHMENT0+i); } - - // attach the renderbuffer to depth attachment point - glFramebufferRenderbuffer(GL_FRAMEBUFFER, - GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, - framebuffer->renderBufferDepth); - + + if (depthTexture){ + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture->textureId, 0); + } else { + glGenRenderbuffers(1,&framebuffer->renderBufferDepth); // Create a renderbuffer object + glBindRenderbuffer(GL_RENDERBUFFER, framebuffer->renderBufferDepth); + glRenderbufferStorage(GL_RENDERBUFFER, + #ifdef GL_ES_VERSION_2_0 + GL_DEPTH_COMPONENT16 + #else + GL_DEPTH_COMPONENT32 + #endif + , size.x, size.y); + + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, + size.x, size.y); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + // attach the renderbuffer to depth attachment point + glFramebufferRenderbuffer(GL_FRAMEBUFFER, + GL_DEPTH_ATTACHMENT, + GL_RENDERBUFFER, + framebuffer->renderBufferDepth); + } +#ifndef EMSCRIPTEN + glDrawBuffers(drawBuffers.size(), drawBuffers.data()); +#endif // Check if FBO is configured correctly checkStatus(); - framebuffer->textures = textures; - glBindFramebuffer(GL_FRAMEBUFFER, 0); - return std::shared_ptr(framebuffer); } -} \ No newline at end of file +} diff --git a/src/sre/Inspector.cpp b/src/sre/Inspector.cpp index b788cb5b..1d6354e6 100644 --- a/src/sre/Inspector.cpp +++ b/src/sre/Inspector.cpp @@ -59,6 +59,8 @@ namespace sre { typeStr = "ivec4"; break; #endif + default: + typeStr = "invalid"; } return typeStr; } @@ -85,6 +87,34 @@ namespace sre { ImGui::LabelText("Size","%ix%i",tex->getWidth(),tex->getHeight()); ImGui::LabelText("Cubemap","%s",tex->isCubemap()?"true":"false"); + const char* depthStr; + switch (tex->getDepthPrecision()){ + case Texture::DepthPrecision::I16: // 16 bit integer + depthStr = "16 bit"; + break; + case Texture::DepthPrecision::I24: // 24 bit integer + depthStr = "24 bit"; + break; + case Texture::DepthPrecision::I32: // 32 bit integer + depthStr = "32 bit"; + break; + case Texture::DepthPrecision::F32: // 32 bit float + depthStr = "32 bit float"; + break; + case Texture::DepthPrecision::I24_STENCIL8: // 24 bit integer 8 bit stencil + depthStr = "24 bit + 8 bit stencil"; + break; + case Texture::DepthPrecision::F32_STENCIL8: // 32 bit float 8 bit stencil + depthStr = "32 bit float + 8 bit stencil"; + break; + case Texture::DepthPrecision::STENCIL8: // 8 bit stencil + depthStr = "8 bit stencil"; + break; + case Texture::DepthPrecision::None: + depthStr = "None"; + break; + } + ImGui::LabelText("Depth",depthStr); ImGui::LabelText("Filtersampling","%s",tex->isFilterSampling()?"true":"false"); ImGui::LabelText("Mipmapping","%s",tex->isMipmapped()?"true":"false"); ImGui::LabelText("Transparent","%s",tex->isTransparent()?"true":"false"); @@ -112,7 +142,7 @@ namespace sre { ImGui::LabelText("Mesh size", "%.2f MB", mesh->getDataSize()/(1000*1000.0f)); if (ImGui::TreeNode("Vertex attributes")){ auto attributeNames = mesh->getAttributeNames(); - for (auto a : attributeNames) { + for (auto & a : attributeNames) { auto type = mesh->getType(a); std::string typeStr = glEnumToString(type.first); typeStr = appendSize(typeStr, type.second); @@ -177,7 +207,7 @@ namespace sre { camera.setPerspectiveProjection(60,0.1,10); camera.lookAt({0,0,4},{0,0,0},{0,1,0}); auto offscreenTexture = getTmpTexture(); - framebuffer->setTexture(offscreenTexture); + framebuffer->setColorTexture(offscreenTexture); auto renderToTexturePass = RenderPass::create() .withCamera(camera) @@ -276,7 +306,7 @@ namespace sre { camera.setPerspectiveProjection(60,0.1,10); camera.lookAt({0,0,4},{0,0,0},{0,1,0}); auto offscreenTexture = getTmpTexture(); - framebuffer->setTexture(offscreenTexture); + framebuffer->setColorTexture(offscreenTexture); auto renderToTexturePass = RenderPass::create() .withCamera(camera) .withWorldLights(&worldLights) @@ -747,7 +777,7 @@ namespace sre { void Inspector::initFramebuffer() { if (framebuffer == nullptr){ - framebuffer = Framebuffer::create().withTexture(getTmpTexture()).withName("SRE Inspector Framebufferobject").build(); + framebuffer = Framebuffer::create().withColorTexture(getTmpTexture()).withName("SRE Inspector Framebufferobject").build(); usedTextures = 0; // reset usedTextures count to avoid an extra texture to be created worldLights.setAmbientLight({0.2,0.2,0.2}); auto light = Light::create().withPointLight({0,0,4}).build(); diff --git a/src/sre/Mesh.cpp b/src/sre/Mesh.cpp index e39434fd..bd7696da 100755 --- a/src/sre/Mesh.cpp +++ b/src/sre/Mesh.cpp @@ -407,6 +407,10 @@ namespace sre { return interleavedData; } + void Mesh::setBoundsMinMax(const std::array& minMax) { + boundsMinMax = minMax; + } + Mesh::MeshBuilder &Mesh::MeshBuilder::withPositions(const std::vector &vertexPositions) { withAttribute("position", vertexPositions); return *this; diff --git a/src/sre/RenderPass.cpp b/src/sre/RenderPass.cpp index e61ddc41..bd8231bd 100755 --- a/src/sre/RenderPass.cpp +++ b/src/sre/RenderPass.cpp @@ -20,8 +20,11 @@ #include #include #include +#include + namespace sre { + RenderPass::RenderPassBuilder RenderPass::create() { return RenderPass::RenderPassBuilder(&Renderer::instance->renderStats); } @@ -344,4 +347,26 @@ namespace sre { bool RenderPass::isFinished() { return mIsFinished; } + + void RenderPass::blit(std::shared_ptr texture, glm::mat4 transformation) { + auto material = Shader::getBlit()->createMaterial(); + material->setTexture(texture); + blit(material, transformation); + } + + void RenderPass::blit(std::shared_ptr material, glm::mat4 transformation) { + static std::shared_ptr mesh; + static bool once = [](){ + mesh = Mesh::create().withQuad().build(); + // always render + float m = std::numeric_limits::max(); + std::array minMax; + minMax[0] = glm::vec3{-m}; + minMax[1] = glm::vec3{m}; + mesh->setBoundsMinMax(minMax); + return true; + } (); + + draw(mesh, transformation, material); + } } diff --git a/src/sre/Renderer.cpp b/src/sre/Renderer.cpp index d218aacc..82e50707 100755 --- a/src/sre/Renderer.cpp +++ b/src/sre/Renderer.cpp @@ -83,7 +83,7 @@ namespace sre { #ifndef GL_POINT_SPRITE #define GL_POINT_SPRITE 0x8861 #endif // !GL_POINT_SPRITE - if (renderInfo.graphicsAPIVersion.find_first_of("3.1") == 0){ + if (renderInfo.graphicsAPIVersion.find("3.1") == 0){ glEnable(GL_POINT_SPRITE); } diff --git a/src/sre/Shader.cpp b/src/sre/Shader.cpp index bacd48cc..ee1b57f9 100755 --- a/src/sre/Shader.cpp +++ b/src/sre/Shader.cpp @@ -26,6 +26,7 @@ namespace sre { std::shared_ptr standardBlinnPhong; std::shared_ptr standardPhong; std::shared_ptr unlit; + std::shared_ptr blit; std::shared_ptr unlitSprite; std::shared_ptr standardParticles; @@ -613,6 +614,19 @@ namespace sre { return unlit; } + std::shared_ptr Shader::getBlit() { + if (blit != nullptr){ + return blit; + } + + blit = create() + .withSourceFile("blit_vert.glsl", ShaderType::Vertex) + .withSourceFile("blit_frag.glsl", ShaderType::Fragment) + .withName("Blit") + .build(); + return blit; + } + std::shared_ptr Shader::getUnlitSprite() { if (unlitSprite != nullptr){ return unlitSprite; diff --git a/src/sre/Texture.cpp b/src/sre/Texture.cpp index 156a87f9..b3f54cb6 100755 --- a/src/sre/Texture.cpp +++ b/src/sre/Texture.cpp @@ -281,6 +281,18 @@ namespace sre { return *this; } + Texture::TextureBuilder &Texture::TextureBuilder::withDepth(int width, int height, DepthPrecision precision) { + depthPrecision = precision; + textureTypeData[GL_TEXTURE_2D] = { + width, + height, + false, + 0, + 0, + "DepthTexture" + }; + return *this; + } std::shared_ptr Texture::TextureBuilder::build() { if (textureId == 0){ @@ -291,7 +303,42 @@ namespace sre { } std::map::iterator val; TextureDefinition* textureDefPtr; - if ((val = textureTypeData.find(GL_TEXTURE_2D)) != textureTypeData.end()){ + if (depthPrecision != DepthPrecision::None){ +#ifdef EMSCRIPTEN + LOG_FATAL("Depth texture not supported"); +#else + this->target = GL_TEXTURE_2D; + GLint internalFormat; + GLint format = GL_DEPTH_COMPONENT; + if (depthPrecision == DepthPrecision::I16){ + internalFormat = GL_DEPTH_COMPONENT16; + } else if (depthPrecision == DepthPrecision::I24){ + internalFormat = GL_DEPTH_COMPONENT24; + } else if (depthPrecision == DepthPrecision::I32){ + internalFormat = GL_DEPTH_COMPONENT32; + } else if (depthPrecision == DepthPrecision::I24_STENCIL8){ + internalFormat = GL_DEPTH24_STENCIL8; + format = GL_DEPTH_STENCIL; + } else if (depthPrecision == DepthPrecision::F32_STENCIL8){ + internalFormat = GL_DEPTH32F_STENCIL8; + format = GL_DEPTH_STENCIL; + } else if (depthPrecision == DepthPrecision::F32){ + internalFormat = GL_DEPTH_COMPONENT32F; + } else if (depthPrecision == DepthPrecision::STENCIL8){ + internalFormat = GL_STENCIL_INDEX8; + format = GL_STENCIL_INDEX; + } else { + assert(false); + } + GLint border = 0; + GLenum type = GL_UNSIGNED_BYTE; + glBindTexture(target, textureId); + auto td = textureTypeData.find(GL_TEXTURE_2D); + textureDefPtr = &td->second; + glTexImage2D(target, 0, internalFormat, textureDefPtr->width, + textureDefPtr->height, border, format, type, nullptr); +#endif + } else if ((val = textureTypeData.find(GL_TEXTURE_2D)) != textureTypeData.end()){ auto& textureDef = val->second; textureDefPtr = &textureDef; this->target = GL_TEXTURE_2D; @@ -365,6 +412,7 @@ namespace sre { res->generateMipmap = this->generateMipmaps; res->transparent = this->transparent; res->samplerColorspace = this->samplerColorspace; + res->depthPrecision = this->depthPrecision; if (this->generateMipmaps){ res->invokeGenerateMipmap(); } @@ -633,4 +681,13 @@ namespace sre { sre::Texture::SamplerColorspace Texture::getSamplerColorSpace() { return samplerColorspace; } + + bool Texture::isDepthTexture() { + return depthPrecision != DepthPrecision::None; + } + + Texture::DepthPrecision Texture::getDepthPrecision() { + return depthPrecision; + } + } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index abefbb3a..ce451878 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,5 +1,5 @@ # List of single-file tests -SET(scr_files spinning-sphere-cubemap particle-test polygon-offset-example multiple-lights particle-sprite sprite-test multi-cameras static_vertex_attribute custom-mesh-layout-default-values imgui_demo texture-test screen-point-to-ray pbr-test gamma primitives-test imgui-color-test) +SET(scr_files render-depth spinning-sphere-cubemap particle-test polygon-offset-example multiple-lights particle-sprite sprite-test multi-cameras static_vertex_attribute custom-mesh-layout-default-values imgui_demo texture-test screen-point-to-ray pbr-test gamma primitives-test imgui-color-test) # Create custom build targets FOREACH(scr_file ${scr_files}) diff --git a/test/multiple-lights.cpp b/test/multiple-lights.cpp index 54088ec2..0b1aee5d 100644 --- a/test/multiple-lights.cpp +++ b/test/multiple-lights.cpp @@ -119,7 +119,7 @@ class MultipleLightsExample { } if (ImGui::TreeNode("Material")){ - char* options = "BlinnPhong\0Phong\0PBR\0"; + const char* options = "BlinnPhong\0Phong\0PBR\0"; ImGui::Combo("Shader",&selectedMaterial,options); if (selectedMaterial<2){ diff --git a/test/render-depth.cpp b/test/render-depth.cpp new file mode 100644 index 00000000..0c2084d4 --- /dev/null +++ b/test/render-depth.cpp @@ -0,0 +1,99 @@ +#include +#include +#include + +#include "sre/Texture.hpp" +#include "sre/Renderer.hpp" +#include "sre/Material.hpp" +#include "sre/SDLRenderer.hpp" +#include "sre/impl/GL.hpp" + +#include +#include +#include + +using namespace sre; + +class RenderDepth { +public: + RenderDepth(){ + r.init(); + + camera.lookAt({0,0,3},{0,0,0},{0,1,0}); + camera.setPerspectiveProjection(60,1.5f,3.5f); + + depthTexture = Texture::create().withDepth(1024,1024, Texture::DepthPrecision::I16).build(); + texture = Texture::create().withRGBData(nullptr, 1024,1024).build(); + framebuffer = Framebuffer::create().withColorTexture(texture).build(); + framebufferDepth = Framebuffer::create().withDepthTexture(depthTexture).build(); + + materialOffscreen = Shader::getStandardBlinnPhong()->createMaterial(); + materialOffscreen->setSpecularity({1,1,1,120}); + material = Shader::getStandardBlinnPhong()->createMaterial(); + material->setTexture(texture); + + mesh = Mesh::create().withCube().build(); + worldLights.addLight(Light::create().withPointLight({0,0,3}).withColor({1,1,1}).withRange(20).build()); + + r.frameRender = [&](){ + render(); + }; + + r.startEventLoop(); + } + + void render(){ + static bool useDepthTex = false; + auto renderToTexturePass = RenderPass::create() // Create a renderpass which writes to the texture using a framebuffer + .withCamera(camera) + .withWorldLights(&worldLights) + .withFramebuffer(useDepthTex?framebufferDepth:framebuffer) + .withClearColor(true, {0, 1, 1, 0}) + .withGUI(false) + .build(); + + renderToTexturePass.draw(mesh, glm::eulerAngleY(glm::radians((float)i)), materialOffscreen); + + auto renderPass = RenderPass::create() // Create a renderpass which writes to the screen. + .withCamera(camera) + .withWorldLights(&worldLights) + .withClearColor(true, {1, 0, 0, 1}) + .withGUI(true) + .build(); + + static bool useBlit = false; + + ImGui::Checkbox("Use blit",&useBlit); + ImGui::Checkbox("Use depth tex",&useDepthTex); + if (useBlit){ + renderPass.blit(useDepthTex?depthTexture:texture); + } else { + material->setTexture(useDepthTex?depthTexture:texture); + renderPass.draw(mesh, glm::eulerAngleY(glm::radians((float)i)), material); + } + + // The offscreen texture is used in material + static Inspector prof; + prof.update(); + prof.gui(true); + + i++; + } +private: + SDLRenderer r; + Camera camera; + WorldLights worldLights; + std::shared_ptr mesh; + std::shared_ptr materialOffscreen; + std::shared_ptr material; + std::shared_ptr texture; + std::shared_ptr depthTexture; + std::shared_ptr framebuffer; + std::shared_ptr framebufferDepth; + int i=0; +}; + +int main() { + new RenderDepth(); + return 0; +} diff --git a/test/sprite-test.cpp b/test/sprite-test.cpp index 891e50d0..60419a9f 100644 --- a/test/sprite-test.cpp +++ b/test/sprite-test.cpp @@ -98,7 +98,7 @@ class SpriteExample { static int selectedSprite = 0; selectedSprite = selectedSprite % (int)names.size(); ImGui::Combo("Sprite", &selectedSprite,namesPtr,(int)names.size()); - delete namesPtr; + delete[] namesPtr; auto sprite = atlasPtr->get(names.at(selectedSprite)); static glm::vec4 color (1,1,1,1); diff --git a/version-history.md b/version-history.md index b785b0e8..c3a83cb5 100644 --- a/version-history.md +++ b/version-history.md @@ -1,5 +1,6 @@ ## Version history + * 1.0.3 Add RenderPass.blit(). Add depth/stencil as Texture type and add support for depth attachment in Framebuffer. * 1.0.2 Add Phong shader. Improve light (PBR and Blinn-Phong). * 1.0.1 Improve specular highlight for Blinn-Phong. Add attenuation to PBR. * 1.0.0 Physically based rendering. Profiler renamed to Inspector. Added shader specialization constants. Trimmed sprites.