GLSL,传递函数

时间:2014-11-20 01:10:04

标签: glsl webgl processing

我在Processing(Java)中编写了一个简单的Sphere Tracer,并将其移植到WebGL / GLSL。当我在Processing中编写它时,我有一个基类Shape,并将其扩展为特定的形状,如BoxPlaneSphere等。每个特定的形状都有成员与它相关的,例如Sphere个实例有一个半径,Box个实例有一个长度向量,等等。另外,每个实例都有一个特定于形状的距离函数。

不幸的是我不能在GLSL中使用这样的类,因此我创建了一个可以表示任何形状的struct(我在下面将其称为Object):

struct Object {
    vec3 pos, len, nDir;
    float rad;
} objects[4];

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

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;
}

现在我遇到了一个不同的问题,即用另一个函数(如旋转)包裹形状特定的距离函数,如何在GLSL中有效地完成这一操作并不明显。我添加了一个成员到Objectint 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];

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

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;
}

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

,那就太好了。

1 个答案:

答案 0 :(得分:8)

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

要记住的一些事情:

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

你可能会看到一些好主意,看看人们为https://www.shadertoy.com/所写的内容。

最后,GLSL 子例程具有与函数指针类似的意图,但在全局范围内用于所有着色器执行,并且在此处不会提供帮助。