Hello
I want to open this thread because I am very interested in this topic.
Probably, however, as often happens to me I will not find interested people, but I try anyway.
preamble.
Some time ago I came across a rendering technique called RayMarching. In my opinion it is the most interesting that exists. Because it allows you to achieve superb effects and quality without having to deal with Meshes.
It consists in having as input the screen pixel coordinates (UV) normalized in the range (-1 to 1, -1 to 1).
For each coordinate a Ray is started from the observation point to it (screen pixel).
Using this Ray we measure the distance from the objects we want to represent using the SDF technique.
Once the distance has been obtained, the normal of the encountered surface is calculated.
This normal (together with that of the direction of a light source) is used to calculate the diffusion and refraction of light.
In addition, the shadow can also be calculated.
All this if you intend to create a "realistic" rendering, but there is also the possibility of creating "abstract" outputs.
Here you can see an exhaustive and better explanation of the above.
OK, all great, but then what's the problem?
Well, it took a lot of processing time to get these animations. (many seconds per frames)
For each pixel the amount of calculations to be carried out is truly remarkable and performing this with VB6 is an infinite slowness.
I am sure it is possible to realize RayMarching using the HLSL code. (integrated via wqweto library with VB6)
And not taking advantage of this opportunity would be a mortal sin.
What we need is someone quite knowledgeable about VertexShader and FragmentShader (PixelShader)
That is all for the moment.
v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v v EDIT 29/12/2020
Progress has been made. In this starter post I will keep the main content list updated.
As a starting point I used wqweto tutorial 03.
(this uses a texture but I don't use it, but I started from here because of the Quad)
As a reference I used the "RayMarching for Dummy" video and wrote the code in HLSL. (with small customizations)
The first version is in the "01. Test" folder
I discovered a very sad thing, it seems that the HLSL cannot access the Time or GetTime (iTime) function so in this demo the light is static.
Good start, then will come other questions and problems to solve ...
such as:
- How to pass variables (inside Do While m_isRunning LOOP) from VB6 to Shader ?
and some other ones a little bit off-topic (but surely interesting): - How to pass an image (Texture) from VB6 memory to Shader ?
- How to retrieve the shader output pixels from it to VB6 ?
Last edited by reexre; Dec 14th, 2020 at 05:40 PM.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
Originally Posted by baka
it seems to be #Const DebugBuild = True
need to be set to false or I can't run it!
That is weird because reexre sample is retrying D3D11CreateDevice w/o D3D11_CREATE_DEVICE_DEBUG flag if initialization failed.
The original problem is that you don't have DX11 SDK installed (which is normal) so can use the runtime only in release mode (no debug facilities shipped in OS provided one).
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
I managed to add the variable time (essential.)
After countless attempts using the DX11 TypeLib I gave up.
I found an excellent solution: Using the free TV3D engine.
Just Download and install "TV3D 6.5 SDK Only" http://www.truevision3d.com/downloads.php (few MBs)
With TV3D it's really much simpler!
Although I admit that I ran into quite a few difficulties that I solved with intensive research on the internet.
I am happy with the result. It is also very easy to pass Variables from VB6 to the shader (as in Godot).
The visible result (three objects in black and white) is not very interesting for the moment, but look at the FPS!
I just finished it right now and without much improvement I shared it, couldn't wait.
Download the Code and Take a look GitHub Repo ZIP
Files are in folder "03 TRUEVISION3D OK"
Last edited by reexre; Dec 27th, 2020 at 07:13 PM.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
This is done using my example above as a starting point.
It runs at around 25 FPS
ie it draws a frame in 1/25 of a second while using pure VB6 it took about 1.5 - 2 minutes per frame.
Original 480p
(the LOW FPS displayed is due to saving to disk each frame. )
Last edited by reexre; Dec 27th, 2020 at 09:07 PM.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
Originally Posted by The trick
I made the example of using your HLSL code in DirectX9. See example here.
...
Fantastic ! Thanks!
Well, I'll study it!
I'll point out two things:
one is the oversight in the link. correct the last word of the link: Raymatching -> Raymarching
The other one is more important:
Please on gituhub change the .gitattributes (<- Here is what I use) file otherwise there are problems with the interpretation of the newline character, and it does not open the files correctly, for example when I open a .FRM file in VB6 it appears even this in the editor (of VB6 IDE)
Code:
VERSION 5 #
Begin VB.Form frmMain
BorderStyle = 1 'Fixed Single
Caption = "Raymarching using Direct3D9 shaders"
ClientHeight = 8085
ClientLeft = 45
ClientTop = 375
ClientWidth = 12705
LinkTopic = "Form1"
MaxButton = 0 'False
MinButton = 0 'False
ScaleHeight = 8085
ScaleWidth = 12705
StartUpPosition = 3 'Windows Default
Begin VB.Timer tmrFPS
Interval = 1000
Left = 4320
Top = 3540
End
End
# Set the default behavior, in case people don't have core.autocrlf set.
* binary
*.bas text eol=crlf
*.frm text eol=crlf
*.ctl text eol=crlf
*.log text eol=crlf
*.vbp text eol=crlf
*.cls text eol=crlf
*.vbw text eol=crlf
*.dsr text eol=crlf
*.ini text eol=crlf
*.txt text eol=crlf
*.cfg text eol=crlf
*.md text eol=crlf
don't know which one is better ...
(should be enough to put it in the root folder.)
Last edited by reexre; Dec 28th, 2020 at 08:24 PM.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
one is the oversight in the link. correct the last word of the link: Raymatching -> Raymarching
Oh yes, was my mistake. I've fixed.
Please on gituhub change the .gitattributes (<- Here is what I use) file otherwise there are problems with the interpretation of the newline character, and it does not open the files correctly, for example when I open a .FRM file in VB6 it appears even this in the editor (of VB6 IDE)
Thanks, i've fixed that (always forgot about the .gitignore/.gitattributes files).
# Set the default behavior, in case people don't have core.autocrlf set.
* binary
*.bas text eol=crlf
*.frm text eol=crlf
*.ctl text eol=crlf
*.log text eol=crlf
*.vbp text eol=crlf
*.cls text eol=crlf
*.vbw text eol=crlf
*.dsr text eol=crlf
*.ini text eol=crlf
*.txt text eol=crlf
*.cfg text eol=crlf
*.md text eol=crlf
Must set diff option like this
* binary diff
. . . otherwise risk loosing the ability to inspect diffs in visual tools like SourceTree. (I forgot to set it on all 16000+ PSC repos so I know from first hand. :-))
This config is missing on quite a few native VB6 source files (like .pag, .dob, .vbr, etc.) but includes .md, .ini and more non-VB6 non-source files for no apparent reason. Keeping config files in binary is enough not to screw line-endings because in binary whatever the original file uses for line-endings is kept in the source control too. On the other hand marking non-VB6 source files as text has some merrit for inspecting diffs only.
For a full list of native VB6 source file + non-VB6 source files (like C/C++, PHP, whatever) as extracted by file extension from all 16000+ PSC repos you can check out this .gitattributes.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
for a full list of native vb6 source file + non-vb6 source files (like c/c++, php, whatever) as extracted by file extension from all 16000+ psc repos you can check out this .gitattributes.
Now I have to take other steps to complete this class:
- How do you add a "variable"?
- How do you add / manage multiple variables?
- How do the Array variables work (in this case)? (Array of float array of float3)
Then there are others (but in case we see later) such as:
- Send Bytes array of texture from VB6 to shader
- (Inside Shader duplicate in and manipulate)
- Get BytesArray of manipulated texture back to VB6
I hope you can Help!
Last edited by reexre; Dec 29th, 2020 at 09:23 AM.
Re: RAYMARCHING using wqweto DirectX 11 for VB6 Type Library
Originally Posted by The trick
I can help you but i need the working shader (a typical case of your task) because i don't fully understand what's exactly you try to do.
Main Topic:
1) How to have/manage multiple Variables ( the using of cPSConstTbl)
and other types of variables such as Arrays and FLOAT3 I'll show here a/some working shader examples as soon as I can.
2) Secondary. The other questions I put are not related to RayMarching, but about a sort of image-manipulation done through the GPU. We can see them in a second time.
- Send Bytes array of texture from VB6 to shader
- Get BytesArray of manipulated texture back to VB6
The main difference from the other example is that it has more variables ... also of type FLOAT3
In TV3D it is very simple: there are the functions: Shader.SetEffectParamFloat ()
Shader.SetEffectParamVector3 ()
And this: (that I have yet to understand how it works) Shader.SetEffectParamVectorArray3 ()
I would like to be able to pass Vector3 arrays (even two-dimensional)
In the Shader code the variables (called Parameters / Registers) to be exchanged are described as follows: uniform float TIME1 = 0.0; // Variabliles passed from / To VB6
uniform float3 CAMLOOKAT = float3 (10,0,10);
Then with TV3D you can Read (GetEffectParamVector3) or Write (SetEffectParamVector3)
PS: This has camera Vector up (0,1,0)
Last edited by reexre; Dec 30th, 2020 at 06:16 AM.
I changed my original example to use the new shader. I made the small changes in the shader because your shader tries to change the constant 'SunLight'. I've added a global variable. Because of the shader doesn't use the TIME1 constant the optimizer removes it (you can disable optimizer if you need).
The SetPixelShaderConstantF method accepts the 4-float-vectors argument so you just use SetPixelShaderConstantF reg, vec, 1 which means set a vector argument.
...
The SetPixelShaderConstantF method accepts the 4-float-vectors argument so you just use SetPixelShaderConstantF reg, vec, 1 which means set a vector argument.
...
thanks!
I was able to pass arrays too.
But I was a little confused in regards to
the second term of SetPixelShaderConstantF which should be a float4, instead your example passes a D3DVECTOR which is float3 (tCamPos).
From what I understand it should be D3DVECTOR4.
For only one value this still works but when passing an array things go wrong and you need to use D3DVECTOR4.
When I pass an array and use D3DVECTOR4 I suppose that even in the HLSL shader side the type must be defined as float4, right? ( float4 myarray[] )
(when, in this context, I use float3 in the HLSL sometimes it goes sometimes not.)
thanks!
I was able to pass arrays too.
But I was a little confused in regards to
the second term of SetPixelShaderConstantF which should be a float4, instead your example passes a D3DVECTOR which is float3 (tCamPos).
From what I understand it should be D3DVECTOR4.
For only one value this still works but when passing an array things go wrong and you need to use D3DVECTOR4.
When I pass an array and use D3DVECTOR4 I suppose that even in the HLSL shader side the type must be defined as float4, right? ( float4 myarray[] )
(when, in this context, I use float3 in the HLSL sometimes it goes sometimes not.)
It's because optimization. The shaders use only the 3 components so we can send D3DVECTOR (the fourth component contains the garbage from the stack). When you pass an array you should pass D3DVECTOR4 (or matrix) of course.
About TheTrick DX9
I have noticed that there are often problems compiling the HLSL shader, especially if it is complex.
Sometimes it takes several minutes.
To get around this problem I discovered that in the function D3DXCompileShaderFromFile
there is a very useful option, that is to use FLAGS
So, if you are testing a shader, modifying it frequently to see the results I suggest to use the following Flags so that the compilation will take few seconds. ' FLAGS -----------------------------------------------------------------
' https://docs.microsoft.com/en-us/win...dxshader-flags
' &H1000 D3DXSHADER_ENABLE_BACKWARDS_COMPATIBILITY
' &H4000 D3DXSHADER_OPTIMIZATION_LEVEL0
' &H20 D3DXSHADER_PARTIALPRECISION
hi TheTrick
As far as RayMarching is concerned I am now at a very good point.
I would like to talk/develop the second topic here.
Here is what it consists of:
1) Load a texture or byteArray rapresenting an image (RGB) or (RGBA) in VB6.
2) Pass this Texture to the Pixel/Fragment Shader.
3) Process it. Apply some image-filter by using PixelShader via HLSL.
(The modification will be done using a new texture or the output of PS itself)
4) Get back the result to VB6 as Texutre or ByteArray.
In this example video you can see one that did it [ https://www.youtube.com/watch?v=UxuFidrukgQ ]
Using GPU via HLSL to perform a graphic effect should be immensely faster than using CPU.
I'll answer later because i have a lot of projects right now which i need to complete. There are the several examples where i used texture check them out.
This version is dynamic (uses timers), but that's just for this demo.
I made some not very significant effects, just to experiment.
Questions:
1 - How to get the (displayed) output Bytes of the modified image (texture) ? [This of course regarding a "static" image taken at a given time].
2 - Is it possible to send a Texture without loading it from a file ? That is, using an array of Bytes present in VB6?
Ok, these for the moment are the main questions.
When you have time I hope to have some answers. (by the way, anyone interested who knows something is kindly requested to speak)
One last thing, I would like (as I did) to use a DX Class in order to have a sort of reusability.
(EG: not all in one vbForm).
Questions:
1 - How to get the (displayed) output Bytes of the modified image (texture) ? [This of course regarding a "static" image taken at a given time].
2 - Is it possible to send a Texture without loading it from a file ? That is, using an array of Bytes present in VB6?
Ok, these for the moment are the main questions.
When you have time I hope to have some answers. (by the way, anyone interested who knows something is kindly requested to speak)
One last thing, I would like (as I did) to use a DX Class in order to have a sort of reusability.
(EG: not all in one vbForm).
Can TheTrick or someone else please help me?
I have searched the internet extensively but have not found any solutions.
The 1st question is difficult and I think it has two ways of solution.
1 - Copy the output of the DX-Device to a texure and get it with Device.GetTexture. (and then get the bytes of the texture)
2 - Get the output of the DX-Device as bytes/image/picture directly with some other system.
Regarding the question 2nd
I think we have to use GdiPlus of which I know almost anything including hImage, hbitmapGdipCreateFromHDC hGraphic _GDIPlus_BitmapCreateFromHBITMAP
For someone familiar with GdiPlus it should not be difficult to create a DirectX texture from a Picture or ByteArray.
@reexre,
please pay attention to this example. It shows how to pass the texture between GDI/Direct3D. This example shows how to extract the data from Direct3D rendering result to GDI+/GDI.
@reexre,
please pay attention to this example. It shows how to pass the texture between GDI/Direct3D. This example shows how to extract the data from Direct3D rendering result to GDI+/GDI.
Ok I was able to set the DX texture from a vb picturebox.
Code:
Public Sub TextureFromPictureBox(PIC_hDC As Long, PIC_hWnd&, PIC_W As Long, PIC_H As Long)
Dim tmpDC As Long
Dim oldBmp As Long
Dim rect As D3DLOCKED_RECT
Dim delta As Long
Dim lpDat As Long
biWnd.bmiHeader.biSize = Len(biWnd.bmiHeader)
biWnd.bmiHeader.biBitCount = 32
biWnd.bmiHeader.biHeight = -PIC_H '-Me.Height / Screen.TwipsPerPixelY
biWnd.bmiHeader.biWidth = PIC_W 'Me.Width / Screen.TwipsPerPixelY
biWnd.bmiHeader.biPlanes = 1
bmpShadow = CreateDIBSection(PIC_hDC, biWnd, 0, lpBmpData, 0, 0)
m_cDevice.CreateTexture biWnd.bmiHeader.biWidth, -biWnd.bmiHeader.biHeight, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, m_cTexture
' // Create the bitmap with the contents of window
tmpDC = CreateCompatibleDC(PIC_hDC)
oldBmp = SelectObject(tmpDC, bmpShadow)
PrintWindow PIC_hWnd, tmpDC, 0
SelectObject tmpDC, oldBmp
DeleteDC tmpDC
' // Move data from the bitmap to the m_cTexture
m_cTexture.LockRect 0, rect, ByVal 0&, 0
lpDat = lpBmpData
' // Copy each scan-line
For delta = 0 To (-biWnd.bmiHeader.biHeight) - 1
memcpy ByVal rect.pBits, ByVal lpDat, biWnd.bmiHeader.biWidth * 4
rect.pBits = rect.pBits + rect.Pitch
lpDat = lpDat + biWnd.bmiHeader.biWidth * 4
Next
m_cTexture.UnlockRect 0
m_cDevice.SetTexture 0, m_cTexture
Dim V As D3DVECTOR
V.X = biWnd.bmiHeader.biWidth
V.Y = -biWnd.bmiHeader.biHeight
Me.SetVariableFloat3 Me.getRegisterNum("texRes"), V
End Sub
[ There is probably something "redundant" in this code (although it works). Which I don't know how it could be simplified. Because it was "extracted" from code used to create a texture from a Window (not a picturebox). ]
Now, in addition to the other question, it remains how to set a texture from a bytearray.
I came up with an extremely easy way to set a DX-texture from a ByteArray.
In this case the ByteArray width is 4 times the width of the image, each value represents the colors B G R A.
The origin coordinate is (0,0) [not (1,1)].
EG before calling BytesArrayToTexture
Code:
W = 127 : H = 127 : ReDim BGRA((W + 1) * 4 - 1, H) 'ReDim BGRA(W * 4 + 3, H)
For X = 0 To W
For Y = 0 To H
BGRA(X * 4 + 0, Y) = 255
BGRA(X * 4 + 1, Y) = X * 255 / W
BGRA(X * 4 + 2, Y) = Y * 255 / H
BGRA(X * 4 + 3, Y) = 255
Next
Next
Code:
Public Sub BytesArrayToTexture(BGRA() As Byte, TextureSamplerDest As Long)
' BGRA = Bytes array (Width is 4x)
Dim rect As D3DLOCKED_RECT
Dim DXtexture As IDirect3DTexture9
Dim W&, H&, Nbytes&
W = UBound(BGRA, 1)
H = UBound(BGRA, 2)
Nbytes = (W + 1) * (H + 1)
m_cDevice.CreateTexture (W + 1) \ 4, H + 1, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, DXtexture
DXtexture.LockRect 0, rect, ByVal 0&, 0
memcpy ByVal rect.pBits, BGRA(0, 0), Nbytes
DXtexture.UnlockRect 0
m_cDevice.SetTexture TextureSamplerDest, DXtexture
End Sub
Last edited by reexre; Mar 10th, 2021 at 07:13 AM.
Using the code above as a starting point, and with some research , here is the code to get the output of the DX device as a ByteArray.
I haven't tested it well yet and I'm not sure if it works correctly yet.
More information will follow.
Suggestions are of course welcome.
Code:
Public Sub GetDeviceOutAsBytes(OUT() As Byte)
Dim RECT As D3DLOCKED_RECT
Dim SRF As IDirect3DSurface9
Dim W&, H&, Nbytes&
' Dim pp As D3DDEVICE_CREATION_PARAMETERS
' Dim wr As twRECT
' m_cDevice.GetCreationParameters pp
' GetClientRect pp.hFocusWindow, wr
' W = (wr.Right - wr.Left) * 4 - 1
' H = wr.Bottom - wr.Top - 1
W = m_WW * 4 - 1
H = m_HH - 1
ReDim OUT(W, H)
Nbytes = (W + 1) * (H + 1)
m_cDevice.CreateOffscreenPlainSurface (W + 1) \ 4, H + 1, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, SRF
m_cDevice.GetRenderTargetData m_cDevice.GetRenderTarget(0), SRF
SRF.LockRect RECT, ByVal 0&, D3DLOCK_READONLY
memcpy OUT(0, 0), ByVal RECT.pBits, Nbytes
SRF.UnlockRect
End Sub
Last edited by reexre; Mar 10th, 2021 at 06:28 AM.
I am planning to deploy an application that uses the last technique shown.
1) What DX version will the user need on his system? (Will a version higher than 9 work?)
2) Will it be sufficient to put dx9vb.tbl and d3dxvb.tbl in the root directory of the program?
3) How to make sure (algorithm/function) that the user's system has the necessary characteristics to work? (and if not, show an appropriate message to the user)
Last edited by reexre; Mar 10th, 2021 at 07:33 AM.
1) What DX version will the user need on his system? (Will a version higher than 9 work?)
DirectX9 and higher (installed by default).
Originally Posted by reexre
2) Will it be sufficient to put dx9vb.tbl and d3dxvb.tbl in the root directory of the program?
You don't need to redistribute tlb at all. A single executable is enough. If you use d3dx9_xx.dll it should be reditributed.
Originally Posted by reexre
3) How to make sure (algorithm/function) that the user's system has the necessary characteristics to work? (and if not, show an appropriate message to the user)