diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a02c0b3e..fba58711 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -10,8 +10,12 @@ # CONTRIBUTING.md for details. list(APPEND SOURCES + gl/framebuffer.cpp gl/renderer.cpp gl/renderer_core.cpp + gl/renderer_msaa.cpp + gl/renderer_print.cpp + gl/depth_peel_oit.cpp gl/shader.cpp gl/types.cpp aux_vis.cpp @@ -28,10 +32,14 @@ list(APPEND SOURCES vsvector3d.cpp) list(APPEND HEADERS + gl/framebuffer.hpp gl/attr_traits.hpp gl/platform_gl.hpp gl/renderer.hpp gl/renderer_core.hpp + gl/renderer_msaa.hpp + gl/renderer_print.hpp + gl/depth_peel_oit.hpp gl/shader.hpp gl/types.hpp aux_vis.hpp diff --git a/lib/aux_js.cpp b/lib/aux_js.cpp index e4b3b6e2..75cf9c4d 100644 --- a/lib/aux_js.cpp +++ b/lib/aux_js.cpp @@ -36,9 +36,6 @@ bool startVisualization(const std::string input, const std::string data_type, // 0 - scalar data, 1 - vector data, 2 - mesh only, (-1) - unknown const int field_type = stream_state.ReadStream(ss, data_type); - // reset antialiasing - GetAppWindow()->getRenderer().setAntialiasing(0); - std::string line; double minv = 0.0, maxv = 0.0; while (ss >> line) diff --git a/lib/aux_vis.cpp b/lib/aux_vis.cpp index 1ec14c18..ba981fb1 100644 --- a/lib/aux_vis.cpp +++ b/lib/aux_vis.cpp @@ -33,6 +33,10 @@ using namespace mfem; #include #endif +#include "gl/renderer_msaa.hpp" +#include "gl/renderer_print.hpp" +#include "gl/depth_peel_oit.hpp" + int visualize = 0; VisualizationScene * locscene; @@ -41,6 +45,7 @@ static int glvis_multisample = GLVIS_MULTISAMPLE; #else static int glvis_multisample = -1; #endif +bool multisample_status = false; float line_w = 1.f; float line_w_aa = gl3::LINE_WIDTH_AA; @@ -48,6 +53,10 @@ float line_w_aa = gl3::LINE_WIDTH_AA; SdlWindow * wnd = nullptr; bool wndLegacyGl = false; +gl3::DefaultPass rndr_main_pass; +gl3::DepthPeeler rndr_depth_peeled; +gl3::MultisamplePass rndr_msaa_pass; + SdlWindow * GetAppWindow() { return wnd; @@ -85,12 +94,16 @@ int InitVisualization (const char name[], int x, int y, int w, int h) wnd->clearEvents(); } + multisample_status = false; + + rndr_main_pass.SetGLDevice(wnd->getRenderer().getDevice()); + rndr_msaa_pass.SetGLDevice(wnd->getRenderer().getDevice()); + rndr_depth_peeled.SetGLDevice(wnd->getRenderer().getDevice()); + #ifdef GLVIS_DEBUG cout << "Window should be up" << endl; #endif InitFont(); - wnd->getRenderer().setLineWidth(line_w); - wnd->getRenderer().setLineWidthMS(line_w_aa); // auxReshapeFunc (MyReshape); // not needed, MyExpose calls it // auxReshapeFunc (NULL); @@ -109,6 +122,7 @@ int InitVisualization (const char name[], int x, int y, int w, int h) wnd->setTouchPinchCallback(TouchPinch); + wnd->setOnKeyDown('A', KeyAPressed); // auxKeyFunc (AUX_p, KeyCtrlP); // handled in vsdata.cpp wnd->setOnKeyDown (SDLK_s, KeyS); wnd->setOnKeyDown ('S', KeyS); @@ -317,7 +331,6 @@ void SetVisualizationScene(VisualizationScene * scene, int view, // SendKeySequence(keys); CallKeySequence(keys); } - wnd->getRenderer().setPalette(&locscene->palette); } void RunVisualization() @@ -377,16 +390,35 @@ void MyReshape(GLsizei w, GLsizei h) void MyExpose(GLsizei w, GLsizei h) { MyReshape (w, h); - GLuint color_tex = locscene->palette.GetColorTexture(); - GLuint alpha_tex = locscene->palette.GetAlphaTexture(); - wnd->getRenderer().setColorTexture(color_tex); - wnd->getRenderer().setAlphaTexture(alpha_tex); + bool use_depth_peel = !(locscene->matAlpha == 1.0); + if (!use_depth_peel) + { + rndr_main_pass.setFontTexture(GetFont()->getFontTex()); + rndr_main_pass.setPalette(locscene->palette); + } + else + { + rndr_depth_peeled.setFontTexture(GetFont()->getFontTex()); + rndr_depth_peeled.setPalette(locscene->palette); + } + // Set antialiasing parameters + rndr_msaa_pass.SetAntialiasing(multisample_status); + rndr_msaa_pass.SetNumSamples(GetMultisample()); + rndr_msaa_pass.SetLineWidth(line_w); + rndr_msaa_pass.SetLineWidthMS(line_w_aa); gl3::SceneInfo frame = locscene->GetSceneObjs(); for (auto drawable_ptr : frame.needs_buffering) { wnd->getRenderer().buffer(drawable_ptr); } - wnd->getRenderer().render(frame.queue); + if (!use_depth_peel) + { + wnd->getRenderer().render({&rndr_main_pass}, {&rndr_msaa_pass}, frame.queue); + } + else + { + wnd->getRenderer().render({&rndr_depth_peeled}, {&rndr_msaa_pass}, frame.queue); + } } void MyExpose() @@ -1096,6 +1128,8 @@ void PrintCaptureBuffer(gl3::CaptureBuffer& cbuf) } } +gl3::CapturePass print_pass; + void KeyCtrlP() { #ifdef __EMSCRIPTEN__ @@ -1126,7 +1160,10 @@ void KeyCtrlP() GL2PS_NO_BLENDING | GL2PS_NO_OPENGL_CONTEXT, GL_RGBA, 0, NULL, 16, 16, 16, 0, fp, "a" ); - gl3::CaptureBuffer cbuf = wnd->getRenderer().capture(wnd_scn.queue); + print_pass.setFontTexture(GetFont()->getFontTex()); + print_pass.setPalette(locscene->palette); + wnd->getRenderer().render( {&print_pass}, {}, wnd_scn.queue); + gl3::CaptureBuffer cbuf = print_pass.GetLastCaptureBuffer(); PrintCaptureBuffer(cbuf); gl2psEndPage(); } @@ -1137,6 +1174,28 @@ void KeyCtrlP() #endif } + +void KeyAPressed() +{ + if (glvis_multisample < 0) + { + cout << "Multisampling disabled." << endl; + return; + } + multisample_status = !multisample_status; + + if (multisample_status) + { + cout << "Multisampling/Antialiasing: on" << endl; + } + else + { + cout << "Multisampling/Antialiasing: off" << endl; + } + + SendExposeEvent(); +} + void KeyQPressed() { wnd->signalQuit(); @@ -1501,18 +1560,13 @@ void SetLineWidth(float width) line_w = width; if (wnd) { - wnd->getRenderer().setLineWidth(line_w); + wnd->getRenderer().SetLineWidth(line_w); } } void SetLineWidthMS(float width_ms) { line_w_aa = width_ms; - if (wnd) - { - wnd->getRenderer().setLineWidthMS(line_w_aa); - } - } float GetLineWidth() @@ -1555,7 +1609,7 @@ void InitFont() { // This function is called after the window is created. GLenum alphaChannel = - gl3::GLDevice::useLegacyTextureFmts() ? GL_ALPHA : GL_RED; + gl3::GLDevice::isOpenGL3() ? GL_RED : GL_ALPHA; glvis_font.setAlphaChannel(alphaChannel); bool try_fc_patterns = true; if (!priority_font.empty()) @@ -1580,7 +1634,6 @@ void InitFont() << endl; } } - wnd->getRenderer().setFontTexture(glvis_font.getFontTex()); } GlVisFont * GetFont() diff --git a/lib/aux_vis.hpp b/lib/aux_vis.hpp index 93542868..8660dd11 100644 --- a/lib/aux_vis.hpp +++ b/lib/aux_vis.hpp @@ -55,6 +55,7 @@ void RightButtonUp (EventInfo *event); void TouchPinch(SDL_MultiGestureEvent & e); +void KeyAPressed(); void KeyCtrlP(); void KeyS(); void KeyQPressed(); diff --git a/lib/gl/depth_peel_oit.cpp b/lib/gl/depth_peel_oit.cpp new file mode 100644 index 00000000..e7bfe325 --- /dev/null +++ b/lib/gl/depth_peel_oit.cpp @@ -0,0 +1,372 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "depth_peel_oit.hpp" +#include "renderer_core.hpp" +#include "shader.hpp" + +const std::string DepthPeelVS[2] = +{ +#include "shaders/default.vert" + , +#include "shaders/depth_peel_passthrough.vert" +}; + +const std::string DepthPeelFS[] = +{ +#include "shaders/lighting.glsl" +#include "shaders/depth_peel.frag" + , +#include "shaders/depth_peel_blend_back.frag" + , +#include "shaders/depth_peel_finalize.frag" +}; + + +namespace gl3 +{ + +TextureHandle DepthPeeler::CreateScreenTexture(GLenum internalFmt, + GLenum format, + GLenum type) +{ + GLuint tex_id; + glGenTextures(1, &tex_id); + + TextureHandle texture = tex_id; + glBindTexture(GL_TEXTURE_2D, texture); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage2D(GL_TEXTURE_2D, 0, internalFmt, screen_w, screen_h, 0, format, + type, nullptr); + return texture; +} + +void DepthPeeler::SetGLDevice(GLDevice* dev) +{ + IMainRenderPass::SetGLDevice(dev); + { + GLuint vbo; + glGenBuffers(1, &vbo); + rect_buf = BufObjHandle{vbo}; + float quad_verts[] = + { + -1.f, 1.f, -1.f, -1.f, 1.f, -1.f, + -1.f, 1.f, 1.f, -1.f, 1.f, 1.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), quad_verts, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + + std::unordered_map attribMap = + { + { CoreGLDevice::ATTR_VERTEX, "vertex"}, + { CoreGLDevice::ATTR_TEXT_VERTEX, "textVertex"}, + { CoreGLDevice::ATTR_NORMAL, "normal"}, + { CoreGLDevice::ATTR_COLOR, "color"}, + { CoreGLDevice::ATTR_TEXCOORD0, "texCoord0"} + }; + if (!main_prgm.create(DepthPeelVS[0], DepthPeelFS[0], attribMap, 3)) + { + std::cerr << "Unable to create depth peeling main program." << std::endl; + } + if (!blend_prgm.create(DepthPeelVS[1], DepthPeelFS[1], attribMap, 1)) + { + std::cerr << "Unable to create depth peeling blending program." << std::endl; + } + if (!finalize_prgm.create(DepthPeelVS[1], DepthPeelFS[2], attribMap, 1)) + { + std::cerr << "Unable to create depth peeling blending program." << std::endl; + } + for (int i = 0; i < 2; i++) + { + main_peel_fbs[i].Init(); + color_fbs[i].Init(); + } + + blend_back_fb.Init(); + opaque_fb.Init(); +} + +void DepthPeeler::CreateScreenPeelObjs() +{ + GLint vp[4]; + device->getViewport(vp); + screen_w = vp[2]; + screen_h = vp[3]; + + if (GLDevice::isOpenGL3()) + { + backBlendTex = CreateScreenTexture(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); + opaqueColorTex = CreateScreenTexture(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); + opaqueDepthTex = CreateScreenTexture(GL_DEPTH_COMPONENT24, + GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT); + } + else + { + backBlendTex = CreateScreenTexture(GL_RGBA, GL_RGBA, GL_FLOAT); + opaqueColorTex = CreateScreenTexture(GL_RGBA, GL_RGBA, GL_FLOAT); + opaqueDepthTex = CreateScreenTexture(GL_DEPTH_COMPONENT, + GL_DEPTH_COMPONENT, + GL_UNSIGNED_INT); + } + + blend_back_fb.Attach(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, backBlendTex); + + opaque_fb.Attach(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, opaqueColorTex); + opaque_fb.Attach(GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, opaqueDepthTex); + + for (int i = 0; i < 2; i++) + { + if (GLDevice::isOpenGL3()) + { + depthTex[i] = CreateScreenTexture(GL_RG32F, GL_RG, GL_FLOAT); + frontColorTex[i] = CreateScreenTexture(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); + backColorTex[i] = CreateScreenTexture(GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); + } + else + { + depthTex[i] = CreateScreenTexture(GL_RGBA, GL_RGBA, GL_FLOAT); + frontColorTex[i] = CreateScreenTexture(GL_RGBA, GL_RGBA, GL_FLOAT); + backColorTex[i] = CreateScreenTexture(GL_RGBA, GL_RGBA, GL_FLOAT); + } + + main_peel_fbs[i].Attach(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, depthTex[i]); + main_peel_fbs[i].Attach(GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, frontColorTex[i]); + main_peel_fbs[i].Attach(GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, backColorTex[i]); + main_peel_fbs[i].Attach(GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, opaqueDepthTex); + + color_fbs[i].Attach(GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, frontColorTex[i]); + color_fbs[i].Attach(GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, backColorTex[i]); + } + + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + + +void DepthPeeler::PreRender() +{ + CreateScreenPeelObjs(); + // Clear back blend buffer + { + blend_back_fb.Bind(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + for (int i = 0; i < 2; i++) + { + // Initial depth texture should be set to [-MAX_DEPTH, MAX_DEPTH] + main_peel_fbs[i].Bind(1); + glClearColor(MAX_DEPTH, MAX_DEPTH, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + color_fbs[i].Bind(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + device->enableBlend(); +} + +void DepthPeeler::RenderOpaque(const RenderQueue& queue) +{ + GLint vp[4]; + device->getViewport(vp); + int tex_w = vp[2]; + int tex_h = vp[3]; + DefaultPass opaque_pass; + opaque_pass.SetGLDevice(device); + opaque_pass.SetTargetFramebuffer(opaque_fb); + opaque_pass.setPalette(*palette); + opaque_pass.setFontTexture(font_tex); + // Render opaque pass objects - this fills in the correct depth attachment + // texture for the depth testing of translucent peels. + opaque_pass.PreRender(); + opaque_pass.Render(queue); + opaque_pass.PostRender(); + // The opaque object depth data will be passed onto the peeling render + // passes, so all we need to do now is to blit the color data to the output + // framebuffer. + target->BlitFrom(opaque_fb, tex_w, tex_h, GL_LINEAR); +} + +void DepthPeeler::DoRenderPass(int i, const RenderQueue& queue) +{ + int src_i = i % 2; + int dst_i = 1 - src_i; + // Clear our target buffers for this pass + { + main_peel_fbs[dst_i].Bind(1); + glClearColor(-1., -1., 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + + color_fbs[dst_i].Bind(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + } + + // Setup main peel program and framebuffer + dynamic_cast(device)->bindExternalProgram(main_prgm); + main_peel_fbs[dst_i].Bind(); + if (main_prgm.uniform("screenCoords") != -1) + { + glUniform2i(main_prgm.uniform("screenCoords"), screen_w, screen_h); + } + + // Bind source depth and front color texture + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, depthTex[src_i]); + + glActiveTexture(GL_TEXTURE0 + 3); + glBindTexture(GL_TEXTURE_2D, frontColorTex[src_i]); + + glUniform1i(main_prgm.uniform("lastDepthTex"), 2); + glUniform1i(main_prgm.uniform("lastFrontColorTex"), 3); + + int color_tex = palette->GetColorTexture(); + int alpha_tex = palette->GetAlphaTexture(); + glBlendEquation(GL_MAX); + glDepthMask(GL_FALSE); + // Render the geometry to peel + for (auto& geom : queue) + { + const RenderParams& params = geom.first; + device->setTransformMatrices(params.model_view.mtx, params.projection.mtx); + device->setMaterial(params.mesh_material); + device->setNumLights(params.num_pt_lights); + for (int i = 0; i < params.num_pt_lights; i++) + { + device->setPointLight(i, params.lights[i]); + } + device->setAmbientLight(params.light_amb_scene); + device->setStaticColor(params.static_color); + device->setClipPlaneUse(params.use_clip_plane); + device->setClipPlaneEqn(params.clip_plane_eqn); + // aggregate buffers with common parameters + std::vector tex_bufs, no_tex_bufs; + GlDrawable* curr_drawable = geom.second; + auto buffers = curr_drawable->getArrayBuffers(); + for (const IVertexBuffer* buf : buffers) + { + if (buf->getVertexLayout() == LAYOUT_VTX_TEXTURE0 + || buf->getVertexLayout() == LAYOUT_VTX_NORMAL_TEXTURE0) + { + tex_bufs.emplace_back(buf->getHandle()); + } + else + { + no_tex_bufs.emplace_back(buf->getHandle()); + } + } + device->attachTexture(GLDevice::SAMPLER_COLOR, color_tex); + device->attachTexture(GLDevice::SAMPLER_ALPHA, alpha_tex); + for (auto buf : tex_bufs) + { + device->drawDeviceBuffer(buf); + } + device->detachTexture(GLDevice::SAMPLER_COLOR); + device->detachTexture(GLDevice::SAMPLER_ALPHA); + for (auto buf : no_tex_bufs) + { + device->drawDeviceBuffer(buf); + } + device->attachTexture(1, font_tex); + device->setNumLights(0); + device->drawDeviceBuffer(curr_drawable->getTextBuffer()); + } + + // Blend just-written back layer separately + blend_prgm.bind(); + blend_back_fb.Bind(); + glBlendEquation(GL_FUNC_ADD); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, + GL_ONE_MINUS_SRC_ALPHA); + + if (blend_prgm.uniform("screenCoords") != -1) + { + glUniform2i(blend_prgm.uniform("screenCoords"), screen_w, screen_h); + } + + glActiveTexture(GL_TEXTURE0 + 2); + glBindTexture(GL_TEXTURE_2D, backColorTex[dst_i]); + glUniform1i(blend_prgm.uniform("lastBackColor"), 2); + + glBindBuffer(GL_ARRAY_BUFFER, rect_buf); + glEnableVertexAttribArray(CoreGLDevice::ATTR_VERTEX); + glVertexAttribPointer(CoreGLDevice::ATTR_VERTEX, + 2, GL_FLOAT, false, 0, 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + glDepthMask(GL_TRUE); +} + +void DepthPeeler::PostRender() +{ + int src_i = NUM_PASSES % 2; + + finalize_prgm.bind(); + target->Bind(); + glClear(GL_DEPTH_BUFFER_BIT); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, frontColorTex[src_i]); + + glActiveTexture(GL_TEXTURE0 + 1); + glBindTexture(GL_TEXTURE_2D, backBlendTex); + + if (finalize_prgm.uniform("screenCoords") != -1) + { + glUniform2i(finalize_prgm.uniform("screenCoords"), screen_w, screen_h); + } + + glUniform1i(finalize_prgm.uniform("lastFrontColor"), 0); + glUniform1i(finalize_prgm.uniform("lastBackColor"), 1); + + glBindBuffer(GL_ARRAY_BUFFER, rect_buf); + glEnableVertexAttribArray(CoreGLDevice::ATTR_VERTEX); + glVertexAttribPointer(CoreGLDevice::ATTR_VERTEX, + 2, GL_FLOAT, false, 0, 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + + // Reset to the default program state + device->initRenderMode(); + glBlendEquation(GL_FUNC_ADD); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + +void DepthPeeler::Render(const RenderQueue& queue) +{ + // elements containing opaque objects should be rendered first + RenderQueue sorted_queue = queue; + auto begin_translucent = + std::stable_partition(sorted_queue.begin(), sorted_queue.end(), + [](RenderQueue::value_type& renderPair) + { + return !renderPair.first.contains_translucent; + }); + // Partition into two queues, one with opaque objects and one with + // translucent objects + RenderQueue opaque_queue(sorted_queue.begin(), begin_translucent); + RenderQueue translucent_queue(begin_translucent, sorted_queue.end()); + + RenderOpaque(opaque_queue); + for (int i = 0; i < NUM_PASSES; i++) + { + DoRenderPass(i, translucent_queue); + } +} + +} + + diff --git a/lib/gl/depth_peel_oit.hpp b/lib/gl/depth_peel_oit.hpp new file mode 100644 index 00000000..3c3d2d4e --- /dev/null +++ b/lib/gl/depth_peel_oit.hpp @@ -0,0 +1,73 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_DEPTH_PEEL_OIT_HPP +#define GLVIS_DEPTH_PEEL_OIT_HPP + +#include "renderer.hpp" +#include "shader.hpp" +#include "framebuffer.hpp" + +namespace gl3 +{ + +class DepthPeeler : public IMainRenderPass +{ +public: + virtual void SetGLDevice(GLDevice* device); + + virtual bool Filter(const RenderParams& param) + { + return true; + } + + virtual void PreRender(); + virtual void Render(const RenderQueue& queue); + virtual void PostRender(); +private: + const double MAX_DEPTH = 1.0; + const int NUM_PASSES = 4; + + void CreateScreenPeelObjs(); + + void RenderOpaque(const RenderQueue& queue); + + void DoRenderPass(int i, const RenderQueue& queue); + + TextureHandle CreateScreenTexture(GLenum internalFmt, + GLenum format, + GLenum type); + + int screen_w, screen_h; + + ShaderProgram main_prgm; + ShaderProgram blend_prgm; + ShaderProgram finalize_prgm; + + TextureHandle depthTex[2]; + TextureHandle frontColorTex[2]; + TextureHandle backColorTex[2]; + TextureHandle opaqueColorTex, opaqueDepthTex; + + TextureHandle backBlendTex; + + Framebuffer main_peel_fbs[2]; + Framebuffer color_fbs[2]; + Framebuffer blend_back_fb; + Framebuffer opaque_fb; + + // Drawing full-screen rectangles + BufObjHandle rect_buf; +}; + +} + +#endif diff --git a/lib/gl/framebuffer.cpp b/lib/gl/framebuffer.cpp new file mode 100644 index 00000000..c58aa728 --- /dev/null +++ b/lib/gl/framebuffer.cpp @@ -0,0 +1,107 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "framebuffer.hpp" +#include "renderer.hpp" +#include "shader.hpp" + +static const std::string BlitVertexShader = +#include "shaders/depth_peel_passthrough.vert" + ; + +static const std::string BlitFragShader = +#include "shaders/fb_blit.frag" + ; + +namespace gl3 +{ + +class ShaderBasedBlit +{ +public: + ShaderBasedBlit() + { + if (!passthrough_shader.create(BlitVertexShader, BlitFragShader, + {{ 0, "vertex" }}, 1)) + { + std::cerr << "Unable to create blit main program." << std::endl; + } + GLuint vbo; + glGenBuffers(1, &vbo); + quad_buffer = BufObjHandle{vbo}; + float quad_verts[] = + { + -1.f, 1.f, -1.f, -1.f, 1.f, -1.f, + -1.f, 1.f, 1.f, -1.f, 1.f, 1.f, + }; + glBindBuffer(GL_ARRAY_BUFFER, vbo); + glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), quad_verts, GL_STATIC_DRAW); + glBindBuffer(GL_ARRAY_BUFFER, 0); + } + ShaderProgram& GetPassthroughShader() { return passthrough_shader; } + BufObjHandle& GetQuadBuffer() { return quad_buffer; } +private: + ShaderProgram passthrough_shader; + BufObjHandle quad_buffer; +}; + +static ShaderBasedBlit& GetShaderBasedBlit() +{ + static ShaderBasedBlit blit; + return blit; +} + +void Framebuffer::BlitFrom(const Framebuffer &fb_from, int w, int h, + GLenum filter) const +{ + if (GLDevice::isOpenGL3()) + { + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, handle); + glBindFramebuffer(GL_READ_FRAMEBUFFER, fb_from.handle); + glBlitFramebuffer(0, 0, w, h, + 0, 0, w, h, + GL_COLOR_BUFFER_BIT, filter); + } + else + { + if (!(filter == GL_NEAREST || filter == GL_LINEAR)) + { + std::cerr << "Blit error: no texture bound to color attachment 0 " + << "of source framebuffer" << std::endl; + return; + } + ShaderProgram& blit_shader = GetShaderBasedBlit().GetPassthroughShader(); + BufObjHandle& quad_buffer = GetShaderBasedBlit().GetQuadBuffer(); + + blit_shader.bind(); + Bind(); + GLuint texture_src = fb_from.color_attached_textures[0]; + if (texture_src == 0) + { + std::cerr << "Blit error: no texture bound to color attachment 0 " + << "of source framebuffer" << std::endl; + return; + } + glUniform2i(blit_shader.uniform("texDims"), w, h); + glUniform1i(blit_shader.uniform("sourceColor"), 0); + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture_src); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, filter); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, filter); + + glBindBuffer(GL_ARRAY_BUFFER, quad_buffer); + glEnableVertexAttribArray(0); + glVertexAttribPointer(0, 2, GL_FLOAT, false, 0, 0); + glDrawArrays(GL_TRIANGLES, 0, 6); + } +} + +} diff --git a/lib/gl/framebuffer.hpp b/lib/gl/framebuffer.hpp new file mode 100644 index 00000000..31a16aa5 --- /dev/null +++ b/lib/gl/framebuffer.hpp @@ -0,0 +1,167 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_FRAMEBUFFER_HPP +#define GLVIS_FRAMEBUFFER_HPP + +#include "types.hpp" +#include + +namespace gl3 +{ + +class Framebuffer +{ +public: + Framebuffer() = default; + + explicit operator bool() const + { + return handle; + } + + bool IsComplete() const + { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE; + } + + void Init() + { + GLuint fb_id; + glGenFramebuffers(1, &fb_id); + handle = fb_id; + } + + void Detach(GLuint attach_point) + { + if (handle == 0) + { + std::cerr << "Can't attach textures from a default framebuffer." + << std::endl; + return; + } + if (attach_point >= GL_COLOR_ATTACHMENT0 && + attach_point < GL_COLOR_ATTACHMENT0 + NUM_ATTACHMENTS) + { + int attach_idx = attach_point - GL_COLOR_ATTACHMENT0; + color_attach_active[attach_idx] = false; + color_attached_textures[attach_idx] = 0; + } + } + + void Attach(GLuint attach_point, + GLuint texture_binding, + const resource::TextureHandle& tex_handle) + { + if (handle == 0) + { + std::cerr << "Can't attach textures to a default framebuffer." + << std::endl; + return; + } + if (attach_point >= GL_COLOR_ATTACHMENT0 && + attach_point < GL_COLOR_ATTACHMENT0 + NUM_ATTACHMENTS) + { + int attach_idx = attach_point - GL_COLOR_ATTACHMENT0; + color_attach_active[attach_idx] = true; + color_attached_textures[attach_idx] = tex_handle; + } + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferTexture2D(GL_FRAMEBUFFER, attach_point, + texture_binding, tex_handle, 0); + } + + + void Attach(GLuint attach_point, + const resource::RenderBufHandle& renderbuf_handle) + { + if (handle == 0) + { + std::cerr << "Can't attach renderbuffers to a default framebuffer." + << std::endl; + return; + } + if (attach_point >= GL_COLOR_ATTACHMENT0 && + attach_point < GL_COLOR_ATTACHMENT0 + NUM_ATTACHMENTS) + { + int attach_idx = attach_point - GL_COLOR_ATTACHMENT0; + color_attach_active[attach_idx] = true; + color_attached_textures[attach_idx] = 0; + } + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, attach_point, + GL_RENDERBUFFER, renderbuf_handle); + } + + void Bind(unsigned int nbufs = 0) const + { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + if (handle == 0) + { + if (nbufs > 1) + { + std::cerr << "Default framebuffer only has one valid output " + << "buffer." << std::endl; + } + glDrawBuffer(GL_BACK); + return; + } + // Set active color attachments in order + std::vector output_bufs; + for (int ibuf = 0; ibuf < NUM_ATTACHMENTS; ibuf++) + { + if (color_attach_active[ibuf]) + { + output_bufs.push_back(GL_COLOR_ATTACHMENT0 + ibuf); + } + } + if (nbufs == 0) + { + nbufs = output_bufs.size(); + } + while (output_bufs.size() < nbufs) + { + output_bufs.push_back(GL_NONE); + } + glDrawBuffers(nbufs, output_bufs.data()); + } + + void Bind(const std::vector& drawbufs) const + { + glBindFramebuffer(GL_FRAMEBUFFER, handle); + glDrawBuffers(drawbufs.size(), drawbufs.data()); + } + + // Unbinds the current framebuffer and binds the default framebuffer. + void Unbind() const + { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + GLenum drawOutput = GL_BACK; + glDrawBuffers(1, &drawOutput); + } + + // Blits an image located on GL_COLOR_ATTACHMENT0 of a source framebuffer + // to all the color attachments of this framebuffer. + void BlitFrom(const Framebuffer& fb_from, + int w, int h, + GLenum filter = GL_NEAREST) const; + +private: + resource::FBOHandle handle; + static constexpr int NUM_ATTACHMENTS=8; + bool color_attach_active[NUM_ATTACHMENTS] = {}; + GLuint color_attached_textures[NUM_ATTACHMENTS] = {}; +}; + +} + +#endif diff --git a/lib/gl/renderer.cpp b/lib/gl/renderer.cpp index 7d6442b4..da019d29 100644 --- a/lib/gl/renderer.cpp +++ b/lib/gl/renderer.cpp @@ -24,90 +24,28 @@ namespace gl3 // unsized formats like GL_RED and GL_RGBA no longer support floating-point // data being passed in, so use of the sized internal formats is obligatory in // WebGL 2. -bool GLDevice::useLegacyTextureFmts() +bool GLDevice::isOpenGL3() { #ifdef __EMSCRIPTEN__ const std::string versionString = reinterpret_cast(glGetString(GL_VERSION)); if (versionString.find("OpenGL ES 3.0") != std::string::npos) - { - return false; - } - else { return true; } -#else - return !GLEW_VERSION_3_0; -#endif -} - -void MeshRenderer::setAntialiasing(bool aa_status) -{ - if (msaa_enable != aa_status) - { - msaa_enable = aa_status; - if (msaa_enable) - { - if (!feat_use_fbo_antialias) - { - glEnable(GL_MULTISAMPLE); - glEnable(GL_LINE_SMOOTH); - device->enableBlend(); - } - device->setLineWidth(line_w_aa); - } - else - { - if (!feat_use_fbo_antialias) - { - glDisable(GL_MULTISAMPLE); - glDisable(GL_LINE_SMOOTH); - device->disableBlend(); - } - device->setLineWidth(line_w); - } - } -} - -void MeshRenderer::setLineWidth(float w) -{ - line_w = w; - if (device && !msaa_enable) - { - device->setLineWidth(line_w); - } -} - -void MeshRenderer::setLineWidthMS(float w) -{ - line_w_aa = w; - if (device && msaa_enable) - { - device->setLineWidth(line_w_aa); - } -} - -void MeshRenderer::init() -{ -#ifdef __EMSCRIPTEN__ - const std::string versionString - = reinterpret_cast(glGetString(GL_VERSION)); - bool is_webgl2 = (versionString.find("OpenGL ES 3.0") != std::string::npos); - feat_use_fbo_antialias = is_webgl2; - if (feat_use_fbo_antialias) + else { - glGetIntegerv(GL_MAX_SAMPLES, &msaa_samples); + return false; } #else - // TODO: we could also support ARB_framebuffer_object - feat_use_fbo_antialias = GLEW_VERSION_3_0; - glGetIntegerv(GL_MAX_SAMPLES, &msaa_samples); + return GLEW_VERSION_3_0; #endif } -void MeshRenderer::render(const RenderQueue& queue) +void DefaultPass::Render(const RenderQueue& queue) { + auto clear_color = device->getClearColor(); + glClearColor(clear_color[0], clear_color[1], clear_color[2], clear_color[3]); // elements containing opaque objects should be rendered first RenderQueue sorted_queue = queue; std::stable_partition(sorted_queue.begin(), sorted_queue.end(), @@ -115,52 +53,11 @@ void MeshRenderer::render(const RenderQueue& queue) { return !renderPair.first.contains_translucent; }); - RenderBufHandle renderBufs[2]; - FBOHandle msaaFb; - if (feat_use_fbo_antialias && msaa_enable) - { - GLuint colorBuf, depthBuf; - glGenRenderbuffers(1, &colorBuf); - glGenRenderbuffers(1, &depthBuf); - renderBufs[0] = RenderBufHandle(colorBuf); - renderBufs[1] = RenderBufHandle(depthBuf); - - GLuint fbo; - glGenFramebuffers(1, &fbo); - - int vp[4]; - device->getViewport(vp); - int width = vp[2]; - int height = vp[3]; - glBindRenderbuffer(GL_RENDERBUFFER, colorBuf); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_samples, - GL_RGBA8, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, depthBuf); - glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_samples, - GL_DEPTH_COMPONENT24, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, colorBuf); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, - GL_RENDERBUFFER, depthBuf); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - cerr << "Unable to create multisampled renderbuffer." << flush; - glDeleteFramebuffers(1, &fbo); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - else - { - msaaFb = FBOHandle(fbo); - } -#ifndef __EMSCRIPTEN__ - glEnable(GL_MULTISAMPLE); -#endif - } + target->Bind(1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + int color_tex = palette->GetColorTexture(); + int alpha_tex = palette->GetAlphaTexture(); + bool always_blend = device->isBlendEnabled(); for (auto& q_elem : sorted_queue) { const RenderParams& params = q_elem.first; @@ -177,38 +74,20 @@ void MeshRenderer::render(const RenderQueue& queue) device->setClipPlaneEqn(params.clip_plane_eqn); // aggregate buffers with common parameters std::vector tex_bufs, no_tex_bufs; - std::vector text_bufs; GlDrawable* curr_drawable = q_elem.second; - for (int i = 0; i < NUM_LAYOUTS; i++) + auto buffers = curr_drawable->getArrayBuffers(); + for (const IVertexBuffer* buf : buffers) { - for (size_t j = 0; j < GlDrawable::NUM_SHAPES; j++) + if (buf->getVertexLayout() == LAYOUT_VTX_TEXTURE0 + || buf->getVertexLayout() == LAYOUT_VTX_NORMAL_TEXTURE0) { - if (curr_drawable->buffers[i][j]) - { - if (i == LAYOUT_VTX_TEXTURE0 || i == LAYOUT_VTX_NORMAL_TEXTURE0) - { - tex_bufs.emplace_back(curr_drawable->buffers[i][j].get()->getHandle()); - } - else - { - no_tex_bufs.emplace_back(curr_drawable->buffers[i][j].get()->getHandle()); - } - } - if (curr_drawable->indexed_buffers[i][j]) - { - if (i == LAYOUT_VTX_TEXTURE0 || i == LAYOUT_VTX_NORMAL_TEXTURE0) - { - tex_bufs.emplace_back(curr_drawable->indexed_buffers[i][j].get()->getHandle()); - } - else - { - no_tex_bufs.emplace_back( - curr_drawable->indexed_buffers[i][j].get()->getHandle()); - } - } + tex_bufs.emplace_back(buf->getHandle()); + } + else + { + no_tex_bufs.emplace_back(buf->getHandle()); } } - text_bufs.emplace_back(&curr_drawable->text_buffer); if (params.contains_translucent) { device->enableBlend(); @@ -236,162 +115,77 @@ void MeshRenderer::render(const RenderQueue& queue) } device->attachTexture(1, font_tex); device->setNumLights(0); - for (TextBuffer* buf : text_bufs) - { - device->drawDeviceBuffer(*buf); - } + device->drawDeviceBuffer(curr_drawable->getTextBuffer()); device->enableDepthWrite(); - if (feat_use_fbo_antialias || !msaa_enable) { device->disableBlend(); } - } - if (feat_use_fbo_antialias && msaa_enable && msaaFb) - { - device->enableBlend(); - int vp[4]; - device->getViewport(vp); - int width = vp[2]; - int height = vp[3]; - GLuint colorBufId; - glGenRenderbuffers(1, &colorBufId); - RenderBufHandle colorBuf(colorBufId); - - GLuint fboId; - glGenFramebuffers(1, &fboId); - FBOHandle resolveFb(fboId); - - glBindRenderbuffer(GL_RENDERBUFFER, colorBuf); - glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); - glBindRenderbuffer(GL_RENDERBUFFER, 0); - - glBindFramebuffer(GL_FRAMEBUFFER, resolveFb); - glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, - GL_RENDERBUFFER, colorBuf); - - if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) - { - cerr << "Unable to create resolve renderbuffer." << endl; - glBindFramebuffer(GL_FRAMEBUFFER, 0); - } - - // bind our draw framebuffer and blit the multisampled image - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, resolveFb); - glBindFramebuffer(GL_READ_FRAMEBUFFER, msaaFb); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glBlitFramebuffer(0, 0, width, height, - 0, 0, width, height, - GL_COLOR_BUFFER_BIT, - GL_NEAREST); -#ifndef __EMSCRIPTEN__ - glDisable(GL_MULTISAMPLE); -#endif - glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); - glBindFramebuffer(GL_READ_FRAMEBUFFER, resolveFb); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); - glBlitFramebuffer(0, 0, width, height, - 0, 0, width, height, - GL_COLOR_BUFFER_BIT, - GL_LINEAR); - device->disableBlend(); + if (!always_blend) { device->disableBlend(); } } } -CaptureBuffer MeshRenderer::capture(const RenderQueue& queue) +void MeshRenderer::render(const vector& main_passes, + const vector& extra_passes, + const RenderQueue& queued) { - CaptureBuffer cbuf; - device->initXfbMode(); - for (auto& q_elem : queue) + // Step 1: Match renderables in the queue with the *first* render pass that + // can handle them. + std::vector matched_queues(main_passes.size()); + for (auto drawable : queued) { - const RenderParams& params = q_elem.first; - device->setTransformMatrices(params.model_view.mtx, params.projection.mtx); - device->setMaterial(params.mesh_material); - device->setNumLights(params.num_pt_lights); - for (int i = 0; i < params.num_pt_lights; i++) + for (size_t ipass = 0; ipass < main_passes.size(); ipass++) { - device->setPointLight(i, params.lights[i]); - } - device->setAmbientLight(params.light_amb_scene); - device->setStaticColor(params.static_color); - device->setClipPlaneUse(params.use_clip_plane); - device->setClipPlaneEqn(params.clip_plane_eqn); - // aggregate buffers with common parameters - std::vector tex_bufs, no_tex_bufs; - std::vector text_bufs; - GlDrawable* curr_drawable = q_elem.second; - for (int i = 0; i < NUM_LAYOUTS; i++) - { - for (size_t j = 0; j < GlDrawable::NUM_SHAPES; j++) + if (main_passes[ipass]->Filter(drawable.first)) { - if (curr_drawable->buffers[i][j]) - { - if (i == LAYOUT_VTX_TEXTURE0 || i == LAYOUT_VTX_NORMAL_TEXTURE0) - { - tex_bufs.emplace_back(curr_drawable->buffers[i][j].get()->getHandle()); - } - else - { - no_tex_bufs.emplace_back(curr_drawable->buffers[i][j].get()->getHandle()); - } - } - if (curr_drawable->indexed_buffers[i][j]) - { - if (i == LAYOUT_VTX_TEXTURE0 || i == LAYOUT_VTX_NORMAL_TEXTURE0) - { - tex_bufs.emplace_back(curr_drawable->indexed_buffers[i][j].get()->getHandle()); - } - else - { - no_tex_bufs.emplace_back( - curr_drawable->indexed_buffers[i][j].get()->getHandle()); - } - } + matched_queues[ipass].emplace_back(drawable); + break; } } - text_bufs.emplace_back(&curr_drawable->text_buffer); + } + // Step 2: Setup the framebuffer with the first extra pass, and render the + // queue with the main passes. + Framebuffer default_target; + std::reference_wrapper curr_out = default_target; + if (extra_passes.size() > 0) + { + extra_passes[0]->PreRender(); + curr_out = extra_passes[0]->GetSourceFramebuffer(); + } + for (size_t ipass = 0; ipass < main_passes.size(); ipass++) + { + main_passes[ipass]->SetTargetFramebuffer(curr_out); + main_passes[ipass]->PreRender(); + main_passes[ipass]->Render(matched_queues[ipass]); + main_passes[ipass]->PostRender(); + } - device->attachTexture(GLDevice::SAMPLER_COLOR, color_tex); - device->attachTexture(GLDevice::SAMPLER_ALPHA, alpha_tex); - for (auto buf : tex_bufs) - { - device->captureXfbBuffer(*pal, cbuf, buf); - } - device->detachTexture(GLDevice::SAMPLER_COLOR); - device->detachTexture(GLDevice::SAMPLER_ALPHA); - for (auto buf : no_tex_bufs) - { - device->captureXfbBuffer(*pal, cbuf, buf); - } - if (!params.contains_translucent) - { - device->enableBlend(); - device->disableDepthWrite(); - } - device->attachTexture(1, font_tex); - device->setNumLights(0); - for (TextBuffer* buf : text_bufs) + if (extra_passes.size() > 0) + { + for (size_t ipass = 1; ipass < extra_passes.size(); ipass++) { - device->captureXfbBuffer(cbuf, *buf); + // Finalize last stage's results onto next stage + extra_passes[ipass]->PreRender(); + curr_out = extra_passes[ipass]->GetSourceFramebuffer(); + extra_passes[ipass-1]->PostRender(); } + extra_passes[extra_passes.size() - 1]->SetTargetFramebuffer(default_target); + extra_passes[extra_passes.size() - 1]->PostRender(); } - device->exitXfbMode(); - return cbuf; } void MeshRenderer::buffer(GlDrawable* buf) { - for (int i = 0; i < NUM_LAYOUTS; i++) + auto buffers = buf->getArrayBuffers(); + for (IVertexBuffer* buf : buffers) { - for (size_t j = 0; j < GlDrawable::NUM_SHAPES; j++) + IIndexedBuffer* ibuf = dynamic_cast(buf); + if (ibuf) { - if (buf->buffers[i][j]) - { - device->bufferToDevice((array_layout) i, *(buf->buffers[i][j].get())); - } - if (buf->indexed_buffers[i][j]) - { - device->bufferToDevice((array_layout) i, *(buf->indexed_buffers[i][j].get())); - } + device->bufferToDevice(buf->getVertexLayout(), *ibuf); + } + else + { + device->bufferToDevice(buf->getVertexLayout(), *buf); } } - device->bufferToDevice(buf->text_buffer); + device->bufferToDevice(buf->getTextBuffer()); } void GLDevice::init() @@ -441,18 +235,4 @@ void GLDevice::setTransformMatrices(glm::mat4 model_view, glm::mat4 projection) proj_mtx = projection; } -void GLDevice::captureXfbBuffer(CaptureBuffer& capture, const TextBuffer& t_buf) -{ - for (const auto& entry : t_buf) - { - glm::vec3 raster = glm::project( - glm::vec3(entry.rx, entry.ry, entry.rz), - model_view_mtx, - proj_mtx, - glm::vec4(0, 0, vp_width, vp_height)); - capture.text.emplace_back(raster, glm::make_vec4(static_color.data()), - entry.text); - } -} - } diff --git a/lib/gl/renderer.hpp b/lib/gl/renderer.hpp index 6144faa0..1d5f7a7d 100644 --- a/lib/gl/renderer.hpp +++ b/lib/gl/renderer.hpp @@ -19,6 +19,7 @@ #include "platform_gl.hpp" #include "types.hpp" +#include "framebuffer.hpp" #include "../material.hpp" #include "../palettes.hpp" @@ -57,42 +58,14 @@ struct RenderParams typedef vector> RenderQueue; +struct CaptureBuffer; + struct SceneInfo { vector needs_buffering; RenderQueue queue; }; -struct FeedbackVertex -{ - glm::vec3 position; - glm::vec4 color; - - FeedbackVertex() = default; - FeedbackVertex(glm::vec3 pos, glm::vec4 c) - : position(pos), color(c) { } - FeedbackVertex(glm::vec4 pos, glm::vec4 c) - : position(pos), color(c) { } -}; - -struct FeedbackText -{ - glm::vec3 offset; - glm::vec4 color; - std::string text; - - FeedbackText() = default; - FeedbackText(glm::vec3 t_off, glm::vec4 t_color, std::string txt) - : offset(t_off), color(t_color), text(std::move(txt)) { } -}; - -struct CaptureBuffer -{ - vector lines; - vector triangles; - vector text; -}; - // OpenGL device interface representing rendering capabilities class GLDevice { @@ -103,6 +76,9 @@ class GLDevice glm::mat4 proj_mtx; std::array static_color; + std::array clear_color { 1.f, 1.f, 1.f, 1.f }; + + bool blend_enabled = false; protected: TextureHandle passthrough_texture; @@ -133,10 +109,11 @@ class GLDevice // If true, use unsized internal formats and GL_ALPHA for single-channel // data. Otherwise, use the newer sized internal formats and GL_RED. - static bool useLegacyTextureFmts(); + static bool isOpenGL3(); - void enableBlend() { glEnable(GL_BLEND); } - void disableBlend() { glDisable(GL_BLEND); } + void enableBlend() { blend_enabled = true; glEnable(GL_BLEND); } + void disableBlend() { blend_enabled = false; glDisable(GL_BLEND); } + bool isBlendEnabled() { return blend_enabled; } void enableDepthWrite() { glDepthMask(GL_TRUE); } void disableDepthWrite() { glDepthMask(GL_FALSE); } void setLineWidth(float w) { glLineWidth(w); } @@ -149,6 +126,10 @@ class GLDevice void getViewport(GLint (&vp)[4]); // Set the color to use, if a color attribute is not provided. void setStaticColor(const std::array& rgba) { static_color = rgba; } + // Set the background clear color to use. + void setClearColor(const std::array& rgba) { clear_color = rgba; } + // Gets the current background color. + std::array getClearColor() const { return clear_color; } // === Render pipeline functions === @@ -171,8 +152,8 @@ class GLDevice // === Buffer management functions === // Load a client-side vertex buffer into a device buffer. - virtual void bufferToDevice(array_layout layout, IVertexBuffer& buf) = 0; - virtual void bufferToDevice(array_layout layout, IIndexedBuffer& buf) = 0; + virtual void bufferToDevice(ArrayLayout layout, IVertexBuffer& buf) = 0; + virtual void bufferToDevice(ArrayLayout layout, IIndexedBuffer& buf) = 0; virtual void bufferToDevice(TextBuffer& t_buf) = 0; // Draw the data loaded in a device buffer. virtual void drawDeviceBuffer(int hnd) = 0; @@ -183,7 +164,7 @@ class GLDevice // Initializes state needed for transform feedback. virtual void initXfbMode() {} // Prepares state when exiting transform feedback. - virtual void exitXfbMode() {} + virtual void initRenderMode() {} // Capture the next drawn vertex buffer to a feedback buffer instead of // drawing to screen. virtual void captureXfbBuffer(PaletteState& pal, CaptureBuffer& capture, @@ -193,23 +174,63 @@ class GLDevice }; +class IRenderPass +{ +public: + IRenderPass() { } + virtual void SetGLDevice(GLDevice* dev) { device = dev; } + virtual void PreRender() = 0; + virtual void PostRender() = 0; + + virtual const Framebuffer& GetSourceFramebuffer() const + { return default_target; } + + virtual void SetTargetFramebuffer(const Framebuffer& fbo) { target = &fbo; } +protected: + GLDevice* device; + const Framebuffer* target = nullptr; + const Framebuffer default_target; +}; + +// Generic interface for an action on a queue of renderable objects. +class IMainRenderPass : public IRenderPass +{ +public: + IMainRenderPass() { } + // If the class will handle objects with the given set of render + // parameters, returns true. + virtual bool Filter(const RenderParams& param) = 0; + // Renders objects in the queue to the setup output surfaces. + virtual void Render(const RenderQueue& queue) = 0; + // Sets the palette state for this render pass. + void setPalette(PaletteState& pal) { palette = &pal; } + // Sets the texture handle of the font atlas. + void setFontTexture(GLuint tex_h) { font_tex = tex_h; } +protected: + bool use_default_output = true; + PaletteState* palette = nullptr; + GLuint font_tex = 0; +}; + +class DefaultPass : public IMainRenderPass +{ +public: + DefaultPass() { } + virtual bool Filter(const RenderParams& param) { return true; } + + virtual void PreRender() {} + virtual void Render(const RenderQueue& queued); + virtual void PostRender() {} +}; + class MeshRenderer { unique_ptr device; - bool msaa_enable; - int msaa_samples; - GLuint color_tex, alpha_tex, font_tex; - float line_w, line_w_aa; - PaletteState* pal; - - bool feat_use_fbo_antialias; - void init(); + float line_w; + public: MeshRenderer() - : msaa_enable(false) - , msaa_samples(0) - , line_w(1.f) - , line_w_aa(LINE_WIDTH_AA) { init(); } + : line_w(1.f) { } template void setDevice() @@ -217,7 +238,6 @@ class MeshRenderer device.reset(new TDevice()); device->setLineWidth(line_w); device->init(); - msaa_enable = false; } template @@ -225,43 +245,23 @@ class MeshRenderer { device.reset(new TDevice(device)); } - void setPalette(PaletteState* pal) { this->pal = pal; } - // Sets the texture handle of the color palette. - void setColorTexture(GLuint tex_h) { color_tex = tex_h; } - // Sets the texture handle of the alpha texture. - void setAlphaTexture(GLuint tex_h) { alpha_tex = tex_h; } - // Sets the texture handle of the font atlas. - void setFontTexture(GLuint tex_h) { font_tex = tex_h; } + GLDevice* getDevice() const { return device.get(); } - void setAntialiasing(bool aa_status); - bool getAntialiasing() { return msaa_enable; } - void setSamplesMSAA(int samples) + void SetLineWidth(float w) { - if (msaa_samples < samples) - { - std::cerr << "GL_MAX_SAMPLES = " << msaa_samples - << " but requested " << samples << "x MSAA. "; - std::cerr << "Setting antialiasing mode to " - << msaa_samples << "x MSAA." << endl; - } - else - { - msaa_samples = samples; - } + line_w = w; + if (device) { device->setLineWidth(w); } } - int getSamplesMSAA() { return msaa_samples; } - - void setLineWidth(float w); - float getLineWidth() { return line_w; } - void setLineWidthMS(float w); - float getLineWidthMS() { return line_w_aa; } + float GetLineWidth() { return line_w; } - void setClearColor(float r, float g, float b, float a) { glClearColor(r, g, b, a); } + void setClearColor(float r, float g, float b, float a) + { device->setClearColor({r,g,b,a}); } void setViewport(GLsizei w, GLsizei h) { device->setViewport(w, h); } - void render(const RenderQueue& queued); - CaptureBuffer capture(const RenderQueue& queued); + void render(const std::vector& main_passes, + const std::vector& extra_passes, + const RenderQueue& queued); void buffer(GlDrawable* buf); }; diff --git a/lib/gl/renderer_core.cpp b/lib/gl/renderer_core.cpp index dc761643..5bc37522 100644 --- a/lib/gl/renderer_core.cpp +++ b/lib/gl/renderer_core.cpp @@ -11,6 +11,7 @@ #include "attr_traits.hpp" #include "renderer_core.hpp" +#include "renderer_print.hpp" #include "../aux_vis.hpp" #include @@ -109,14 +110,7 @@ bool CoreGLDevice::compileShaders() #ifndef __EMSCRIPTEN__ if (GLEW_EXT_transform_feedback || GLEW_VERSION_3_0) { - const char * xfrm_varyings[] = - { - "gl_Position", - "fColor", - "fClipCoord", - }; - glTransformFeedbackVaryings(feedback_prgm.getProgramId(), 3, xfrm_varyings, - GL_INTERLEAVED_ATTRIBS); + feedback_prgm.setFeedbackVaryings({"gl_Position", "fColor", "fClipCoord"}); if (!feedback_prgm.create(PRINTING_VS, PRINTING_FS, attribMap, 1)) { @@ -146,14 +140,17 @@ void CoreGLDevice::initializeShaderState(const ShaderProgram& prog) } } #ifdef GLVIS_DEBUG - unordered_set expectedUnifs(unif_list.begin(), unif_list.end()); - for (const auto& pairunif : uniforms) + if (prog == default_prgm) { - if (expectedUnifs.find(pairunif.first) == expectedUnifs.end()) + unordered_set expectedUnifs(unif_list.begin(), unif_list.end()); + for (const auto& pairunif : uniforms) { - std::cerr << "Warning: unexpected uniform \"" - << pairunif.first - << "\" found in shader." << std::endl; + if (expectedUnifs.find(pairunif.first) == expectedUnifs.end()) + { + std::cerr << "Warning: unexpected uniform \"" + << pairunif.first + << "\" found in shader." << std::endl; + } } } #endif @@ -244,7 +241,7 @@ void CoreGLDevice::setClipPlaneEqn(const std::array &eqn) glUniform4fv(uniforms["clipPlane"], 1, glm::value_ptr(clip_plane)); } -void CoreGLDevice::bufferToDevice(array_layout layout, IVertexBuffer &buf) +void CoreGLDevice::bufferToDevice(ArrayLayout layout, IVertexBuffer &buf) { if (buf.getHandle() == 0) { @@ -264,7 +261,7 @@ void CoreGLDevice::bufferToDevice(array_layout layout, IVertexBuffer &buf) buf.getData(), GL_STATIC_DRAW); } -void CoreGLDevice::bufferToDevice(array_layout layout, IIndexedBuffer& buf) +void CoreGLDevice::bufferToDevice(ArrayLayout layout, IIndexedBuffer& buf) { if (buf.getHandle() == 0) { diff --git a/lib/gl/renderer_core.hpp b/lib/gl/renderer_core.hpp index 5804c1a6..afb382c3 100644 --- a/lib/gl/renderer_core.hpp +++ b/lib/gl/renderer_core.hpp @@ -56,7 +56,7 @@ class CoreGLDevice : public GLDevice BufObjHandle elem_buf; GLenum shape; size_t count; - array_layout layout; + ArrayLayout layout; }; std::vector vbos; @@ -90,8 +90,8 @@ class CoreGLDevice : public GLDevice void setClipPlaneUse(bool enable) override; void setClipPlaneEqn(const std::array& eqn) override; - void bufferToDevice(array_layout layout, IVertexBuffer& buf) override; - void bufferToDevice(array_layout layout, IIndexedBuffer& buf) override; + void bufferToDevice(ArrayLayout layout, IVertexBuffer& buf) override; + void bufferToDevice(ArrayLayout layout, IIndexedBuffer& buf) override; void bufferToDevice(TextBuffer& t_buf) override; void drawDeviceBuffer(int hnd) override; void drawDeviceBuffer(const TextBuffer& t_buf) override; @@ -102,7 +102,7 @@ class CoreGLDevice : public GLDevice initializeShaderState(feedback_prgm); glEnable(GL_RASTERIZER_DISCARD); } - void exitXfbMode() override + void initRenderMode() override { glDisable(GL_RASTERIZER_DISCARD); initializeShaderState(default_prgm); diff --git a/lib/gl/renderer_ff.cpp b/lib/gl/renderer_ff.cpp index 0d641335..47a72792 100644 --- a/lib/gl/renderer_ff.cpp +++ b/lib/gl/renderer_ff.cpp @@ -157,7 +157,7 @@ void FFGLDevice::setClipPlaneEqn(const std::array& eqn) glClipPlane(GL_CLIP_PLANE0, eqn.data()); } -void FFGLDevice::bufferToDevice(array_layout layout, IVertexBuffer& buf) +void FFGLDevice::bufferToDevice(ArrayLayout layout, IVertexBuffer& buf) { if (buf.getHandle() == 0) { @@ -196,7 +196,7 @@ void FFGLDevice::bufferToDevice(array_layout layout, IVertexBuffer& buf) } } -void FFGLDevice::bufferToDevice(array_layout layout, IIndexedBuffer& buf) +void FFGLDevice::bufferToDevice(ArrayLayout layout, IIndexedBuffer& buf) { if (buf.getHandle() == 0) { @@ -326,136 +326,4 @@ void FFGLDevice::drawDeviceBuffer(const TextBuffer& buf) glPopMatrix(); } -void FFGLDevice::captureXfbBuffer(PaletteState& pal, CaptureBuffer& cbuf, - int hnd) -{ - if (hnd == 0) { return; } - if (disp_lists[hnd].count == 0) { return; } - GLenum fbType; - int fbStride; - if (disp_lists[hnd].layout == VertexTex::layout - || disp_lists[hnd].layout == VertexNormTex::layout) - { - //capture texture values too - // [ X Y Z ] [ R G B A ] [ U V - - ] - fbType = GL_3D_COLOR_TEXTURE; - fbStride = 11; - } - else - { - // only capture pos and color - // [ X Y Z ] [ R G B A ] - fbType = GL_3D_COLOR; - fbStride = 7; - } - // compute feedback buffer size - int sizebuf = 0; - if (disp_lists[hnd].shape == GL_LINES) - { - // for each line: LINE_TOKEN [Vtx] [Vtx] - sizebuf = (disp_lists[hnd].count / 2) + disp_lists[hnd].count * fbStride; - } - else if (disp_lists[hnd].shape == GL_TRIANGLES) - { - // for each tri: POLY_TOKEN 3 [Vtx] [Vtx] [Vtx] - // NOTE: when clip plane is enabled, we might get two triangles - // or a quad for an input triangle. However, the other clipped - // triangles get discarded, so this *should* be enough space. - sizebuf = (disp_lists[hnd].count / 3) * (2 + fbStride * 4); - } - else - { - std::cerr << "Warning: unhandled primitive type in FFPrinter::preDraw()" << - std::endl; - return; - } - // allocate feedback buffer - vector xfb_buf; - xfb_buf.resize(sizebuf); - glFeedbackBuffer(sizebuf, fbType, xfb_buf.data()); - // draw with feedback capture - glRenderMode(GL_FEEDBACK); - drawDeviceBuffer(hnd); -#ifndef GLVIS_DEBUG - glRenderMode(GL_RENDER); -#else - if (glRenderMode(GL_RENDER) < 0) - { - std::cerr << "Warning: feedback data exceeded available buffer size" << - std::endl; - } -#endif - size_t tok_idx = 0; - // process feedback buffer - while (tok_idx < xfb_buf.size()) - { - switch ((GLuint)xfb_buf[tok_idx]) - { - case GL_LINE_TOKEN: - case GL_LINE_RESET_TOKEN: - { - tok_idx++; - glm::vec3 coord0 = glm::make_vec3(&xfb_buf[tok_idx]), - coord1 = glm::make_vec3(&xfb_buf[tok_idx + fbStride]); - glm::vec4 color0 = glm::make_vec4(&xfb_buf[tok_idx + 3]), - color1 = glm::make_vec4(&xfb_buf[tok_idx + 3 + fbStride]); - if (fbStride == 11) - { - // get texture - pal.GetColorFromVal(xfb_buf[tok_idx + 7], glm::value_ptr(color0)); - pal.GetColorFromVal(xfb_buf[tok_idx + 7 + fbStride], glm::value_ptr(color1)); - } - cbuf.lines.emplace_back(coord0, color0); - cbuf.lines.emplace_back(coord1, color1); - tok_idx += fbStride * 2; - } - break; - case GL_POLYGON_TOKEN: - { - int n = xfb_buf[tok_idx + 1]; - tok_idx += 2; - // get vertex 0, 1 - glm::vec3 coord0 = glm::make_vec3(&xfb_buf[tok_idx]), - coord1 = glm::make_vec3(&xfb_buf[tok_idx + fbStride]); - glm::vec4 color0 = glm::make_vec4(&xfb_buf[tok_idx + 3]), - color1 = glm::make_vec4(&xfb_buf[tok_idx + 3 + fbStride]); - if (fbStride == 11) - { - // get texture - pal.GetColorFromVal(xfb_buf[tok_idx + 7], glm::value_ptr(color0)); - pal.GetColorFromVal(xfb_buf[tok_idx + 7 + fbStride], glm::value_ptr(color1)); - } - // decompose polygon into n-2 triangles [0 1 2] [0 2 3] ... - for (int i = 0; i < n-2; i++) - { - // get last vertex of current triangle - int vtxStart = fbStride * (2 + 3*i); - glm::vec3 coord2 = glm::make_vec3(&xfb_buf[tok_idx + vtxStart]); - glm::vec4 color2 = glm::make_vec4(&xfb_buf[tok_idx + 3 + vtxStart]); - if (fbStride == 11) - { - pal.GetColorFromVal(xfb_buf[tok_idx + 7 + vtxStart], glm::value_ptr(color2)); - } - cbuf.triangles.emplace_back(coord0, color0); - cbuf.triangles.emplace_back(coord1, color1); - cbuf.triangles.emplace_back(coord2, color2); - // last vertex becomes second vertex of next triangle - coord1 = coord2; - color1 = color2; - } - tok_idx += n * fbStride; - } - break; - case GL_POINT_TOKEN: - case GL_BITMAP_TOKEN: - case GL_DRAW_PIXEL_TOKEN: - case GL_COPY_PIXEL_TOKEN: - default: - // commands containing the token + a single vertex ignore for now - tok_idx += 1 + fbStride; - break; - } - } -} - } diff --git a/lib/gl/renderer_ff.hpp b/lib/gl/renderer_ff.hpp index 565ba517..950963cd 100644 --- a/lib/gl/renderer_ff.hpp +++ b/lib/gl/renderer_ff.hpp @@ -26,7 +26,7 @@ class FFGLDevice : public GLDevice DispListHandle list; GLenum shape; size_t count; - array_layout layout; + ArrayLayout layout; }; std::vector disp_lists; @@ -52,8 +52,8 @@ class FFGLDevice : public GLDevice void setClipPlaneUse(bool enable) override; void setClipPlaneEqn(const std::array& eqn) override; - void bufferToDevice(array_layout layout, IVertexBuffer& buf) override; - void bufferToDevice(array_layout layout, IIndexedBuffer& buf) override; + void bufferToDevice(ArrayLayout layout, IVertexBuffer& buf) override; + void bufferToDevice(ArrayLayout layout, IIndexedBuffer& buf) override; void bufferToDevice(TextBuffer& t_buf) override; void drawDeviceBuffer(int hnd) override; void drawDeviceBuffer(const TextBuffer& t_buf) override; diff --git a/lib/gl/renderer_msaa.cpp b/lib/gl/renderer_msaa.cpp new file mode 100644 index 00000000..2ade0d5a --- /dev/null +++ b/lib/gl/renderer_msaa.cpp @@ -0,0 +1,152 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "renderer_msaa.hpp" + +namespace gl3 +{ + +void MultisamplePass::SetGLDevice(GLDevice* dev) +{ + IRenderPass::SetGLDevice(dev); +#ifdef __EMSCRIPTEN__ + const std::string versionString + = reinterpret_cast(glGetString(GL_VERSION)); + bool is_webgl2 = (versionString.find("OpenGL ES 3.0") != std::string::npos); + feat_use_fbo_antialias = is_webgl2; + if (feat_use_fbo_antialias) + { + glGetIntegerv(GL_MAX_SAMPLES, &max_msaa_samples); + } +#else + // TODO: we could also support ARB_framebuffer_object + feat_use_fbo_antialias = GLEW_VERSION_3_0; + glGetIntegerv(GL_MAX_SAMPLES, &max_msaa_samples); +#endif +} + +void MultisamplePass::CreateFramebuffer() +{ + GLuint colorBuf, depthBuf; + glGenRenderbuffers(1, &colorBuf); + glGenRenderbuffers(1, &depthBuf); + renderBufs[0] = RenderBufHandle(colorBuf); + renderBufs[1] = RenderBufHandle(depthBuf); + + int vp[4]; + device->getViewport(vp); + int width = vp[2]; + int height = vp[3]; + glBindRenderbuffer(GL_RENDERBUFFER, colorBuf); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_samples, + GL_RGBA8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, depthBuf); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, msaa_samples, + GL_DEPTH_COMPONENT24, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + msaaFb.Init(); + msaaFb.Attach(GL_COLOR_ATTACHMENT0, renderBufs[0]); + msaaFb.Attach(GL_DEPTH_ATTACHMENT, renderBufs[1]); + if (!msaaFb.IsComplete()) + { + cerr << "Unable to create multisampled renderbuffer." << flush; + // Reset to default framebuffer + msaaFb = Framebuffer{}; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +void MultisamplePass::PreRender() +{ + if (msaa_enable) + { + if (feat_use_fbo_antialias) + { + CreateFramebuffer(); + msaaFb.Bind(); + } + else + { + glEnable(GL_LINE_SMOOTH); + device->enableBlend(); + } +#ifndef __EMSCRIPTEN__ + glEnable(GL_MULTISAMPLE); +#endif + device->setLineWidth(line_w_aa); + } + else + { + device->setLineWidth(line_w); + } +} + +void MultisamplePass::PostRender() +{ + if (msaa_enable && feat_use_fbo_antialias && msaaFb) + { + int vp[4]; + device->getViewport(vp); + int width = vp[2]; + int height = vp[3]; + GLuint colorBufId; + glGenRenderbuffers(1, &colorBufId); + RenderBufHandle colorBuf(colorBufId); + + Framebuffer resolveFb; + resolveFb.Init(); + + glBindRenderbuffer(GL_RENDERBUFFER, colorBuf); + glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height); + glBindRenderbuffer(GL_RENDERBUFFER, 0); + + resolveFb.Attach(GL_COLOR_ATTACHMENT0, colorBuf); + // bind our draw framebuffer and blit the multisampled image + resolveFb.BlitFrom(msaaFb, width, height); +#ifndef __EMSCRIPTEN__ + glDisable(GL_MULTISAMPLE); +#endif + target->BlitFrom(resolveFb, width, height, GL_LINEAR); + } + else if (msaa_enable && !feat_use_fbo_antialias) + { + glDisable(GL_MULTISAMPLE); + glDisable(GL_LINE_SMOOTH); + device->disableBlend(); + } + device->setLineWidth(line_w); +} + +void MultisamplePass::SetAntialiasing(bool aa_status) +{ + msaa_enable = aa_status; +} + +void MultisamplePass::SetLineWidth(float w) +{ + line_w = w; + if (device && !msaa_enable) + { + device->setLineWidth(line_w); + } +} + +void MultisamplePass::SetLineWidthMS(float w) +{ + line_w_aa = w; + if (device && msaa_enable) + { + device->setLineWidth(line_w_aa); + } +} + +} diff --git a/lib/gl/renderer_msaa.hpp b/lib/gl/renderer_msaa.hpp new file mode 100644 index 00000000..7baef835 --- /dev/null +++ b/lib/gl/renderer_msaa.hpp @@ -0,0 +1,75 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_RENDERER_MSAA_HPP +#define GLVIS_RENDERER_MSAA_HPP + +#include "renderer.hpp" + +namespace gl3 +{ + +class MultisamplePass : public IRenderPass +{ +public: + MultisamplePass() { } + virtual void SetGLDevice(GLDevice* dev); + virtual void PreRender(); + virtual void PostRender(); + + virtual const Framebuffer& GetSourceFramebuffer() const + { + if (msaa_enable && msaaFb) + { + return msaaFb; + } + else + { + return IRenderPass::default_target; + } + } + + void SetAntialiasing(bool aa_status); + bool GetAntialiasing() { return msaa_enable; } + void SetNumSamples(int samples) + { + msaa_samples = samples; + if (msaa_samples > max_msaa_samples) + { + std::cerr << "GL_MAX_SAMPLES = " << max_msaa_samples + << " but requested " << msaa_samples << "x MSAA. "; + std::cerr << "Setting antialiasing mode to " + << max_msaa_samples << "x MSAA." << endl; + msaa_samples = max_msaa_samples; + } + } + int GetNumSamples() { return msaa_samples; } + + void SetLineWidth(float w); + float GetLineWidth() { return line_w; } + void SetLineWidthMS(float w); + float GetLineWidthMS() { return line_w_aa; } +private: + void CreateFramebuffer(); + + bool feat_use_fbo_antialias = false; + bool msaa_enable{false}; + int max_msaa_samples = 1; + int msaa_samples = 1; + float line_w, line_w_aa; + + RenderBufHandle renderBufs[2]; + Framebuffer msaaFb; +}; + +} + +#endif // GLVIS_RENDERER_MSAA_HPP diff --git a/lib/gl/renderer_print.cpp b/lib/gl/renderer_print.cpp new file mode 100644 index 00000000..60b95dd9 --- /dev/null +++ b/lib/gl/renderer_print.cpp @@ -0,0 +1,226 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#include "renderer_print.hpp" +#include "renderer_ff.hpp" + +namespace gl3 +{ + +// Defined in renderer.hpp +void GLDevice::captureXfbBuffer(CaptureBuffer& capture, const TextBuffer& t_buf) +{ + for (const auto& entry : t_buf) + { + glm::vec3 raster = glm::project( + glm::vec3(entry.rx, entry.ry, entry.rz), + model_view_mtx, + proj_mtx, + glm::vec4(0, 0, vp_width, vp_height)); + capture.text.emplace_back(raster, glm::make_vec4(static_color.data()), + entry.text); + } +} + +// Defined in renderer_ff.hpp +void FFGLDevice::captureXfbBuffer(PaletteState& pal, CaptureBuffer& cbuf, + int hnd) +{ + if (hnd == 0) { return; } + if (disp_lists[hnd].count == 0) { return; } + GLenum fbType; + int fbStride; + if (disp_lists[hnd].layout == VertexTex::layout + || disp_lists[hnd].layout == VertexNormTex::layout) + { + //capture texture values too + // [ X Y Z ] [ R G B A ] [ U V - - ] + fbType = GL_3D_COLOR_TEXTURE; + fbStride = 11; + } + else + { + // only capture pos and color + // [ X Y Z ] [ R G B A ] + fbType = GL_3D_COLOR; + fbStride = 7; + } + // compute feedback buffer size + int sizebuf = 0; + if (disp_lists[hnd].shape == GL_LINES) + { + // for each line: LINE_TOKEN [Vtx] [Vtx] + sizebuf = (disp_lists[hnd].count / 2) + disp_lists[hnd].count * fbStride; + } + else if (disp_lists[hnd].shape == GL_TRIANGLES) + { + // for each tri: POLY_TOKEN 3 [Vtx] [Vtx] [Vtx] + // NOTE: when clip plane is enabled, we might get two triangles + // or a quad for an input triangle. However, the other clipped + // triangles get discarded, so this *should* be enough space. + sizebuf = (disp_lists[hnd].count / 3) * (2 + fbStride * 4); + } + else + { + std::cerr << "Warning: unhandled primitive type in FFPrinter::preDraw()" << + std::endl; + return; + } + // allocate feedback buffer + vector xfb_buf; + xfb_buf.resize(sizebuf); + glFeedbackBuffer(sizebuf, fbType, xfb_buf.data()); + // draw with feedback capture + glRenderMode(GL_FEEDBACK); + drawDeviceBuffer(hnd); +#ifndef GLVIS_DEBUG + glRenderMode(GL_RENDER); +#else + if (glRenderMode(GL_RENDER) < 0) + { + std::cerr << "Warning: feedback data exceeded available buffer size" << + std::endl; + } +#endif + size_t tok_idx = 0; + // process feedback buffer + while (tok_idx < xfb_buf.size()) + { + switch ((GLuint)xfb_buf[tok_idx]) + { + case GL_LINE_TOKEN: + case GL_LINE_RESET_TOKEN: + { + tok_idx++; + glm::vec3 coord0 = glm::make_vec3(&xfb_buf[tok_idx]), + coord1 = glm::make_vec3(&xfb_buf[tok_idx + fbStride]); + glm::vec4 color0 = glm::make_vec4(&xfb_buf[tok_idx + 3]), + color1 = glm::make_vec4(&xfb_buf[tok_idx + 3 + fbStride]); + if (fbStride == 11) + { + // get texture + pal.GetColorFromVal(xfb_buf[tok_idx + 7], glm::value_ptr(color0)); + pal.GetColorFromVal(xfb_buf[tok_idx + 7 + fbStride], glm::value_ptr(color1)); + } + cbuf.lines.emplace_back(coord0, color0); + cbuf.lines.emplace_back(coord1, color1); + tok_idx += fbStride * 2; + } + break; + case GL_POLYGON_TOKEN: + { + int n = xfb_buf[tok_idx + 1]; + tok_idx += 2; + // get vertex 0, 1 + glm::vec3 coord0 = glm::make_vec3(&xfb_buf[tok_idx]), + coord1 = glm::make_vec3(&xfb_buf[tok_idx + fbStride]); + glm::vec4 color0 = glm::make_vec4(&xfb_buf[tok_idx + 3]), + color1 = glm::make_vec4(&xfb_buf[tok_idx + 3 + fbStride]); + if (fbStride == 11) + { + // get texture + pal.GetColorFromVal(xfb_buf[tok_idx + 7], glm::value_ptr(color0)); + pal.GetColorFromVal(xfb_buf[tok_idx + 7 + fbStride], glm::value_ptr(color1)); + } + // decompose polygon into n-2 triangles [0 1 2] [0 2 3] ... + for (int i = 0; i < n-2; i++) + { + // get last vertex of current triangle + int vtxStart = fbStride * (2 + 3*i); + glm::vec3 coord2 = glm::make_vec3(&xfb_buf[tok_idx + vtxStart]); + glm::vec4 color2 = glm::make_vec4(&xfb_buf[tok_idx + 3 + vtxStart]); + if (fbStride == 11) + { + pal.GetColorFromVal(xfb_buf[tok_idx + 7 + vtxStart], glm::value_ptr(color2)); + } + cbuf.triangles.emplace_back(coord0, color0); + cbuf.triangles.emplace_back(coord1, color1); + cbuf.triangles.emplace_back(coord2, color2); + // last vertex becomes second vertex of next triangle + coord1 = coord2; + color1 = color2; + } + tok_idx += n * fbStride; + } + break; + case GL_POINT_TOKEN: + case GL_BITMAP_TOKEN: + case GL_DRAW_PIXEL_TOKEN: + case GL_COPY_PIXEL_TOKEN: + default: + // commands containing the token + a single vertex ignore for now + tok_idx += 1 + fbStride; + break; + } + } +} + +void CapturePass::Render(const RenderQueue& queue) +{ + int color_tex = palette->GetColorTexture(); + int alpha_tex = palette->GetAlphaTexture(); + bool always_blend = device->isBlendEnabled(); + for (auto& q_elem : queue) + { + const RenderParams& params = q_elem.first; + device->setTransformMatrices(params.model_view.mtx, params.projection.mtx); + device->setMaterial(params.mesh_material); + device->setNumLights(params.num_pt_lights); + for (int i = 0; i < params.num_pt_lights; i++) + { + device->setPointLight(i, params.lights[i]); + } + device->setAmbientLight(params.light_amb_scene); + device->setStaticColor(params.static_color); + device->setClipPlaneUse(params.use_clip_plane); + device->setClipPlaneEqn(params.clip_plane_eqn); + // aggregate buffers with common parameters + std::vector tex_bufs, no_tex_bufs; + GlDrawable* curr_drawable = q_elem.second; + auto buffers = curr_drawable->getArrayBuffers(); + for (const IVertexBuffer* buf : buffers) + { + if (buf->getVertexLayout() == LAYOUT_VTX_TEXTURE0 + || buf->getVertexLayout() == LAYOUT_VTX_NORMAL_TEXTURE0) + { + tex_bufs.emplace_back(buf->getHandle()); + } + else + { + no_tex_bufs.emplace_back(buf->getHandle()); + } + } + + device->attachTexture(GLDevice::SAMPLER_COLOR, color_tex); + device->attachTexture(GLDevice::SAMPLER_ALPHA, alpha_tex); + for (auto buf : tex_bufs) + { + device->captureXfbBuffer(*palette, cbuf, buf); + } + device->detachTexture(GLDevice::SAMPLER_COLOR); + device->detachTexture(GLDevice::SAMPLER_ALPHA); + for (auto buf : no_tex_bufs) + { + device->captureXfbBuffer(*palette, cbuf, buf); + } + if (!params.contains_translucent) + { + device->enableBlend(); + device->disableDepthWrite(); + } + device->attachTexture(1, font_tex); + device->setNumLights(0); + device->captureXfbBuffer(cbuf, curr_drawable->getTextBuffer()); + if (!always_blend) { device->disableBlend(); } + } +} + +} diff --git a/lib/gl/renderer_print.hpp b/lib/gl/renderer_print.hpp new file mode 100644 index 00000000..d0b3b411 --- /dev/null +++ b/lib/gl/renderer_print.hpp @@ -0,0 +1,73 @@ +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +#ifndef GLVIS_RENDERER_PRINT_HPP +#define GLVIS_RENDERER_PRINT_HPP + +#include "renderer.hpp" + +namespace gl3 +{ + +struct FeedbackVertex +{ + glm::vec3 position; + glm::vec4 color; + + FeedbackVertex() = default; + FeedbackVertex(glm::vec3 pos, glm::vec4 c) + : position(pos), color(c) { } + FeedbackVertex(glm::vec4 pos, glm::vec4 c) + : position(pos), color(c) { } +}; + +struct FeedbackText +{ + glm::vec3 offset; + glm::vec4 color; + std::string text; + + FeedbackText() = default; + FeedbackText(glm::vec3 t_off, glm::vec4 t_color, std::string txt) + : offset(t_off), color(t_color), text(std::move(txt)) { } +}; + +struct CaptureBuffer +{ + vector lines; + vector triangles; + vector text; +}; + +class CapturePass : public IMainRenderPass +{ +public: + CapturePass() { } + virtual bool Filter(const RenderParams& param) { return true; } + + virtual void PreRender() { device->initXfbMode(); } + virtual void Render(const RenderQueue& queued); + virtual void PostRender() { device->initRenderMode(); } + + CaptureBuffer GetLastCaptureBuffer() + { + CaptureBuffer b_mov = std::move(cbuf); + cbuf = {}; + return b_mov; + } + +private: + CaptureBuffer cbuf; +}; + +} + +#endif // GLVIS_RENDERER_PRINT_HPP diff --git a/lib/gl/shader.cpp b/lib/gl/shader.cpp index c5d54c00..daafa134 100644 --- a/lib/gl/shader.cpp +++ b/lib/gl/shader.cpp @@ -139,8 +139,6 @@ std::string ShaderProgram::formatShader(const std::string& inShader, formatted = std::regex_replace(formatted, std::regex("varying"), "in"); for (int i = 0; i < num_outputs; i++) { - std::string indexString = "gl_FragData["; - indexString += std::to_string(i) + "]"; std::string outputString = "out vec4 fragColor_"; outputString += std::to_string(i) + ";\n"; if (glsl_version >= 300) @@ -178,7 +176,7 @@ std::string ShaderProgram::formatShader(const std::string& inShader, formatted = std::regex_replace(formatted, std::regex("texture2D"), "texture"); } - if (GLDevice::useLegacyTextureFmts()) + if (!GLDevice::isOpenGL3()) { formatted = "#define USE_ALPHA\n" + formatted; } @@ -226,7 +224,7 @@ GLuint ShaderProgram::compileShader(const std::string& inShader, // glGetObjectParameteriv if (stat == GL_FALSE) { - std::cerr << "failed to compile shader" << std::endl; + std::cerr << "Failed to compile shader" << std::endl; int err_len; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &err_len); char *error_text = new char[err_len]; @@ -240,6 +238,26 @@ GLuint ShaderProgram::compileShader(const std::string& inShader, bool ShaderProgram::linkShaders(const std::vector& shaders) { + program_id = glCreateProgram(); + if (program_id == 0) + { + std::cerr << "Failed to create an OpenGL program object." << std::endl; + } + // Set transform feedback varyings, if any + if (!xfrm_varyings.empty()) + { + std::vector varyings_c_str; + for (const std::string& var : xfrm_varyings) + { + varyings_c_str.push_back(var.c_str()); + } +#ifndef __EMSCRIPTEN__ + glTransformFeedbackVaryings(program_id, + xfrm_varyings.size(), + varyings_c_str.data(), + GL_INTERLEAVED_ATTRIBS); +#endif + } // Bind all incoming attributes to their VAO indices. for (auto attrib_pair : attrib_idx) { diff --git a/lib/gl/shader.hpp b/lib/gl/shader.hpp index 780bf600..1c35b4c0 100644 --- a/lib/gl/shader.hpp +++ b/lib/gl/shader.hpp @@ -23,11 +23,6 @@ class ShaderProgram public: ShaderProgram() { - program_id = glCreateProgram(); - if (program_id == 0) - { - std::cerr << "Failed to create an OpenGL program object." << std::endl; - } } bool create(std::string vertexShader, @@ -37,6 +32,11 @@ class ShaderProgram bool isCompiled() const { return is_compiled; } + void setFeedbackVaryings(const std::vector& varyings) + { + xfrm_varyings = varyings; + } + int uniform(std::string uniformName) const { auto unifId = uniform_idx.find(uniformName); @@ -56,6 +56,11 @@ class ShaderProgram void bind() const { glUseProgram(program_id); } + bool operator== (const ShaderProgram& other) const + { + return program_id == other.program_id; + } + private: static void GetGLSLVersion(); @@ -74,6 +79,7 @@ class ShaderProgram ShaderHandle fragment_shader = 0; bool is_compiled = false; std::unordered_map uniform_idx; + std::vector xfrm_varyings; }; } diff --git a/lib/gl/shaders/depth_peel.frag b/lib/gl/shaders/depth_peel.frag new file mode 100644 index 00000000..d26a377e --- /dev/null +++ b/lib/gl/shaders/depth_peel.frag @@ -0,0 +1,96 @@ +R"( +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +uniform sampler2D alphaTex; +uniform sampler2D colorTex; +uniform sampler2D lastDepthTex; +uniform sampler2D lastFrontColorTex; + +varying vec3 fNormal; +varying vec3 fPosition; +varying vec4 fColor; +varying vec2 fTexCoord; + +uniform bool useClipPlane; +varying float fClipVal; + +uniform ivec2 screenCoords; +vec4 getScreenTexel(sampler2D tex, vec2 coords) +{ +#if __VERSION__ < 140 + vec2 normalizedCoords = coords / vec2(screenCoords); + return texture2D(tex, normalizedCoords); +#else + return texelFetch(tex, ivec2(coords), 0); +#endif +} + +#define MAX_DEPTH 1.0 + +// location 0: depth +// location 1: front color +// location 2: back color + +// adapted from "Order Independent Transparency with Dual Depth Peeling": +// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.193.3485&rep=rep1&type=pdf +// and https://github.com/tsherif/webgl2examples/blob/master/oit-dual-depth-peeling.html + +void main() +{ + vec2 lastDepths = getScreenTexel(lastDepthTex, gl_FragCoord.xy).xy; + vec4 lastFrontColor = getScreenTexel(lastFrontColorTex, gl_FragCoord.xy); + float nearestDepth = -lastDepths.x; + float farthestDepth = lastDepths.y; + float thisDepth = gl_FragCoord.z; + float alphaMultiplier = 1.0 - lastFrontColor.a; + + gl_FragData[1] = lastFrontColor; + gl_FragData[2] = vec4(0.0); + + if (thisDepth < nearestDepth || thisDepth > farthestDepth) + { + gl_FragData[0].xy = vec2(-MAX_DEPTH); + return; + } + + if (useClipPlane && fClipVal < 0.0) + { + gl_FragData[0].xy = vec2(-MAX_DEPTH); + return; + } + + if (thisDepth > nearestDepth && thisDepth < farthestDepth) + { + gl_FragData[0].xy = vec2(-thisDepth, thisDepth); + return; + } + + gl_FragData[0].xy = vec2(-MAX_DEPTH); + vec4 color = fColor * texture2D(colorTex, vec2(fTexCoord)); + color = blinnPhong(fPosition, fNormal, color); +#ifdef USE_ALPHA + color.a *= texture2D(alphaTex, vec2(fTexCoord)).a; +#else + color.a *= texture2D(alphaTex, vec2(fTexCoord)).r; +#endif + + if (thisDepth == nearestDepth) + { + gl_FragData[1].rgb += color.rgb * color.a * alphaMultiplier; + gl_FragData[1].a = 1.0 - alphaMultiplier * (1.0 - color.a); + } + else + { + gl_FragData[2] += color; + } +} +)" diff --git a/lib/gl/shaders/depth_peel_blend_back.frag b/lib/gl/shaders/depth_peel_blend_back.frag new file mode 100644 index 00000000..00e53857 --- /dev/null +++ b/lib/gl/shaders/depth_peel_blend_back.frag @@ -0,0 +1,38 @@ +R"( +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +uniform sampler2D lastBackColor; + +uniform ivec2 screenCoords; +vec4 getScreenTexel(sampler2D tex, vec2 coords) +{ +#if __VERSION__ < 140 + vec2 normalizedCoords = coords / vec2(screenCoords); + return texture2D(tex, normalizedCoords); +#else + return texelFetch(tex, ivec2(coords), 0); +#endif +} + +// adapted from "Order Independent Transparency with Dual Depth Peeling": +// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.193.3485&rep=rep1&type=pdf +// and https://github.com/tsherif/webgl2examples/blob/master/oit-dual-depth-peeling.html + +void main() +{ + gl_FragColor = getScreenTexel(lastBackColor, gl_FragCoord.xy); + if (gl_FragColor.a == 0.0) + { + discard; + } +} +)" diff --git a/lib/gl/shaders/depth_peel_finalize.frag b/lib/gl/shaders/depth_peel_finalize.frag new file mode 100644 index 00000000..bf20f5b5 --- /dev/null +++ b/lib/gl/shaders/depth_peel_finalize.frag @@ -0,0 +1,40 @@ +R"( +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +uniform sampler2D lastFrontColor; +uniform sampler2D lastBackColor; + +uniform ivec2 screenCoords; +vec4 getScreenTexel(sampler2D tex, vec2 coords) +{ +#if __VERSION__ < 140 + vec2 normalizedCoords = coords / vec2(screenCoords); + return texture2D(tex, normalizedCoords); +#else + return texelFetch(tex, ivec2(coords), 0); +#endif +} + +// adapted from "Order Independent Transparency with Dual Depth Peeling": +// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.193.3485&rep=rep1&type=pdf +// and https://github.com/tsherif/webgl2examples/blob/master/oit-dual-depth-peeling.html + +void main() +{ + vec4 frontColor = getScreenTexel(lastFrontColor, gl_FragCoord.xy); + vec4 backColor = getScreenTexel(lastBackColor, gl_FragCoord.xy); + float alphaMultiplier = 1.0 - frontColor.a; + + gl_FragColor.rgb = frontColor.rgb + alphaMultiplier * backColor.rgb; + gl_FragColor.a = frontColor.a + backColor.a; +} +)" diff --git a/lib/gl/shaders/depth_peel_passthrough.vert b/lib/gl/shaders/depth_peel_passthrough.vert new file mode 100644 index 00000000..1eb64e4a --- /dev/null +++ b/lib/gl/shaders/depth_peel_passthrough.vert @@ -0,0 +1,19 @@ +R"( +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +attribute vec4 vertex; + +void main() +{ + gl_Position = vertex; +} +)" diff --git a/lib/gl/shaders/fb_blit.frag b/lib/gl/shaders/fb_blit.frag new file mode 100644 index 00000000..310f1210 --- /dev/null +++ b/lib/gl/shaders/fb_blit.frag @@ -0,0 +1,21 @@ +R"( +// Copyright (c) 2010-2021, Lawrence Livermore National Security, LLC. Produced +// at the Lawrence Livermore National Laboratory. All Rights reserved. See files +// LICENSE and NOTICE for details. LLNL-CODE-443271. +// +// This file is part of the GLVis visualization tool and library. For more +// information and source code availability see https://glvis.org. +// +// GLVis is free software; you can redistribute it and/or modify it under the +// terms of the BSD-3 license. We welcome feedback and contributions, see file +// CONTRIBUTING.md for details. + +uniform sampler2D sourceColor; + +uniform ivec2 texDims; + +void main() +{ + gl_FragColor = texture2D(sourceColor, gl_FragCoord.xy / vec2(texDims)); +} +)" diff --git a/lib/gl/shaders/lighting.glsl b/lib/gl/shaders/lighting.glsl index b957ad63..c92697a4 100644 --- a/lib/gl/shaders/lighting.glsl +++ b/lib/gl/shaders/lighting.glsl @@ -67,6 +67,6 @@ vec4 blinnPhong(in vec3 pos, in vec3 norm, in vec4 color) float specular_factor = max(dot(half_v, norm), 0.0); lit_color += lights[i].specular * material.specular * pow(specular_factor, material.shininess); } - return lit_color; + return vec4(lit_color.rgb, color.a); } )" diff --git a/lib/gl/types.hpp b/lib/gl/types.hpp index 506d9e20..32f74343 100644 --- a/lib/gl/types.hpp +++ b/lib/gl/types.hpp @@ -53,11 +53,7 @@ class Handle : hnd{other.hnd} { other.hnd = 0; } Handle& operator = (Handle&& other) noexcept { - if (this != &other) - { - hnd = other.hnd; - other.hnd = 0; - } + std::swap(hnd, other.hnd); return *this; } operator GLuint() const { return hnd; } @@ -165,7 +161,7 @@ struct GlMatrix } }; -enum array_layout +enum ArrayLayout { LAYOUT_VTX = 0, LAYOUT_VTX_NORMAL, @@ -206,7 +202,8 @@ struct alignas(16) Vertex { return Vertex {(float) d[0], (float) d[1], (float) d[2]}; } -static const int layout = LAYOUT_VTX; + +static const ArrayLayout layout = LAYOUT_VTX; }; struct alignas(16) VertexColor @@ -214,7 +211,7 @@ struct alignas(16) VertexColor std::array coord; std::array color; - static const int layout = LAYOUT_VTX_COLOR; + static const ArrayLayout layout = LAYOUT_VTX_COLOR; }; struct alignas(16) VertexTex @@ -222,7 +219,7 @@ struct alignas(16) VertexTex std::array coord; std::array texCoord; - static const int layout = LAYOUT_VTX_TEXTURE0; + static const ArrayLayout layout = LAYOUT_VTX_TEXTURE0; }; struct alignas(16) VertexNorm @@ -230,7 +227,7 @@ struct alignas(16) VertexNorm std::array coord; std::array norm; - static const int layout = LAYOUT_VTX_NORMAL; + static const ArrayLayout layout = LAYOUT_VTX_NORMAL; }; struct alignas(16) VertexNormColor @@ -239,7 +236,7 @@ struct alignas(16) VertexNormColor std::array norm; std::array color; - static const int layout = LAYOUT_VTX_NORMAL_COLOR; + static const ArrayLayout layout = LAYOUT_VTX_NORMAL_COLOR; }; struct alignas(16) VertexNormTex @@ -248,7 +245,7 @@ struct alignas(16) VertexNormTex std::array norm; std::array texCoord; - static const int layout = LAYOUT_VTX_NORMAL_TEXTURE0; + static const ArrayLayout layout = LAYOUT_VTX_NORMAL_TEXTURE0; }; @@ -449,6 +446,8 @@ class IVertexBuffer virtual void clear() = 0; /// Gets the number of vertices contained in the buffer. virtual size_t count() const = 0; + /// Gets the vertex format held by the buffer. + virtual ArrayLayout getVertexLayout() const = 0; /// Gets the primitive type contained by the vertex buffer. virtual GLenum getShape() const = 0; /// Gets the stride between vertices. @@ -473,6 +472,7 @@ class VertexBuffer : public IVertexBuffer virtual void clear() { vertex_data.clear(); } virtual size_t count() const { return vertex_data.size(); } + virtual ArrayLayout getVertexLayout() const { return T::layout; } virtual GLenum getShape() const { return primitive; } virtual size_t getStride() const { return sizeof(T); } @@ -520,6 +520,7 @@ class IndexedVertexBuffer : public IIndexedBuffer virtual size_t count() const { return vertex_data.size(); } + virtual ArrayLayout getVertexLayout() const { return T::layout; } virtual GLenum getShape() const { return primitive; } virtual size_t getStride() const { return sizeof(T); } @@ -543,7 +544,7 @@ class IndexedVertexBuffer : public IIndexedBuffer } }; -class TextBuffer : public IVertexBuffer +class TextBuffer { public: struct Entry @@ -560,11 +561,15 @@ class TextBuffer : public IVertexBuffer private: std::vector vertex_data; size_t num_chars; + int handle = 0; public: TextBuffer() : num_chars(0) { } ~TextBuffer() { } + int getHandle() const { return handle; } + void setHandle(int dev_hnd) { handle = dev_hnd; } + /// Adds a text element at the specified local space (pre-transform) /// coordinates. void addText(float x, float y, float z, int ox, int oy, @@ -651,6 +656,29 @@ class GlDrawable } public: + std::vector getArrayBuffers() const + { + std::vector out_buffers; + for (int i = 0; i < NUM_LAYOUTS; i++) + { + for (size_t j = 0; j < NUM_SHAPES; j++) + { + if (buffers[i][j]) + { + out_buffers.emplace_back(buffers[i][j].get()); + } + if (indexed_buffers[i][j]) + { + out_buffers.emplace_back(indexed_buffers[i][j].get()); + } + } + } + return out_buffers; + } + + TextBuffer& getTextBuffer() { return text_buffer; } + const TextBuffer& getTextBuffer() const { return text_buffer; } + /// Adds a string at the given position in object coordinates. void addText(float x, float y, float z, const std::string& text) { diff --git a/lib/palettes.cpp b/lib/palettes.cpp index b9ff8e80..566ac843 100644 --- a/lib/palettes.cpp +++ b/lib/palettes.cpp @@ -7718,19 +7718,19 @@ void PaletteState::Init() alpha_tex = alphaTexId; GLenum alpha_internal; - if (gl3::GLDevice::useLegacyTextureFmts()) - { - alpha_internal = GL_ALPHA; - alpha_channel = GL_ALPHA; - rgba_internal = GL_RGBA; - } - else + if (gl3::GLDevice::isOpenGL3()) { // WebGL 2 requires sized internal format for float texture alpha_internal = GL_R32F; alpha_channel = GL_RED; rgba_internal = GL_RGBA32F; } + else + { + alpha_internal = GL_ALPHA; + alpha_channel = GL_ALPHA; + rgba_internal = GL_RGBA; + } // set alpha texture to 1.0 std::vector alphaTexData(MaxTextureSize * 2); std::fill(alphaTexData.begin(), alphaTexData.end(), 1.0f); diff --git a/lib/sdl.cpp b/lib/sdl.cpp index 63b31533..e590e2b1 100644 --- a/lib/sdl.cpp +++ b/lib/sdl.cpp @@ -304,7 +304,6 @@ bool SdlWindow::createWindow(const char * title, int x, int y, int w, int h, << "." << (int)sdl_ver.patch << std::endl); renderer.reset(new gl3::MeshRenderer); - renderer->setSamplesMSAA(GetMultisample()); #ifndef __EMSCRIPTEN__ if (!GLEW_VERSION_1_1) { diff --git a/lib/vsdata.cpp b/lib/vsdata.cpp index aa5c5fc9..d1680d7f 100644 --- a/lib/vsdata.cpp +++ b/lib/vsdata.cpp @@ -740,18 +740,6 @@ void KeyKPressed() SendExposeEvent(); } -void KeyAPressed() -{ - bool curr_aa = GetAppWindow()->getRenderer().getAntialiasing(); - GetAppWindow()->getRenderer().setAntialiasing(!curr_aa); - - cout << "Multisampling/Antialiasing: " - << strings_off_on[!curr_aa ? 1 : 0] << endl; - - // vsdata -> EventUpdateColors(); - SendExposeEvent(); -} - void KeyCommaPressed() { locscene->matAlphaCenter -= 0.25; @@ -1111,7 +1099,6 @@ void VisualizationSceneScalarData::Init() // wnd->setOnKeyDown('a', KeyaPressed); wnd->setOnKeyDown('a', Key_Mod_a_Pressed); - wnd->setOnKeyDown('A', KeyAPressed); wnd->setOnKeyDown('r', KeyrPressed); wnd->setOnKeyDown('R', KeyRPressed); diff --git a/makefile b/makefile index 3c1a2a1a..50261110 100644 --- a/makefile +++ b/makefile @@ -212,7 +212,8 @@ Ccc = $(strip $(CC) $(CFLAGS) $(GL_OPTS)) # generated with 'echo lib/gl/*.c* lib/*.c*', does not include lib/*.m (Obj-C) ALL_SOURCE_FILES = \ - lib/gl/renderer.cpp lib/gl/renderer_core.cpp lib/gl/renderer_ff.cpp \ + lib/gl/renderer.cpp lib/gl/renderer_core.cpp lib/gl/framebuffer.cpp lib/gl/renderer_ff.cpp \ + lib/gl/depth_peel_oit.cpp lib/gl/renderer_msaa.cpp lib/gl/renderer_print.cpp \ lib/gl/shader.cpp lib/gl/types.cpp lib/aux_js.cpp lib/aux_vis.cpp lib/font.cpp \ lib/gl2ps.c lib/material.cpp lib/openglvis.cpp lib/palettes.cpp lib/sdl.cpp \ lib/stream_reader.cpp lib/threads.cpp lib/vsdata.cpp lib/vssolution.cpp \ @@ -227,8 +228,9 @@ COMMON_SOURCE_FILES = $(filter-out \ # generated with 'echo lib/gl/*.h* lib/*.h*' HEADER_FILES = \ - lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp lib/gl/renderer.hpp \ + lib/gl/attr_traits.hpp lib/gl/platform_gl.hpp lib/gl/framebuffer.hpp lib/gl/renderer.hpp \ lib/gl/shader.hpp lib/gl/renderer_core.hpp lib/gl/renderer_ff.hpp \ + lib/gl/depth_peel_oit.hpp lib/gl/renderer_msaa.hpp lib/gl/renderer_print.hpp \ lib/gl/types.hpp lib/aux_vis.hpp lib/font.hpp lib/geom_utils.hpp lib/gl2ps.h \ lib/logo.hpp lib/material.hpp lib/openglvis.hpp lib/palettes.hpp lib/sdl.hpp \ lib/sdl_helper.hpp lib/sdl_mac.hpp lib/sdl_x11.hpp lib/stream_reader.hpp \