Help us understand the problem. What is going on with this article?

[Unity] サークルプログレスバーをシェーダで書いてみる

More than 3 years have passed since last update.

モバイルのVRではキーボードやマウスなどが使えないケースが多く、視点による選択を行うことが少なくありません。
ただ、一瞬見ただけで選択されてしまうと問題があるので、一定時間見つめたのちに選択、ということをします。

そして見つめた時間の経過を円形のプログレスバーで表示しようと思ったんですが、テクスチャで行うと荒れてしまったり太さなどを自由に変更できなかったのでシェーダだけで実現しようと書いたのが今回の内容です。

ちなみに実行結果はこんな感じになります↓
サークルプログレスバーイメージ

透明の扱いを変更する

まず、透明度を物理ベースのレンダリングで行わないように変更する必要があります。
(Unity5以降では透明オブジェクトは現実世界のプラスチックやガラスのように、透明だけどライティングの影響を受けて、「そこに物体がある」ことが分かるようにレンダリングされるためです)

そのためには以下のようにTags#pragmaを少し変更します。

Tags { "RenderType"="Opaque" "Queue"="Transparent" }

// ... 中略

#pragma surface surf Standard fullforwardshadows alpha:fade

"Queue"="Transparent"alpha:fadeを追加しました。

シェーダを書く

さて、ここが本題のところです。
考え方はテクスチャの中心からの角度と距離を元に計算を行います。

それぞれの計算の意味についてはコメントに記載しています。

サーフェースシェーダ部分

以下はサーフェースシェーダ部分です。

void surf (Input IN, inout SurfaceOutputStandard o) {
     // 中央からの位置を-1 〜 1の範囲に正規化
    float2 pos = (IN.uv_MainTex - float2(0.5)) * 2.0;

    // 上記の位置から角度を算出(0〜1の範囲)
    // また今回は上端から(つまり90度)のところから時計回りに徐々に透明にしたいので
    // 90度だけ回転した状態に変換しておく
    float angle = (atan2(pos.y, pos.x) - atan2(1.0, 0.0)) / (PI * 2);

    // atan2では-180〜180度の値が返されるので、0〜360になるように正規化する
    // 角度を0〜1にしているため、実質足すのは1。
    if (angle < 0) {
        angle += 1.0;
    }

    float len = length(pos);

    // 縁の長さ。極端に短いとエッジがギザギザになる
    float edge = 0.03;
    float width = 1 - _Width;

    // サークルの内円
    float inner = smoothstep(width, width + edge, len);

    // サークルの外円
    float outer = smoothstep(1.0 - edge, 1.00, len);

    // 上記を合算して円のための数値を計算する
    float opaque = inner - outer;

    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;

    o.Albedo = c.rgb;
    o.Metallic = 0;
    o.Smoothness = 0;

    // 指定された`cutoff`の値がしきい値以上の場合のみ透明度を有効にする
    float cutoff = angle < _Cutoff ? 0 : 1;
    o.Alpha = _Color.a * opaque * cutoff;
}

全体のコード

プロパティなど、全体のコードを載せておきます。

[2016.02.01追記]

最初に公開していたコードだと、Unityの標準のライティングが適用されてちょっと色味がおかしなことになるので若干
修正しました。

具体的には #pragma でのライティングの指定の変更とライティング関数の追加です。

Shader "Custom/CircleProgress" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Cutoff ("Cutoff", Range(0, 1)) = 1.0
        _Width ("Width", Range(0, 1)) = 0.6
    }
    SubShader {
        Tags { "RenderType"="Opaque" "Queue"="Transparent" }
        LOD 200

        CGPROGRAM

        // カスタムの「Original」ライティング関数を指定
        #pragma surface surf Original fullforwardshadows alpha:fade

        #pragma target 3.0

        static const float PI = 3.14159265f;

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        fixed4 _Color;
        float _Cutoff;
        float _Width;

        // カスタムライティングを適用する
        half4 LightingOriginal (SurfaceOutput s, half3 lightDir, half atten) {
            // ライティングの影響を受けさせないため、受け取った色情報をそのまま返す
            return half4(s.Albedo, s.Alpha);
        }

        void surf (Input IN, inout SurfaceOutput o) {
            float2 pos = (IN.uv_MainTex - float2(0.5, 0.5)) * 2.0;
            float angle = (atan2(pos.y, pos.x) - atan2(1.0, 0.0)) / (PI * 2);

            if (angle < 0) {
                angle += 1.0;
            }

            float len = length(pos);
            float edge = 0.03;
            float width = 1 - _Width;
            float inner = smoothstep(width, width + edge, len);
            float outer = smoothstep(1.0 - edge, 1.00, len);
            float opaque = inner - outer;

            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            float cutoff = angle < _Cutoff ? 0 : 1;
            o.Alpha = _Color.a * opaque * cutoff;
        }
        ENDCG
    } 
    FallBack "Diffuse"
}
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした