ぷるぷるの雑記

低レイヤーがんばるぞいなブログ. 記事のご利用は自己責任で.

cssについてメモ

cssで何度も調べてしまうことをメモに残しておきます.

完全に自分用のメモなので変なことを書いてるかもしれないです.

display

その要素をブロック要素とインライン要素のどちらとして扱うか、および子要素をどのようにレイアウトするかということを指定します.

block, inline

その要素自体をブロック要素とインライン要素のどちらとして扱うかということを指定します.

inline-block もこの部類に入りますが、MDNには旧来のものとして掲載されています. 後述する2値の構文が使用可能になったcss3ではよい方法とは言えないようです(より良い方法があるという意味).

flex, grid

子要素をどのようにレイアウトするかということを指定します. どちらも子要素をブロック要素のように扱いつつも横に並べることが出来ますが、グリッドモデルの方がより自由度が高いです. 具体的には、フレックスボックスモデルはブロック要素を縦か横の一列にしかレイアウトできないのに対し、グリッドモデルは多次元に配置することが出来るそうです.

なお、フレックスボックスモデルの代わりに floatプロパティ を使用することもできるらしいですが、現代風ではない?方法のようです.

none

表示しない(領域そのものを生成しない).

2値の構文

すでに見てきたようにdisplayプロパティはその要素自身のレイアウトに関するものと子要素のレイアウトに関するものがあります. この2種類のプロパティはスペース区切りで同時に指定することが出来ます(2値の構文).

display: block flex;
display: inline flex;

displayプロパティの参考ページ

developer.mozilla.org

coliss.com

position

その要素の位置(正確には要素の左上の座標)をどのように指定するかを決めることが出来ます.

とる値が5種類しかありません.

static

デフォルトの値. top、bottom、right、left、z-indexプロパティが適用されません.

relative

要素が本来配置されるべき位置が基準となります. デフォルトではstaticと変わりませんが、top、bottom、right、left、z-indexプロパティを適用することが出来るようになります.

absolute

親要素の配置位置(親要素の左上の座標)が基準になります. 適用するためには 親要素のpositionがstatic以外である必要があります.

fixed

fixedはビューポートの左上を基準にして要素を固定します. 画面に張り付くような見栄えになりますが、position:fixedを指定した要素は高さがなくなります.

sticky

親要素を基準にしたfixedという感じです. fixedとは違い高さはなくなりません.

position:stickyを指定しただけではスクロールに追従してくれません. 必ずtop or left プロパティなどを指定する必要があります.

positionプロパティの参考ページ

developer.mozilla.org

blog.codecamp.jp

mightyace.co.jp

www.jungleocean.com

Blenderでガラスを表現する - アルファブレンディングの設定 -

Blenderでガラスの容器を作っているときにガラスの中身を表示するときに少し戸惑ったのでその時のメモ.

ガラスのマテリアル

まず「Blender ガラス マテリアル」で検索をかけPrincipled BSDF でガラスを表現する方法を探しましょう.

light11.hatenadiary.com

こちらの記事の通りMetallic=0.0、Roughness=0.0、Transmission=1.0にしてみましょう.

マテリアルプロパティ

結果はこちらになります.

ガラスのマテリアルの結果 - その1 -

たしかにガラスっぽくなっていますが中身が透けて見えませんね.

アルファブレンディングの設定

中身が透けなかった原因はBlend ModeをOpaqueにしているからです. 透明なオブジェクトを表すにはマテリアル-> Settings -> Blend Mode をAlpha Blendにします.

マテリアルのブレンドモードをAlpha Blendに

さらにマテリアルプロパティのAlphaを小さい値にします. 今回は0.4としました.

Alpha BlendにしてからAlpha値も変更する

さあ、これで準備が出来ました! 得られた画像はこちらになります

ブレンドモードをアルファブレンドした結果

成功ですね

Transmissionをいじる

Transmissionを0にした場合の結果がこちらになります.

Transmission=0の結果

少しトゥーンっぽい感じになりましたかね.

感想

今では何度検索しても忘れてしまっていたのですが、Unityのシェーダーを書くようになってからは割とすんなり頭に入ってきました. いろんな経験を積むものですね

雑記1

思いっきりプログラムを書くぞーっと意気揚々にGWに入ったのに何もせずにGWが終わってしまいました. こうすればよかったなーと思うことが2つあります


  1. 時間を目標にせずに、その日ごとに具体的な目標を立てるべきだった
  2. GWには普段できないこと、やらないことをやるべきだった


1.についてはどんなことでも時間を目標にしてしまう辛いものになってしまいます. どんなに好きなゲームでも○○時間ストーリーを進めなさいと言われるとやる気がそがれます.xxの町まで行くということを目標にして、それを過ぎたら続けるのもやめるのも自由という風にした方が結果としてゲームを楽しめると思いませんか?


2.についてはGWが普段の休日の延長になってしまったことへの反省です. 私のGW休みは10日あったので5週間分の週末を過ごしたようなものでしたが、逆に言えばGWがなくとも1ヶ月頑張れば得られた休みだったということでもあります.これでは10連休である意義がありませんね. 今思えばランニングするなり遠出するなり、2連休ではやる気にもならないことに時間を注いでもよかったなぁと思います.


冷静に見てみると、受験生へのアドバイスかな?って感じのことばかり書いてありますね.時間をうまく使うにはうまいこと遊びを作ることが必要だということが改めて分かりました.

Unityで鏡面反射成分を入れるとなぜか黒くなる

UnityでBlinn-Phongの陰影付モデルを自分で実装したところ予期しないところが黒くなるという現象に出くわしました. その時に試したことのメモ.

実際のコードと実行結果

実際のコードは次になります. 拡散反射光と鏡面反射光を考慮しています.

// リスト1

Shader "Custom/Blinn-Phong"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _NormalTex("Normal", 2D) = "white" {}
        _Kshi("Kshi", Range(0,100)) = 50
        _Color("Color", Color) = (1,1,1,1)
        _F("Frenel", float) = 0.02
    }

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

        LOD 100
        Blend SrcAlpha OneMinusSrcAlpha
        
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag          

            #include "UnityCG.cginc"


            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float4 tangent : TANGENT;
                float3 normal:NORMAL;
            };

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 lightDir : TEXCOORD1;
                float3 viewDir : TEXCOORD2;
            };

            sampler2D _MainTex;
            sampler2D _NormalTex;
            float _Kshi;
            float4 _Color;
            float _F;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);

                TANGENT_SPACE_ROTATION;
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));

                o.uv = v.uv;

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // 正規化
                i.lightDir = normalize(i.lightDir);
                i.viewDir = normalize(i.viewDir);
                float3 H = normalize(i.lightDir + i.viewDir);
                
                // ノーマルマップからタンジェントスペースのVector3を取得
                float3 tangent = UnpackNormal(
                    tex2D(_NormalTex, i.uv)
                );

                tangent = normalize(tangent);

                float4 diff = max(
                    0, dot(tangent, i.lightDir)
                );

                float3 spec = pow(
                    max(0, dot(tangent, H)),
                    _Kshi
                );

                // Albedoサンプリング
                float4 albedo = tex2D(_MainTex, i.uv);
                float4 col;
                col.rgb = albedo * _Color;

                // Diffuse
                col.rgb = col.rgb * diff;

                // Specular
                col.rgb = col.rgb  + spec;

                // フレネル効果
                float fresnel = _F + (1 - _F) * pow(1 - dot(i.viewDir, tangent), 5);
                col.a = fresnel;
     
                return col;
            }
            ENDCG
        }
    }
}

マテリアルの設定

リスト1の実行結果

やけに左下が黒いのは拡散反射光の影響かなと思い、 diffを乗算する部分をコメントアウト したところ、次のようになりました.

diffの影響をコメントアウトした結果

うーん、あまり変わってない. 一応 specを加算する部分をコメントアウト したところ、次のようになりました.

specの影響をコメントアウトした結果

なんでや、左下も白くなってるやん!? ということはつまり、 specの値が負になっとるやん!?.

いやいやいや、だってspecの定義は次のようにしてるんですよ.....

 float3 spec = pow(max(0, dot(tangent, H)), _Kshi );

非負数の実数乗ですよね?なんで負の数になってるんですか...

どうやら0の0乗が原因らしい

再びspecの加算をコメントイン させて今度はパラメーターをいじってみたところ、どうも _Ksiを0以外の値 にすると左下が黒くなる現象が消えることが分かりました. つまり、 0の0乗 が悪さをしていたようです.

パラメーターをいじった結果

まとめ

シェーダーに限らないですが各変数がとりうる値の範囲というのは常に気にした方がいいんでしょうね. それとやっぱり0が出てくるところは怖いですね.

UnityでTerrain用Brushをインポートする

Terrain(地形)を編集するときに使うBrushですが、思いのほか簡単に自作できたのでその時のメモ.

1. Terrainを作成する

3D Object->TerrainからTerrainを作成する

2. 画像をインポート

Brushのもととなる画像(テクスチャ)をインポートします. 専用のフォルダを作成しておくとよいでしょう. 画像の形式はなんでもよいです. グレースケールでもRGBでも.pngでも.jpgでもよいですが、グレースケールがベター?

今回は次のモノクロ画像(二値)をインポートしました

インポートした画像

3. テクスチャをBrushとして登録

  1. で作成したTerrainを選択し、Inspector -> Terrain -> Paint Terrain -> New Brush をクリックします.
    Paint Terrain -> New Brush をクリック

するとテクスチャを選択するウインドウが表示されるので、2. でインポートしたテクスチャを選択します.

Brushとして利用するテクスチャを選択

ここまでくればBrushesに新しくBrushが追加されます.

Brushesがついかされた

4. Falloffを調整

今回インポートしたテクスチャはモノクロ(二値)であったのに、このテクスチャを参照しているBrushがなぜかグレースケールになっています. 二値にしたい場合はBrushのInspectorからFalloffをConstantにします.

BrushのInspectorを開く

BrushのInspector

Falloutを調整

すると参照元のテクスチャの通りのBrushになりました.

5. Brushを削除する

Assetsフォルダ内のBrushを削除すればTerrainのBrush一覧からも削除されます.

Unityでディスプレイスメントシェーダーを実装する

タイトルの通りUnityでディスプレイスメントシェーダーを実装しました.

シェーダースクリプト

Shader "Custom/Displace"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DisplaceMap ("DisplaceMap", 2D) = "white" {}
        _OffsetStrength("OffsetStrength", Range(0,10)) = 1
        _NormalMap("NormalMap", 2D) = "white" {}
        _Spec("SpacularStrength", Range(0,10)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

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

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 viewDir: TEXCOORD1;
                float3 lightDir: TEXCOORD2;
            };

            sampler2D _MainTex;
            sampler2D _DisplaceMap;
            sampler2D _NormalMap;
            float _OffsetStrength;
            float _Spec;

        

            v2f vert (appdata v)
            {
                v2f o;
                // VS内で使えるテクスチャサンプリング関数
                float4 _offset = tex2Dlod(_DisplaceMap, float4(v.uv, 0, 0));

                // rgbaをサンプリングしたのちrチャネルを抽出
                // 画像が4チャネルでもグレースケールならr=g=b
                float offset = _offset.r;

                // モデル空間での法線に沿って頂点を変形させる
                v.vertex += float4(
                    v.normal * offset * _OffsetStrength,
                    0
                );

                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;

                // ライトとビューから頂点へのベクトルをオブジェクト空間にしたのち
                // タンジェントスペースに変換する
                TANGENT_SPACE_ROTATION;
                o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex));
                o.viewDir = mul(rotation, ObjSpaceViewDir(v.vertex));

                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                // 正規化
                i.lightDir = normalize(i.lightDir);
                i.viewDir = normalize(i.viewDir);
                // sample Normal in Tangent Space
                float3 normal = UnpackNormal(
                    tex2D(_NormalMap, i.uv)
                );
                normal = normalize(normal);

                float3 halfDir = normalize(i.lightDir + i.viewDir);
                float4 diff = saturate(
                    dot(normal, i.lightDir)
                );

                float3 spec = pow(
                    max(0, dot(normal, halfDir)),
                    _Spec * 128.0
                );

                
                // sample Albedo
                float4 tex = tex2D(_MainTex, i.uv);
                 
                // add diffuse and specular effects
                fixed4 col;
                col.rgb = tex.rgb * diff + spec*tex.rgb;
                return col;
            }
            ENDCG
        }
    }
}

なお、拡散反射と鏡面反射の部分は下記のサイトを参考にしました.

blog.applibot.co.jp

頂点テクスチャフェッチ(VTF)とは

通常テクスチャのサンプリングはフラグメントシェーダー内で行いますが、バーテックスシェーダー内でサンプリングを行うことを頂点テクスチャフェッチ(Vertex Texture Fetch)というらしいです. 特別な手続きは特に必要なく、VTF用の関数を用いるだけです.

// VS内で使えるテクスチャサンプリング関数
float4 _offset = tex2Dlod(_DisplaceMap, float4(v.uv, 0, 0));

tex2D() ではなく tex2Dlod() を利用することだけ覚えておきましょう.

ここで注意なのがFS内で使用するtex2D()とは異なり、 uv座標をfloat4で指定します .

docs.microsoft.com

docs.microsoft.com

Standardシェーダーとの比較

上記の自作シェーダーとUnityにデフォルトで備わっているStandardシェーダーをオブジェクトに適用してその見栄えを比較してみましょう.

テクスチャにはambientCGのTiles 107を利用しました.

ambientcg.com

公平な比較をするために、自作シェーダーで対応しているAlbedo、Normal Map、Height Mapの3テクスチャを適用した場合の比較になります. またStandardシェーダーのテクスチャ以外の値はデフォルトです.

自作シェーダー

Standardシェーダー

思いのほか自作シェーダーもよい感じですね. しかし、自作シェーダーとなるといずれ光の表現などに限界を感じることでしょう.

Standard Assetsのシェーダーを使おう

さて、これまでディスプレイスメントシェーダーを自作してきたわけですが、大きな問題が2つあります.

  1. ディスプレイスメントしたことによる法線の再計算をしていない
  2. ポリゴンの頂点数が少ないときにうまくディスプレイスメント出来ない

1に関してはNormal Mapを使ったりすればよいですし、再計算を実装することもそこまで難しくはありません. 2に関してもUnityにはテッセレーションに関する便利な機能があります. しかしながら、これらの機能を盛り込んだディスプレイスメントシェーダーがStandard Assets内に存在するので、特に理由がない限りはこれを使うべきでしょう.

Standard Assetsのディスプレイスメントシェーダー

assetstore.unity.com

参考

docs.unity3d.com

tsumikiseisaku.com

www.wwwmaplesyrup-cs6.work

Blenderで調べたことのメモ- その2 -

ループ選択

Ctrl + Alt + 左クリック

NormalノードとTangentノード

どちらも法線の情報を入力することには変わりないが、Normalノードにはオブジェクトスペースでの法線ベクトルを入力し、Tangentノードには接ベクトル空間での法線ベクトルを入力する。

オブジェクトスペースでの法線マップは色とりどりになるのに対し、接ベクトル空間での法線マップは青っぽくなるのはお約束.

Ico SphereのNormalマップ(Object Space)

Ico SphereのNormalマップ(Tangent Space)

ダウンロードしたテクスチャにノーマルマップが二種類ある

DirectXOpenGLで向きが違うらしくそれぞれに合ったノーマルマップを使う必要がある

seesaawiki.jp

タイリング

Input -> Texture CoordinateとVector -> Mappingを使用し、MappingのScaleをいじる

ノードベースでのタイリングの設定

yuki3dcg.com

頂点数を表示する

右下のバージョンが書いてあるところを右クリックしてScene Stastics にチェックをつける

reflectorange.net

ディスプレイスメントとテッセレーション

ディスプレイスメントはテクスチャに基づいて頂点をずらすこと. テッセレーションはポリゴンを細分化すること. テッセレーションを適用してからディスプレイスメントを適用することが多い.