How to grab a part of screen fast with render2texture?

Started by takis76, August 19, 2018, 18:50:44

Previous topic - Next topic

takis76

Now having the render2texture installed I should be able to copy part of image fast. Something similar to old grabimage.
Which is the function which copy a part of image?


Derron

as done in the examples:
set your render texture as target

render your stuff

remove render texture target
-> rendered stuff is now in the texture/image ("grabbed").


bye
Ron

takis76


Derron

Open up an example - this is all you need.

-> set a texture as target
-> do your stuff
-> remove the target (render to screen again)
-> "do your stuff" is now drawn on the texture instead of the screen


bye
Ron

takis76

I am trying to do the treditional torch light effect with the render2texture
According to the examples I have seen.
There is not way to grab the image. At least with the way I believe or with the old grabimage way. But I have understand so far.
That I am preparing some render images.

For example I am preparing the render_screen image which will be an 800x600 image.


Global render_screen:TRenderimage = CreateRenderImage(graph, 800, 600)


This render_screen will be my visible screen for the game and everything will be drawn over this screen. I suppose.
I am setting the render_screen as an active render image.


SetRenderImage(render_screen)


So  the current render image is active to draw something on it.
Some random squares.

For Local i:Int = 1 To 100
SetColor Rand(0, 100), Rand(0, 100), Rand(0, 100)
DrawRect Rand(0, 800), Rand(0, 600), Rand(50, 50), Rand(50, 50)
Next
SetColor 255, 255, 255


Then I am preparing another one render image with name torch_flare_spot
the size of this image will be 100x100 pixels


Global torch_flare_spot:Trenderimage = createRenderImage(graph, 100, 100)


Then draw one rectangle at position 0,0 because this torch_flare_spot is not the screen in the buffer image.


SetColor(255, 255, 255)
DrawRect(0, 0, 100, 100)


Then there is the main game loop



SetRenderImage(Null)
Cls
DrawImage(render_screen, 0, 0)

Flip 1



According what I understood, I need to set the active render image as null for the library to show all of these secret buffered screens on the real screen.

And I am drawing the render_screen as normal image and I see the random squares.
If I need to see the rectangle as a torch light I will need to set the blend as light blend.

Here is the complete program


SuperStrict
Framework brl.StandardIO
Import "renderimage.bmx"

Global render_screen:TRenderimage = CreateRenderImage(graph, 800, 600)
Global torch_flare_spot:Trenderimage = createRenderImage(graph, 100, 100)

MidHandleImage(torch_flare_spot)

SetRenderImage(render_screen)
For Local i:Int = 1 To 100
SetColor Rand(0, 100), Rand(0, 100), Rand(0, 100)
DrawRect Rand(0, 800), Rand(0, 600), Rand(50, 50), Rand(50, 50)
Next
SetColor 255, 255, 255

SetRenderImage(torch_flare_spot)
SetColor(255, 255, 255)
DrawRect(0, 0, 100, 100)

While Not AppTerminate() And Not KeyHit(KEY_ESCAPE)



SetRenderImage(Null)
Cls
DrawImage(render_screen, 0, 0)

SetBlend LIGHTBLEND
DrawImage(torch_flare_spot, MouseX (), MouseY ())
SetBlend ALPHABLEND

Flip 1

Wend


But the effect is not correct. What I am doing wrong?




TomToad

In your main loop, you want to first draw everything you want to see in white and everything you don't want to see in black.  You can also draw in shades of grey to simulate a dropoff at the edge of the light.  You can even use colors to tint the image.
After drawing the "light" you then use SHADEBLEND and draw your main image on top of that.

This example renders to the screen, then uses GrabImage() to grab the images, but this is done before the main loop so performance won't suffer.  In a real game, the screen will most likely change every loop, so that is where the Render2texture comes into play.
Code (blitzmax) Select
SuperStrict
Graphics 800,600

Local Screen:TImage = CreateImage(800,600)
Cls
For Local i:Int = 1 To 100
SetColor Rand(0,255),Rand(0,255),Rand(0,255)
DrawRect Rand(0,750),Rand(0,550),Rand(10,100),Rand(10,100)
Next
SetColor 255,255,255
GrabImage Screen,0,0

Local Torch:TImage = CreateImage(100,100)
MidHandleImage Torch
For Local i:Int = 0 To 20
SetColor i*12,i*12,i*12
DrawRect i,i,100-i*2,100-i*2
Next
SetColor 255,255,255
DrawRect 21,21,58,58
GrabImage(torch,0,0)

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
SetBlend SOLIDBLEND
Cls
DrawImage Torch,MouseX(),MouseY()

SetBlend SHADEBLEND
DrawImage Screen,0,0
Flip
Wend
------------------------------------------------
8 rabbits equals 1 rabbyte.

takis76

Somethis like this is closer to the effect I am looking.
I do not want to have the background total black but able to have some light volume.


SuperStrict
Graphics 800,600

Local Screen:TImage = CreateImage(800, 600)

Cls
For Local i:Int = 1 To 100
        SetColor Rand(0,255),Rand(0,255),Rand(0,255)
        DrawRect Rand(0,750),Rand(0,550),Rand(10,100),Rand(10,100)
Next
SetColor 255,255,255
GrabImage(Screen, 0, 0)

Local Torch:TImage = CreateImage(100, 100)
MidHandleImage Torch
For Local i:Int = 0 To 20
        SetColor i*12,i*12,i*12
        DrawRect i,i,100-i*2,100-i*2
Next

SetColor 255, 255, 255
DrawRect 21,21,58,58
GrabImage(Torch, 0, 0)

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
        SetBlend SOLIDBLEND
        Cls
        SetColor(100, 100, 100)
DrawImage Screen, 0, 0
SetColor(250, 250, 250)
DrawImage Torch,MouseX(),MouseY()
       
        SetBlend SHADEBLEND
        DrawImage Screen, 0, 0

        Flip
Wend


But the light have a black square line around is not translucent.

Somewere I need to put the SetBlend ALPHABLEND.

TomToad

If you look at the "torch", you will see that the black border was rendered to it.  I used a soft gradient at the edges so it doesn't look too sharp.  I assumed that all non-lit areas would be completely black, so that is why the edges are black.  If the surrounding areas are going to be brighter, then you need to make the edges of the torch brighter to blend in.  Try commenting out the render of the screen to see exactly how the torch is being rendered.  This modified version shows how to blend it with a brighter background.  Just use SetClsColor to set the overall brightness, and set the torch graphic to as bright as you want the lit area to be.
Code (blitzmax) Select
SuperStrict
Graphics 800,600

Local Screen:TImage = CreateImage(800, 600)

Cls
For Local i:Int = 1 To 100
        SetColor Rand(0,255),Rand(0,255),Rand(0,255)
        DrawRect Rand(0,750),Rand(0,550),Rand(10,100),Rand(10,100)
Next
SetColor 255,255,255
GrabImage(Screen, 0, 0)

Local Torch:TImage = CreateImage(100, 100)
MidHandleImage Torch
For Local i:Int = 0 To 18
        SetColor i*8+100,i*8+100,i*8+100
        DrawRect i,i,100-i*2,100-i*2
Next

SetColor 250, 250, 250
DrawRect 19,19,62,62
GrabImage(Torch, 0, 0)

SetClsColor 100,100,100
SetColor 255,255,255
While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
        SetBlend SOLIDBLEND
        Cls
DrawImage Torch,MouseX(),MouseY()
       
        SetBlend SHADEBLEND
        DrawImage Screen, 0, 0

        Flip
Wend
------------------------------------------------
8 rabbits equals 1 rabbyte.

Derron

An explanation (at least a try) why "render 2 texture" is needed when doing as TomToad suggested:

As I tried to explain already in the sample about "GUI menus" you would see the problem when replacing the "shadeblend + drawimage" thing with a "shadeblend + draw some static overlapping rectangles" command-chain.
The rectangles would "shine through" each other - or affect the colors of these rectangles.

This is why everything needs to be collapsed into a single layer / a single image first: our render screen image here. This image can then get tinted, darkened, ... without further hassle.



@ Torchlight effect
Without the whole grabimage/renderimage stuff you can fake the most simple torchlight effect by:

- render your game stuff
- render the image having the "torchlight gradient" (center completely transparent, to the outer the color fades to black). SetAlpha to your needs (0.0 = no torchlight, 1.0 = torchlight and outside black) Draw the torch image at the torchlight center.
- render black rectangles around the torchlight:
- - rectangle 0,0,w,yOfTorchlight
- - rectangle 0,yOfTorchlight,xOfTorchlight, hOfTorchlight
- - rectangle xOfTorchlight+wOfTorchlight, yOfTorchlight, hOfTorchlight
- - rectangle 0, yOfTorchlight+hOfTorchlight, wOfTorchlight, hOfApplication-yOfTorchlight-hOfTorchlight
- SetAlpha back to original

Render to texture could be used to draw multiple torchlights on the "torchlight image" - so you could simulate moving torches or flickering ones.

An alternative is to use ShadeBlend (or Lightblend) - in that case the torch image would go from white to black. Using "SetColor x,x,x" with x<255 you could then modify the strength of the effect. Scaling the torch image here and there would then create the "flicker effect" a fire-based-torch would have (moving flames ...).


bye
Ron

takis76

In the sample image I present how the effect looks like in the game.


1: Derron's Idea using one black image and with a smooth eraser we erase the center of this black image and the transparent area is visible and
around is black making the torch effect. The image should be large enough to cover all dungeon view.
2: Adding a second smaller light with the same technique using another one smaller black image and erasing smoothly the center we have a second light but we see the black area. In fact we do not see the black area. The sprite is smooth and not have black area around like we discussed in the first example, but you see the end of the sprite size. Both lights will not merge smoothly in the scene.
3: TomToad's Technique we have the light effect but all graphics act as lights on the view or act as a visible pixel. The simple example code looks nice but adding more sprites, all sprites become lights because after the SetBlend SHADEBLEND command everything will be shade blended.

Also you can't add color to the light. Only you can create another one erased image just the same but inverted and present it with the LIGHTBLEND and set some color and make it translucent with the setalpha(0.4) to make some colored light.


My initial code I used the grabimage in the game loop and it was slow that's because I was asking a way to have a quick grabbing a part of screen fast and that's I was tried to use the render2texture technique. But the lights were able to change color and were merged nicely in the scene.

Like this:




With the techniques you presented I can simulate the light but having multiple lights there is a problem you see the the limits of the sprites and their black areas around.
In fact Tomtoad's code is not correct.  It looks correct with 1 light and 1 background, but if you add and other graphics and more lights you will see what will happen. Also the light is different if is rounded or if it is square.


Graphics(800, 600, 0, 60)
SetMaskColor(255, 0, 255)

Local Screen:TImage = CreateImage(800, 600)
Local Smile:TImage

Smile = LoadImage("Smile.png", MASKEDIMAGE) '<---- I added this

Cls
For Local i:Int = 1 To 100
        SetColor Rand(0,255),Rand(0,255),Rand(0,255)
        DrawRect Rand(0, 750), Rand(0, 550), Rand(10, 100), Rand(10, 100)
Next
SetColor 255,255,255
GrabImage(Screen, 0, 0)

Local Torch:TImage = CreateImage(100, 100)
MidHandleImage Torch
For Local i:Int = 0 To 18
        SetColor i * 8 + 100, i * 8 + 100, i * 8 + 100
        DrawRect i,i,100-i*2,100-i*2
Next

SetColor 250, 250, 250
DrawRect 19,19,62,62
GrabImage(Torch, 0, 0)

SetClsColor 100,100,100
SetColor 255,255,255
While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
        SetBlend SOLIDBLEND
        Cls

        DrawImage Torch, MouseX(), MouseY()
               

        SetBlend SHADEBLEND
        DrawImage Screen, 0, 0
DrawImage Smile, 150, 50 '<----- I added this
               
        Flip
Wend


Use this for smile.png


In this code if you draw and other graphics they are not present well. This background with rectangles seems nice but if you put other graphics they are hiding behind the black area of the screen. In fact the colors of the background squares acts as lights that's you see the smile behind the colored rectangles and the first image of the game example all colors acts as a visible light and the other acts as darkness.


col

Try this as an expreriment :-

If you are clearing the 'render-texture' each frame using Cls you may see what you have.
The default Cls command clears to an opaque alpha value ( 255 ). You could try this ClearTo routine as a replacement for the default Cls routine to see if it helps - it will let you specify the red, green, blue and alpha values used to clear the currently set 'render texture'. Note that each render-texture is cleared to 0, 0, 0, 0 when it is initialised.



The gc:TGraphics parameter would be the value returned from a Graphics command call.
The Red, Green, Blue and Alpha parameters are to be between 0 and 255 inclusive.


Function ClearTo(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
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

Derron

@ "Derron's idea two lights"
Both lights would have to get rendered on a single image via render2texture. Think it would need a light/shade-blend to join multiple lights "nicely". This is because normal "alphablend" will additively add colors - 50% black + 50% black means final 75% black instead of staying at 50%.


Another idea is to have a rendertexture of the size of the screen, then draw your lights on it - and then draw the rendertexture on your normal game. Sounds simple? Yes, you would need to alter your light-image. You would need to have a "white in the center, transparent white outside" image. Then draw it normal (alphablended) on a black (or transparent)-cls'd render texture. That way you receive an image consisting of "white spots" fading out into transparency. If they overlap, the white will stay "white" (max light). Means if you want "two lights to be brighter than one light", you should not use "white" in the center, but "white at X percent transparency". If then two lamps overlap a bit, the transparencies will add as described above (50% white + another 50% white means 75% white at the end).
Afterwards you could use lightblend/shadeblend to your needs.

As said, use "render2texture" to easily draw stuff on images instead of doing a "drawing individual pixels on a pixmap"-basis (software rendering). You can draw texts on images and play with transparencies which was not possible with default max2d commands (50% + 50% = 75% you remember?, but draw both with 100% on a render texture and you can draw the resulting image with 50% alpha at the end).


bye
Ron

takis76

I am near:


SuperStrict

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

Small_Flare = LoadImage("Torch_Flare_1.png", MASKEDIMAGE)
Tree = LoadImage("Tree.png", MASKEDIMAGE)


Small_Torch1:TRenderImage = CreateRenderImage(Graph, 240, 244) 'Torch Flare
Visible_Screen:TRenderImage = CreateRenderImage(Graph, 800, 600) 'Game Visible Screen

Setrenderimage(Small_Torch1)

SetBlend LIGHTBLEND
DrawImage(Small_Flare, 0, 0)


MidHandleImage(Small_Torch1)


While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()


Setrenderimage(Small_Torch1)
ClearTo(Graph, 0, 0, 0, 0)
DrawImage(Small_Flare, 0, 0)

Setrenderimage(Visible_Screen)
ClearTo(Graph, 0, 0, 0, 0)
SetColor(255, 255, 255)
DrawImage(Tree, 0, 0)


SetRenderImage(Null)
ClearTo(Graph, 0, 0, 0, 0)


SetColor(100, 100, 100)
DrawImage(Visible_Screen, 0, 0)
SetColor(255, 255, 255)

SetBlend(LIGHTBLEND)
SetColor(255, 0, 0)
DrawImage(Small_Torch1, 100, 100)
SetColor(255, 255, 255)
DrawImage(Small_Torch1, MouseX(), MouseY())
SetBlend(ALPHABLEND)

       
Flip 1
Wend


For torch flare use this


For tree use this


I am trying to understand how this render2texture library works.



col

QuoteI am trying to understand how this render2texture library works.

A render-texture or TRenderImage is a texture that acts like a back buffer - you draw what you want into the image instead of to the back buffer. That image is then immediately available as a regular TImage without the need to use GrabImage.

For clarity I've used your example above ( not sure if that's the actual effect that you're looking for? ) and reproduced it using a render-texture as it is designed to be used. I added a little alpha to the white flare to reduce the saturation. Hopefully it can help clear up how a render-texture works:

SuperStrict

Import "renderimage.bmx"

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

Local Small_Flare:TImage = LoadImage("Torch_Flare_1.png", MASKEDIMAGE)
Local Tree:TImage = LoadImage("Tree.png", MASKEDIMAGE)
Local Visible_Screen:TRenderImage = CreateRenderImage(Graph, 800, 600) 'Game Visible Screen

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
' Render all of your game into a texture instead of to the backbuffer
' - this saves using GrabImage to create a TImage from the backbuffer :- you are drawing into a texture, just need to DrawImage the render-texture later.
' All graphics related commands will now affect the Visible_Screen texture instead of the backbuffer
Setrenderimage(Visible_Screen)
Cls

' draw the background to the render-texture
SetBlend SOLIDBLEND
SetColor(255, 255, 255)
DrawImage(Tree, 0, 0)

SetBlend LIGHTBLEND
SetAlpha(1)

' draw a red flare at top left into the render-texture
SetColor(255, 0, 0)
SetImageHandle(Small_Flare, 0, 0)
DrawImage(Small_Flare, 0, 0)

' draw a white flare centred at the mouse position
SetAlpha(0.5)
SetColor(255, 255, 255)
MidHandleImage(Small_Flare)
DrawImage(Small_Flare, MouseX(), MouseY())

' set to render to the backbuffer
Setrenderimage(Null)
SetBlend SOLIDBLEND
SetColor(255, 255, 255)
Cls

' draw the render-texture to the back buffer so it can be seen - this has saved using GrabImage
DrawImage(Visible_Screen, 0, 0)
       
Flip 1
Wend
https://github.com/davecamp

"When you observe the world through social media, you lose your faith in it."

TomToad

Here you go.  Use the Torch, Tree, and Smile images you posted before.
Code (blitzmax) Select
SuperStrict

Import "renderimage.bmx"

SetGraphicsDriver GLMax2DDriver()
Local Graph:TGraphics = Graphics(640,420, 0, 60)
SeedRnd(MilliSecs())

'load in the images
Local Small_Flare:TImage = LoadImage("Torch_Flare_1.png", MASKEDIMAGE)
Local Tree:TImage = LoadImage("Tree.png", MASKEDIMAGE)
SetMaskColor 255,0,255
Local Smile:TImage = LoadImage("Smile.png",MASKEDIMAGE)
Local Visible_Screen:TRenderImage = CreateRenderImage(Graph, 640,400) 'Game Visible Screen

'we will have some objects moving around on the screen
Type TSprite
Field x:Int
Field y:Int
Field dx:Int
Field dy:Int

Method New()
Global Direction:Int[] = [-1,1]
x = Rand(0,640)
y = Rand(0,400)
dx = Direction[Rand(0,1)]
dy = Direction[Rand(0,1)]
End Method

Method Update()
x :+ dx
y :+ dy
If x > 640 Then dx = -1
If x < 0 Then dx = 1
If y > 400 Then dy = -1
If y < 0 Then dy = 1
End Method
End Type
Local Smiles:TList = CreateList() 'a bunch of smiley faces

For Local i:Int = 1 To 5 'we will have 5 of them
Local Sprite:TSprite = New TSprite
Smiles.AddLast(Sprite)
Next

Local Torch1:TSprite = New TSprite 'Red torch
Local Torch2:TSprite = New TSprite 'Green Torch
Local Torch3:TSprite = New TSprite 'Blue torch

MidHandleImage(Small_Flare)
MidHandleImage Smile

'The ambient color of our scene
Local AmbientRed:Int = 50
Local AmbientGreen:Int = 50
Local AmbientBlue:Int = 50

While Not KeyHit(KEY_ESCAPE) And Not AppTerminate()
'We will first render all the graphics without lights onto the render texture
Setrenderimage(Visible_Screen)
SetColor 255,255,255
Cls
SetBlend AlphaBlend 'We want to use the image's alpha
DrawImage Tree,0,0
For Local Sprite:TSprite = EachIn Smiles 'Draw, then update each sprite in one go :)
DrawImage Smile,Sprite.x,Sprite.y
Sprite.Update()
Next

'Once we are done with that, we switch to the screen and render the lights
SetRenderImage(Null)
Cls
'We will cover the screen with a rectangle colored with our ambient light.
SetColor AmbientRed,AmbientGreen,AmbientBlue
DrawRect 0,0,640,400
'now we will draw the lights
SetBlend LightBlend 'use light blend so overlapping lights will add to thier color
SetColor 255,0,0
DrawImage Small_Flare,Torch1.x,Torch1.y
Torch1.Update() 'move the torch
SetColor 0,255,0
DrawImage Small_Flare,Torch2.x,Torch2.y
Torch2.Update()
SetColor 0,0,255
DrawImage Small_Flare,Torch3.x,Torch3.y
Torch3.Update()

'We will draw a white light where the mouse is
SetColor 255,255,255
DrawImage Small_Flare,MouseX(),MouseY()

' Now we will draw our previously rendered texture with Shadeblend, which will darken the areas
'   not exposed to the light
SetBlend ShadeBlend
DrawImage Visible_Screen,0,0

'Draw the ambient values to the screen
SetBlend AlphaBlend
DrawRect 0,400,640,20
SetColor 0,0,0
DrawText "R:"+String(AmbientRed)[..3]+"; G:"+String(AmbientGreen)[..3]+";B:"+String(AmbientBlue)[..3]+ ..
"  Keys 1-6 adjust ambient color",10,402
Flip

'Now lets adjust the ambient light a bit so we can see the effect
If KeyDown(KEY_1) Then AmbientRed = Max(0,AmbientRed-1)
If KeyDown(KEY_2) Then AmbientRed = Min(255,AmbientRed+1)
If KeyDown(KEY_3) Then AmbientGreen = Max(0,AmbientGreen-1)
If KeyDown(KEY_4) Then AmbientGreen = Min(255,AmbientGreen+1)
If KeyDown(KEY_5) Then AmbientBlue = Max(0,AmbientBlue-1)
If KeyDown(KEY_6) Then AmbientBlue = Min(255,AmbientBlue+1)

Wend
------------------------------------------------
8 rabbits equals 1 rabbyte.