モザイクフィルタを作ってみたところ不可解な境界線が現れてしまいました

画像をモザイクにしようと思ってこんなシェーダーを作成していました

uniform sampler2D tex;
void main() {
    vec2 uv = floor(texCoord * 30.0) / 30.0;
    gl_FragColor = texture2D(tex, uv);
}

実際に使ってみると↓のような見た目になり、なにやらモザイクのセル間に謎の境界線が...

f:id:kaneta1011:20180622164101p:plain

UVを表示してペイントのスポイトで問題の箇所のUVがおかしくなっていないか確認したが問題は見つかりませんでした

実装はどう考えても問題なかったので、それ以外の要因があると2時間ほど頭を抱えていたところ、原因はミップマップがonになっていたからでした

モザイク処理したことによってセル間で急激にUVが切り替わり、境界線でミップマップレベルが大きくなっていたようです

ミップマップ off後

f:id:kaneta1011:20180622165427p:plain

ミップマップには気を付けましょう

シェーダーで条件分岐は遅いらしいのでベクトル演算に置き換えようとしてできなかった話

一般的にシェーダーでの条件分岐は遅いという話がある

今回作ったシェーダーではブレンドモードや使用するマスクのチャンネルをアプリから送信してシェーダー内で利用するということをした

これを条件分岐を使って素直に実装するとこんな感じになる(空で書いたので動くか不明)

fixed3 blendColor(fixed3 src, fixed4 dest, float blend, int mode) {
    float alpha = dest.a * blend;
    fixed3 blended_dest = dest.rgb * alpha;
    if (mode == 0) {
        return lerp(src, dest, alpha);
    } else if (mode == 1) {
        return src + blended_dest;
    } else if (mode == 2) {
        return src - blended_dest;
    } else if (mode == 3) {
        return src * blended_dest;
    }
    return src;
}

fixed useMask(fixed4 mask, int use_mask_number) {
    if (use_mask_number == 0) {
        return 1.0 - mask.r;
    } else if (use_mask_number == 1) {
        return 1.0 - mask.g;
    } else if (use_mask_number == 2) {
        return 1.0 - mask.b;
    } else if (use_mask_number == 3) {
        return 1.0 - mask.a;
    }
    return 0.0;
}

これの modeuse_mask_number を使用したいチャンネルが1.0のベクトルで渡すことで、if文を行列とベクトル演算に置き換えることができる

fixed3 blendColor(fixed3 src, fixed4 dest, float blend, float4 mode) {
    float alpha = dest.a * blend;
    fixed3 blended_dest = dest.rgb * alpha;
    // GLESでは非正方行列が使用できないので使わないところは0で埋める
    return mul(mode, fixed4x4(lerp(src, dest, alpha), 0,
                                src + blended_dest, 0,
                                src - blended_dest, 0,
                                src * blended_dest, 0)).rgb;
}

fixed useMask(fixed4 mask, float4 use_mask_vector) {
    return 1.0 - dot(mask, use_mask_vector);
}

これで高速化できただろう!と思ってましたが、結局最初に書いたifのパターンの方がパフォーマンスが出てました。

こちらのリンク先のとおりであれば、実行中に実際に分岐されない場合ではifの方がパフォーマンスがでるんじゃないかと思いました。(未検証)

But on modern GPUs, they don't cause a performance issue unless they actually do diverge at runtime.

www.khronos.org

またピクセル毎に分岐するような場合はDivergent分岐と呼ばれるらしくて、こういう時にifのパフォーマンスが悪くなるらしい(uvで偶数奇数を分岐するなど)

ただし、この場合でも算術演算によるブランチングがifと比べて速くなるかは検証していないので不明...

(想像の話しかしてないのでアセンブリ読んでみたいけどどこから参照すればいいのかわからない...)

Unityのマテリアルエディタ拡張でテクスチャのプロパティを表示する

最近Unityを触っていてマテリアルエディタを拡張したくなったのでテクスチャのプロパティを表示するところから始めてみた

最初は自前でUIを組み立ててどうやらするのかと思ってその方面で調べていたけど、標準のテクスチャプロパティのスタイルであれば TextureProperty 関数を呼ぶだけで表示できることが分かったのでそれを使用した

using UnityEngine;
using System.Collections;
using UnityEditor;

public class CardShaderInspector : MaterialEditor
{

    public override void OnInspectorGUI()
    {
        if (!isVisible) { return; }
        MaterialProperty mask1 = GetMaterialProperty(targets, "_Mask1Tex");
        // 第三引数をtrueにするとタイリングとオフセットのUIも表示される
        TextureProperty(mask1, "Mask1", false);
    }
}

ここに行き着くのに二時間もかかってしまった・・

windowsにgccをインストールするメモ

go-glというgolangopenglラッパーをgo getしようとしたところ、cgoを利用しているようでgccが必要という旨が表示されたのでインストールした

MSYS2/MinGW-w64 (64bit/32bit) インストール手順 メモ · GitHub こちらをみて64bitMsys2インストール

pacman -Syuu
pacman -S base-devel
pacman -S mingw-w64-x86_64-toolchain

このコマンドで開発環境インストール 途中のインストールを指定するところはALLにした

C:\msys64\mingw64\bin

上記gcc等開発ツール一式がインストールされるので、こちらを環境変数のPATHに加えた

そのあとgo get -uすれば問題無くインストールできた

XPS15が届いた

2/28に注文して今日届いた www.dell.com

帰宅後一通りフィルムを貼ったりセットアップをした

このフィルムを購入したが、ベゼル部分までフィルムの幅があるので端の方に空気が入らざるを得ない感じで少し残念だった

http://amzn.asia/enuhOYN

本体は片手で持てるぐらい軽いのでとても満足している

スペックのよさげなPCが手に入ったので3D系で何かやりたいな

ボクセルコーントレーシングの2bounce以上について考えていた

今日はボクセルコーントレーシングで2Bounce以上はどうやって実現するんだろうと考えてました

unityで有名なこちらのアセットを30分ほど眺めてみたところ、どうやらフィードバックループによって実現しているみたいでした

github.com

フレーム間で交互にボクセライズとバウンスをしてる箇所

github.com

ボクセライズ時に過去のバウンスボリュームを合成している箇所

github.com

深く追っていないのでよくわからないのが、カメラを急激に動かした際のポッピングはどう対応しているのだろうか?

今のところ対応箇所を見つけれていない

フィードバックの係数を指定できるようになっているのでそちらで調節する感じなのか??

golangとGmail APIで大量の未読メールを既読にする

きっかけ

メールを3年ぶりに整理しようとしたところ、未読メールが20万件ほど溜まっていたので適当に検索して出てきた未読メールの一括既読を試してみたのですが、どうも件数が多すぎてエラーを吐かれているらしい

ブラウザから一括既読する方法についての記事

news.livedoor.com

発生したエラー

f:id:kaneta1011:20180209165943p:plain

上記の方法でも100件ずつすれば問題ないのですが2000回も反復作業はできないので、gmailAPIを利用して分割して既読にすることにしました

今回作成したコードはこちら

github.com

動作環境

~$ go version
go version go1.8.3 linux/amd64

OAuth認証

googleAPIにアクセスするためには認証をしないといけないのでこちらのクイックスタートを見て認証を終えた(認証部分やアクセストークンの保存等の実装は丸まる拝借した)

https://developers.google.com/gmail/api/quickstart/go

後はマニュアルを見つつ、既読にするコードに変更していく

godoc.org

未読メールを100件取得する

メールを取得するメソッドはこちら

https://godoc.org/google.golang.org/api/gmail/v1#UsersMessagesService.List

ただし、上記のメソッドをそのまま利用すると未読既読関係なく最新のメールを100件取得してしまうのでクエリパラメータを加える、それが以下のメソッド

https://godoc.org/google.golang.org/api/gmail/v1#UsersMessagesListCall.Q

上記のメソッドに文字列でブラウザのgmail上で利用できる検索ボックスと同じものを指定して取得するメールを限定できる、今回は未読メールなので is:unread を指定しました

以上を踏まえて、未読100件のメールを取得するコードは以下のようになる

mes, err := srv.Users.Messages.List(user).Q("is:unread").Do()
if err != nil {
    log.Fatalf("Error: %v", err)
}

ちなみに、取得時に件数が0の場合はエラーになる

メールを既読にする

APIのスコープを変更する

未読メールには UNREAD というラベルが設定されているので、そのラベルをメールから削除して既読にする

チュートリアルのサンプルではAPIのスコープがReadOnlyになっているので、スコープを gmail.GmailModifyScope に変更しました

config, err := google.ConfigFromJSON(b, gmail.GmailModifyScope)
if err != nil {
    log.Fatalf("Unable to parse client secret file to config: %v", err)
}

一度に100件既読にする

メールの状態を変更するメソッドは ModifyBatchModify があるが、今回は100件一気に変更したいので BatchModify を利用する

https://godoc.org/google.golang.org/api/gmail/v1#UsersMessagesService.Modify

https://godoc.org/google.golang.org/api/gmail/v1#UsersMessagesService.BatchModify

先程取得した100件のメールからIDのスライスを作成して UNREAD ラベルを削除するリクエストの構造体を作成後に実行すれば未読100件の既読が達成できる

ids := []string{}
for _, e := range mes.Messages {
    fmt.Println(e.Id)
    ids = append(ids, e.Id)
}

request := &gmail.BatchModifyMessagesRequest{
    Ids:             ids,
    AddLabelIds:     []string{},
    RemoveLabelIds:  []string{"UNREAD"},
    ForceSendFields: []string{},
    NullFields:      []string{},
}
err = srv.Users.Messages.BatchModify(user, request).Do()
if err != nil {
    log.Fatalf("Error: %v", err)
}

未読0件になるまで繰り返す

実は100件の未読メールを取得する際に、取得件数が0の場合はエラーを返すようになっているので、上記の流れをループすればOK

3秒毎に100件ずつ既読にしているので結構時間がかかりますが一日寝かせてみます