贴图自旋

遍历线段计算贴图点时,线段与水平的夹角会作为贴图的自旋角度。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// WDPath.m

- (void) paintFromPoint:(WD3DPoint *)lastLocation toPoint:(WD3DPoint *)location randomizer:(WDRandom *)randomizer
{
    ...
    CGPoint         vector = WDSubtractPoints(location.CGPoint, lastLocation.CGPoint);
    ...
    float           vectorAngle = atan2(vector.y, vector.x);
    ...
    for (f = remainder_; f <= distance; f += step, pressure += pressureStep) {
        ...

        // 某些笔触会影响自旋角度,此时可以暂时忽略
        float rotationalScatter = [randomizer nextFloat] * brush.rotationalScatter.value * M_PI * 2;
        float angleOffset = brush.angle.value * (M_PI / 180.0f);

        ...
        [points_ addObject:[NSValue valueWithCGPoint:pos]];
        [sizes_ addObject:@(brushSize)];
        [angles_ addObject:@(vectorAngle * brush.angleDynamics.value + rotationalScatter + angleOffset)];
        [alphas_ addObject:@(alpha)];
        ...
    }
    ...
}

如果加入贴图自旋,可以看到跟随书写方向的效果更自然。

后续流程把贴图点作为贴图矩形的中点,利用矩阵计算出自旋后的四角坐标。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// WDPath.m

- (CGRect) drawData
{
    ...
    for (int i = 0; i < points_.count; i++) {
        CGPoint result = [points_[i] CGPointValue];
        float angle = [angles_[i] floatValue];
        float size = [sizes_[i] floatValue] / 2;
        float alpha = [alphas_[i] floatValue];

        CGRect rect = CGRectMake(result.x - size, result.y - size, size*2, size*2);
        CGPoint a = CGPointMake(CGRectGetMinX(rect), CGRectGetMinY(rect));
        CGPoint b = CGPointMake(CGRectGetMaxX(rect), CGRectGetMinY(rect));
        CGPoint c = CGPointMake(CGRectGetMinX(rect), CGRectGetMaxY(rect));
        CGPoint d = CGPointMake(CGRectGetMaxX(rect), CGRectGetMaxY(rect));

        CGAffineTransform t = CGAffineTransformMakeTranslation(WDCenterOfRect(rect).x, WDCenterOfRect(rect).y);
        t = CGAffineTransformRotate(t, angle);
        t = CGAffineTransformTranslate(t, -WDCenterOfRect(rect).x, -WDCenterOfRect(rect).y);

        a = CGPointApplyAffineTransform(a, t);
        b = CGPointApplyAffineTransform(b, t);
        c = CGPointApplyAffineTransform(c, t);
        d = CGPointApplyAffineTransform(d, t);

        ...
    }
    ...
}

已知贴图点和矩形大小,再结合自旋角度,计算四角坐标的步骤如下图所示。

留意"绕某点旋转"的矩阵生成过程,平移和旋转操作有严格的先后顺序。

贴图顶点

计算出贴图的四角坐标后,会按顺序保存在顶点缓存中。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
// WDPath.m

- (CGRect) drawData
{
    ...
    int n = 0;
    for (int i = 0; i < points_.count; i++) {
        ...

        // 自旋后的贴图四角坐标
        a = ...;
        b = ...;
        c = ...;
        d = ...;
        ...

        if (n != 0) {
            vertexD[n].x = a.x;
            vertexD[n].y = a.y;
            vertexD[n].s = 0;
            vertexD[n].t = 0;
            vertexD[n].a = alpha;
            n++;
        }

        vertexD[n].x = a.x;
        vertexD[n].y = a.y;
        vertexD[n].s = 0;
        vertexD[n].t = 0;
        vertexD[n].a = alpha;
        n++;

        vertexD[n].x = b.x;
        vertexD[n].y = b.y;
        vertexD[n].s = 1;
        vertexD[n].t = 0;
        vertexD[n].a = alpha;
        n++;

        vertexD[n].x = c.x;
        vertexD[n].y = c.y;
        vertexD[n].s = 0;
        vertexD[n].t = 1;
        vertexD[n].a = alpha;
        n++;

        vertexD[n].x = d.x;
        vertexD[n].y = d.y;
        vertexD[n].s = 1;
        vertexD[n].t = 1;
        vertexD[n].a = alpha;
        n++;

        if (i != (points_.count - 1)) {
            vertexD[n].x = d.x;
            vertexD[n].y = d.y;
            vertexD[n].s = 1;
            vertexD[n].t = 1;
            vertexD[n].a = alpha;
            n++;
        }
    }
    ...
    glDrawArrays(GL_TRIANGLE_STRIP, 0, n);
    ...
}

绘制模式是GL_TRIANGLE_STRIP,当顶点数大于等于3时,每增加1个顶点则与前2个顶点形成三个形。

每次输出四周坐标时有重复的顶点,如果使用上面的绘制模式,会发现矩形之间有细线相连。

留意重复点的作用,是把三角形折叠成不会显示的细线,方便批量绘制不相连的三角形。