I question about light effects using render image library

Started by takis76, March 11, 2020, 20:01:29

Previous topic - Next topic

takis76

Hello, my dear developers :)
I have one little problem and I would like to ask your question.
I am still programming my nice role-playing game here and I have posted and one nice video to see what miracles this wonderful programming language does and of course with the help of the wonderful people in these forums, who were very kind, answering all of my questions.  :D

The game presentation


Well, what is my problem! In the dungeons, there will be torches on the walls and I use the classic spot light effect.
I am using the render image library which solved many problems about copying portions of the screen and being very slow. The library works excellently and everything is nice.

The torches on the walls will emit light (The fake light). I have 2 TRenderImage type variables, 1 with name "Dungeon_Viewport_Layer" and 1 with name "Flare_Layer". The "Dungeon_Viewport_Layer" is the final image you will see when all images rendered with command Setrenderimage(Dungeon_Viewport_Layer). And when I have the command Setrenderimage(Flare_Layer) all image graphics will render there. Something like a back buffer.

When there will be a torch you will see the light in front of you. But the game is a dungeon and there will be torches in front of you and behind the current walls. The light effects in fact are not 3D are just 2D areas in which you draw a gray gradient image on the screen just over a rendered image buffer using the "render image" library. So if you place many gray gradient images on the screen technically you using the LIGHTBLEND and SHADEBLEND you are making the illusion of light, but in fact, there is not any light.
The Flare_Layer is the layer all gradient images will be rendered to create this FAKE light effect and the SHADEBLEND will make the whole area dark.
But, if I like to dynamically remove portions of these lights. Something like the Flare_Layer is a canvas and I like to erase it. When there is a wall in front of a wall you continue to see the light spot which is behind it. It supposes to see the light spot when there is a torch, but if behind the wall there is a torch and in front of this wall there is another wall without a torch, you still see the spotlight, also if there is a light at the left and there is a wall in front of any object, but this object (It supposed to be another image). you will see a part of the light behind this object, for example, you see a corridor for another angle and at the distance, there is a torch, but there are walls which partially covers the view but you see the portion of the visible light, that's I need to dynamically erase portions of the graphics on Flare_Layer buffer. Maybe to place a new BLACK gradient image. I use gray white gradient images to make lights, BLACK images may remove the lights?

I have created a small example source code instead of posting a huge amount of source code lines:

On the Flare_Layer I use a grayscale shade png image. There is the attached image with the name "Flare.png".
Also, there are another 2 images. The "Wall_Tile1.png" is the back wall and the "Wall_Tile2.png" is the front wall which partially covers the back wall. In the example, it covers half of the wall behind. But the light effect keeps being whole, it supposes to be half. Of course, is whole because I haven't found a way to erase the portion of the graphics on runtime on the Flare_Layer buffer.

The Dungeon_Viewport_Layer buffer is just the final view you see the graphics only not the gradient images blended. You see the "Wall_Tiles 1&2".

Copy and place the source code and download the attached images to run the example correctly.

Thank you very very much.  :)

The source code of the example:

SuperStrict

Import Srs.shaderframework
Import srs.d3d11max2d

Import "renderimage.bmx"

Import brl.PNGLoader

Global Graph:TGraphics

SetGraphicsDriver(GLMax2DDriver())
Graph = Graphics(800, 600, 0, 60)

SetVirtualResolution(800, 600)

Global Dungeon_Viewport_Layer:TRenderImage = CreateRenderImage(Graph, 352, 240)
Global Flare_Layer:TRenderImage = CreateRenderImage(Graph, 352, 241)

Global Wall_Tile1:TImage
Global Wall_Tile2:TImage
Global Flare:TImage
Global Pointer_x:Int
Global Pointer_y:Int

Global Place_New_Wall:Byte 'The flag that indicates if I like to place a new wall over the old one


Wall_Tile1 = LoadImage("Wall_Tile1.png")
Wall_Tile2 = LoadImage("Wall_Tile2.png")

Flare = LoadImage("Flare.png")
'MidHandleImage(Flare)

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)

Pointer_x = VirtualMouseX()
Pointer_y = VirtualMouseY()

DrawImage(Wall_Tile1, 20, 80)

Setrenderimage(Flare_Layer)
Cls_To(Graph, 100, 100, 100, 255)
SetBlend(LIGHTBLEND)

SetColor(255, 100, 100)
DrawImageRect(Flare, 60, 110, 120, 120) 'static light

SetColor(255, 255, 255)
DrawImageRect(Flare, Pointer_x - 60, Pointer_y - 110, 120, 120) 'movable light


DrawRect(50, 50, 50, 50) 'Square light

'SetAlpha(1)
SetBlend(ALPHABLEND)

Setrenderimage(Dungeon_Viewport_Layer)
cls
SetColor(255, 255, 255)
DrawImage(Wall_Tile1, 0, 0)

If Place_New_Wall = 1 Then DrawImage(Wall_Tile2, 128, 0)

SetBlend SHADEBLEND




DrawImage(Flare_Layer, 0, 0)
SetBlend ALPHABLEND

Setrenderimage(Null)
Cls


DrawImageRect(Dungeon_Viewport_Layer, 20, 80, 352, 240)

If KeyHit(KEY_NUMADD) Then Place_New_Wall = 1
If KeyHit(KEY_NUMSUBTRACT) Then Place_New_Wall = 0


PollSystem()
'Check_GUI_Events()
Flip 1

WEnd


'Clear the screen to specific color

Function Cls_To(gc:TGraphics, RED:Int, Green:Int, Blue:Int, Alpha:Int)
Local max2d:TMax2DGraphics = TMax2DGraphics(gc)
Global OneOver255:Float = 1.0/255.0

Red=Min(Max(Red, 0), 255)
Green=Min(Max(Green, 0), 255)
Blue=Min(Max(Blue, 0), 255)
Alpha=Min(Max(Alpha, 0), 255)

?debug
Assert max2d <> Null
?

?win32
If TD3D9Graphics(max2d._graphics)
Local colour:Int = (Alpha Shl 24) | (Red Shl 16) | (Green Shl 8) | Blue
Local d3ddev:IDirect3DDevice9 = TD3D9Graphics(max2d._graphics).GetDirect3DDevice()
If Not d3ddev Return
d3ddev.Clear(0, Null, D3DCLEAR_TARGET, colour , 0.0, 0)

Return
EndIf
If TD3D11Graphics(max2d._graphics)
Local d3ddevcon:ID3D11DeviceContext = TD3D11Graphics(max2d._graphics).GetDirect3DDeviceContext()
If Not d3ddevcon Return

Local view:ID3D11RenderTargetView
d3ddevcon.OMGetRenderTargets(1, Varptr view, Null)
If Not view Return

d3ddevcon.ClearRenderTargetView(view, [Red * OneOver255, Green * OneOver255, Blue * OneOver255, Alpha * OneOver255])
view.Release_()

Return
EndIf
?
If TGLGraphics(max2d._graphics)
Local colour:Float[4]
glGetFloatv(GL_COLOR_CLEAR_VALUE, colour)
glClearColor(Red * OneOver255, Green * OneOver255, Blue * OneOver255, Alpha * OneOver255)
glClear(GL_COLOR_BUFFER_BIT)
glClearColor(colour[0], colour[1], colour[2], colour[3])
EndIf
EndFunction


There is and an additional video with the name "Torch.mp4" which presents the light effect with the torch on the wall.

I uploaded and the "Light Effect Problem.mp4" to see the effect in the game better.

Derron

If I understood correctly, then your problem are "full/partly covered light sources".

Couldn't you split the "walls/corners/..." in your map into a grid? And then for each "y" you have all the items/elements/walls of that y-cell drawn + side lights affecting them. Then you draw the y which is more near to the camera - draw their walls, draw their side lights.

At the end you draw for fake "torch light" coming from your party - ignoring potential falloff-differences on angular positioned walls (so the same as you do now).


To make it "realistic" you would need the 3D information of the walls and multiply these information into the light texture (on each "step" to avoid doing it on each frame). So a path to the left would have the information of "not there" (so not stopping the light from emitting).

Dunno if it is worth the hassle - let's hope someone can come up with a smart and nifty idea on how to solve your problem.


bye
Ron

takis76

QuoteCouldn't you split the "walls/corners/..." in your map into a grid? And then for each "y" you have all the items/elements/walls of that y-cell drawn + side lights affecting them. Then you draw the y which is more near to the camera - draw their walls, draw their side lights.

All of my wall graphics are 23 sprites which arranged to make the illusion of the corridor. The map is a grid. First I am drawing the far sprites and then I am drawing the nearest to cover the far one. But the light is on the nearest layer in front of all the graphics. Like the flashlight effect which is in front of all.
My problem is to cover the lights on the torches, not the flashlight. I believe if I will try a way to remove gradient graphics from the Flare_Layer, the image buffer which holds the lightmap, then each time I will draw a wall, I will remove graphics from the Flare_Layer in the same size and the shape of the wall it covers the light behind. We do not care how we see the walls, we care how to remove pixels or graphic parts from the (lightmap) Flare layer back buffered image. Imagine to have an eraser and erase the canvas.

Look the example if the lightmap was visible and was cut by walls. (I did this with paint program is not actual runtime effect).
I believe the solution is to cut the lightmap on runtime.

The visible areas that no wall covers the lightmap will not be cut.


takis76

Searching in the forums I found this:
https://www.syntaxbomb.com/index.php?topic=4183.0

You mentioned about some image helper functions in the past and there is a source code with name "Draw Image to Image" -> "base.gfx.imagehelper.bmx"
What I need to download, it doesn't compile because there are and other following source code files which included.
Is this a module?
If I will replace files in Blitz Max will it break other things?

But I don't know if it serves with any purpose. The render image library supposes to do this already.

Derron

My functions in the file are for "Software rendering", TRenderImage does "hardware rendering" - so it is faster.

The functions in the file might need other files from my framework - the image stuff requires often TPixmap (brl.pixmap) and maybe even TImage (brl.image) but not brl.max2D - so as long as "TImage" can be used, you should be able to copy this file (and the other required files).

It is not a module but raw source files you place next to yours (eg in a subfolder).


bye
Ron

ImJustAnotherCoder

If I understand you correctly I think it would be better to not draw the part of the light that you are trying to erase.

If that is the case then I'd suggest looking into SetViewport to clip the rendering output. TRenderImage has a .SetViewport method so it may take some playing around to get the right.

takis76

QuoteIf that is the case then I'd suggest looking into SetViewport to clip the rendering output. TRenderImage has a .SetViewport method so it may take some playing around to get the right.

You gave me a nice idea now. But I don't know if it work because some wall shapes are not square, but I like your suggestion. I will try it and I will report if it worked.

takis76

You said the SetViewport for TRenderImage and I tried the normal SetViewport which it doesn't work. Because there are 23 separate wall sprite pieces and setting one viewport for one cancels the other.
Maybe and the SetViewport for TRenderImage will do the same if you call it many times.



Derron

When you draw something onto your TRenderImage, it will most probably take the currently set viewport into account.


Function SetRenderImageViewport(renderimage:TRenderimage, x:Int, y:Int, width:Int, height:Int)
' sanity check
?debug
Assert _ric <> Null, "No TRenderImage instances have been created"
?

renderimage.SetViewport(x, y, width, height)
EndFunction


This is in the code there ... so either use "yourrenderimage.SetViewport(...)" or "SetRenderImageViewport(yourrenderimage, ...)"


bye
Ron

takis76

When I am setting a viewport, anything I will draw at this time do will affect the specific viewport area?

If yes, then how is it possible to erase the contents of this particular area of viewport?

Derron

Draw a black rectangle?
- opposite to the black to white-circle-gradient zu surely use for your lights (+falloff).


bye
Ron

takis76

As I see the Function SetRenderImageViewport(...) It affects only the area of the viewport.
After some tests I made and I changed my code a little I think I made it.
Any object in front cuts the light effect behind it, correctly.

If I like to remove all contents (Let's say) of the selected viewport I use the function Cls_to(...)

I Use this code to place a wall without light. In fact I believe it clears only the selected render image viewport

Setrenderimage(Flare_Layer)
SetRenderImageViewport(Flare_Layer, 048, 016, 256, 192)
Cls_To(Graph, Ambience_Red, Ambience_Green, Ambience_Blue, 255)
Draw_Torch_Light_Again1()
Setrenderimage(Dungeon_Viewport_Layer)


And I use this code to place a new light when the wall has some torch or light-emitting object:

Setrenderimage(Flare_Layer)
SetRenderImageViewport(Flare_Layer, 048, 016, 256, 192)
SetBlend(LIGHTBLEND)
SetAlpha(1.0)

Flare_Tremble_factor = 7 'According to torch distance the flame will tremble differently
SetColor(Torch_Sconce[Party.Level, All_Torch_Sconces].Flare_Red, Torch_Sconce[Party.Level, All_Torch_Sconces].Flare_Green, Torch_Sconce[Party.Level, All_Torch_Sconces].Flare_Blue)
DrawImageRect(Small_Flare1, 96, -20, (160 + Torch_Sconce[Party.Level, All_Torch_Sconces].Flare_tremble), (160 + Torch_Sconce[Party.Level, All_Torch_Sconces].Flare_tremble))

SetBlend(ALPHABLEND)
Setrenderimage(Dungeon_Viewport_Layer)
SetColor(255, 255, 255)


The Flare_Tremble and other variables are to make the light flickering and change the color of the light.

I will try to update the code with all (23) wall positions to tell you finally if this worked completely. But it seems it is working.
Thank you very very much in advance.

Look the screenshot result.

ImJustAnotherCoder


takis76

Thank you very much. I encounter a few problems with the sidewalls and I try to figure the problem out. The front walls are rectangular and it looks nice. But the side one are trapezium causing to have a different light volume (Darker and lighter areas) with neighbor side walls and the floor. I am making, even more changes and arrangements in the sprites and in the order of the lights that will appear and I will see if it is finally working properly at the end.

takis76

I managed to make the light effect not present behind walls and corrected the sidewalls, but the trick is working only in rectangular areas.
There will not be pixel perfect light covering if there will be other not so rectangular sprites. For example doors or holes or windows or any other objects with no rectangular transparent areas.