2

I have the following challenge. I am doing a series of experiments in graphics where I need to author sdf functions. These functions must exist in glsl and c++ simultaneously for different purposes. Said otherwise, in both glsl and c++ there must be a function SdfFunction that computes the exact same SDF.

Current possible approaches:

  • Author the function twice by hand. This super slow and very prone to error.
  • Make a parser script that must be run to convert glsl code into C++ or vice versa. This suffers from only working at build time and needing an additional programming language (to make it portable).
  • Have the C++ code itself parse the files. Then compile a dynamic loaded library and link at runtime to the generated files. I have no idea how to actually do this one and it sounds very messy.

Do I have an alternative?

Makogan
  • 8,208
  • 7
  • 44
  • 112

1 Answers1

1

I am using a bit different approach. I created GLSL_math.h template for CPU side C++ code with the same syntax and functionality as GLSL (swizzling included) so I can run the same math code on GLSL and CPU for debugging purposes. The template also contains local and global rotations which are not present in native GLSL (as I intended to use this also as replacement for my old reper and vector math classes and needed the functionality)

The template was done in Embarcadero (Borland) BDS2006 Turbo C++ so it might need some tweeking in different C++ IDE / compiler. Most of the code was autogenerated with function _vec_generate which is included but commented out as it uses AnsiString which is not present outside VCL as coding this manually would be insane (~244KByte).

The texture access and stuff differences between CPU/GLSL I handle by macro statements like in here:

where only the macro differs for GLSL and CPU ...

This way I can use my C++ IDE debugging features like breakpoints, tracing, watches ... without which I would never accomplish more complex shaders like the raytracers through mesh or voxel maps ...

For cases when behavior differs (different FPU implementations or GLSL quirks and bugs related to drivers) I use this:

to directly print out sub-results from fragment shader

Here a sample test code in order to test the template operator syntax fuctionality (different compiler might need to change the operator header syntax slightly until it compiles):

//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
template <class mat,class vec,class T> void test_operators_syntax()
    {
    mat m0,m1,m2;
    vec v0,v1,v2;
    T   c1=1;

    m0=+m1;
    m0=-m1;
    m0=m1*m2;
    m0=c1*m1;
    m0=m1*c1;
    m0=m1/c1;

    m0*=m1;
    m0*=c1;
    m0/=c1;

    v0=m1*v1;
    v0=m1*v1;
    v0=m1*v1;

    v0=+v1;
    v0=-v1;
    v0=v1++;
    v0=++v1;
    v0=v1--;
    v0=--v1;

    v0=v1+v2;
    v0=v1-v2;
    v0=v1*v2;
    v0=v1/v2;

    v0+=v1;
    v0-=v1;
    v0*=v1;
    v0/=v1;
    v0++=v1;
    v0--=v1;

    v0=c1+v1;
    v0=c1-v1;
    v0=c1*v1;

    v0=v1+c1;
    v0=v1-c1;
    v0=v1*c1;
    v0=v1/c1;

    v0+=c1;
    v0-=c1;
    v0*=c1;
    v0/=c1;
    };
//---------------------------------------------------------------------------
void test_operators()
    {
    test_operators_syntax< mat2, vec2,float >();  vec2  v2= vec2(0.0,0.0);           mat2  m2= mat2(0.0,0.0,0.0,0.0);
    test_operators_syntax< mat3, vec3,float >();  vec3  v3= vec3(0.0,0.0,0.0);       mat3  m3= mat3(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
    test_operators_syntax< mat4, vec4,float >();  vec4  v4= vec4(0.0,0.0,0.0,0.0);   mat4  m4= mat4(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
    test_operators_syntax<dmat2,dvec2,double>(); dvec2 dv2=dvec2(0.0,0.0);          dmat2 dm2=dmat2(0.0,0.0,0.0,0.0);
    test_operators_syntax<dmat3,dvec3,double>(); dvec3 dv3=dvec3(0.0,0.0,0.0);      dmat3 dm3=dmat3(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);
    test_operators_syntax<dmat4,dvec4,double>(); dvec4 dv4=dvec4(0.0,0.0,0.0,0.0);  dmat4 dm4=dmat4(0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0,0.0);

    m3=rotate(m3,15.0*deg,v2);
    m4=rotate(m4,15.0*deg,v3,v3);
    m3=grotz(m3,15.0*deg); m3=lrotz(m3,15.0*deg);
    m4=grotx(m4,15.0*deg); m4=lrotx(m4,15.0*deg);
    m4=groty(m4,15.0*deg); m4=lroty(m4,15.0*deg);
    m4=grotz(m4,15.0*deg); m4=lrotz(m4,15.0*deg);
    m2=inverse(m2); m2=inverse2(m2); m2=transpose(m2);
    m3=inverse(m3); m3=inverse2(m3); m3=transpose(m3);
    m4=inverse(m4); m4=inverse2(m4); m4=transpose(m4);
    float  f=1.0; f=max(0.0f,f); f=min(0.0f,f); f=abs(f);
    double d=2.0; d=max(0.0 ,d); d=min(0.0 ,d); d=abs(d);
    f=length(v2)+length2(v2)+dot(normalize(v2),cross(v2));
    f=length(v3)+length2(v3)+dot(normalize(v3),cross(v3,v3));
    f=length(v4)+length2(v4)+dot(normalize(v4),cross(v4,v4,v4));
    }
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
Spektre
  • 49,595
  • 11
  • 110
  • 380
  • what do you do to get your header into GLSL? Include? – Makogan Aug 31 '21 at 19:26
  • @Makogan you do not need this in GLSL its only for CPU side code and yes you just `#include "GLSL_math.h"` after that you can use `vec` and `mat` stuff in the same way so once you wrote and debug your SDF on CPU side just copy the code to shader without any changes. However different compilers usually needs minor tweaks in header syntax of the operators you know swapping `&,*` or using `const` in operands ... in order o be compilable. This is tweaked for BCC32 5.5 ... – Spektre Aug 31 '21 at 21:24
  • " side just copy the code to shader without any changes." This is not much different than having to author twice, you reduce the possibility of error somewhat but you still have to manually sync your changes through copy pasting. And you could achieve something similar with glm. :\ Seems that if I want auto syncing I will have to compile and dynamically link. – Makogan Aug 31 '21 at 22:30
  • @Makogan its possible to go one step further and create a calling system that would simulate the GLSL pipeline and rasterization. so you include each shader into different namespace (so the `main()` will not conflict) and create a function above that that will take in geometry primitive (for example triangle) call vertex / geometry what ever then rasterize the result and call fragment ... its a bit of work but still doable had done something similar for the raytracers however not for full geometry. But you would need to implement the missing functions you use ... – Spektre Sep 01 '21 at 07:22
  • @Makogan I wanted to do something like this in the past but never found time/mood for it as the GLSL_math is more or less sufficient for my purposes. Also I do not use GLM but from what I saw it does not have the same syntax as GLSL – Spektre Sep 01 '21 at 07:24