keid-render-basic-0.1.6.1: src/Render/Unlit/Sprite/Pipeline.hs
{-# LANGUAGE OverloadedLists #-}
module Render.Unlit.Sprite.Pipeline
( Pipeline
, allocate
-- , allocateOutline
) where
import RIO
import Control.Monad.Trans.Resource (ResourceT)
import Data.Tagged (Tagged(..))
import Vulkan.Core10 qualified as Vk
import Engine.Vulkan.Pipeline qualified as Pipeline
import Engine.Vulkan.Types (HasVulkan, HasRenderPass(..), DsBindings)
import Render.Code (compileVert, compileFrag, glsl)
import Render.DescSets.Set0 (Scene)
import Render.DescSets.Set0.Code (set0binding0, set0binding1, set0binding2)
import Render.Unlit.Sprite.Model qualified as Model
type Config = Pipeline.Configure Pipeline (Float, Bool)
type Pipeline = Pipeline.Pipeline '[Scene] () Model.InstanceAttrs
allocate
:: ( HasVulkan env
, HasRenderPass renderpass
)
=> Vk.SampleCountFlagBits
-> Maybe Float
-> Bool
-> Tagged Scene DsBindings
-> renderpass
-> ResourceT (RIO env) Pipeline
allocate multisample discardAlpha outline set0 = do
fmap snd . Pipeline.allocate
Nothing
multisample
(config discardAlpha outline set0)
config
:: Maybe Float
-> Bool
-> Tagged Scene DsBindings
-> Config
config discardAlpha outline (Tagged set0) =
Pipeline.baseConfig
{ Pipeline.cVertexCode = Just vertCode
, Pipeline.cFragmentCode = Just fragCode
, Pipeline.cDescLayouts = Tagged @'[Scene] [set0]
, Pipeline.cVertexInput = vertexInput
, Pipeline.cDepthTest = False
, Pipeline.cDepthWrite = False
, Pipeline.cBlend = True
, Pipeline.cCull = Vk.CULL_MODE_NONE
, Pipeline.cSpecialization = specs
}
where
vertexInput = Pipeline.vertexInput
[ (Vk.VERTEX_INPUT_RATE_INSTANCE, Model.vkInstanceAttrs)
]
specs =
( fromMaybe 0.0 discardAlpha
, outline
)
vertCode :: ByteString
vertCode =
$(compileVert [glsl|
#version 450
#extension GL_ARB_separate_shader_objects : enable
${set0binding0}
layout(location = 0) in vec4 iVert;
layout(location = 1) in vec4 iFrag;
layout(location = 2) in vec4 iTint;
layout(location = 3) in vec4 iOutline;
layout(location = 4) in ivec2 iTextureIds;
layout(location = 5) in uvec2 iTextureSize;
layout(location = 0) out vec2 fTexCoord;
layout(location = 1) flat out vec4 fTint;
layout(location = 2) flat out vec4 fOutline;
layout(location = 3) flat out ivec2 fTextureIds;
layout(location = 4) flat out uvec2 fTextureSize;
vec2 positions[6] = vec2[](
vec2(-0.5, -0.5),
vec2(0.5, -0.5),
vec2(-0.5, 0.5),
vec2(0.5, 0.5),
vec2(-0.5, 0.5),
vec2(0.5, -0.5)
);
vec2 texCoords[6] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0),
vec2(0.0, 1.0),
vec2(1.0, 0.0)
);
void main() {
vec2 pos = positions[gl_VertexIndex];
gl_Position
= scene.projection
* scene.view
* vec4(iVert.xy + pos * iVert.zw, 0, 1.0);
vec2 uv = texCoords[gl_VertexIndex];
fTexCoord = iFrag.xy + uv * iFrag.zw;
fTint = iTint;
fOutline = iOutline;
fTextureIds = iTextureIds;
fTextureSize = iTextureSize;
}
|])
fragCode :: ByteString
fragCode =
$(compileFrag [glsl|
#version 450
#extension GL_ARB_separate_shader_objects : enable
#extension GL_EXT_nonuniform_qualifier : enable
${set0binding1}
${set0binding2}
layout(location = 0) in vec2 fTexCoord;
layout(location = 1) flat in vec4 fTint;
layout(location = 2) flat in vec4 fOutline;
layout(location = 3) flat in ivec2 fTextureIds;
layout(location = 4) flat in uvec2 fTextureSize;
layout(location = 0) out vec4 outColor;
layout(constant_id=0) const float discardAlpha = 0.0;
layout(constant_id=1) const bool doOutline = false;
vec4 tap(vec2 uv) {
return texture(
sampler2D(
textures[nonuniformEXT(fTextureIds.t)],
samplers[nonuniformEXT(fTextureIds.s)]
),
fTexCoord + uv
);
}
vec2 step_size = 1.0 / vec2(fTextureSize);
void main() {
vec4 texel = tap(vec2(0));
if (discardAlpha != 0.0) {
if (texel.a < discardAlpha) {
discard;
}
}
outColor = texel * fTint * texel.a;
if (doOutline) {
if (fOutline.a > 0.0 && texel.a < 1.0) {
float alpha = 0.0;
alpha += tap(vec2(step_size.x, 0.0) ).a / 4.0;
alpha += tap(vec2(-step_size.x, 0.0) ).a / 4.0;
alpha += tap(vec2(0.0, step_size.y) ).a / 4.0;
alpha += tap(vec2(0.0, -step_size.y)).a / 4.0;
outColor += fOutline * alpha;
}
}
}
|])