GLSL,传递函数

asi*_*mes 4 processing glsl webgl

我在Processing(Java)中编写了一个简单的Sphere Tracer,并将其移植到WebGL/GLSL.当我在处理写我有一个基类Shape并将其扩展为特定的形状,例如Box,Plane,Sphere等每个特定的形状已经那名相关的它,例如成员Sphere的实例有半径,Box实例有长度矢量,等另外每个都具有形状特定的距离函数.

不幸的是我不能在GLSL中使用这样的类,所以我创建了一个struct可以表示任何形状的单词(我在Object下面引用它):

struct Object {
    vec3 pos, len, nDir;
    float rad;
} objects[4];
Run Code Online (Sandbox Code Playgroud)

然后我为每种形状写了一个距离函数:

float boxSignedDist(Object inBox, vec3 inPos) {
    vec3 boxDelta = abs(inPos-inBox.pos)-inBox.len;
    return min(max(boxDelta.x, max(boxDelta.y, boxDelta.z)), 0.0)+length(max(boxDelta, 0.0));
}

float planeSignedDist(Object inPlane, vec3 inPos) {
    return dot(inPos-inPlane.pos, inPlane.nDir);
}

float roundBoxUnsignedDist(Object inRoundBox, vec3 inPos) {
    return length(max(abs(inPos-inRoundBox.pos)-inRoundBox.len, 0.0))-inRoundBox.rad;
}

float sphereSignedDist(Object inSphere, vec3 inPos) {
    return length(inPos-inSphere.pos)-inSphere.rad;
}
Run Code Online (Sandbox Code Playgroud)

现在我遇到了一个不同的问题,即用另一个函数(如旋转)包装形状特定的距离函数,如何在GLSL中有效地完成这一操作并不明显.我添加了一个成员Object,int type然后#define为我支持的每个形状做了几个:

#define BOX_SIGNED 1
#define PLANE_SIGNED 2
#define ROUNDBOX_UNSIGNED 3
#define SPHERE_SIGNED 4

struct Object {
    int type;
    vec3 pos, len, nDir;
    float rad;
} objects[4];
Run Code Online (Sandbox Code Playgroud)

所以现在我可以将旋转包装器编写为这样的距离函数:

float rotateY(Object inObject, vec3 inPos, float inRadians) {
    inPos -= inObject.pos;
    inObject.pos = vec3(0.0, 0.0, 0.0);

    float cRad = cos(inRadians);
    float sRad = sin(inRadians);

    if (inObject.type == BOX_SIGNED)
        return boxSignedDist(inObject, vec3(cRad*inPos.x-sRad*inPos.z, inPos.y, cRad*inPos.z+sRad*inPos.x));
    else if (inObject.type == PLANE_SIGNED)
        return planeSignedDist(inObject, vec3(cRad*inPos.x-sRad*inPos.z, inPos.y, cRad*inPos.z+sRad*inPos.x));
    else if (inObject.type == ROUNDBOX_UNSIGNED)
        return roundBoxUnsignedDist(inObject, vec3(cRad*inPos.x-sRad*inPos.z, inPos.y, cRad*inPos.z+sRad*inPos.x));
    else if (inObject.type == SPHERE_SIGNED)
        return sphereSignedDist(inObject, vec3(cRad*inPos.x-sRad*inPos.z, inPos.y, cRad*inPos.z+sRad*inPos.x));
    else
        return 0.0;
}
Run Code Online (Sandbox Code Playgroud)

看来这是必要的,有没有更好的方法呢?如果rotateY能够接收一个函数指针来调用适当的函数而不是全部函数,那将是很好的else if

joz*_*yqk 9

GLSL确实是一种非常有限的语言.编译器在优化某些事情方面做得很好,但并不完美.

要记住的一些事项:

  • 本地内存很昂贵,只需要声明和访问
  • 动态索引数组放在本地内存中.
  • 填充数组和对象以对齐16字节边界.一个int[4]阵列采用相同的存储器中,作为vec4[4]阵列.你Object应该vec3floats 组.
  • 没有函数调用这样的东西.一切都是内联的.
  • 传递给函数的参数将被复制并复制出来.当函数内联时,编译器并不总是优化这些副本.保持尽可能全球化.
  • Switch语句没有跳转运算符,它们被扩展为嵌套的if语句.
  • 分散是一个棘手的事情来优化.您的if (type == ...代码可以通过inPos预先构造旋转来改进,但我无法看到if-statements的方法.也许您可以为每个对象类型(或使用宏)编写函数的排列并分别跟踪批量类型?

您可能会看到一些好的想法,看看人们为https://www.shadertoy.com/撰写的内容.

最后,GLSL 子例程具有与函数指针类似的意图,但在全局范围内用于所有着色器执行,并且在这里没有帮助.