C++幼女先輩

プログラミング成分多め

ジム計画

ジムに通い始めた件

ジムに通い始めてもうすぐ1か月がたちます
正直いいジムなんですが、失敗したなと思う点が数点あります

  1. 朝が10時から。夜は22時まで。木曜定休日と、常駐していると通いつらい
    どうしても夜遅くになる事が多いので 22時までだと業務後に行っても時間がない
    始業前には無理
    別のジムだと朝7時 からあるので、朝一ジムから出社も可能

  2. 人が多い
    行く時間帯のせいか人が多く、マシンも少し待つこともあるし プールも初心者コース多い
    プールに関してはもっと泳げるようになり上級者に入れば問題はないが
    あと 老人が多い

3.マシンにタブレットが置けない
すぐ近くのジムだと 古いマシンだったのでタブレットが置けたので、映画やアニメみながらランニングできた

4.場所の魅力がない
ついでに買い物とかする場所がない

目的

健康とダイエットです
経営者の健康リスクは会社のリスク

KPI

ここはプログラマなので、だらだらとジムしません KPIからです
ジムの体重計が体組成計で データが取れるので
最初に ExcelでKPI をグラフにするものを作った
KPIとしては 体重、体脂肪率、脂肪量、筋肉量、内臓脂肪レベル、基礎代謝 を選び、週一で計測する

チェック項目

基本ルールとして、今の時期は体重ではなく筋肉量に注目することにした
筋肉量(基礎代謝)さえ増えれば体重がいくら増えたって 有酸素運動して食事制限すれば すぐに落ちるはずだ
そのため、最も筋肉の多い 足の筋トレを中心に考えている

罰ゲーム

体重はどうでもいいといいながら、軽い罰ゲームをもうける
最初は 先週より体重が増えたら炭水化物カットとか考えていたが、デメリットもあるので辞め
体重が先週より増えたら パーソナルトレーナーをつけることにした
そうすれば お財布も痛み、トレーニング効果も上がる 罰ゲームとしては適切である

こんなかんじで、しばらくジム続けていこうとおもう

会社の事業方針

わが 株式会社ムラサメ研究所は、ゲームを中心としつつ、VTuber、Web開発や仮想通貨などいろいろな開発を行っています
特にゲームでは、サイバーエージェント、スクエアエニックス、KLab などなど お世話になりまくっています
残念ながら私の力には限界があり、時間も有限なので、やりたいことの1%も実現できません
ので 私の求める社会に近づけるため、事業を集約しなければならず 下記の5個に集約した
本当は3本の柱にしたかったが 絞り切れない・・

3D事業

私はゲームエンジンを作ったり、GPUハードウェアの事もなぜか知っているため、いま最も仕事がきてます
ゲームのクライアントはもちろん、VTuberやAR/VR、画像解析なども
UNITYやUnreal案件が多いが、WebGLなど エンジンから作る案件もよく来ます
3Dはこれから 3Dデータのマイニングが必要になり需要は減らないはずです
たとえば自動運転などをとってみても 3Dが必要であるし、仕事でARを使われることも増える
iPhoneのカメラは3D撮影が可能になっている
今時点では UNITYの ノンフォトリアルシェーダーを書くことが多いので
ノンフォトリアルシェーダーライブラリを作ろうかと

サーバインフラ

サーバインフラはITあるかぎり需要はなくならないと思ってる
特に C++をつかった高速Webサーバを作る人間はほとんどいない
高速なサーバインフラは最も得意としている

ブロックチェーン、仮想通貨

これからブロックチェーン技術は色々なものに使われていく
また、私の中では 仮想通貨を使った経済圏を作るべきなので、仮想通貨プロジェクトは続けていきたい
取引所開発を手伝っていた&知人に取引所CTOがいるため、こちらももっと研究していきたい
また、Bitcoin他のコードはC++で書かれているため、私のC++力は役に立つ

AI、機械学習

賢いAIが現れれば 人手にかわりAIを導入します
経営者としては当然のこと
そして、今はどんどん人間よりも賢いAIが増えている
DeepLearningのアルゴリズムなどは一通り勉強しているし
特にこの分野は高速化が重要
C++力&GPGPU力は 役に立つだろう

セキュリティーチーム

どの分野でも ネットワークにつながったPCがある以上、セキュリティーは重要
たとえば 仮想通貨の取引所は、サーバのセキュリティが弱く 盗難がおこった
ホワイトハッカーの需要は どの業界でも高い
私はカーネルを作ったこともあり C言語も得意、その知識は役立つだろう

#まとめ 以上が今後集中したい事業

UNITYで影にテクスチャをはる(スクリーンスペース)

やること

影にテクスチャを張っていこうと思います
具体的には、戦場のヴァルキュリアのような影部分にスクリーンスペースで ハッチングかけます

f:id:murasame-labo:20180928223528p:plain
わかりいくいけど、影部分にハッチングいれてます

ソースはこちら
github.com

考えられる手法

マルチレンダーターゲットを使いポストプロセスで処理

MRTを使い影部分を別で書き出し、ポスプロでコンポーズする

何度か試したが、UNITYではレンダーターゲットを別のものを設定すると
デフォルトのレンダーターゲットが外れ、EarlyDepth等の値が失われ
Zバッファがうまくとれなかった
もう少し研究すれば うまくいくかもしれないが、挫折

ステンシルバッファを使いポスプロで処理

MRTと似たかんじではあるが、レンダーターゲットを変更する必要がない

VPOSを使い通常パスで行う

ShaderModel 3.0からある VPOSセマンティクスを使えば
フラグメントシェーダーで、スクリーンでのポジションを取得することが出来る
その座標を使えば、ポスプロを使わず 通常パスにてスクリーン座標系でのテクスチャマッピングが可能になる
ので この手法で行ってみる

VPOSでの影テクスチャマッピング

テクスチャの指定

このあたりは普通のUNITYなのでサラッと

properties{
        _ShadowTex ("ShadowTexture", 2D) = "white" {}
}

uniform sampler2D _ShadowTex; uniform float4 _ShadowTex_ST;

VPOSの設定

VPOSを使いたいが制限が色々ある
SV_POSITIONと同時に使うことが出来ない
そのため 通常は 頂点シェーダの返却値と フラグメントシェーダの入力値に同じ構造体を使うが
それを別にしなければならない
なるべく構造体増やしたくないので、同じ構造体を使う方法もある
ただし今回は問題があり それが使えないが記しておく

struct v2f{
    float4 vertexW: TEXCOORD0;
    float2 uv     : TEXCOORD1;
    float3 normalWorld : TEXCOORD2;
    SHADOW_COORDS(3)
};

v2f vert(appdata_full v, out float4 vertex: SV_POSITION){


float4 frag(v2f i, float4 pos: VPOS) : SV_Target{

同時に使えないので、構造体には SV_POSITIONもVPOSも使わない
代わりに 頂点シェーダの引数に out属性でSV_POSITION を指定し
フラグメントシェーダの引数に VPOSを指定する
この方法であれば構造体は1つでよく メンテナンスがしやすいが
UNITYでは TRANSFER_SHADOW(o); などのマクロにて
構造体のposメンバを必要とするため、問題が生じる

そのため、不本意だが構造体を2個作る方法にする

         struct v2f
            {
                float4 pos : SV_POSITION;
                float4 vertexW: TEXCOORD0;
                float2 uv     : TEXCOORD1;
                float3 normalWorld : TEXCOORD2;
                SHADOW_COORDS(3)
            };


            struct v2f_in
            {
                UNITY_VPOS_TYPE vpos : VPOS;
                float4 vertexW: TEXCOORD0;
                float2 uv     : TEXCOORD1;
                float3 normalWorld : TEXCOORD2;
                SHADOW_COORDS(3)
            };

単純に、頂点シェーダの返却値は SV_POSITION、フラグメントシェーダの入力値は VPOS
にすればいい

影の計算

まず、通常の影では濃いので通常影を薄くするため LIGHT_ATTENUATIONの影響を減らし
先程のテクスチャを乗算してみる
SHADOW_ATTENUATIONは影が0で光が1になっているので そのあたりを考慮
また、VPOSはスクリーン座標なのでそれをuv座標 0~1にするには _ScreenParamsで割らなければならない

//LightColor
float3 lightCol = _LightColor0.rgb * lerp( 0.5, 1.0,  LIGHT_ATTENUATION(i));

i.vpos.xy /= _ScreenParams.xy;
float3 shadowTex = tex2D(_ShadowTex, i.vpos.xy) * lerp( 0.5, 0.0,  SHADOW_ATTENUATION(i));
return float4( (ambient + diffuse) * tex * (1-shadowTex) + specular, 1.0);

これで 影の部分のみハッチングがかかるようになった

影のパラメータを追加

影や影テクスチャの強さを設定できるように パラメータを2つ追加する

        _ShadowPower ("ShadowPower", Range(0.0, 1.0)) = 0.5
        _ShadowTexPower ("ShadowTexPower", Range(0.0, 1.0)) = 0.5


//LightColor
float3 lightCol = _LightColor0.rgb * lerp( (1-_ShadowPower), 1.0,  LIGHT_ATTENUATION(i));

i.vpos.xy /= _ScreenParams.xy;
float3 shadowTex = tex2D(_ShadowTex, i.vpos.xy) * lerp( _ShadowTexPower, 0.0,  SHADOW_ATTENUATION(i));
return float4( (ambient + diffuse) * tex * (1-shadowTex) + specular, 1.0);

これで影の強さや影テクスチャの強さを変更できるようになった!

UNITYの影調査

先程まででUNITYの影の作成ができましたが
今回はもう少し調査してみます

通常表示

f:id:murasame-labo:20180923225658p:plain
ShadowCasterパスを追加し、ちゃんと影を表示できてます

影のみ表示させてみる

影のみは SHADOW_ATTENUATION で取れる
今回は R要素のみに SHADOW_ATTENUATION値を入れる
フラグメントシェーダーの戻り値を下記のように変更

                return float4( SHADOW_ATTENUATION(i), 1,1,1);

SHADOW_ATTENUATIONは 影の部分が0、表が1なので、影の部分のみ青緑になるはず
f:id:murasame-labo:20180926141446p:plain

ちゃんと影部分のみ 青緑になりました

ShadowMapを表示してみる

調査していくなかでシェーダーコードを見ていて発見した
_ShadowMapTexture という名前で シャドウマップを生成しているので、それを表示してみることに
ポストプロセスでもシャドウマップは残っているため ポスプロで表示してみる

ポストプロセス スクリプト

using UnityEngine;

public class PP : MonoBehaviour {

    public Material mat;
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Graphics.Blit(src, dest, mat);
    }
}

(本当は Require Cameraとかしたほうが良いが) シンプルに 現在のフレームをそのままシェーダーに通すだけ

シェーダー

Shader "Custom/PPShadowMap"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            
           #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }
            
            sampler2D _MainTex;
            sampler2D _ShadowMapTexture;

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_ShadowMapTexture, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

_ShadowMapTexture という名前で定義されているシャドウマップをそのまま表示してみる

f:id:murasame-labo:20180926154529p:plain

これが シャドウマップだ! ライトからの透視変換なので、すでにこの時点で 元の透視変換行列を失っており スクリーンに投影し辛い

マルチレンダーターゲットで影を別のターゲットにしポストプロセスで合成

UNITYでMRTを使うには 普通のシェーダと同じく、フラグメントシェーダの戻り値をfloatではなく構造体にし SV_Targetn を使えばいい

            struct fout{
                fixed4 c0 : SV_Target0;
                fixed4 c1 : SV_Target1;
                fixed4 c2 : SV_Target2;
                fixed4 c3 : SV_Target3;
                fixed4 c4 : SV_Target4;
                fixed4 c5 : SV_Target5;
                fixed4 c6 : SV_Target6;
                fixed4 c7 : SV_Target7;
            };


...

                o.c0 = fixed4( (ambient + diffuse) * tex + specular, 1.0);
                o.c1 = fixed4( LIGHT_ATTENUATION(i), 1, 1, 1 );

                return o;

ポストプロセス

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;

public class PP_HatchShadow : MonoBehaviour {
    
    public Material mat;


    private RenderTexture camTarget;
    private RenderTexture[] buf = new RenderTexture[8];
    private RenderTexture depth;



    private Camera cam;

    private void Awake()
    {
        cam = GetComponent<Camera>();
        cam.depthTextureMode = DepthTextureMode.Depth;

        for (int i = 0; i < 8; i++){
            buf[i] = new RenderTexture(Screen.width, Screen.height, 0);
            buf[i].Create();
        }

        CommandBuffer cmd = new CommandBuffer();
        RenderTargetIdentifier tar = new RenderTargetIdentifier(BuiltinRenderTextureType.CurrentActive);


        cam.SetTargetBuffers(new RenderBuffer[8] {
            buf[0].colorBuffer, buf[1].colorBuffer
            , buf[2].colorBuffer, buf[3].colorBuffer
            , buf[4].colorBuffer, buf[5].colorBuffer
            , buf[6].colorBuffer, buf[7].colorBuffer
            }, buf[0].depthBuffer);

        mat.SetTexture("_RT0", buf[0]);
        mat.SetTexture("_RT1", buf[1]);
        mat.SetTexture("_RT2", buf[2]);
        mat.SetTexture("_RT3", buf[3]);

        cmd.name = "PPHatchingShadow";
        cmd.Blit(tar, tar, mat);

        cam.AddCommandBuffer(CameraEvent.BeforeImageEffects, cmd);
    }
}

これで、ポストプロセスにて o.c1.x 成分として影がとれるはずだが
結果を言うと デプスがおかしい

原因はどうも、UNITYでは ポストプロセスにてレンダーターゲットが変わって(アーリーデプスの結果が消え)
のようだ

ってことで、今度はこのあたりの調査をしようとおもう

UNITYで影をつくる〜アクネ退治

先程 酷いアクネを出していました

f:id:murasame-labo:20180919203509p:plain

アクネというのはニキビという意味ですが、3DCGでは上記のようなゴミが出ることをいいます
上記は、影でよく出る種の デプスバッファの量子化におけるアクネではないのですが
似ているのでアクネといってみます

原因は、実は ShadowCasterにて、Posしか計算していなかったのが理由で
今回は
TRANSFER_SHADOW_CASTER_NORMALOFFSET
を使うようにします

ShadowCasterパスを直します

構造体

まず 今までは 頂点、ピクセル座標しか入力してなかったのを、色々と

頂点シェーダの入力

appdata_base構造体を使います
中身は

            struct appdata_base {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

と、頂点、法線、UVがあります

フラグメントシェーダの入力

             struct v2f {
                V2F_SHADOW_CASTER;
            };

とUNITYのマクロを使いますが、DirectionalLightだと下記のように posのみです

             struct v2f {
                float4 pos : SV_POSITION;
            };

TRANSFER_SHADOW_CASTER_NORMALOFFSET

UnityObjectToClipPosから変更

                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)

結果

アクネなくなった
f:id:murasame-labo:20180919205513p:plain

UNITYの影 LIGHT_ATTENUATIONは SHADOW_ATTENUATIONを乗算している

#ifdef DIRECTIONAL
#define DECLARE_LIGHT_COORDS(idx)
#define COMPUTE_LIGHT_COORDS(a)
#define LIGHT_ATTENUATION(a) SHADOW_ATTENUATION(a)
#endif

上記はDirectionalだが、他のライトも同じで、LIGHT_ATTENUATIONには影が乗算されている

UNITYで影を受け取る

前回は影を落としましたがまだこのシェーダでは影を受け取れません
地面(Plane)メッシュのシェーダーを 今使ってる yUnitに変えると影はおちないし、セルフシャドウもありません
今回は影を受け取るようにします

https://github.com/YukiMiyatake/UnityLesson/tree/Shadow_2github.com

仕組み

UNITYではシャドウマップを使っています
シャドウマップの具体的な説明は他のサイトにお願いして
ライトからみたデプス値を事前に別のテクスチャに書き込んでおきます
レンダリング時に、ライトからみたデプス値と比較をして、ライトのほうが近い場合は、そのポイントは影になる
という仕組みです
その ライトからみたデプステクスチャを作るパスが、前回つくった ShadowCasterパスになります

今度は、そのデプステクスチャを参照して影を作るのですが
以前のUNITYではShadowReceiveパスで影を処理していたようですが、今は通常のカラーパスにて行うことができます
具体的には、SHADOW_ATTENUATIONマクロを使えば、影は0、表は1で、その間を補完した値が手に入ります
ので そのままカラーに乗算すれば影は黒くなります

pragma

今回は ShadowのON/OFFがあるので、その両方のシェーダをコンパイルする必要があるので下記のpragmaが必要です
本当は 各種ライトに対するシェーダも必要なので もっとたくさん必要ですが、今回は最低限

#pragma multi_compile_fwdbase

構造体

フラグメントシェーダにて SHADOW_ATTENUATIONマクロを使いますが、そのために必要な入力値があるので追加
v2fにSHADOW_COORDS(n) を追加し、影用のテクスチャを追加します。今回はテクスチャ2番まで使ったので3を割り当て
あと、私が少しハマったのが、ポジションを posという名前にしなければ計算できないようです・・・
ので 以前 vertexとしていたメンバ名を posに変える

影の取得

SHADOW_ATTENUATIONに、入力構造体の中身を渡せば 0.0~1.0の範囲で取得できます
今回は単純にそれをdiffuseに乗算します

             float3 diffuse = (NdotL*0.5 + 0.5) * lightCol * SHADOW_ATTENUATION(i);

完成コード(ForwardBaseパスのみ)

     Pass{
            Tags{
                "LightMode" = "ForwardBase"
            }

            Cull Back

            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag

           #include "UnityCG.cginc"
           #include "Lighting.cginc"
           #include "AutoLight.cginc"
                         #pragma multi_compile_fwdbase

            struct appdata
            {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 texcoord   : TEXCOORD0;
            };

            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 vertexW: TEXCOORD0;
                float2 uv     : TEXCOORD1;
                float3 normalWorld : TEXCOORD2;
                SHADOW_COORDS(3)
            };

            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            uniform float _Spec1Power;
            uniform float4 _Spec1Color;


            v2f vert(appdata_full v){
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.vertexW = mul(unity_ObjectToWorld, v.vertex);

                o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
                o.normalWorld = UnityObjectToWorldNormal(v.normal);

                half3 eyeVec = normalize(o.vertexW.xyz - _WorldSpaceCameraPos);

                TRANSFER_SHADOW(o);

                return o;
            }

            float4 frag(v2f i) : SV_Target{
                float3 L = normalize(_WorldSpaceLightPos0.xyz);
                float3 V = normalize(_WorldSpaceCameraPos - i.vertexW.xyz);
                float3 N = i.normalWorld;
                float3 H = normalize(L + V);


                //LightColor
                float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);

                //Ambient
                float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                
                // texture albedo
                float4 tex = tex2D(_MainTex, i.uv);

                // Diffuse(HalfLambert)
                float3 NdotL = dot(N, L);
                float3 diffuse = (NdotL*0.5 + 0.5) * lightCol * SHADOW_ATTENUATION(i);
                // Speculer
            // float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz;  // reflection
                float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz * lightCol;  // Half vector

                return float4( (ambient + diffuse) * tex + specular, 1.0);
            }
            ENDCG
        }

f:id:murasame-labo:20180919185417p:plain

影ができた(でもアクネがあるね)