The power of GPU shaders

Discussions related to graphics (2D and 3D), animation and games programming
Zaphod
Posts: 78
Joined: Sat 23 Jun 2018, 15:51

Re: The power of GPU shaders

Post by Zaphod » Sat 19 Jan 2019, 15:42

You will note that this thread only mentions BBCSDL. I suspect you were trying the program on BB4W which does not have the same libraries or support for graphics chips that the SDL version offers.

Z

Interloper
Posts: 3
Joined: Sun 20 Jan 2019, 21:55

Re: The power of GPU shaders

Post by Interloper » Sun 20 Jan 2019, 22:07

Zaphod wrote:
Sat 19 Jan 2019, 15:42
BB4W which does not have the same ... support for graphics chips that the SDL version offers.
Why would BB4W have poorer "support for graphics chips" than BBCSDL? BB4W's principal advantage over BBCSDL is its support for Windows 98, 2000 and XP, but those would be very unlikely to have GPUs with a shader capability anyway, so I can't see much point in porting the seascape demo. But if you must...

Code: Select all

REM 'Procedural Seascape' by Alexander Alekseev, tdmaav@gmail.com, 2014
REM BB4W version by Richard Russell, www.rtrussell.co.uk, 20-Jan-2019

REM Create arrays:
DIM Object%(17), Vertex$(10), Fragment$(999), Float%(1)

REM Fill vertex and fragment shader arrays from DATA statements:
PROCreadshader(Vertex$())
PROCreadshader(Fragment$())

REM Create a simple square object:
Object%() = FN_f4(+1), FN_f4(+1), FN_f4(0), FN_f4(-1), FN_f4(+1), FN_f4(0), \
\     FN_f4(-1), FN_f4(-1), FN_f4(0), FN_f4(-1), FN_f4(-1), FN_f4(0), \
\     FN_f4(+1), FN_f4(-1), FN_f4(0), FN_f4(+1), FN_f4(+1), FN_f4(0)

REM Set OpenGL constants:
PFD_MAIN = 0
PFD_TYPE_RGBA = 0
PFD_DOUBLEBUFFER = 1
PFD_DRAW_TO_WINDOW = 4
PFD_SUPPORT_OPENGL = &20
GL_FLOAT = &1406
GL_TRIANGLES = 4
GL_COMPILE = &1300
GL_FRAGMENT_SHADER = &8B30
GL_VERTEX_SHADER = &8B31
GL_COMPILE_STATUS = &8B81
GL_LINK_STATUS = &8B82
GL_INFO_LOG_LENGTH = &8B84

REM Initialise window and get its size:
ON MOVE IF @msg% <> 5 RETURN ELSE PROCcleanup
VDU 26
IF POS REM SDL thread sync
ScrW% = @vdu%!208
ScrH% = @vdu%!212

REM Ensure cleanup on exit:
ON CLOSE PROCcleanup : QUIT
ON ERROR PROCcleanup : IF ERR=17 MODE 3 : PRINT REPORT$ " at line "; ERL : END

REM Get addresses of OpenGL functions:
SYS "LoadLibrary", "OPENGL32.DLL" TO ogl%
SYS "GetProcAddress", ogl%, "wglCreateContext"  TO `wglCreateContext`
SYS "GetProcAddress", ogl%, "wglDeleteContext"  TO `wglDeleteContext`
SYS "GetProcAddress", ogl%, "wglMakeCurrent"    TO `wglMakeCurrent`
SYS "GetProcAddress", ogl%, "wglGetProcAddress" TO `wglGetProcAddress`
SYS "GetProcAddress", ogl%, "glGenLists"  TO `glGenLists`
SYS "GetProcAddress", ogl%, "glNewList"   TO `glNewList`
SYS "GetProcAddress", ogl%, "glEndList"   TO `glEndList`
SYS "GetProcAddress", ogl%, "glCallList"  TO `glCallList`
SYS "GetProcAddress", ogl%, "glVertexPointer"   TO `glVertexPointer`
SYS "GetProcAddress", ogl%, "glDrawArrays"TO `glDrawArrays`

REM Create OpenGL context:
DIM pfd{Size{l&,h&},Version{l&,h&},Flags%,PixelType&,ColorBits&,\
\ RedBits&,RedShift&,GreenBits&,GreenShift&,BlueBits&,BlueShift&,\
\ AlphaBits&,AlphaShift&,AccumBits&,AccumRedBits&,AccumGreenBits&,\
\ AccumBlueBits&,AccumAlphaBits&,DepthBits&,StencilBits&,AuxBuffers&,\
\ LayerType&,Reserved&,LayerMask%,VisibleMask%,DamageMask%}
pfd.Size.l&=DIM(pfd{})
pfd.Version.l&=1
pfd.Flags%=PFD_DRAW_TO_WINDOW OR PFD_SUPPORT_OPENGL OR PFD_DOUBLEBUFFER
pfd.LayerMask%=PFD_MAIN
pfd.PixelType&=PFD_TYPE_RGBA
pfd.ColorBits&=24
pfd.DepthBits&=16
SYS"GetDC",@hwnd% TO ghDC%
SYS"ChoosePixelFormat",ghDC%,pfd{} TO pf%
SYS"SetPixelFormat",ghDC%,pf%,pfd{}
SYS `wglCreateContext`,ghDC% TO ghRC%
SYS `wglMakeCurrent`,ghDC%,ghRC%

REM Get addresses of more OpenGL functions:
`glCreateShader`= FNgpa("glCreateShader")
`glCreateShader`= FNgpa("glCreateShader")
`glAttachShader`= FNgpa("glAttachShader")
`glDeleteShader`= FNgpa("glDeleteShader")
`glShaderSource`= FNgpa("glShaderSource")
`glCompileShader`     = FNgpa("glCompileShader")
`glGetShaderiv` = FNgpa("glGetShaderiv")
`glCreateProgram`     = FNgpa("glCreateProgram")
`glDeleteProgram`     = FNgpa("glDeleteProgram")
`glLinkProgram` = FNgpa("glLinkProgram")
`glGetProgramiv`= FNgpa("glGetProgramiv")
`glGetShaderInfoLog`  = FNgpa("glGetShaderInfoLog")
`glGetProgramInfoLog` = FNgpa("glGetProgramInfoLog")
`glUseProgram`  = FNgpa("glUseProgram")
`glGetUniformLocation`= FNgpa("glGetUniformLocation")
`glBindAttribLocation`= FNgpa("glBindAttribLocation")
`glVertexAttribPointer`= FNgpa("glVertexAttribPointer")
`glEnableVertexAttribArray`= FNgpa("glEnableVertexAttribArray")
`glUniform1fv`  = FNgpa("glUniform1fv")
`glUniform2fv`  = FNgpa("glUniform2fv")

REM Create shader objects:
SYS `glCreateShader`, GL_VERTEX_SHADER TO oVertex%
SYS `glCreateShader`, GL_FRAGMENT_SHADER TO oFragment%

REM Compile shaders:
PROCcompileshader(oVertex%, Vertex$())
PROCcompileshader(oFragment%, Fragment$())

REM Create program object and link:
SYS `glCreateProgram` TO oProgram%
SYS `glAttachShader`, oProgram%, oVertex%
SYS `glAttachShader`, oProgram%, oFragment%
SYS `glBindAttribLocation`, oProgram%, 0, "vPosition"
SYS `glLinkProgram`, oProgram%
SYS `glGetProgramiv`, oProgram%, GL_LINK_STATUS, ^linked%
IF linked% = 0 THEN
  SYS `glGetProgramiv`, oProgram%, GL_INFO_LOG_LENGTH, ^blen%
  DIM plog% blen%
  SYS `glGetProgramInfoLog`, oProgram%, blen%, ^slen%, plog%
  ERROR 100, "Program object failed to link:" + CHR$&D + CHR$&A + LEFT$($$plog%,220)
ENDIF

REM Use shaders:
SYS `glUseProgram`, oProgram%
SYS `glGetUniformLocation`, oProgram%, "iTime" TO pTime%
SYS `glGetUniformLocation`, oProgram%, "iMouse"TO pMouse%
SYS `glGetUniformLocation`, oProgram%, "iResolution" TO pResolution%

REM Set and enable vertex array pointer:
SYS `glVertexAttribPointer`, 0, 3, GL_FLOAT, FALSE, 0, ^Object%(0)
SYS `glEnableVertexAttribArray`, 0

REM CreateObject:
SYS `glGenLists`,1 TO List%
SYS `glNewList`, List%, GL_COMPILE
SYS `glVertexPointer`, 3, GL_FLOAT, 0, ^Object%(0)
SYS `glDrawArrays`, GL_TRIANGLES, 0, 6
SYS `glEndList`

REM Render loop:
REPEAT
  MOUSE X%,Y%,B%
  ftime% = FN_f4(TIME/100)
  IF B%=0 SYS `glUniform1fv`, pTime%, 1, ^ftime%
  Float%() = FN_f4(X%/2),FN_f4(Y%/2)
  SYS `glUniform2fv`, pMouse%, 1, ^Float%(0)
  Float%() = FN_f4(ScrW%),FN_f4(ScrH%)
  SYS `glUniform2fv`, pResolution%, 1, ^Float%(0)
  SYS `glCallList`, List%
  SYS "SwapBuffers", ghDC%
  WAIT 1
UNTIL FALSE
END

DEF PROCcleanup
ON ERROR OFF
*REFRESH ON
oProgram% += 0  : IF oProgram%  SYS `glDeleteProgram`, oProgram%
oVertex% += 0   : IF oVertex%   SYS `glDeleteShader`, oVertex%
oFragment% += 0 : IF oFragment% SYS `glDeleteShader`, oFragment%
ghRC% += 0: IF ghRC%SYS `wglDeleteContext`, ghRC%
ghDC% += 0: IF ghDC%SYS "ReleaseDC", @hwnd%, ghDC%
ENDPROC

DEF PROCreadshader(shader$())
LOCAL I%, a$
shader$(0) = "#version 110" + CHR$&A
REPEAT
  READ a$
  I% += 1
  shader$(I%) = a$ + CHR$&A : REM LF-terminate
UNTIL a$ = ""
ENDPROC

DEF PROCcompileshader(object%, shader$())
LOCAL compiled%, blen%, slen%, code%, plog%, code$
code$ = SUM(shader$()) + CHR$0
code% = PTR(code$)
SYS `glShaderSource`, object%, 1, ^code%, FALSE
SYS `glCompileShader`, object%
SYS `glGetShaderiv`, object%, GL_COMPILE_STATUS, ^compiled%
IF compiled% = 0 THEN
  SYS `glGetShaderiv`, object%, GL_INFO_LOG_LENGTH, ^blen%
  DIM plog% blen%
  SYS `glGetShaderInfoLog`, object%, blen%, ^slen%, plog%
  ERROR 100, "Shader failed to compile:" + CHR$&D + CHR$&A + LEFT$($$plog%,220)
ENDIF
ENDPROC

DEF FNgpa(function$)
LOCAL function%
SYS `wglGetProcAddress`, function$ TO function%
= function%

REM Convert to 4-byte float
DEF FN_f4(a#)LOCALp%%:p%%=^a#:IFABSa#<1E-38THEN=FALSE
=p%%!4ANDNOT&7FFFFFFFOR(p%%!4-&38000000<<3OR!p%%>>29AND7)+(!p%%>>28AND1)

REM Minimal vertex shader:
DATA "attribute vec4 vPosition;"
DATA "void main()"
DATA "{"
DATA "gl_Position = vPosition ;"
DATA "}"
DATA ""

REM Fragment Shader code from https://www.shadertoy.com/view/Ms2SD1
DATA "uniform float iTime;"
DATA "uniform vec2 iMouse;"
DATA "uniform vec2 iResolution;"
DATA "uniform sampler2D iChannel0;"

DATA "/*"
DATA "* 'Seascape' by Alexander Alekseev aka TDM - 2014"
DATA "* License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License."
DATA "* Contact: tdmaav@gmail.com"
DATA "*/"

DATA "const int NUM_STEPS = 8;"
DATA "const float PI = 3.141592;"
DATA "const float EPSILON= 1e-3;"
DATA "#define EPSILON_NRM (8e-4)"

DATA "// sea"
DATA "const int ITER_GEOMETRY = 3;"
DATA "const int ITER_FRAGMENT = 5;"
DATA "const float SEA_HEIGHT = 0.8;"
DATA "const float SEA_CHOPPY = 4.0;"
DATA "const float SEA_SPEED = 0.8;"
DATA "const float SEA_FREQ = 0.16;"
DATA "const vec3 SEA_BASE = vec3(0.1,0.19,0.22);"
DATA "const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6);"
DATA "#define SEA_TIME (iTime * SEA_SPEED)"
DATA "const mat2 octave_m = mat2(3.04,2.28,-2.28,3.04);"

DATA "// math"
DATA "mat3 fromEuler(vec3 ang) {"
DATA "vec2 a1 = vec2(sin(ang.x),cos(ang.x));"
DATA "vec2 a2 = vec2(sin(ang.y),cos(ang.y));"
DATA "vec2 a3 = vec2(sin(ang.z),cos(ang.z));"
DATA "mat3 m;"
DATA "m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x);"
DATA "m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x);"
DATA "m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y);"
DATA "return m;"
DATA "}"

DATA "float hash( vec2 p ) {"
DATA "float h = dot(p,vec2(127.1,311.7));"
DATA "return fract(sin(h)*43758.5453123);"
DATA "}"

DATA "float noise( in vec2 p ) {"
DATA "vec2 i = floor( p );"
DATA "vec2 f = fract( p );"
DATA "vec2 u = f*f*(3.0-2.0*f);"
DATA "return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ),"
DATA "hash( i + vec2(1.0,0.0) ), u.x),"
DATA "mix( hash( i + vec2(0.0,1.0) ),"
DATA "hash( i + vec2(1.0,1.0) ), u.x), u.y);"
DATA "}"

DATA "// lighting"
DATA "float diffuse(vec3 n,vec3 l,float p) {"
DATA "return pow(dot(n,l) * 0.4 + 0.6,p);"
DATA "}"

DATA "float specular(vec3 n,vec3 l,vec3 e,float s) {"
DATA "float nrm = (s + 8.0) / (PI * 8.0);"
DATA "return pow(max(dot(reflect(e,n),l),0.0),s) * nrm;"
DATA "}"

DATA "// sky"
DATA "vec3 getSkyColor(vec3 e) {"
DATA "e.y = max(e.y,0.0);"
DATA "return vec3(pow(1.0-e.y,2.0), 1.0-e.y, 0.6+(1.0-e.y)*0.4);"
DATA "}"

DATA "// sea"
DATA "float sea_octave(vec2 uv, float t, float choppy) {"
DATA "uv += noise(uv);"
DATA "vec2 suv = sin(uv), cuv = cos(uv);"
DATA "float st = sin(t), ct = cos(t);"
DATA "vec2 wv = 1.0-abs(suv * ct + cuv * st);"
DATA "vec2 swv = abs(cuv * ct - suv * st);"
DATA "wv = mix(wv,swv,wv);"
DATA "return pow(1.001-pow(abs(wv.x * wv.y),0.65),choppy);"
DATA "}"

DATA "float map(vec3 p) {"
DATA "float amp = SEA_HEIGHT;"
DATA "float choppy = SEA_CHOPPY;"
DATA "vec2 uv = p.xz * SEA_FREQ; uv.x *= 0.75;"
DATA "float h = 0.0, t = SEA_TIME * SEA_FREQ;"
DATA "for(int i = 0; i < ITER_GEOMETRY; i++) {"
DATA "h += amp * sea_octave(uv, t, choppy);"
REM DATA "h += amp * sea_octave(uv, -t, choppy);"
DATA "t *= -1.9; uv *= octave_m; amp *= 0.22;"
DATA "choppy = mix(choppy,1.0,0.2);"
DATA "}"
DATA "return p.y - h;"
DATA "}"

DATA "float map_detailed(vec2 uv) {"
DATA "float amp = SEA_HEIGHT;"
DATA "float choppy = SEA_CHOPPY;"
DATA "uv *= SEA_FREQ; uv.x *= 0.75;"
DATA "float h = 0.0, t = SEA_TIME * SEA_FREQ;"
DATA "for(int i = 0; i < ITER_FRAGMENT; i++) {"
DATA "h += amp * sea_octave(uv, t, choppy);"
REM DATA "h += amp * sea_octave(uv, -t, choppy);"
DATA "t *= -1.9; uv *= octave_m; amp *= 0.22;"
DATA "choppy = mix(choppy,1.0,0.2);"
DATA "}"
DATA "return h;"
DATA "}"

DATA "vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) {"
DATA "float fresnel = clamp(1.0 - dot(n,-eye), 0.0, 1.0);"
DATA "fresnel = pow(fresnel,3.0) * 0.65;"

DATA "vec3 reflected = getSkyColor(reflect(eye,n));"
DATA "vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12;"

DATA "vec3 color = mix(refracted,reflected,fresnel);"

DATA "float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0);"
DATA "color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten;"

DATA "color += vec3(specular(n,l,eye,60.0));"

DATA "return color;"
DATA "}"

DATA "// tracing"
DATA "vec3 getNormal(vec3 p, float eps) {"
DATA "vec3 n;"
DATA "n.y = p.y - map_detailed(p.xz);"
DATA "n.x = p.y - map_detailed(vec2(p.x+eps,p.z)) - n.y;"
DATA "n.z = p.y - map_detailed(vec2(p.x,p.z+eps)) - n.y;"
DATA "n.y = eps;"
DATA "return normalize(n);"
DATA "}"

DATA "vec3 heightMapTracing(vec3 ori, vec3 dir) {"
DATA "vec3 p = vec3(0.0, 0.0, 0.0);"
DATA "float tm = 0.0;"
DATA "float tx = 1000.0;"
DATA "float hx = map(ori + dir * tx);"
DATA "if(hx > 0.0) return p;"
DATA "float hm = map(ori + dir * tm);"
DATA "float tmid = 0.0;"
DATA "for(int i = 0; i < NUM_STEPS; i++) {"
DATA "tmid = mix(tm,tx, hm/(hm-hx));"
DATA "p = ori + dir * tmid;"
DATA "float hmid = map(p);"
DATA "if(hmid < 0.0) {"
DATA "tx = tmid;"
DATA "hx = hmid;"
DATA "} else {"
DATA "tm = tmid;"
DATA "hm = hmid;"
DATA "}"
DATA "}"
DATA "return p;"
DATA "}"

DATA "// main"
DATA "void main() {"
DATA "vec2 uv = gl_FragCoord.xy / iResolution.xy;"
DATA "uv = uv * 2.0 - 1.0;"
DATA "uv.x *= iResolution.x / iResolution.y;"
DATA "float time = iTime * 0.3 + iMouse.x * 0.01;"

DATA "// ray"
DATA "vec3 ang = vec3(sin(time*3.0)*0.1,0.0,time);"
DATA "vec3 ori = vec3(0.0,3.5,time*5.0);"
DATA "vec3 dir = normalize(vec3(uv.xy,-2.0)); // dir.z += length(uv) * 0.15;"
DATA "dir = normalize(dir) * fromEuler(ang);"

DATA "// tracing"
DATA "vec3 p = heightMapTracing(ori,dir);"
DATA "vec3 dist = p - ori;"
DATA "vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM);"
DATA "vec3 light = normalize(vec3(0.0,1.0,0.8));"

DATA "// color"
DATA "vec3 color = mix("
DATA "getSkyColor(dir),"
DATA "getSeaColor(p,n,light,dir,dist),"
DATA "pow(smoothstep(0.0,-0.05,dir.y),0.3));"

DATA "// post"
DATA "gl_FragColor = vec4(pow(color,vec3(0.75)), 1.0);"
DATA "}"
DATA ""

Zaphod
Posts: 78
Joined: Sat 23 Jun 2018, 15:51

Re: The power of GPU shaders

Post by Zaphod » Sun 20 Jan 2019, 23:56

Thank you Richard. Now I am really puzzled what the advantage of the SDL versions are on a Windows platform. I thought it was the graphics capabilities that the SDL layer gave you but perhaps I was quite wrong.

Z

Interloper
Posts: 3
Joined: Sun 20 Jan 2019, 21:55

Re: The power of GPU shaders

Post by Interloper » Mon 21 Jan 2019, 13:58

Zaphod wrote:
Sun 20 Jan 2019, 23:56
Now I am really puzzled what the advantage of the SDL versions are on a Windows platform.
If you limit it to a Windows platform like that, almost 'by definition' what you can achieve using SYS calls is going to be substantially the same whether it's BB4W or BBCSDL. Ultimately it's the same BASIC interpreter controlling the same hardware (and in the case of OpenGL via the same middleware and drivers) after all.

With that restriction (i.e. discounting the value of programs being much more portable to other platforms) the advantages of BBCSDL are going to be largely non-technical, such as being free and Open Source.

DDRM
Administrator
Posts: 146
Joined: Mon 02 Apr 2018, 18:04

Re: The power of GPU shaders

Post by DDRM » Mon 21 Jan 2019, 17:32

Ooh, interesting, thank you. I've been meaning to ask if/how it's possible to use OpenGl in BB4W! :-)

Here this version runs much less well: the sea surface looks quantized (steps in the surface), and colouring is less good. any ideas why? I assume the actual shader code is the same, and presumably the OpenGL running it is the same.

Best wishes,

D

Interloper
Posts: 3
Joined: Sun 20 Jan 2019, 21:55

Re: The power of GPU shaders

Post by Interloper » Tue 22 Jan 2019, 16:48

DDRM wrote:
Mon 21 Jan 2019, 17:32
I've been meaning to ask if/how it's possible to use OpenGl in BB4W!
The demo program OPENGL.BBC, in the EXAMPLES\GRAPHICS folder (and documented in the main BB4W Help file as "A translation to BBC BASIC of an OpenGL 3D graphics demonstration program") has been distributed with both trial and full versions of BBC BASIC for Windows for something like twelve years. The earliest version I can find is dated 12th August 2006, which fits perfectly with the date of the article OpenGL programming at the Wiki, which is also dated August 2006.

So considering that you obviously haven't bothered to check the Help file, the Wiki or the Example Programs for an answer you will have to forgive me if I don't take your comment seriously.
the sea surface looks quantized (steps in the surface), and colouring is less good. any ideas why?
Here I've tested the BB4W version on four different PCs (two running Windows 7 and two Windows 10) and the results are substantially identical to what they are from the BBCSDL version (on Windows, Linux, MacOS, Android and iOS). Only on my Raspberry Pi is the quality not quite as good as the rest, but it's not awful.

DDRM
Administrator
Posts: 146
Joined: Mon 02 Apr 2018, 18:04

Re: The power of GPU shaders

Post by DDRM » Wed 23 Jan 2019, 08:57

Hi RIchard,

OpenGL: OK, don't take it seriously, but your response is useful, thank you.

Difference in rendering: interesting. I'll try it on some different machines.

Best wishes,

D

Post Reply