美文网首页
Unity翻页Shader

Unity翻页Shader

作者: CZKGO | 来源:发表于2024-05-03 08:51 被阅读0次

    参考链接:Android自定义View——从零开始实现书籍翻页效果(一)

    一,区域划分

    二,关键点定义

    三,关键点公式

    a是可滑动,的定义为 a = (x,y)
    f点固定不变的,定义为f = (width,height)
    g定义为a和f的中点,定义为g = ((a.x+f.x)/2,(a.y+f.y)/2))
    eh我们设置为af的垂直平分线,有
    e = (g.x - (f.y - g.y) * (f.y - g.y) / (f.x - g.x), f.y)
    h = (f.x, g.y - (f.x - g.x) * (f.x - g.x) / (f.y - g.y))
    曲线cdb是起点为c,控制点为e,终点为b的二阶贝塞尔曲线,有
    c = (e.x - (f.x - e.x) / 2, f.y)
    b = get_intersection_point(line(ae), line(cj))
    d = ((c.x + 2e.x + b.x) / 4, (c.y + 2e.y + b.y) / 4
    曲线kij是起点为k,控制点为h,终点为j的二阶贝塞尔曲线,有
    j = (f.x, h.y - (f.y - h.y) / 2)
    k = get_intersection_point(line(ah), line(cj))
    i = ((j.x + 2 h.x + k.x) / 4, (j.y + 2h.y +k.y) / 4)

    四,区域公式

    B区域
    最下一层,最先绘制

    area(B) = line(上边界) + line (右边界) + line (下边界) + line(左边界)
    
    C区域
    1. 区域C理论上应该是由点a,b,d,i,k连接而成的闭合区域,但由于d和i是曲线上的点,我们没办法直接从d出发通过path绘制路径连接b点(i,k同理)
    2. 区域C是 由直线ab,bd,dj,ik,ak连接而成的区域 减去 与区域A交集部分 后剩余的区域
      3.所以先绘制C,然后用A覆盖掉C
    area(C) = line(id) + line (db) + line (ba) + line(a,k) + line(k,i)
    
    A区域

    可以通过曲线定返回,最后绘制

    area(A) = line(fc) + quad (cb,e) + line(ba) + line(ak) + quad(kj,h) +line(jf)
    

    五,Shader

    Shader "Custom/PageTurnShader"
    {
        Properties
        {
            main_tex ("Texture", 2D) = "white" {}
            progress_x ("Progress_x", Range(0, 1)) = 0
            progress_y ("Progress_y", Range(0, 1)) = 0
            is_up ("Is Up", Int) = 0
        }
        SubShader
        {
            Tags
            {
                "Queue"="Transparent" "RenderType"="Transparent"
            }
            LOD 200
    
            Blend SrcAlpha OneMinusSrcAlpha
    
            Pass
            {
                CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #include "UnityCG.cginc"
    
                struct appdata
                {
                    float4 vertex : POSITION;
                    float2 uv : TEXCOORD0;
                };
    
                struct v2_f
                {
                    float2 uv : TEXCOORD0;
                    float4 vertex : SV_POSITION;
                };
    
                sampler2D main_tex;
                float progress_x, progress_y;
                int is_up;
    
                v2_f vert(appdata v)
                {
                    v2_f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    return o;
                }
    
                //获取两条相交线段的相交点
                float2 get_intersection_point(float2 line_one_v1, float2 line_one_v2, float2 line_two_v1, float2 line_two_v2)
                {
                    const float x1 = line_one_v1.x;
                    const float y1 = line_one_v1.y;
                    const float x2 = line_one_v2.x;
                    const float y2 = line_one_v2.y;
                    const float x3 = line_two_v1.x;
                    const float y3 = line_two_v1.y;
                    const float x4 = line_two_v2.x;
                    const float y4 = line_two_v2.y;
    
                    float denominator = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
                    // 检查直线是否平行
                    if (denominator == 0)
                    {
                        denominator = 0.00001;
                    }
    
                    const float ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
                    float point_x = x1 + ua * (x2 - x1);
                    float point_y = y1 + ua * (y2 - y1);
                    return float2(point_x, point_y);
                }
    
                //二次贝塞尔曲线上给定参数 t(取值范围为 0 到 1)对应的点位置。它接受三个控制点 p0(起点),p2(终点),p1(控制点)通过计算贝塞尔曲线方程得出相应位置的点。
                float2 quadratic_bezier_curve(const float2 start, const float2 control, const float2 end, const float index)
                {
                    const float u = 1.0 - index;
                    const float tt = index * index;
                    const float uu = u * u;
    
                    float2 p = uu * start;
                    p += 2.0 * u * index * control;
                    p += tt * end;
    
                    return p;
                }
    
                //判断点是否在线上
                bool intersects(float2 line_p1, float2 line_p2, float2 p)
                {
                    const float dy = line_p2.y - line_p1.y;
                    if (dy == 0)
                    {
                        // 线段是水平的,判断是否在两端点之间
                        return p.y == line_p1.y && p.x >= min(line_p1.x, line_p2.x) && p.x <= max(line_p1.x, line_p2.x);
                    }
    
                    const float dx = line_p2.x - line_p1.x;
                    const float slope = dx / dy;
    
                    if (line_p1.y > p.y != line_p2.y > p.y && p.x < (p.y - line_p1.y) * slope + line_p1.x)
                    {
                        return true;
                    }
    
                    return false;
                }
    
                //判断点是否在区域A
                bool is_in_a(const float2 f, const float2 c, const float2 b, const float2 e, const float2 a, const float2 k, const float2 j,
                             const float2 h, const float2 uv_size)
                {
                    // 在区域A内的点数
                    uint num_points_inside = 0;
    
                    if (intersects(f, c, uv_size))
                    {
                        num_points_inside++;
                    }
                    // 计算曲线上的像素位置
                    float2 pos = c;
                    for (float index = 0.0; index <= 1.0; index += 0.01)
                    {
                        const float2 pos1 = quadratic_bezier_curve(c, e, b, index);
                        if (intersects(pos, pos1, uv_size))
                        {
                            num_points_inside++;
                        }
                        pos = pos1;
                    }
    
                    if (intersects(b, a, uv_size))
                    {
                        num_points_inside++;
                    }
    
                    if (intersects(a, k, uv_size))
                    {
                        num_points_inside++;
                    }
    
                    pos = k;
                    for (float index = 0.0; index <= 1.0; index += 0.01)
                    {
                        const float2 pos1 = quadratic_bezier_curve(k, h, j, index);
                        if (intersects(pos, pos1, uv_size))
                        {
                            num_points_inside++;
                        }
                        pos = pos1;
                    }
    
                    if (intersects(j, f, uv_size))
                    {
                        num_points_inside++;
                    }
    
                    return num_points_inside % 2 == 0;
                }
    
                // 判断是否在区域B
                bool is_in_b(const float2 f, const float2 c, const float2 j, const float2 uv_size)
                {
                    uint num_points_inside = 0;
    
                    if (intersects(j, f, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(f, c, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(c, j, uv_size))
                    {
                        num_points_inside++;
                    }
    
                    return num_points_inside % 2 == 1;
                }
    
                //判断是否在区域C
                bool is_in_c(const float2 i, const float2 d, const float2 b, const float2 a, const float2 k, const float2 uv_size)
                {
                    uint num_points_inside = 0;
    
                    if (intersects(i, d, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(d, b, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(b, a, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(a, k, uv_size))
                    {
                        num_points_inside++;
                    }
                    if (intersects(k, i, uv_size))
                    {
                        num_points_inside++;
                    }
    
                    return num_points_inside % 2 == 1;
                }
    
                fixed4 frag(const v2_f current) : SV_Target
                {
                    // 获取纹理坐标
                    const float2 uv = current.uv;
                    fixed4 col = tex2D(main_tex, uv);
                    fixed4 no_change_col = col;
                    //获取屏幕坐标
                    float2 screen_size = _ScreenParams.xy;
                    const float screen_width = screen_size.x;
                    const float screen_height = screen_size.y;
                    const float2 uv_size = float2(uv.x * screen_width, uv.y * screen_height);
    
                    /////////////////////////////////////////////////
                    //通过改变a点来翻页
                    const float ax = progress_x;
                    const float ay = progress_y;
    
                    float2 a, f, g, e, h, c, j, b, k, d, i;
    
                    a = float2(screen_width * ax, screen_height * ay);
    
                    f = screen_size;
                    if (is_up > 0)
                    {
                        f = float2(screen_width, 0);
                    }
    
                    g = (a + f) / 2.0;
                    float fg_x = f.x - g.x;
                    if (fg_x == 0)
                    {
                        fg_x = 0.0001; // 避免除数为 0 的情况
                    }
                    e.x = g.x - (f.y - g.y) * (f.y - g.y) / fg_x;
                    e.y = f.y;
                    h.x = f.x;
                    float fg_y = f.y - g.y;
                    if (fg_y == 0)
                    {
                        // 避免除数为 0 的情况,
                        if (f.y == 0)//f.y这时不能大于0
                        {
                            fg_y = -0.0001;
                        }
                        else
                        {
                            fg_y = 0.0001;
                        }
                    }
                    h.y = g.y - (f.x - g.x) * (f.x - g.x) / fg_y;
                    c.x = e.x - (f.x - e.x) / 2.0;
                    c.y = f.y;
                    j.x = f.x;
                    j.y = h.y - (f.y - h.y) / 2.0;
                    b = get_intersection_point(a, e, c, j);
                    k = get_intersection_point(a, h, c, j);
                    d.x = (c.x + 2.0 * e.x + b.x) / 4.0;
                    d.y = (2.0 * e.y + c.y + b.y) / 4.0;
                    i.x = (j.x + 2.0 * h.x + k.x) / 4.0;
                    i.y = (2.0 * h.y + j.y + k.y) / 4.0;
    
                    
                    const bool is_in_region_a = is_in_a(f, c, b, e, a, k, j, h, uv_size);
                    const bool is_in_region_b = true;
                    const bool is_in_region_c = is_in_c(i, d, b, a, k, uv_size);
    
                    if (is_in_region_b)
                    {
                        col = fixed4(0.0, 0.0, 0.0, 0.0);
                    }
    
                    if (is_in_region_c)
                    {
                        col = fixed4(1.0, 1.0, 1.0, 1.0);
                    }
    
                    if (is_in_region_a)
                    {
                        col = no_change_col;
                    }
                    return col;
                }
                ENDCG
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Unity翻页Shader

          本文链接:https://www.haomeiwen.com/subject/rxmgxjtx.html