为SDF模型添加纹理

Number of views 281

RayMarching结合SDF可以渲染出不同形状的形体,那如何为SDF模型添加纹理呢?

1.Tri-Planar-Mapping[三面映射]

我们通过如下的方式为Cube添加纹理。

vec2 t = raymarch(ro, rd);
if(t.x > 0.) {
    col = texture(iChannel0, p.xy).rgb;
}

输出结果:

image1740792704369.png

看到前后两个面的纹理有正常显示,但是Repeat了,那是因为 p.xy映射范围为 [-1, -1][1, 1],我们把其映射回 [0, 0][1, 1]的范围。

vec2 t = raymarch(ro, rd);
if(t.x > 0.) {
    // 通过-1~1的范围映射回0~1
    col = texture(iChannel0, p.xy*0.5+0.5).rgb;
}

image1740793300299.png

但是除了前后两个面,其它面的纹理都被拉伸了。如何解决呢?我们把 p.xy(前后)改为 p.xz(上下)p.zy(左右)试试?

改为 p.zy(左右)后:

image1740793738134.png

改为 p.xz(上下)后:

image1740793872642.png

所以我们现在有:

vec2 t = raymarch(ro, rd);
if(t.x > 0.) {
    vec3 mate = vec3(0.2);
    vec3 sunCol = vec3(1.);
    vec3 p = ro + rd*t.x;
    vec3 colXZ = texture(iChannel0, p.xz*0.5+0.5).rgb;
    vec3 colZY = texture(iChannel0, p.zy*0.5+0.5).rgb;
    vec3 colXY = texture(iChannel0, p.xy*0.5+0.5).rgb;
}

现在问题的关键是怎么让这六个面同时贴到Cube中?我们得借助下法线:

通过以下代码直接输出法线颜色:

    vec2 t = raymarch(ro, rd);
    if(t.x > 0.) {
        vec3 mate = vec3(0.2);
        vec3 sunCol = vec3(1.);
        vec3 p = ro + rd*t.x;
        vec3 n = calNormal(p);
        vec3 colXZ = texture(iChannel0, p.xz*0.5+0.5).rgb;
        vec3 colZY = texture(iChannel0, p.zy*0.5+0.5).rgb;
        vec3 colXY = texture(iChannel0, p.zy*0.5+0.5).rgb;
        col = n;
    }

image1740794562623.png

不过也会看到背面是黑的,所以我们应该对法线取绝对值 col = abs(n):

image1740794882493.png

好了,我们现在已经有法线了,也有各个面的纹理颜色了,剩下的就是想办法怎么让法线与各个纹理颜色结合。

很简单,通过如下代码即可完成:

vec2 t = raymarch(ro, rd);
    if(t.x > 0.) {
        vec3 mate = vec3(0.2);
        vec3 sunCol = vec3(1.);
        vec3 p = ro + rd*t.x;
        vec3 n = calNormal(p);
        vec3 colXZ = texture(iChannel0, p.xz*0.5+0.5).rgb;
        vec3 colZY = texture(iChannel0, p.zy*0.5+0.5).rgb;
        vec3 colXY = texture(iChannel0, p.xy*0.5+0.5).rgb;
        vec3 absN = abs(n);
        col = colXZ*absN.y + colZY*absN.x + colXY*absN.z;
    }

image1740795390712.png

如果对Cube做旋转操作,我们会发现纹理飘在了Cube上了,遇到这种情况,Cube怎么做旋转,采样的UV也要做同样的变换操作,才能让UV贴在Cube上。这种方式也可以让可重复纹理贴在类似球体之类的物体上。

image1740797562416.png

以上是其中一种纹理贴图的方案,称为 Tri-Planar-Mapping[三面映射]

下面介绍另一种纹理映射方案,它基于极坐标的纹理映射方案。

2.极坐标的纹理映射

我们要让贴图贴到球体上,需要让u从0开始并到1结束,并让p.y作为v,且环绕球体一圈的范围。如下图:

image1740803689927.png

我们采用极坐标,并使用 atan(p.z, p.x) 得出的范围是 [-PI,PI]的范围,现在要做的是把 [-PI,PI]范围映射为 [0, 1],使用如下的方法:

(atan(p.z, p.x)/3.1415)*0.5+0.5

接着把p.y作为v,如下:

col = texture(iChannel1, vec2(((atan(p.z, p.x)/3.1415)*0.5+0.5), p.y)).rgb;

图像输出结果:

image1740804071421.png

0 Answers