God Rays Effect


(Posted 1 year ago) RonTek

Port of Max3D's God Rays shader to OpenB3DMax

example code is wip. haaaaaaah..

BlitzMax Blitz3D God Rays Shader Light Scattering

sunblue.png

godrays.frag.glsl

uniform sampler2D colortex;
uniform sampler2D depthtex;

vec3 GodRaysColor = vec3(1.0, 1.0, 0.0);
uniform float GodRaysExposure;
uniform float GodRaysLightX;
uniform float GodRaysLightY;

const int NUM_SAMPLES = 100;

void main() {

  vec2 textCoo = gl_TexCoord[0].xy;
  vec2 lightPos = vec2(GodRaysLightX, GodRaysLightY);
  vec2 deltaTextCoord = (textCoo - lightPos) / float(NUM_SAMPLES);

  vec3 color = vec3(0.0);

  for (int i = 0; i < NUM_SAMPLES; i++) {
    textCoo -= deltaTextCoord;
    color += GodRaysColor * floor(texture2D(depthtex, textCoo).r);

  }
  gl_FragColor = texture2D(colortex, gl_TexCoord[0].xy) + vec4(color / float(NUM_SAMPLES) * GodRaysExposure, 0.0);

}

godrays.bmx

' godrays.bmx
' postprocess effect - Light Scattering / God Rays

Strict

Framework Openb3dMax.B3dglgraphics
Import Brl.Random
?Not bmxng
Import Brl.Timer
?bmxng
Import Brl.TimerDefault
?

'Local width%=DesktopWidth(),height%=DesktopHeight()

Local width%=800,height%=600

Graphics3D width, height,0,2

SeedRnd MilliSecs()
ClearTextureFilters ' remove mipmap flag for postfx texture

Global camera:TCamera=CreateCamera()
CameraRange camera,0.5,1000.0 ' near must be closer than screen sprite to prevent clipping
CameraClsColor camera,64,128,255

Global postfx_cam:TCamera=CreateCamera() ' copy main camera
CameraRange postfx_cam,0.5,2000.0
CameraClsColor postfx_cam,64,128,255

HideEntity postfx_cam

AmbientLight 128,128,128

Local Light:TLight=CreateLight()
'Local lightYaw#=0,lightPitch#=0,lightRoll#
'TurnEntity Light,lightYaw,lightPitch,lightRoll
'PositionEntity Light,100,60,100
Local lightYaw#=45,lightPitch#=45,lightRoll#
TurnEntity Light,lightPitch,lightYaw,lightRoll
'PointEntity Light,Camera

Local brush:TBrush=CreateBrush()

Local lsprite:TSprite=CreateSprite()
EntityColor lsprite,255,255,255
'ScaleSprite lsprite,TParticleFunc.scalex,TParticleFunc.scaley
Local noisetex:TTexture=LoadTexture("../media/sunblue.tga",2)

'MoveEntity lsprite,-.5,-.5,0
EntityTexture lsprite,noisetex
'EntityFX lsprite,1+4+8+16
'UpdateNormals lsprite
EntityOrder lsprite,1

Local hx:Int=2.5,hy:Int=-2

HandleSprite lsprite,hx,hy

'EntityFX(lsprite,32)

Local efx%=1,eblend%=2,etexenv%=2,multitexfactor#=0.5,ealpha#=1

'Local size:Int=256, vsize:Float=30, maxheight:Float=10
'Local terrain:TTerrain=LoadTerrain("../media/heightmap_256.BMP") ' path case-sensitive on Linux
'ScaleEntity terrain,1,(1*maxheight)/vsize,1 ' set height
'terrain.UpdateNormals() ' correct lighting

'terrain.UpdateNormals()
'PositionEntity terrain,-size/2,-10,size/2

Local terrain:TMesh=CreateCube()
ScaleEntity terrain,2000,1,2000
'MoveEntity terrain,0,-.5,0

' Texture terrain
Local grass_tex:TTexture=LoadTexture( "../media/terrain-1.jpg" )
EntityTexture terrain,grass_tex
ScaleTexture grass_tex,.01,.01

Local pivot:TPivot=CreatePivot()
PositionEntity pivot,0,0,0
Local anim_time:Float
Local anim_ent:TMesh=LoadAnimMesh("../media/zombie.b3d",pivot)
PositionEntity anim_ent,0,0,12
TurnEntity anim_ent,0,-90,0

Local cube:TMesh=LoadMesh("../media/castle1.b3d")
'ScaleMesh cube,4,2,4
PositionEntity cube,0,-5,0
ScaleEntity cube,.5,.5,.5
'Local cube_tex:TTexture=LoadTexture("../media/crate.bmp",1)
'EntityTexture cube,cube_tex
'TurnEntity cube,0,0,0

Global colortex:TTexture=CreateTexture(width,height,1+256)
ScaleTexture colortex,1.0,-1.0

Global depthtex:TTexture=CreateTexture(width,height,1+256)
ScaleTexture depthtex,1.0,-1.0

' in GL 2.0 render textures need attached before other textures (EntityTexture)
CameraToTex colortex,camera
DepthBufferToTex depthtex,camera
TGlobal.CheckFramebufferStatus(GL_FRAMEBUFFER_EXT) ' check for framebuffer errors

' screen sprite - by BlitzSupport
Global screensprite:TSprite=CreateSprite()
EntityOrder screensprite,-1
ScaleSprite screensprite,1.0,Float( GraphicsHeight() ) / GraphicsWidth() ' 0.75
MoveEntity screensprite,0,0,0.99 ' set z to 0.99 - instead of clamping uvs
EntityParent screensprite,camera

Global screensprite2:TSprite=CreateSprite()
ScaleSprite screensprite2,1.0,Float( GraphicsHeight() ) / GraphicsWidth() ' 0.75
EntityOrder screensprite2,-1
EntityParent screensprite2,camera
MoveEntity screensprite2,0,0,1.0 ' set z to 1.0
EntityTexture screensprite2,depthtex

PositionEntity camera,0,7,0 ' move camera now sprite is parented to it
MoveEntity camera,0,0,-25

Local shader:TShader=LoadShader("","../glsl/default.vert.glsl", "../glsl/godrays.frag.glsl")

Local GRExp:Float
Local GRLightX:Float
Local GRLightY:Float

ShaderTexture(shader,colortex,"colortex",0) ' Our render texture
ShaderTexture(shader,depthtex,"depthtex",1) ' 1 is depth texture

Local px#,py#

'UseFloat2(shader,"lightPositionOnScreen",px,py)
UseFloat(shader,"GodRaysExposure", GRExp)
UseFloat(shader,"GodRaysLightX", GRLightX)
UseFloat(shader,"GodRaysLightY", GRLightY)
ShadeEntity(screensprite, shader)

';ScaleTexture colortex,1.0,1.0

Global postprocess%=1
Local time#=0, framerate#=60.0, animspeed#=10
Local timer:TTimer=CreateTimer(framerate)
UseFloat(shader,"time",time) ' Time used to scroll the distortion map

' fps code
Local old_ms%=MilliSecs()
Local renders%, fps%

Local update% = 1

Local CamProj:String = "NO"

'HideEntity terrain;HideEntity cube

Local spx% = 0,spy% = 0,spz% = 5
Local grx# = 0.0, gry# = 0.0

'HideEntity lsprite

'PointEntity Light,camera

EntityFX(lsprite,32)

While Not KeyHit(KEY_ESCAPE)

    ' FX blending, alpha / nothing
    If KeyHit(KEY_N)
        efx=Not efx
        If efx
            EntityFX(lsprite,32)
        Else
            EntityFX(lsprite,0)
        EndIf
    EndIf

    'If KeyHit(KEY_TAB) Then toggle = Not toggle

    time=Float((TimerTicks(timer) / framerate) * animspeed)

    If KeyDown(KEY_MINUS) Then anim_time#=anim_time#-0.1
    If KeyDown(KEY_EQUALS) Then anim_time#=anim_time#+0.1

    anim_time:+0.5
    If anim_time>20 Then anim_time=2
    SetAnimTime(anim_ent,anim_time)
    TurnEntity pivot,0,1,0

    If KeyHit(KEY_SPACE) Then postprocess=Not postprocess

    ' control camera
    Rem
    If KeyDown( KEY_RIGHT )=True Then TurnEntity Camera,0,-1,0
    If KeyDown( KEY_LEFT )=True Then TurnEntity Camera,0,1,0
    If KeyDown( KEY_DOWN )=True Then MoveEntity Camera,0,0,-2.5
    If KeyDown( KEY_UP )=True Then MoveEntity Camera,0,0,2.5
    End Rem
    If KeyDown( KEY_LEFT )
        TurnEntity camera,0,3,0
    Else If KeyDown( KEY_RIGHT )
        TurnEntity camera,0,-3,0
    EndIf

    If KeyDown( KEY_A )
        MoveEntity camera,0,0,3
    Else If KeyDown( KEY_Z )
        MoveEntity camera,0,0,-3
    EndIf

    'Rem
    If KeyDown( KEY_UP )
        TurnEntity camera,-3,0,0
    Else If KeyDown( KEY_DOWN )
        TurnEntity camera,3,0,0
    EndIf   
    'End Rem

    PositionEntity postfx_cam,EntityX(camera),EntityY(camera),EntityZ(camera)
    RotateEntity postfx_cam,EntityPitch(camera),EntityYaw(camera),EntityRoll(camera)

    If KeyHit(KEY_Q) Then update=Not update

    Local cx#=EntityX(camera)
    Local cy#=EntityY(camera)
    Local cz#=EntityZ(camera)

    lsprite.PositionEntity(cx,cy,cz)
    lsprite.RotateEntity(-lightYaw,-lightPitch,0)

    If KeyHit(KEY_T) Then spx = spx + 1
    If KeyHit(KEY_G) Then spy = spy + 1
    If KeyHit(KEY_B) Then spz = spz + 1
    If KeyHit(KEY_R) Then spx = spx - 1
    If KeyHit(KEY_F) Then spy = spy - 1
    If KeyHit(KEY_V) Then spz = spz - 1

    If KeyHit(KEY_3) Then hx = hx + .1
    If KeyHit(KEY_4) Then hx = hx - .1
    If KeyHit(KEY_W) Then hy = hy + .1
    If KeyHit(KEY_E) Then hy = hy - .1

    MoveEntity lsprite,spx,spy,spz

    Local sx#=EntityX(lsprite)
    Local sy#=EntityY(lsprite)
    Local sz#=EntityZ(lsprite)

    'grx = (grx*100)/100
    'gry = (gry*100)/100

    'PositionEntity lsprite,EntityX(Camera),EntityY(Camera), EntityZ(Camera)
    'MoveEntity lsprite,0,6,-10

    'CameraProject(Camera,EntityX(lsprite),EntityY(lsprite),EntityZ(lsprite))

    CameraProjMode camera,2
    CameraZoom camera,.1

    CameraProject(camera,sx,sy,sz)
        Local x#=ProjectedX(),y#=ProjectedY()
        px = x#
        y# = height - y#
        py = y#

        grx = px/(width/2)-1
        gry = py/(height/2)-1

        Local dx#=x#-512,dy#=y#-384
        Local d#=Sqr( dx*dx+dy*dy )/384.0
        If d>1 d=1
        GRExp = (1-d)*.5
        GRLightX = grx 'px# / width 'calculate percentage
        GRLightY = gry 'py# / height 'calculate percentage
        'px = 0
        'py = 0
    'Else
    '   Local x#,y#,d#,dx#,dy#
    '   GRExp = 0
    '   GRLightX = 0
    '   GRLightY = 0
    'EndIf      

    CameraProjMode camera,1
    CameraZoom camera,1

    UpdateWorld

    While update = 0
        'If KeyHit(KEY_TAB) Then toggle = Not toggle
        If KeyHit(KEY_SPACE) Then postprocess=Not postprocess
        If KeyHit(KEY_Q) Then update=Not update
        Update1Pass()
        Flip
    Wend

    Update1Pass()

    ' calculate fps
    renders=renders+1
    If MilliSecs()-old_ms>=1000
        old_ms=MilliSecs()
        fps=renders
        renders=0
    EndIf

    Text 0,20,"FPS: "+fps
    Text 0,40,"Space: postprocess = "+postprocess
    Text 0,60,"anim_time="+anim_time

    Text 0,80, GRLightX + " " + GRLightY
    Text 0,100, px + " " + py

    Text 0,120, grx + " " + gry

    'Text xx,yy,"Cube"
    px=0
    py=0
    Flip
Wend
End

Function Update1Pass()

    If postprocess=0
        HideEntity postfx_cam
        ShowEntity camera
        HideEntity screensprite2
        HideEntity screensprite

        RenderWorld
    ElseIf postprocess=1
        HideEntity camera
        ShowEntity postfx_cam
        HideEntity screensprite2
        HideEntity screensprite

        CameraToTex colortex,postfx_cam

        HideEntity postfx_cam
        ShowEntity camera

        DepthBufferToTex depthtex,camera

        ShowEntity screensprite

        RenderWorld
    EndIf

End Function
(Posted 1 year ago) markcwm commented:

Wow, awesome! Thanks Ron!

So this is basically sunlight with proper light scattering (diffraction).

Here's a good GLSL 1.4 article https://medium.com/community-play-3d/god-rays-whats-that-5a67f26aeac2

(Posted 1 year ago) RonTek commented:

Thanks Mark! and yes it is. The shader code is actually good to go and I'm just having some issues with 3D to 2D spirte screen position. That's why on some angles the sprite to ray effect is off a bit. Nice article, actually that was one of the reference (Irrlicht shader?) if I recall when doing some changes with the port. I'll try to revisit that again next time. cheers.

(Posted 6 months ago) markcwm commented:

Hi Ron,

I've added this to openb3dmax.docs now (thanks again) but had to work on it adding some changes to the calculations, I dropped the sun height so the rays are visible at low angles, I stopped it shining brightly when turned away from the sun, I un-inverted ProjectedY as the rays seemed to be upside-down, and I put the sun on a pivot so it can be related to directional light, I still think there's something wrong with the rays angle but it's useable now.

(Posted 6 months ago) RonTek commented:

that's awesome Mark! I will try these new changes. Yes, it was really a wip and took some time figuring out, particularly converting the sun ray and direction.

Reply To Topic

Please log in to reply