diff options
| author | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-02-26 18:04:06 +0000 |
|---|---|---|
| committer | Dan Goodliffe <dan@randomdan.homeip.net> | 2026-02-28 13:41:37 +0000 |
| commit | 1e08193a1678675ac5869b76e8a2339f6de5b773 (patch) | |
| tree | c9a47be9f06f69a06be2f43d371a88e1c507669e | |
| parent | Tidy the preFrame logic into a single function in SceneRenderer (diff) | |
| download | ilt-1e08193a1678675ac5869b76e8a2339f6de5b773.tar.bz2 ilt-1e08193a1678675ac5869b76e8a2339f6de5b773.tar.xz ilt-1e08193a1678675ac5869b76e8a2339f6de5b773.zip | |
Support for the BillboardPainter
Based on the ShadowStenciller, creates flattened images of a model for
simplified mass use in a scene.
| -rw-r--r-- | gfx/frustum.h | 6 | ||||
| -rw-r--r-- | gfx/gl/billboardPainter.cpp | 92 | ||||
| -rw-r--r-- | gfx/gl/billboardPainter.h | 28 | ||||
| -rw-r--r-- | gfx/gl/sceneRenderer.cpp | 2 | ||||
| -rw-r--r-- | gfx/gl/sceneRenderer.h | 2 | ||||
| -rw-r--r-- | gfx/gl/shaders/billboardPainter.frag | 22 | ||||
| -rw-r--r-- | gfx/gl/shaders/billboardPainter.geom | 34 | ||||
| -rw-r--r-- | gfx/gl/shaders/billboardPainter.vert | 23 | ||||
| -rw-r--r-- | gfx/renderable.cpp | 5 | ||||
| -rw-r--r-- | gfx/renderable.h | 2 |
10 files changed, 216 insertions, 0 deletions
diff --git a/gfx/frustum.h b/gfx/frustum.h index cd4cbe1..5b7947b 100644 --- a/gfx/frustum.h +++ b/gfx/frustum.h @@ -17,6 +17,12 @@ public: } [[nodiscard]] auto & + getView() const + { + return view; + } + + [[nodiscard]] auto & getViewProjection() const { return viewProjection; diff --git a/gfx/gl/billboardPainter.cpp b/gfx/gl/billboardPainter.cpp new file mode 100644 index 0000000..e72e72d --- /dev/null +++ b/gfx/gl/billboardPainter.cpp @@ -0,0 +1,92 @@ +#include "billboardPainter.h" +#include "gfx/models/mesh.h" +#include "glArrays.h" +#include "gl_traits.h" +#include "gldebug.h" +#include "maths.h" +#include <gfx/gl/shaders/billboardPainter-frag.h> +#include <gfx/gl/shaders/billboardPainter-geom.h> +#include <gfx/gl/shaders/billboardPainter-vert.h> +#include <stdexcept> + +const auto VIEWS = []<GLint... Ep>(std::integer_sequence<GLint, Ep...>) { + constexpr float STEP = two_pi / BillboardPainter::VIEW_ANGLES<decltype(two_pi)>; + return std::array {rotate_yaw<4>(Ep * STEP)...}; +}(std::make_integer_sequence<GLint, BillboardPainter::VIEW_ANGLES<GLint>>()); + +BillboardPainter::BillboardPainter() : + program {billboardPainter_vert, billboardPainter_geom, billboardPainter_frag}, view {} +{ + glDebugScope _ {fbo}; + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + static constexpr std::array<GLenum, 2> ATTACHMENTS {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1}; + glDrawBuffers(ATTACHMENTS.size(), ATTACHMENTS.data()); + glReadBuffer(GL_NONE); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glUseProgram(program); + glUniform(viewLoc, std::span<const glm::mat4> {VIEWS}); +} + +void +BillboardPainter::setView(const glm::mat4 & newView) +{ + view = newView; +} + +glTextures<3> +BillboardPainter::createBillBoardTextures(GLsizei width, GLsizei height) +{ + glDebugScope _ {0}; + glTextures<3> textures; + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + + const auto configuregdata = [width, height](const GLuint texture, const GLint iformat, const GLenum format) { + glBindTexture(GL_TEXTURE_2D_ARRAY, texture); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameter(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, iformat, width, height, VIEW_ANGLES<GLint>, 0, format, GL_BYTE, nullptr); + }; + configuregdata(textures[0], GL_DEPTH_COMPONENT, GL_DEPTH_COMPONENT); + configuregdata(textures[1], GL_RGB8_SNORM, GL_RGB); + configuregdata(textures[2], GL_RGB5_A1, GL_RGBA); + + return textures; +} + +void +BillboardPainter::renderBillBoard( + const glTextures<3> & billboard, const MeshBase & mesh, const Texture::AnyPtr texture) const +{ + glDebugScope _ {fbo}; + glEnable(GL_BLEND); + glEnable(GL_DEPTH_TEST); + glDisable(GL_CULL_FACE); + glCullFace(GL_BACK); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glClearColor(0, 0, 0, 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, billboard[0], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, billboard[1], 0); + glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, billboard[2], 0); + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + throw std::runtime_error("Billboard framebuffer not complete!"); + } + if (texture) { + texture->bind(); + } + glUseProgram(program); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + const TextureDimensions billboardSize = {256, 256, 8}; // Texture::getSize(billboard[0]); + glViewport(0, 0, billboardSize.x, billboardSize.y); + const auto & centre = mesh.getDimensions().centre; + const auto & size = mesh.getDimensions().size; + glUniform(viewProjectionLoc, + std::span<const glm::mat4> {VIEWS * + [extentsMat = glm::translate(glm::ortho(-size, size, -size, size, -size, size), -centre), this]( + const auto & view) { + return this->view * view * extentsMat; + }}); + mesh.Draw(); +} diff --git a/gfx/gl/billboardPainter.h b/gfx/gl/billboardPainter.h new file mode 100644 index 0000000..4ce7b10 --- /dev/null +++ b/gfx/gl/billboardPainter.h @@ -0,0 +1,28 @@ +#pragma once + +#include "gfx/gl/program.h" +#include "gfx/models/mesh.h" +#include "gfx/models/texture.h" +#include "glArrays.h" + +class LightDirection; + +class BillboardPainter { +public: + template<typename T> static constexpr T VIEW_ANGLES = 8; + + BillboardPainter(); + + [[nodiscard]] + static glTextures<3> createBillBoardTextures(GLsizei width, GLsizei height); + void setView(const glm::mat4 &); + void renderBillBoard(const glTextures<3> &, const MeshBase &, Texture::AnyPtr texture) const; + +private: + glFrameBuffer fbo; + Program program; + Program::RequiredUniformLocation viewProjectionLoc {program, "viewProjection"}; + Program::RequiredUniformLocation viewLoc {program, "view"}; + + glm::mat4 view; +}; diff --git a/gfx/gl/sceneRenderer.cpp b/gfx/gl/sceneRenderer.cpp index c1ec987..463ee8a 100644 --- a/gfx/gl/sceneRenderer.cpp +++ b/gfx/gl/sceneRenderer.cpp @@ -83,8 +83,10 @@ SceneRenderer::preFrame(const SceneProvider & scene, const LightDirection lightD { glDebugScope _ {output}; const auto lightView = shadowMapper.preFrame(lightDirection, camera); + billboardPainter.setView(camera.getView()); scene.forEachRenderable([&lightView, this](Renderable * renderable) { renderable->preFrame(camera, lightView); + renderable->updateBillboard(billboardPainter); }); } diff --git a/gfx/gl/sceneRenderer.h b/gfx/gl/sceneRenderer.h index d6f5dd1..a7f7770 100644 --- a/gfx/gl/sceneRenderer.h +++ b/gfx/gl/sceneRenderer.h @@ -1,5 +1,6 @@ #pragma once +#include "billboardPainter.h" #include "gfx/lightDirection.h" #include "glArrays.h" #include "gldebug.h" @@ -62,4 +63,5 @@ protected: glBuffer displayVBO; SceneShader shader; ShadowMapper shadowMapper; + BillboardPainter billboardPainter; }; diff --git a/gfx/gl/shaders/billboardPainter.frag b/gfx/gl/shaders/billboardPainter.frag new file mode 100644 index 0000000..354f2ab --- /dev/null +++ b/gfx/gl/shaders/billboardPainter.frag @@ -0,0 +1,22 @@ +#version 460 core + +layout(binding = 0) uniform sampler2D textureAlbedo; +layout(location = 0) out vec4 bNormal; +layout(location = 1) out vec4 bAlbedoSpec; + +include(`materialDetail.glsl') +include(`materialCommon.glsl') +in vec2 gTexCoords; +in vec3 gNormal; +in vec4 gColour; +flat in MaterialDetail gMaterial; + +void +main() +{ + vec4 textureColour = getTextureColour(gMaterial, gTexCoords); + float opaque = step(0.5, mix(textureColour.a, 1, gColour.a)); + bNormal = vec4(gNormal, opaque); + gl_FragDepth = mix(1.0, gl_FragCoord.z, opaque); + bAlbedoSpec = mix(textureColour, vec4(gColour.rgb, 1), gColour.a); +} diff --git a/gfx/gl/shaders/billboardPainter.geom b/gfx/gl/shaders/billboardPainter.geom new file mode 100644 index 0000000..abd5f83 --- /dev/null +++ b/gfx/gl/shaders/billboardPainter.geom @@ -0,0 +1,34 @@ +#version 460 core + +include(`materialDetail.glsl') + +layout(triangles) in; +layout(triangle_strip, max_vertices = 24) out; + +uniform mat4 viewProjection[8]; +uniform mat4 view[8]; +in vec3 FragPos[]; +in vec2 TexCoords[]; +flat in MaterialDetail Material[]; +in vec3 Normal[]; +in vec4 Colour[]; +out vec2 gTexCoords; +out vec3 gNormal; +out vec4 gColour; +flat out MaterialDetail gMaterial; + +void +main() +{ + for (gl_Layer = 0; gl_Layer < viewProjection.length(); ++gl_Layer) { + for (int v = 0; v < FragPos.length(); ++v) { + gl_Position = viewProjection[gl_Layer] * vec4(FragPos[v], 1); + gNormal = (view[gl_Layer] * vec4(Normal[v], 1)).xyz; + gTexCoords = TexCoords[v]; + gMaterial = Material[v]; + gColour = Colour[v]; + EmitVertex(); + } + EndPrimitive(); + } +} diff --git a/gfx/gl/shaders/billboardPainter.vert b/gfx/gl/shaders/billboardPainter.vert new file mode 100644 index 0000000..d7c1219 --- /dev/null +++ b/gfx/gl/shaders/billboardPainter.vert @@ -0,0 +1,23 @@ +#version 460 core + +layout(binding = 1) uniform usampler2DRect materialData; + +include(`meshIn.glsl') +include(`materialDetail.glsl') +include(`getMaterialDetail.glsl') + +out vec3 FragPos; +out vec2 TexCoords; +flat out MaterialDetail Material; +out vec3 Normal; +out vec4 Colour; + +void +main() +{ + TexCoords = texCoord; + Material = getMaterialDetail(material); + FragPos = position; + Colour = colour; + Normal = normal; +} diff --git a/gfx/renderable.cpp b/gfx/renderable.cpp index 9fbc85b..8caab93 100644 --- a/gfx/renderable.cpp +++ b/gfx/renderable.cpp @@ -19,3 +19,8 @@ void Renderable::updateStencil(const ShadowStenciller &) const { } + +void +Renderable::updateBillboard(const BillboardPainter &) const +{ +} diff --git a/gfx/renderable.h b/gfx/renderable.h index ea72bb6..d0870ad 100644 --- a/gfx/renderable.h +++ b/gfx/renderable.h @@ -6,6 +6,7 @@ class SceneShader; class Frustum; class ShadowMapper; class ShadowStenciller; +class BillboardPainter; class Renderable { public: @@ -19,4 +20,5 @@ public: virtual void shadows(const ShadowMapper & shadowMapper, const Frustum &) const; virtual void updateStencil(const ShadowStenciller & lightDir) const; + virtual void updateBillboard(const BillboardPainter &) const; }; |
