Files
loop/src/Render.hs
T
2025-11-14 11:27:16 +00:00

235 lines
8.3 KiB
Haskell

module Render (
createLightMap,
renderLayer,
pingPongBetween,
bindFBO,
) where
import Dodge.Base.Coordinate
import Dodge.Data.Camera
import Control.Lens
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 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 ::
Camera ->
Config ->
ShadowRendering ->
-- | 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 cam cfig shadrendertype = case shadrendertype of
NoShadows -> const . const . const renderLightingNoShadows
NoLighting -> const . const . const . const . const . const renderFlatLighting
_ -> renderShadows cam cfig shadrendertype
-- where
-- shadrendertype = cfig ^. gr_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
withArray [x,y,z,1,r,g,b,rad] $ glNamedBufferSubData (pdata ^. lightUBO) 0 32
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
glDrawArrays GL_TRIANGLES 0 6
--cleanup: may not be necessary, depending on what comes after...
glDisable GL_CULL_FACE
glStencilFunc GL_NOTEQUAL 128 255
glStencilOp GL_KEEP GL_KEEP GL_KEEP
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
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
glStencilFunc GL_NOTEQUAL 128 255
glStencilOp GL_KEEP GL_KEEP GL_KEEP
renderShadows ::
Camera ->
Config ->
ShadowRendering ->
GLsizei ->
Int ->
Int ->
TO ->
TO ->
[(Point3, Float, Point3)] ->
RenderData ->
IO ()
renderShadows cam cfig shadrendertype nWalls nSils nCaps positiontexture normaltexture lightPoints pdata = do
glBindFramebuffer GL_FRAMEBUFFER $ pdata ^. fboLighting . _1 . unFBO
let llinesShad = _lightingLineShadowShader pdata
lcapShad = _lightingCapShader pdata
shadwall = _shadWallShader 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] $
glClearNamedFramebufferfv
(pdata ^. fboLighting . _1 . unFBO)
GL_COLOR
0
-- setup stencil and blending
glBlendFunc GL_ZERO GL_ONE_MINUS_SRC_COLOR
glStencilOpSeparate GL_FRONT GL_KEEP GL_KEEP GL_INCR_WRAP
glStencilOpSeparate GL_BACK GL_KEEP GL_KEEP GL_DECR_WRAP
-- bind world position texture-- these will be used by the lighting shader
glBindTextureUnit 0 (positiontexture ^. unTO)
glBindTextureUnit 1 (normaltexture ^. unTO)
-- 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 and normal
-- TODO stencil out square around light!
glEnable GL_SCISSOR_TEST
glDisable GL_CULL_FACE
flip VFSM.mapM_ (VFSM.fromList lightPoints) $ \(V3 x y z, rad, V3 r g b) -> do
withArray [x,y,z,1,r,g,b,rad] $ glNamedBufferSubData (pdata ^. lightUBO) 0 32
-- note that the stencil shadows rely on the depth buffer
let d = rad * cam ^. camZoom
V2 x' y' = worldPosToResOffset cfig cam (V2 x y)
-- this is probably too strict, it will be possible for high up lights
-- to have some of their light cast high up cut off
glScissor (round (x'-d)) (round (y'-d)) (round $ 2 *d) (round $ 2* d)
glDepthFunc GL_LESS
glColorMask GL_FALSE GL_FALSE GL_FALSE GL_FALSE
-- setup stencil
with 0 $
glClearNamedFramebufferiv (pdata ^. fboLighting . _1 . unFBO) GL_STENCIL 0
glDisable GL_CULL_FACE
-- the first bit has been used to stencil out "ceilings" under which we never draw
glStencilFunc GL_NOTEQUAL 128 255
glBindVertexArray $ pdata ^. dummyVAO
--draw wall shadows
glUseProgram shadwall
glDrawArrays GL_TRIANGLES 0 (nWalls * 18)
case shadrendertype of
GeoObjShads -> do
--draw silhouette shadows
glUseProgram llinesShad
glDrawArrays GL_TRIANGLES 0 ((fromIntegral nSils * 6) `div` 4)
--draw caps on the near plane as required
glEnable GL_CULL_FACE
glUseProgram lcapShad
glDrawArrays GL_TRIANGLES 0 (fromIntegral nCaps)
glDisable GL_DEPTH_CLAMP
_ -> return ()
--draw lightmap itself
glDepthFunc GL_ALWAYS
glDisable GL_CULL_FACE
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_FALSE
glStencilFunc GL_EQUAL 0 255
glUseProgram ltextShad
glDrawArrays GL_TRIANGLES 0 6
--cleanup: may not be necessary, depending on what comes after...
glDisable GL_SCISSOR_TEST
glStencilFunc GL_NOTEQUAL 128 255
glStencilOp GL_KEEP GL_KEEP GL_KEEP
glColorMask GL_TRUE GL_TRUE GL_TRUE GL_TRUE
-- assumes that vertices have already been sent to the shader
pingPongBetween ::
(FBO, TO) ->
(FBO, TO) ->
GLuint ->
IO ()
pingPongBetween (fb1, to1) (fb2, to2) fs = do
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb2)
glBindTextureUnit 0 (_unTO to1)
glUseProgram fs
glDrawArrays GL_TRIANGLES 0 6
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb1)
glBindTextureUnit 0 (_unTO to2)
glDrawArrays GL_TRIANGLES 0 6
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) numShads counts
MV.imapM_ (drawShaderLay ln layerCounts) shads
where
ln = fromEnum layer
bindFBO :: FBO -> IO ()
bindFBO fb =
--bindFramebuffer Framebuffer $= fb
glBindFramebuffer GL_FRAMEBUFFER (_unFBO fb)