Files
loop/src/Render.hs
T

379 lines
13 KiB
Haskell

module Render (
createLightMap,
renderLayer,
pingPongBetween,
bindFBO,
) where
import Control.Lens
import Control.Monad
import Control.Monad.Primitive
import Data.Preload.Render
import qualified Data.Vector.Fusion.Stream.Monadic as VFSM
import qualified Data.Vector.Mutable as MV
import qualified Data.Vector.Unboxed.Mutable as UMV
import Dodge.Data.Config
import Dodge.WindowSize
import Foreign hiding (rotate)
import Geometry.Data
import Graphics.GL.Core45
import Picture.Data
import Shader
import Shader.Data
{- | Determine where light is shining in the world.
think of the produced texture as showing what RGB values should be "taken
away" from the shape colors.
-}
createLightMap ::
Configuration ->
-- | number of walls
GLsizei ->
-- | number of silhoutte lines to draw
Int ->
-- | number of "caps" to attempt to draw
Int ->
-- | the texture object giving positions
TO ->
-- | the texture object giving normals
TO ->
[(Point3, Float, Point3)] -> -- Lights
RenderData ->
IO ()
createLightMap cfig = case shadrendertype of
InstancingShads -> instanceLightMap cfig
NoShadows -> const . const . const renderLightingNoShadows
NoLighting -> const . const . const . const . const . const renderFlatLighting
_ -> renderShadows shadrendertype
where
shadrendertype = cfig ^. graphics_shadow_rendering
renderLightingNoShadows ::
TO ->
TO ->
[(V3 GLfloat, GLfloat, V3 GLfloat)] ->
RenderData ->
IO ()
renderLightingNoShadows positiontexture normaltexture lightPoints pdata = do
glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata)))
let ltextShad = _lightingTextureShader pdata
-- we assume that the renderbuffer's depth has been correctly set elsewhere
-- we will not be changing that here
glDepthMask GL_FALSE
-- clearColor is specified differently in preloadRender
-- the colors are inverted
withArray [1, 1, 1, 1] $ \ptr ->
glClearNamedFramebufferfv
(pdata ^. fboLighting . _1 . unFBO)
GL_COLOR
0
ptr
glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR
glEnable GL_STENCIL_TEST
glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP
glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP
flip VFSM.mapM_ (VFSM.fromList lightPoints) $ \(V3 x y z, rad, V3 r g b) -> do
glDepthFunc GL_LESS
-- setup stencil
glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE
with 0 $ \ptr ->
glClearNamedFramebufferiv
(pdata ^. fboLighting . _1 . unFBO)
GL_STENCIL
0
ptr
glDisable GL_CULL_FACE
glStencilFunc GL_ALWAYS 0 255
--draw lightmap itself
glDepthFunc GL_ALWAYS
-- bind world position texture
glBindTextureUnit 0 (positiontexture ^. unTO)
glBindTextureUnit 1 (normaltexture ^. unTO)
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
glStencilFunc GL_EQUAL 0 255
glUseProgram (ltextShad ^. shaderUINT)
glUniform3f 0 x y z
glUniform4f 1 r g b rad
glBindVertexArray $ ltextShad ^. shaderVAO . vaoName
glDrawArrays
(_unPrimitiveMode (_shaderPrimitive ltextShad))
0
(fromIntegral (4 :: Int))
--cleanup: may not be necessary, depending on what comes after...
glDisable GL_CULL_FACE
glDisable GL_STENCIL_TEST
renderFlatLighting :: RenderData -> IO ()
renderFlatLighting pdata = do
glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata)))
withArray [0, 0, 0, 1] $ \ptr ->
glClearNamedFramebufferfv
(pdata ^. fboLighting . _1 . unFBO)
GL_COLOR
0
ptr
glDisable GL_CULL_FACE
glDepthFunc GL_ALWAYS
glDepthMask GL_FALSE
renderShadows ::
ShadowRendering ->
GLsizei ->
Int ->
Int ->
TO ->
TO ->
[(Point3, Float, Point3)] ->
RenderData ->
IO ()
renderShadows shadrendertype nWalls nSils nCaps positiontexture normaltexture lightPoints pdata = do
glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata)))
let llinesShad = _lightingLineShadowShader pdata
lcapShad = _lightingCapShader pdata
lwallShad = _lightingWallShadShader pdata
ltextShad = _lightingTextureShader pdata
-- we assume that the renderbuffer's depth has been correctly set elsewhere
-- we will not be changing that here
glDepthMask GL_FALSE
-- clearColor is specified differently in preloadRender
-- the colors are inverted
withArray [1, 1, 1, 1] $ \ptr ->
glClearNamedFramebufferfv
(pdata ^. fboLighting . _1 . unFBO)
GL_COLOR
0
ptr
-- for each of the lights:
-- 1. stencil out the shadows from this light's point of view
-- 2. calculate lighting based on each fragment's position
-- to consider: adding normals/a "material" for each fragment
glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR
glEnable GL_STENCIL_TEST
glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP
glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP
flip VFSM.mapM_ (VFSM.fromList lightPoints) $ \(V3 x y z, rad, V3 r g b) -> do
glDepthFunc GL_LESS
-- setup stencil
glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE
with 0 $ \ptr ->
glClearNamedFramebufferiv
(pdata ^. fboLighting . _1 . unFBO)
GL_STENCIL
0
ptr
glDisable GL_CULL_FACE
glStencilFunc GL_ALWAYS 0 255
--draw wall shadows
glUseProgram (_shaderUINT lwallShad)
glUniform3f 0 x y z
glUniform1f 1 rad
glBindVertexArray $ lwallShad ^. shaderVAO . vaoName -- Just (_vao $ _shadVAO lwallShad)
glDrawArrays
(_unPrimitiveMode $ _shaderPrimitive lwallShad)
0
nWalls
case shadrendertype of
GeoObjShads -> do
--draw silhouette shadows
glEnable GL_DEPTH_CLAMP
glUseProgram (_shaderUINT llinesShad)
glUniform3f 0 x y z
glUniform1f 1 rad
glBindVertexArray (_vaoName $ _shaderVAO llinesShad)
glDrawElements
(_unPrimitiveMode $ _shaderPrimitive llinesShad)
(fromIntegral nSils)
GL_UNSIGNED_SHORT
nullPtr
--draw caps on the near plane as required
glEnable GL_CULL_FACE
glCullFace GL_FRONT
glUseProgram (_shaderUINT lcapShad)
glUniform3f 0 x y z
glBindVertexArray $ lcapShad ^. shaderVAO . vaoName --Just (_vao $ _shadVAO lcapShad)
glDrawElements
(_unPrimitiveMode $ _shaderPrimitive lcapShad)
(fromIntegral nCaps)
GL_UNSIGNED_SHORT
nullPtr
glDisable GL_DEPTH_CLAMP
_ -> return ()
--draw lightmap itself
glDepthFunc GL_ALWAYS
-- bind world position texture
glDisable GL_CULL_FACE
glBindTextureUnit 0 (positiontexture ^. unTO)
glBindTextureUnit 1 (normaltexture ^. unTO)
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
glStencilFunc GL_EQUAL 0 255
glUseProgram (ltextShad ^. shaderUINT) --Just (_shadProg ltextShad)
glUniform3f 0 x y z
glUniform4f 1 r g b rad
glBindVertexArray $ ltextShad ^. shaderVAO . vaoName -- Just (_vao $ _shadVAO ltextShad)
glDrawArrays
(_unPrimitiveMode (_shaderPrimitive ltextShad))
0
(fromIntegral (4 :: Int))
--cleanup: may not be necessary, depending on what comes after...
glDisable GL_STENCIL_TEST
lightsToArray :: [(Point3, Float, Point3)] -> [Float]
lightsToArray xs =
concat (take 20 $ map t xs ++ repeat [0, 0, 0, 0])
++ concat (take 20 $ map s xs ++ repeat [0, 0, 0, 0])
where
t (V3 a b c, _, _) = [a, b, c, 1]
s (_, d, V3 e f g) = [e, f, g, d]
instanceLightMap ::
Configuration ->
-- | number of walls
GLsizei ->
-- | number of silhoutte lines to draw
Int ->
-- | number of "caps" to attempt to draw
Int ->
-- | the texture object giving positions
TO ->
TO ->
[(Point3, Float, Point3)] -> -- Lights
RenderData ->
IO ()
instanceLightMap cfig nWalls nSils nCaps toPos tanormals lightPoints pdata = do
let lcapShad = _shadowCapShader pdata
(xsize, ysize) = getWindowSize _graphics_world_resolution cfig
withArray (lightsToArray lightPoints) $ \ptr ->
glNamedBufferSubData (pdata ^. lightsUBO) 0 620 ptr
forM_ [0 .. 19] $ \i ->
glCopyImageSubData
(pdata ^. rboBaseBloom)
GL_RENDERBUFFER
0
0
0
0
(pdata ^. fboShadow . _2 . _2 . unTO)
GL_TEXTURE_2D_ARRAY
0
0
0
i
xsize
ysize
1
glBindFramebuffer GL_FRAMEBUFFER $ pdata ^. fboShadow . _1 . unFBO
glDepthMask GL_FALSE
withArray [0, 0, 0, 0] $ \ptr ->
glClearNamedFramebufferfv
(pdata ^. fboShadow . _1 . unFBO)
GL_COLOR
0
ptr
with 0 $ \ptr ->
glClearNamedFramebufferiv
(pdata ^. fboShadow . _1 . unFBO)
GL_STENCIL
0
ptr
-- stencil out the shadows from each light's point of view
-- setup stencil
glEnable GL_STENCIL_TEST
glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP
glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP
glDepthFunc GL_LESS
glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE
glDisable GL_BLEND
glDisable GL_CULL_FACE
glStencilFunc GL_ALWAYS 0 255
--draw wall shadows
glUseProgram $ pdata ^. shadowWallShader . shaderUINT
glBindVertexArray $ pdata ^. shadowWallShader . shaderVAO . vaoName
glDrawArrays
(_unPrimitiveMode $ pdata ^. shadowWallShader . shaderPrimitive)
0
nWalls
--draw silhouette shadows
glUseProgram $ pdata ^. shadowEdgeShader . shaderUINT
glBindVertexArray $ pdata ^. shadowEdgeShader . shaderVAO . vaoName
glDrawElements
(_unPrimitiveMode $ pdata ^. shadowEdgeShader . shaderPrimitive)
(fromIntegral nSils)
GL_UNSIGNED_SHORT
nullPtr
--draw caps on the near plane as required
glEnable GL_CULL_FACE
glCullFace GL_BACK
--glCullFace GL_FRONT
glUseProgram (_shaderUINT lcapShad)
glBindVertexArray $ lcapShad ^. shaderVAO . vaoName
glDrawElements
(_unPrimitiveMode $ _shaderPrimitive lcapShad)
(fromIntegral nCaps)
GL_UNSIGNED_SHORT
nullPtr
--draw lightmap itself
glDepthFunc GL_ALWAYS
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
glStencilFunc GL_EQUAL 0 255
glUseProgram (pdata ^. shadowLightShader . _1 . shaderUINT)
-- bind world position texture
glBindTextureUnit 0 (toPos ^. unTO)
glBindTextureUnit 1 (tanormals ^. unTO)
glDrawArrays
(_unPrimitiveMode $ pdata ^. shadowLightShader . _1 . shaderPrimitive)
0
1
glDisable GL_CULL_FACE
glDisable GL_STENCIL_TEST
--draw to the lighting framebuffer here
glBindFramebuffer GL_FRAMEBUFFER (_unFBO (fst (_fboLighting pdata)))
withArray [1, 1, 1, 1] $ \ptr ->
glClearNamedFramebufferfv
(pdata ^. fboLighting . _1 . unFBO)
GL_COLOR
0
ptr
glBindTextureUnit 0 (pdata ^. fboShadow . _2 . _1 . unTO)
glEnable GL_BLEND
glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR
glUseProgram (pdata ^. shadowCombineShader . _1 . shaderUINT)
glDrawArrays
GL_POINTS
0
1
withArray [GL_COLOR_ATTACHMENT0, GL_DEPTH_STENCIL_ATTACHMENT] $ \ptr ->
glInvalidateNamedFramebufferData
(pdata ^. fboShadow . _1 . unFBO)
2
ptr
-- assumes that vertices have already been sent to the shader
pingPongBetween ::
(FBO, TO) ->
(FBO, TO) ->
Shader ->
IO ()
pingPongBetween (fb1, to1) (fb2, to2) fs = do
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb2)
glBindTextureUnit 0 (_unTO to1)
drawShader fs 4
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb1)
glBindTextureUnit 0 (_unTO to2)
drawShader fs 4
renderLayer ::
Layer ->
MV.MVector (PrimState IO) (Shader, VBO) ->
UMV.MVector (PrimState IO) Int ->
IO ()
renderLayer layer shads counts = do
let layerCounts = UMV.slice (ln * numLayers) 6 counts
MV.imapM_ (drawShaderLay ln layerCounts) shads
where
ln = layerNum layer
bindFBO :: FBO -> IO ()
bindFBO fb =
--bindFramebuffer Framebuffer $= fb
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb)