ムラサメ研究ブログ

主にゲームやプログラミングのログ

UNITYシェーダー研究 UNITシェーダを読む

今回のソース

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

前回のあらすじ

Surfaceシェーダを新規で作る 中野シスターズに適用
Surfaceシェーダの解説

たいした内容じゃなくてすみません

でも 初歩からやりたいの

UNITシェーダを適用する

本格的なシェーダーを作るには UNITシェーダは欠かせないので Surfaceシェーダと同じように作る
今度は名前を Unit/yUnit にする

Shader "Unlit/yUnit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            // make fog work
           #pragma multi_compile_fog
            
           #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

今度は明らかに、レンダリング結果が変わった

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

シェーダーの中身

今回は前回と違い、テクスチャしかパラメーターがありません!

Pass

     Pass
        {
            CGPROGRAM
           #pragma vertex vert
           #pragma fragment frag
            // make fog work
           #pragma multi_compile_fog

UNITシェーダでは 複数のPassを持つことができる
輪郭線とか影とか
今回は1個

pragmaは 前回はSurfaceでサーフェースシェーダを指定したが、今回は vertex、fragmentと2つのシェーダを指定している
もちろん、それらの後に続くのは エントリポイントである
今回はfogが有効になっている

変数宣言等

            #include "UnityCG.cginc"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };


            sampler2D _MainTex;
            float4 _MainTex_ST;

UnityCG.cginc をincludeしているが、色々と便利なマクロや関数等が入ったヘッダなので使う

構造体は今回2個
頂点毎の入力(attribute)の appdata
フラグメントシェーダに渡す Varyingの v2f

そして シェーダーの引数 uniformの _MainTex

シェーダー中身

         v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }

頂点シェーダ

UnityObjectToClipPosの定義を見てみよう

// Tranforms position from object to homogenous space
inline float4 UnityObjectToClipPos(in float3 pos)
{
    // More efficient than computing M*VP matrix product
    return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0)));
}
inline float4 UnityObjectToClipPos(float4 pos) // overload for float4; avoids "implicit truncation" warning for existing shaders
{
    return UnityObjectToClipPos(pos.xyz);
}

オブジェクトのローカル座標からビューポート座標に変換している

TRANSFORM_TEX

// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)

なんと、トークンマクロで

TRANSFORM_TEX(v.uv, MainTex); は
v.uv.xy *
MainTex_ST.xy + MainTex_ST.zw
になる。
MainTex_ST.xyにTiling、zwにOffsetの値が入ってくるようだ

UNITY_TRANSFER_FOG

        // SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel
        #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z

ビューポート座標系でのZ座標をFOGのパラメータに入れている。距離によるFOG。ここは さらっと読み飛ばしてもいい

結論からいうと、ローカル座標をビューポート座標に変換し、テクスチャのUVを補正して、Zフォグのパラメータを渡しているだけのようだ

フラグメントシェーダ

// sample the texture
fixed4 col = tex2D(_MainTex, i.uv);
// apply fog
UNITY_APPLY_FOG(i.fogCoord, col);
return col;

非常にシンプルで、テクスチャマッピングして、FOGを適用しているだけのようだ
ライトの計算も何も入っていない(実際DirectionalライトをON/OFFしても変化しない)

UNITYシェーダー研究 環境構築~サーフェースシェーダ軽く

このソースは GitHub - YukiMiyatake/UnityLesson at Shader_1

はじめに

UNITYのシェーダの知見が少したまってきたので、自分でアウトプットしておかなければ忘れてしまう
ので メモをしていたが

もっと基礎から知りたいという人が多かったので
超基礎からやります。

UNITYシェーダ入門 タグでしばらく書きます

準備

UNITYでプロジェクト作成します。 今回は UNITY2017.1.0f3 を使用した

ビルトインシェーダのコードは色々と勉強になるので UNITYのAdditionalDownloadから落としておくとよい
https://unity3d.com/jp/get-unity/download/archive
ここから ダウンロードタブを開くと 色々なものをダウンロードできるが ビルトインシェーダを落とす
落とさなくてもビルトインシェーダは使えるが、きっとコードを確認したくなるので。

キャラクタモデルは UNITYちゃんを使おうと思ったけど、さらに権利のゆるそうな 中野シスターズにしました
中野シスターズダウンロードしておいてね!

中野シスターズ(ナカシス) 公式サイト

中野シスターズをインポートし、画面に配置します
UNITYメニューより Import->NewAssetで ダウンロードファイルの naka.fbx、kano.fbxをインポート

そして naka(あるいはkano)のプレファブを画面にドラッグし Positionは(0, 0, 0) Rotateは (0, 180, 0)に

デフォルトではカメラが遠いので、アップになるようカメラを近づけて実行

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

表示された

シェーダーを変更

中野シスターズは Standardというシェーダーが付いている
これは ビルトインシェーダ―の Standard という名前のシェーダーで、物理ベースの標準的なシェーダーである

これを新規のシェーダーに置き換える

Create->Shaderをすると たくさんのリストがでる
f:id:murasame-labo:20170911065031p:plain

  • Standard Surface Shader サーフェースシェーダという独自のシェーダ
    頂点シェーダとフラグメントシェーダを一緒に 簡単に記述できる反面、細かい制御は出来ない
  • Unit Shader 頂点シェーダとフラグメントシェーダを記述する。
    今回はコレ
  • Image Effect Shader ポストプロセスに使うシェーダ
  • Compute Shader GPGPUを行う コンピュートシェーダを記述
  • Shader Variant Collection ヘッダのようなもの

SurfaceShader

上記でSurfaceShaderを作ろう
ファイル名が NewSurfaceShaderになっているが、自由にかえてよい
ySurfaceに変更してみた

シェーダーの中身は下記

Shader "Custom/NewSurfaceShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

非常に短いが名前を変えておこう

 Shader "Custom/NewSurfaceShader" {  

ここがシェーダー名を設定する箇所だ
好きな名前にかえてみよう。 とりあえず Custom/ySurface に変更してみた

中野シスターズモデルのマテリアルに対して、今はStandardがついているが それを上記のシェーダーに変更してみる

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

違いはわからないが、シェーダーが変わった

サーフェースシェーダは今回は扱わないので、軽く流して説明(メモする)

SurfaceShader解説

SurfaceShaderは実際のところ、フラグメントシェーダはビルトイン(あるいは自作)のシェーダを呼び出すという処理である

Shader名

Shader "Custom/yShader" {

この部分は上記の シェーダ名の設定
マテリアルに設定するときの名前である
/ を付けることにより、Editor上で階層構造で表示できるので便利

Property

 Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }

シェーダーに対して渡す変数やテクスチャを宣言する
ここに書くと、Editor上に設定項目として出る
実際にシェーダ内で値を使うときには、パス内で定義しなおさなければならない

Syntaxは 変数名 (“Editor表示名”, 型  ) = デフォルト値
_Color (“Color”,     Color ) = (1,1,1,1)

という風になっている
型やDrawer(属性)は色々あるので マニュアル参照

Unity - マニュアル: ShaderLab :プロパティー

SubShader

 SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

ここにシェーダーの内部実装をかく SubShaderは複数書くことが出来る TagsやLODは、このSubShaderを実行するための条件で、この条件に合致したときのみこのシェーダを実行する
合致しない場合には次のSubShaderを実行する
この機能により、ハードウェア依存や、LODによるシェーダの変更等を制御できる

Pragma

     CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0

CGPROGRAM~ENDCG までの間が実際にシェーダにコンパイルされる部分である

pragmaは色々な拡張機能であるが

        #pragma surface surf Standard fullforwardshadows
       #pragma target 3.0

SurfaceShaderで記述する。 シェーダーのエントリーポイントは surf関数である
ライティングモデルはStandardにする(Lambert、BlinnPhong、StandardSpecularも設定可能)
fullforwardshadows 影はフォワードパスですべて表示する

シェーダーモデル 3.0以上必要

変数

     sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END

Editorから渡された値を、シェーダ変数(Uniform)へ格納している

また Inputという構造体を宣言している
これは頂点シェーダへの頂点毎の入力値(attribute)である

UNITY_INSTANCING_CBUFFER_START
は、CBufferを渡したいときは設定するようです 今回は不要

関数本体

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }

いよいよ本体

INには頂点シェーダに渡ってくる attributeで、今回はUV
inout のSurfaceOutputStandardは 組み込み構造体で中身は下記

struct SurfaceOutputStandard
{
    fixed3 Albedo;      // base (diffuse or specular) color
    fixed3 Normal;      // tangent space normal, if written
    half3 Emission;
    half Metallic;      // 0=non-metal, 1=metal
    half Smoothness;    // 0=rough, 1=smooth
    half Occlusion;     // occlusion (default 1)
    fixed Alpha;        // alpha for transparencies
};

まず テクスチャマップで UV座標のテクスチャカラーを取得し _Colorを乗算し oのAlbedoに設定
その他パラメータを oに設定する

これだけで、サーフェースシェーダでは pragmaで設定したライトモデル(今回はStandard)に 構造体oの値を自動で渡し終了となる
少し簡単になる

Fallback

 FallBack "Diffuse"

SubShaderがどれも条件が満たさなかったら Diffuse(ビルトインシェーダ) を実行する

トゥーンシェーダ―作ってみる 

トゥーンシェーダ―作りたい

シェーダーの色々な知見が出来たので少しずつメモ

中野シスターズ

まず 中野シスターズをダウンロードしてくる
詳細は省略するが、ダウンロードして UNITYにインポートしシーンに配置する
f:id:murasame-labo:20170910113613p:plain

暗いので、今後色々と遊ぶので ポイントライトを4-5個つけてみる
調整はまたこんど
f:id:murasame-labo:20170910113702p:plain

ちなみに Forwardレンダリングだと
DrawCall 74 f:id:murasame-labo:20170910113732p:plain

Deferredレンダリングだと
DrawCall 41 f:id:murasame-labo:20170910113753p:plain

ライトが多いと圧倒的に Deferredのメリットが出てくる

シェーダーをコピー

中野シスターズは Standardシェーダ(Unityのビルトインシェーダ)を使っている
UNITYの公式ページより ビルトインシェーダをダウンロードすれば中身を見れる

UNITYエディタで Create->Shaderで シェーダを作り、Standardをコピーしよう
シェーダー名は 今回は Yuki/Standard にした

// Unity built-in shader source. Copyright (c) 2016 Unity Technologies. MIT license (see license.txt)

Shader "Yuki/Standard"
{
    Properties
    {
        _Color("Color", Color) = (1,1,1,1)
        _MainTex("Albedo", 2D) = "white" {}

    _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5

        _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5
        _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0
        [Enum(Metallic Alpha,0,Albedo Alpha,1)] _SmoothnessTextureChannel("Smoothness texture channel", Float) = 0

        [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0
        _MetallicGlossMap("Metallic", 2D) = "white" {}

    [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0
        [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0

        _BumpScale("Scale", Float) = 1.0
        _BumpMap("Normal Map", 2D) = "bump" {}

    _Parallax("Height Scale", Range(0.005, 0.08)) = 0.02
        _ParallaxMap("Height Map", 2D) = "black" {}

    _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0
        _OcclusionMap("Occlusion", 2D) = "white" {}

    _EmissionColor("Color", Color) = (0,0,0)
        _EmissionMap("Emission", 2D) = "white" {}

    _DetailMask("Detail Mask", 2D) = "white" {}

    _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {}
    _DetailNormalMapScale("Scale", Float) = 1.0
        _DetailNormalMap("Normal Map", 2D) = "bump" {}

    [Enum(UV0,0,UV1,1)] _UVSec("UV Set for secondary textures", Float) = 0


        // Blending state
        [HideInInspector] _Mode("__mode", Float) = 0.0
        [HideInInspector] _SrcBlend("__src", Float) = 1.0
        [HideInInspector] _DstBlend("__dst", Float) = 0.0
        [HideInInspector] _ZWrite("__zw", Float) = 1.0
    }

        CGINCLUDE
#define UNITY_SETUP_BRDF_INPUT MetallicSetup
        ENDCG

        SubShader
    {
        Tags{ "RenderType" = "Opaque" "PerformanceChecks" = "False" }
        LOD 300


        // ------------------------------------------------------------------
        //  Base forward pass (directional light, emission, lightmaps, ...)
        Pass
    {
        Name "FORWARD"
        Tags{ "LightMode" = "ForwardBase" }

        Blend[_SrcBlend][_DstBlend]
        ZWrite[_ZWrite]

        CGPROGRAM
#pragma target 3.0

        // -------------------------------------

#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
#pragma shader_feature _PARALLAXMAP

#pragma multi_compile_fwdbase
#pragma multi_compile_fog
#pragma multi_compile_instancing
        // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
        //#pragma multi_compile _ LOD_FADE_CROSSFADE

#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"

        ENDCG
    }
        // ------------------------------------------------------------------
        //  Additive forward pass (one light per pass)
        Pass
    {
        Name "FORWARD_DELTA"
        Tags{ "LightMode" = "ForwardAdd" }
        Blend[_SrcBlend] One
        Fog{ Color(0,0,0,0) } // in additive pass fog should be black
        ZWrite Off
        ZTest LEqual

        CGPROGRAM
#pragma target 3.0

        // -------------------------------------


#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP

#pragma multi_compile_fwdadd_fullshadows
#pragma multi_compile_fog
        // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
        //#pragma multi_compile _ LOD_FADE_CROSSFADE

#pragma vertex vertAdd
#pragma fragment fragAdd
#include "UnityStandardCoreForward.cginc"

        ENDCG
    }
        // ------------------------------------------------------------------
        //  Shadow rendering pass
        Pass{
        Name "ShadowCaster"
        Tags{ "LightMode" = "ShadowCaster" }

        ZWrite On ZTest LEqual

        CGPROGRAM
#pragma target 3.0

        // -------------------------------------


#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _PARALLAXMAP
#pragma multi_compile_shadowcaster
#pragma multi_compile_instancing
        // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
        //#pragma multi_compile _ LOD_FADE_CROSSFADE

#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster

#include "UnityStandardShadow.cginc"

        ENDCG
    }
        // ------------------------------------------------------------------
        //  Deferred pass
        Pass
    {
        Name "DEFERRED"
        Tags{ "LightMode" = "Deferred" }

        CGPROGRAM
#pragma target 3.0
#pragma exclude_renderers nomrt


        // -------------------------------------

#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature _PARALLAXMAP

#pragma multi_compile_prepassfinal
#pragma multi_compile_instancing
        // Uncomment the following line to enable dithering LOD crossfade. Note: there are more in the file to uncomment for other passes.
        //#pragma multi_compile _ LOD_FADE_CROSSFADE

#pragma vertex vertDeferred
#pragma fragment fragDeferred

#include "UnityStandardCore.cginc"

        ENDCG
    }

        // ------------------------------------------------------------------
        // Extracts information for lightmapping, GI (emission, albedo, ...)
        // This pass it not used during regular rendering.
        Pass
    {
        Name "META"
        Tags{ "LightMode" = "Meta" }

        Cull Off

        CGPROGRAM
#pragma vertex vert_meta
#pragma fragment frag_meta

#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature EDITOR_VISUALIZATION

#include "UnityStandardMeta.cginc"
        ENDCG
    }
    }

        SubShader
    {
        Tags{ "RenderType" = "Opaque" "PerformanceChecks" = "False" }
        LOD 150

        // ------------------------------------------------------------------
        //  Base forward pass (directional light, emission, lightmaps, ...)
        Pass
    {
        Name "FORWARD"
        Tags{ "LightMode" = "ForwardBase" }

        Blend[_SrcBlend][_DstBlend]
        ZWrite[_ZWrite]

        CGPROGRAM
#pragma target 2.0

#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature _ _GLOSSYREFLECTIONS_OFF
        // SM2.0: NOT SUPPORTED shader_feature ___ _DETAIL_MULX2
        // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP

#pragma skip_variants SHADOWS_SOFT DIRLIGHTMAP_COMBINED

#pragma multi_compile_fwdbase
#pragma multi_compile_fog

#pragma vertex vertBase
#pragma fragment fragBase
#include "UnityStandardCoreForward.cginc"

        ENDCG
    }
        // ------------------------------------------------------------------
        //  Additive forward pass (one light per pass)
        Pass
    {
        Name "FORWARD_DELTA"
        Tags{ "LightMode" = "ForwardAdd" }
        Blend[_SrcBlend] One
        Fog{ Color(0,0,0,0) } // in additive pass fog should be black
        ZWrite Off
        ZTest LEqual

        CGPROGRAM
#pragma target 2.0

#pragma shader_feature _NORMALMAP
#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature _ _SPECULARHIGHLIGHTS_OFF
#pragma shader_feature ___ _DETAIL_MULX2
        // SM2.0: NOT SUPPORTED shader_feature _PARALLAXMAP
#pragma skip_variants SHADOWS_SOFT

#pragma multi_compile_fwdadd_fullshadows
#pragma multi_compile_fog

#pragma vertex vertAdd
#pragma fragment fragAdd
#include "UnityStandardCoreForward.cginc"

        ENDCG
    }
        // ------------------------------------------------------------------
        //  Shadow rendering pass
        Pass{
        Name "ShadowCaster"
        Tags{ "LightMode" = "ShadowCaster" }

        ZWrite On ZTest LEqual

        CGPROGRAM
#pragma target 2.0

#pragma shader_feature _ _ALPHATEST_ON _ALPHABLEND_ON _ALPHAPREMULTIPLY_ON
#pragma shader_feature _METALLICGLOSSMAP
#pragma skip_variants SHADOWS_SOFT
#pragma multi_compile_shadowcaster

#pragma vertex vertShadowCaster
#pragma fragment fragShadowCaster

#include "UnityStandardShadow.cginc"

        ENDCG
    }

        // ------------------------------------------------------------------
        // Extracts information for lightmapping, GI (emission, albedo, ...)
        // This pass it not used during regular rendering.
        Pass
    {
        Name "META"
        Tags{ "LightMode" = "Meta" }

        Cull Off

        CGPROGRAM
#pragma vertex vert_meta
#pragma fragment frag_meta

#pragma shader_feature _EMISSION
#pragma shader_feature _METALLICGLOSSMAP
#pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A
#pragma shader_feature ___ _DETAIL_MULX2
#pragma shader_feature EDITOR_VISUALIZATION

#include "UnityStandardMeta.cginc"
        ENDCG
    }
    }


        FallBack "VertexLit"
        CustomEditor "StandardShaderGUI"
}

各メッシュのシェーダを Standardから Yuki/Standardに変更
f:id:murasame-labo:20170910114557p:plain

Passというのが シェーダのパスで
Tags{ “LightMode” = “ForwardBase” } となっている部分が、レンダリングモード

Forwardレンダリング時は
メインライトのパスが ForwardBase
その他のライトがライト毎に ForwardAdd

Deferredレンダリング時は
Deferredパスが呼ばれる

また影の計算時に
ShadowCasterパスが呼ばれる

輪郭線を付ける

今回は単純な オブジェクトの裏側を法線方向に拡大して表示する

アウトラインのパスを作る
先ほどのシェーダーに輪郭線パスを増やす
StandardはLODにより2個あるので 出来れば2つのSubshaderに書くほうが良い

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

    Cull Front

        CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#define _GLOSSYENV 1
#include "UnityCG.cginc"

#pragma fragmentoption ARB_precision_hint_fastest
#pragma multi_compile_shadowcaster
#pragma multi_compile_fog
#pragma only_renderers d3d9 d3d11 glcore gles 
#pragma target 5.0


    struct VertexInput {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };
    struct VertexOutput {
        float4 pos : SV_POSITION;
        UNITY_FOG_COORDS(1)
    };
    VertexOutput vert(VertexInput v) {
        VertexOutput o = (VertexOutput)0;

        float width = 1;

        o.pos = UnityObjectToClipPos(float4(v.vertex.xyz + v.normal*(width*0.01),1));
        UNITY_TRANSFER_FOG(o,o.pos);
        return o;
    }
    float4 frag(VertexOutput i, float facing : VFACE) : COLOR{
        float isFrontFace = (facing >= 0 ? 1 : 0);
        float faceSign = (facing >= 0 ? 1 : -1);
        float4 outlineColor = float4(0,0,0,1);
        return fixed4(outlineColor);
    }
        ENDCG
    }

上記はForwardパスなので Deferredパスにも同じものを追加すると Deferredでも輪郭線がつく
具体的には上記の
Tags{ “LightMode” = “ForwardBase” }

Tags{ “LightMode” = “Deferred” }
にしたものを書けばいい

Forward

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

Deferred
DeferredではMSAAが使えないため、画質が悪い。
Deferredでは他の方法で アンチエイリアスを考える必要がある

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

Overwatch 魔境でのピックを考える

対象

FPS初心者。ランクマで1000未満の初心者

魔境では基本がトロールピックである

魔境ではノーヒーラー、ノータンク、オールアタッカー というのは良くある
たいてい 相手がヒーラータンクいると負けるので、必然的に自分がヒーラータンク選ぶと勝率が上がるはずである

また、魔境ではジャンクラットの人気が高い。ほぼ両チームに必ずいる
他にも ゲンジ、ハンゾー、ウィドウ、ドゥームフィストの使用率も高い
それらに関しても 考慮する

まず、自分のデータより魔境向きなキャラを考える

使用時間

22時間 ソルジャー
8時間 Dva、マーシ―
4時間 ルシオ、アナ、リーパー、トレーサ 
2時間 ウインストン、ロードホッグ、マクリー、ザリア、ファラ、ゼニヤッタ、ジャンクラット、ソンブラ 
1時間 メイ、トールビョン、ハンゾウ、ラインハルト 
1時間未満 オリーサ、バスティオン、シンメトラ、ウィドウメーカー、ゲンジ、ドゥームフィスト 

最初はFPSの標準的な操作であるソルジャーをよく使った。
魔境ではヒーラータンクが不足しているので Dvaマーシールシオ、アナの使用率も高い
ウィドウ、ゲンジ等はあまり使っていない

勝率

93%   トールビョン
60%後半 ルシオ、シンメトラ
50%後半 マーシ―、ソンブラ
50%前半 ハンゾー、ラインハルト
40%後半 ゲンジ、オリーサ、Dva、バスティオン、ロードホッグ
40%前半 ウインストン、ウィドウ、ファラ、アナ、ゼニヤッタ
30%後半 メイ、トレイサー、リーパー
30%前半 マクリー、ソルジャー
20%後半 ジャンクラット、ザリア、ドゥームフィスト

やはり オートエイムで、頭脳を使ってタレットを設置できるトールビョンの勝率がおかしいほど高い
オートエイムで あまりタレットを破壊に来ないため 魔境ではトールビョンが強いと思う
実際に相手したときに嫌である

ルシオ、マーシ―は勝率高いというより、ヒーラーが居ない事 が多い魔境では、ヒーラーをピックするだけで
勝率が上がるのは当然である

シンメトラも やはりタレットの関係で初心者でも強い

ハンゾーは何故だろう。意外とキルできる・・
ラインハルトは やはり盾は重要ということだろう
魔境では盾の後ろから撃つ人も少ないのだけど、敵からしてみると 盾が邪魔で壊しに来るので アタックがしやすいのだろう

マクリー、ソルジャー
遠距離アタッカーが下手なんだと思う。あるいは これらを出す時はファラ対策なので、魔境ではファラが強いという事も考えられる

ジャンクラット、ザリア、ドゥームフィスト データ足りないものもあるけど 勝率悪すぎ
ザリアはほんと素人に難しい。ドゥームフィストはまだ使い方わかってない。ジャンクラットは最初のころ使ってたので今だともっと勝率上がるとおもう

総論

トールビョン、シンメトラ などのタレットは初心者向け
2-2-2の構成を目指し ヒーラーかタンク使うと勝ちやすい
ファラ対策が出来ない人が多いので ファラは無双しやすい
ラインハルトは なんだかんだ強い

練習すべきキャラ

ヒーラーとタンク

ヒーラーでいえば ルシオ、ゼニ、マーシ―あたりが使えるようになりたい
タンクだと ハルト、ザリア あるいは Dva、ゴリラ。
つまり ほぼ全部必要だね・・・

ファラ

やっぱファラは魔境では刺さる事が非常に多いので絶対覚えるべき
特に 必ずピックされるしダメージ高くてうざい ジャンクラットや
タンクを瞬殺リーパーあたりにはめっぽう強い
魔境ではタレット処理にも役立つ

対ファラ

という事でファラに対抗したいが なかなか難しい
一般レベルだと ソルジャー、マクリーだが、ファラマーシーや うまいファラ相手だと倒すの難しい
ウィドウやアナをピックするのも考えるか・・

UNITYでDepthをリニアでEXR形式で出力

概要

Depthマップ欲しい事は多いと思います Depthの取得自体は簡単ですが、Depthの計算上 Z値とリニアで変化しないため感覚的にわかりにくいので リニアにしたい DepthをPNGファイルにすると 256段階に丸められてしまうため、浮動小数点形式で保存したいので EXR形式で保存したい Depthの取得は、別パスにて画面全体から取得する。UNITYではポストプロセスで行うのが楽

UNITYちゃん

今回は題材にUNITY Chanを使う 何を使ってもいいし、UNITYの3DMeshでもいい

ポストプロセス

デプスは画面全体を取得する必要があるので、ポストプロセスで取得する ポストプロセスを使うには カメラに OnRenderImage関数の入ったスクリプトをつける

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;

    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, dest, mat);
    }
}

これで、マテリアルに設定したシェーダーを使い ポストプロセスを実行すると

シェーダーでDepth取得

マテリアルに設定するシェーダーを作成する

まず、デプステクスチャを有効にするため、シェーダーの変数宣言のあたりに

UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

を。これでデプステクスチャに _CameraDepthTexture でアクセスできる

Shader "test/CopyDepth" {
  SubShader {
    ZTest Always
    Cull Off
    ZWrite Off
    Fog{ Mode Off }
    Tags{ "RenderType" = "Opaque" }

    Pass {

      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
      #pragma only_renderers d3d9 d3d11 glcore gles gles3 metal xboxone ps4 
      #pragma target 3.0

      UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);
      
      struct v2f {
    float4 pos : SV_POSITION;
    float2 uv : TEXCOORD0;
      };

      v2f vert (appdata_img v) {
    v2f o = (v2f)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
    return o;
      }

      float frag(v2f i) : SV_TARGET{
        return SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv);
      }
    ENDCG
    }

  }
}

上記シェーダーをポストプロセスに設定すると、Depthっぽいものが表示される (画面表示はこの段階では崩れているが) f:id:murasame-labo:20170805005638p:plain

ただしいDepthを取得

Depthテクスチャ用に RenderTextureを生成し、ポストエフェクト時にそこにコピーする

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

// deplicated
[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;
    private RenderTexture _rtd;


    // Use this for initialization
    void Start() {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

        _rtd = new RenderTexture(2048, 2048, 32);
    }

    
    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
       Graphics.Blit(source, _rtd, mat);
    }
}

見てもわからないが、ポストエフェクトで画面に出力せず、 rtdへコピーされているので rtdの値を出力すればよい

EXRで保存

float形式で保存するために EXR形式を使う UNITYでは EXRへの変換は簡単にできる Aを押したらファイルに保存する

    void Update () {
        if (Input.GetKey(KeyCode.A)){
            RT2EXR(_rtd,  "rtd.exr");
        }
    }

    void RT2EXR(RenderTexture rt, string filename)
    {
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false);
        RenderTexture.active = rt;
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();

        byte[] bytes = tex.EncodeToEXR();
        Object.Destroy(tex);

        File.WriteAllBytes(filename, bytes);

    }

これで exrファイルとして出力できた f:id:murasame-labo:20170805010655p:plain

リニア化

上記のままでは、Depthの値はZとリニアではないので 直観的にわかりやすく リニアに変換する UNITYには便利な組み込み関数があるので それでリニアにできる

// Z buffer to linear 0..1 depth
inline float Linear01Depth( float z )
{
  return 1.0 / (_ZBufferParams.x * z + _ZBufferParams.y);
}

これでzバッファの値をリニアに変換できる _ZBufferParams 等はシェーダーのビルトイン変数といい、UNITYがConstantBufferに送っている

    // x = 1 or -1 (-1 if projection is flipped)
    // y = near plane
    // z = far plane
    // w = 1/far plane
    float4 _ProjectionParams;

    // x = width
    // y = height
    // z = 1 + 1.0/width
    // w = 1 + 1.0/height
    float4 _ScreenParams;

    // Values used to linearize the Z buffer (http://www.humus.name/temp/Linearize%20depth.txt)
    // x = 1-far/near
    // y = far/near
    // z = x/far
    // w = y/far
    // or in case of a reversed depth buffer (UNITY_REVERSED_Z is 1)
    // x = -1+far/near
    // y = 1
    // z = x/far
    // w = 1/far
    float4 _ZBufferParams;

    // x = orthographic camera's width
    // y = orthographic camera's height
    // z = unused
    // w = 1.0 if camera is ortho, 0.0 if perspective
    float4 unity_OrthoParams;

具体的には上記のような、near far等の値が入っている

シェーダーを少し変更する

float frag(v2f i) : SV_TARGET{
  return Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv).x ) ;
}

これでリニアになったはずである(多分)

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

まとめ

using System.IO;
using UnityEngine;
using UnityEngine.Rendering;

[ExecuteInEditMode]
[RequireComponent(typeof(Camera))]
public class SceneScript : MonoBehaviour {

    public Material mat;
    private RenderTexture _rtd;


    // Use this for initialization
    void Start() {
        GetComponent<Camera>().depthTextureMode |= DepthTextureMode.Depth;

        _rtd = new RenderTexture(2048, 2048, 32);
    }

    
    [ImageEffectOpaque]
    public void OnRenderImage(RenderTexture source, RenderTexture destination)
    {
        Graphics.Blit(source, _rtd, mat);
    }

    // Update is called once per frame
    void Update () {
        if (Input.GetKey(KeyCode.A)){
            RT2EXR(_rtd,  "rtd.exr");
        }
    }

    void RT2EXR(RenderTexture rt, string filename)
    {
        Texture2D tex = new Texture2D(rt.width, rt.height, TextureFormat.RGBAHalf, false);
        RenderTexture.active = rt;
        tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
        tex.Apply();

        byte[] bytes = tex.EncodeToEXR();
        Object.Destroy(tex);

        File.WriteAllBytes(filename, bytes);
    }
}
Shader "test/CopyDepth" {
  SubShader {
    ZTest Always
    Cull Off
    ZWrite Off
    Fog{ Mode Off }
    Tags{ "RenderType" = "Opaque" }

    Pass {
      CGPROGRAM
      #pragma vertex vert
      #pragma fragment frag
      #include "UnityCG.cginc"
      #pragma only_renderers d3d9 d3d11 glcore gles gles3 metal xboxone ps4 
      #pragma target 3.0

      UNITY_DECLARE_DEPTH_TEXTURE(_CameraDepthTexture);

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

      v2f vert (appdata_img v) {
    v2f o = (v2f)0;
    o.pos = UnityObjectToClipPos(v.vertex);
    o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
    return o;
      }

      float frag(v2f i) : SV_TARGET{
        return Linear01Depth(SAMPLE_DEPTH_TEXTURE(_CameraDepthTexture, i.uv).x ) ;
      }
      ENDCG
    }
  }
}

弱点&改善点

まず UNITYの調査してるんだけど そろそろまとまったので、書けるかな・・・・と。。 はよ プログラム記事かかないとなー ここは技術的なブログなのに

今の状況

FPS初めてプレイしたので、超下手で何をすべきかわかってないのでランクが500程度からのスタート これは ランクマッチに参加している人のなかで 下から1%のクソザコです プレイ時間としては30時間ほどなので、プレイ時間少なすぎてまだまだだけど 今の予想ではランク1000ぐらいの実力にはなったかなと思う これからランクマッチも頑張っていきたいところだけどまずは考察。

ランク1000以下の魔境では、リスポーンポイントでバスケしたり、ヒーラーなしの構成や、アサルトしばりのスナイパーとか 色々とひどい事もあるし、逆にサブアカウント、AIMボットなど 強すぎるのもいる これは確率的に半々あるとして 事故扱い

事故をのぞくと 自分の能力が今は600なので自分の能力が1000だとすると、平均ランクで80ぐらい負けていても互角のはずだ

弱点

AIM能力が低い。プレイヤースキルはすぐには上達しないので 1つずつ対策していこうと思う

マウス感度

マウス感度設定を調整しよう プレイ環境もだいたい決まったし、トレーニングモードでマウス感度調整しようと思う

逃げ方

逃げるときに真後ろに下がっていて ターゲットされやすい。特にスナイパーに殺されまくるので 逃げるときは頭の位置を動かすよう 斜めジャンプしながら撤退しよう 斜めジャンプも 左右交互や片方だけなど読まれないようにしよう

攻撃時

攻撃時に怖くて真後ろに退きながら攻撃する癖がある事がわかった・・ だいたいは相手は距離をとるのだし 前進するぐらいのつもりで行ってもいいかもしれない ただし深追いしないように

フォーカス

味方とフォーカス合わせる。2v1などこちらが多い状態を作る

敵遭遇時

敵を見た瞬間に焦ってトリガーをひく癖があるので やめよう。なかなかやまらないけど・・ 特にトレイサーやDVA 得意距離まで間合いをつめてから撃たなければ 相手の距離で撃ったらまける 相手が気づいてなければ距離をつめて攻撃、気づいてれば逃げる

また、AIMあわせずにとりあえず撃つ癖があるので、クロスファイアをあわせてから撃つようにする!

敵遭遇時のAIM対策

スナイパーとか ハンゾーしてみてもいいのか?

裏どりしたときのフォーカス

ヒーラー優先

殺しきる

やれる時はとどめをさす。中途半端に当てて逃げさせては損 特に裏どりに対しては こちらはヒーラー付き、相手は撤退にリスクがあるので 圧倒的に有利なはずだ

ULTの使い道

よーく考えよう・・ 時には1匹始末のために使ってもいい(龍神の剣やバレッジに対して タクティカルバイザーとか) 2匹倒せばいいほう

味方を見る

敵に撃たれた時など 無条件で交戦したり逃げてるが やばいと思ったら味方をみて 撤退か追い打ちか考える 見て死んでもいい。とりあえず行動前に味方見る

ロール覚える

いまいちわかってないけど、今考えているのは下記 ただし 例えばタンクでも ウインストンやdvaのような、遊撃が得意なタンクは、前線を張らない事も多い

タンク

盾になる。盾は自分を守らない 味方を守る。なぜなら自分を守っても反撃できず割られるだけ。味方に盾をはれば味方が攻撃してくれるor安全に撤退できる ので 守る対象は主にアタッカー ヒーラーはそもそも 攻撃食らう場所にいるのが間違い

ヒーラー

回復をする。もちろんタンクメイン 一歩下がった位置から回復 と同時に 裏どりなどを常に気を配るべき 全員をヒールしようとしない。敵陣に突っ込んだ奴は 無理に追いかけない

DPS

盾の裏から一方的に攻撃したり、敵の裏から襲撃したり、ヒーラーを狙いに来たDPSを追い払う 状況次第で色々と変わるから難しいね

アンチピック

これは 勉強していこう・・

魔境で刺さるキャラピック

ファラ:ソルのAIMが下手、ジャンクラ多い、ゲンジ多い、爆風こわい トールビョン:圧倒的に正確なAIM バスティオン:気付くのが遅いので 気が付いたら死んでる

念のため上記キャラの練習

とりあえず 上記を気を付けるための メモ

RenderTextureを使いレンダリング結果をTextureに保存する

レンダリング結果をテクスチャでほしい事ってありますよね

例えばレンダリング結果を鏡にうつしたり G-Bufferをファイルに保存したり

ってことで、RenderTextureを作り カメラにAttachし、レンダリング結果を取得します

まず RenderTextureをCreateします MultiRenderTarget機能があるので、最大8個まで取得できます

newでRenderTextureを作成しますが、幅、高さをピクセルサイズで指定し、ビット深度を指定します 幅、高さは2のn乗にしておきましょう

そして カメラにたいして SetTargetBuffersでアタッチします

なんとこれだけ。簡単ですね

    private RenderTexture[] _rt;
    public Camera cam;


    // Use this for initialization
    void Start() {
        _rt = new RenderTexture[8];
        for (int i=0; i< _rt.Length; i++)
        {
            _rt[i] = new RenderTexture(2048, 2048, 32);
        }

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