Yaminabe

らくがきぶろぐ

てすと:Yaminabeちゃんは生まれたばかりのブログです みんなで仲よく使ってね

カテゴリ: シリーズ講座



サーフェスシェーダの作成

ライティングとの対話シェーダを書くことは複雑です。そこに異なるライトの種類、異なるの影のオプション、異なるレンダリングパス(フォワードおよび遅延レンダリング)は、シェーダはすべてその複雑さを処理する必要があります。
unityのサーフェスシェーダでは、低レベルの頂点/ピクセルシェーダプログラムを使用するよりも点灯シェーダを作成することがはるかに容易にコードを生成するアプローチとなります
。サーフェスシェーダに独自の言語は使用しません。すべてのコード生成は手で書かなければいけません。CG / HLSLでシェーダコードを記述します。


いくつかの例については、サーフェスシェーダの例サーフェスシェーダのカスタムライティングの例を見てください。


どのように動作するのか


入力として必要なすべてのUVまたはデータを受け取り、 "サーフェス機能"を定義し、SurfaceOutput出力に必要な値を満たします。SurfaceOutputは、基本的にはサーフェスのプロパティ(アルベドカラー、法線、放射、スペキュラなどです)。Cg / HLSLでこのコードを記述します。
サーフェスシェーダコンパイラは、必要な値が入力されているか、出力の値は全て満たされているかを判別しその後に、
頂点とピクセルシェーダを生成します、同様にレンダリングパスがフォワードレンダリングとディファードレンダリングに値を渡します。



表面シェーダの標準出力の構造



struct SurfaceOutput {
half3 Albedo;
half3 Normal;
half3 Emission;
half Specular;
half Gloss;
half Alpha;
};


サーフェスシェーダの例


http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderExamples.html


カスタムライティングの例


http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLightingExamples.html


サーフェスシェーダのコンパイルディレクティブ


サーフェスシェーダは他のシェーダと同じようにCGPROGRAM..ENDCG、の内部に配置されます。
相違点は次のとおりです。



  • SubShaderの内側に配置する必要がありますPassのブロックではありません。サーフェスシェーダは 複数のPassを含むようにコンパイルされます。

  • #pragma surface を用いて...ディレクティブはサーフェスシェーダを示しています。


#pragma surfaceディレクティブは、次のとおりです。


    #pragma surface surfaceFunction lightModel [optionalparams]

必要なパラメータ:



  • surfaceFunction -Cg関数はサーフェスシェーダコードで定義されます。

  • 関数は、void surf (Input IN, inout SurfaceOutput o) といったフォームが必要で入力はユーザが定義する構造体です。surfaceFunctionには任意のテクスチャ座標とサーフェス機能で必要とされる自動変数を含める必要があります。

  • lightModel -使用する照明モデル。内蔵のものはLambert(拡散)とBlinnPhong(鏡面)。

  • カスタムライティングモデル    を 参照してください。


             http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLighting.html


オプションのパラメータ:




  • alpha -アルファブレンドモード。半透明シェーダに使用します。

  • alphatest:VariableName -アルファテストモード。透明カットアウトシェーダに使用します。カットオフ値のVariableNameは、float変数になっています。

  • VertexVertexFunction -カスタム頂点変更機能。例:樹皮シェーダを参照してください。

  • finalcolor:ColorFunction -カスタム最終的な色の変更機能。サーフェスシェーダの例を参照してください。 http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderExamples.html

  • exclude_path:prepassまたはexclude_path:forward -指定された描画パスを生成しません。

  • addshadow -シャドウキャスターとコレクターのパスを追加します。一般的にカスタム頂点の変更で使用するので、その影のキャスティングは、任意の手続きの頂点アニメーションを取得します。

  • dualforward -dual lightmaps をフォワードレンダリングパスで使用します。

  • fullforwardshadows -フォワードレンダリングパスですべてのシャドウタイプのサポートをします。

  • decal:add- 加算デカールシェーダ(例えばterrain AddPass)。

  • decal:blend -半透明デカールシェーダ。

  • softvegetationは -ソフトベジテーションがオンになっている場合、サーフェスシェーダのみがレンダリングされるようにします。

  • noambientは、 -環境光や ​​球面調和ライトを適用しない。

  • novertexlightsは -フォワードレンダリングで任意の球面調和関数または頂点単位のライトを適用しない。

  • nolightmap -このシェーダでのライトマップのサポートを無効にします(シェーダは小さくなります)。

  • nodirlightmapは -このシェーダでのディレクションライトマップのサポートを無効にします(シェーダが小さくなります)

  • noforwardaddは -フォワードレンダリング添加パスを無効にします。

  • ひとつのディレクショナルライトを作成し他のすべてのライトを頂点単位でper-vertex/SH(頂点単位のSpherical Harmonics =球面調和関数を用いた環境光マッピング)に割り付けますたいへんシェーダが小さくなります)

  • approxviewは -正規化されたビューの方向をピクセル単位でなく頂点単位で計算します。これは高速ですが、カメラがサーフェス表面に近づくとビューの方向が完全には正しくなりません。

  • halfasviewは -ライティング機能の代わりに、ビュー方向にハーフベクトルを渡します。頂点ごとにハーフ方向が計算され正規化されます。これは速いですが完全に正しくはなりません。


追記:#pragma debugをCGPROGRAM 内部に書くことで、コンパイラは生成された多くのコメントコードを切り離します。シェーダインスペクタでコンパイルされた後シェーダを見ることができます。



サーフェスシェーダの入力構造



入力構造の入力部は一般的にシェーダが必要とするテクスチャ座標を持っています。テクスチャ座標は 名前を付ける必要があり、”UV”テクスチャ名(またはUV2を指定で2番目のテクスチャ座標セットを使用する)。



入力構造体に入れることができる追加の値:



  • float3 VIEWDIRは -リムライティング等、視差効果を計算するためのビューの方向が含まれています

  • float4 COLOR-頂点単位の色の補間が含まれています。

  • float4 screenPosは -反射効果のためのスクリーンスペース座標が含まれています。例えばWetStreetシェーダなど

  • float3 worldPosは -ワールド空間の位置座標が含まれています。

  • float3 worldReflは -ワールド空間の反射ベクトルが含まれます。これはサーフェスシェーダがo.Normalに書き込みをしていない場合です。例:リフレクト拡散シェーダを参照してください。

  • float3 worldNormalは -ワールド空間の法線ベクトルが含まれます。ただしサーフェスシェーダがo.Normalに書き込みをしていない場合

  • float3 worldRefl。INTERNAL_DATAは -ワールド空間の反射ベクトルが含まれます。これはサーフェスシェーダがo.Normalに書き込む場合です。ピクセル単位の法線マップに基づいて反射ベクトルを取得するには、(IN、o.Normal)WorldReflectionVector。例:リフレクト・バンプシェーダを参照してください。

  • float3 worldNormal。INTERNAL_DATAは -ワールド空間の法線ベクトルが含まれます。これはサーフェスシェーダがo.Normalに書き込む場合です。 ピクセル単位の法線マップに基づいて、法線ベクトルを取得するには、WorldNormalVectorを(IN、o.Normal)


サーフェスシェーダの例




サーフェスシェーダのカスタムライティングモデル


サーフェスシェーダ書き込み時に、サーフェスの性質を記述している(アルベド、色、法線、...)とライティングの相互作用はライティングモデルにより計算されます。ビルトインのライティングモデルはランバート(拡散照明)とBlinnPhong(スペキュラライティング)。


カスタムライティングモデルを使用するときにはサーフェスシェーダで行うことが可能ですライティングモデルは、いくつかのCG / HLSL関数の組み合わせ以上の何物でもありません。ビルトインランバートBlinnPhongのモデルは、Lighting.cgincで定義されています。


windowsの場合 (Unityインストールパス /Data/ CGIncludes / Lighting.cginc


Macの場合 /Applications/Unity/Unity.app/Contents/CGIncludes/Lighting.cginc


ライティングモデルの宣言


ライティングモデルは通常Lightingで始まる名前を持ついくつかの機能です。シェーダファイルまたはインクルードファイルのいずれかでどこでも宣言することができます。関数は次のとおりです。



  1. half4 Lighting名前(SurfaceOutput S、half3 lightDir、 harf atten) これは、

  2. ライトモデルのフォワードレンダリングパス使用時ビュー方向に依存しません。(例えば、拡散)。

  3. half4 Lighting名前(SurfaceOutput S、half3 lightDir、half3 ViewDir、harf atten) ライトモデルのフォワードレンダリングパス使用時ビュー方向に依存します。

  4. half4 Lighting名前 _PrePass(SurfaceOutput s、half4 light) これはディファードライティングパスで使用されます。


すべての関数を宣言する必要がないことに注意してください。照明モデルはビューの方向を使用してもしなくてもいいです。ライティングモデルは、ディファードライティングではライティングモデルが動作しない場合は_PrePass機能を宣言していないと思います  それを使用するすべてのシェーダはフォワードレンダリングのみを転送するようにコンパイルされています。


デコードの方向ライトマップは、フォワードおよびディファードのライティング機能と同様の方法でいくつかの状況でカスタマイズする必要があります。あなたのライティングモデルがビュー方向に依存しているかどうかに応じて以下の関数のいずれかを使用します。両関数は自動的にフォワードおよびディファードのレンダリングパスを処理します。



  1. half4 Lighting名前 _DirLightmap(SurfaceOutput S、fixed4 color、fixed4 scale、bool surfFuncWritesNormal) これはビュー方向に依存して表示(例えば、拡散)されないライティングモデルに使用されます。

  2. half4 Lighting名前 _DirLightmap(SurfaceOutput S、fixed4 color、fixed4 scale、half3 ViewDir、bool surfFuncWritesNormal、out half3 specColor) これは、ビューの方向依存しているライティングモデルに使用されます。



http://docs.unity3d.com/Documentation/Components/SL-SurfaceShaderLightingExamples.html




バーテックスシェーダおよびフラグメントシェーダを作成


ShaderLabのシェーダは別のグラフィックスハードウェアのための複数のシェーダの実装が含まれている固定機能ハードウェアの状態などを設定し、マテリアルのインスペクタに表示されるプロパティを記述します。実際のプログラマブルシェーダ-頂点プログラムとフラグメントプログラムなどが-全体のShaderLabの"シェーダ"というコンセプトのほんの一部です。
シェーダチュートリアル基本的な導入のために。ここでは、低レベルのハードウェアシェーダと呼ぶシェーダプログラムを見てください。


あなたがライティングとの対話シェーダを作成したい場合は、サーフェスシェーダのマニュアルを見てください。。このページの残りの部分(例えば、特殊効果、Unityのライトと相互作用しないシェーダを想定しています画像効果など)

シェーダプログラムは、どこかの内部に、シェーダテキストの"スニペット"を埋め込 ​​むことにより、CG / HLSL言語で書かれたパスのコマンドを実行します。それは通常、以下のようになります。


 Pass {
// ... the usual pass state setup ...
CGPROGRAM
// compilation directives for this snippet, e.g.:
#pragma vertex vert
#pragma fragment frag
// the Cg code itself
ENDCG
// ... the rest of pass setup ...
}

Cgのスニペット(挿入テキスト)


Cgプログラムのスニペットは、CGPROGRAMとENDCGの間に書かれます。


スニペットの開始時にコンパイル・ディレクティブ(指示)は#pragma文で与えることができます。Unityによって認識されるディレクティブは次のとおりです。



  • #pragma vertex name -nameはバーテックスプログラムを示します。

  • #pragma fragment name -nameはフラグメントプログラムを示します。

  • の#pragma fragmentoption option-追加オプションはOpenGLのフラグメントプログラムにコンパイルされます。ARBフラグメントプログラムの使用可能なオプションのリストの仕様を参照してください。http://www.opengl.org/registry/specs/ARB/fragment_program.txt

  • このディレクティブは非OpenGLをターゲットにコンパイルされたプログラムまたは頂点プログラムには影響を与えません。

  • #pragma target name -シェーダターゲットにコンパイルする。シェーダターゲット詳細を参照してください。

  • #pragma only_renderers space sepatratesd names-のみ与えられたレンダラのためにシェーダをコンパイルします。デフォルトでは、シェーダは、すべてのレンダラー用にコンパイルされています。レンダラ詳細を参照してください。

  • の#pragma exclude_renderers space separated names -指定されたレンダラのためにシェーダをコンパイルしないでください。デフォルトでは、シェーダは、すべてのレンダラー用にコンパイルされています。レンダラー詳細を参照してください。

  • http://docs.unity3d.com/Documentation/Components/SL-ShaderPrograms.html#renderers

  • #pragma glsl -デスクトップOpenGLのプラットフォーム用のシェーダをコンパイルするとき、(代わりに、ARB頂点/フラグメントプログラムでのデフォルト設定の)GLSLにCg / HLSLに変換する


各スニペットは、バーテックスプログラム、フラグメントプログラム、またはその両方を含める必要があります。したがって、#pragma vertexまたは#pragma fragment またはその両方は必要に応じて指示されます。




ベクトル計算の基本



no title


ベクトルはvector(x,y,z)で表現されるある座標からある座標までの差分で、方向と長さを表現する事ができます。
ベクトルは合成でき上図のようにベクトルAとベクトルBを合成したベクトルA+Bとして扱うことができます。



no title



ベクトルは長さを持っていますが、方向を表現したいとき大きさ(長さ)を持っていると扱いにくいので大きさが1の単位ベクトルにして扱います。
これを正規化(normalize)と呼んで シェーダの関数ではnormalize(Vector)を使用します。



no title



ゲームでよく使われる計算に内積があります。2つの交差したベクトルを掛けたものが内積でCosθの値をとります。つまり2つのベクトルが平行な場合1直角の場合0を取ります。



シェーダ関数はdot(a,b)を使用します。外積はcross(a,b)


ベクトルの内積および外積をどのようにゲームで使用するかの例は以下のとおりです。


ベクトル内積の概要





図のようにaベクトルをbベクトルへ投影したベクトルa'があったとします。

このa'の長さにbベクトルの長さをかけた値が内積の大きさです。

ただし2つのベクトルが鈍角であれば内積はマイナス値になります。

2つのベクトルが直角の時は内積は0です。


内積の用途


ゲームプログラミングではベクトルの内積を次のような用途に使います。

・2つのベクトルが直交するか

・2つのベクトルが平行か

・2つのベクトルの向き(鋭角、鈍角かどうか)判定

・2つのベクトルの角度

・点が平面の表側、裏側どちらにあるか判定

・平面上に点があるか

・片方のベクトルをもう片方へ射影したベクトルの計算


ベクトル外積の概要

外積を行うとベクトルa,bに直行するベクトルが作られます。

そのときのベクトルの長さは、ベクトルa,bで平行四辺形を作ったときの面積を表します。

図のようにかける順番によって得られるベクトルの向きが変わります。


外積の用途

ゲームプログラミングではベクトルの外積を次のような用途に使います。

・2つのベクトルに垂直なベクトルを求める。

・ポリゴンの向き(法線ベクトル)を求める。

・2つのベクトルが所属する平面において左右の位置関係を知る

・平面上の三角形と点の内外判定

・ポリゴンの面積を計る

・平面上の閉領域の面積を計る

・平面上の閉領域の向き(時計回りか、反時計回り)

http://www.sousakuba.com/Programming/gs_gaiseki.html


マテリアルのシェーダーでの計算例



no title


no title



L:LightDirection(光源方向のベクトル)
V:ViewDirection(視線方向=カメラ方向のベクトル)
H:HarfVector(ハーフベクトル=ポリゴン面の法線とライト方向Lの中間に向かうベクトル)
N:Normal(ポリゴン平面の法線方向)
Shininess:(光沢成分の乗数)
La:LightAmbient(ライトの環境光成分)
Ld:LightDiffuse(ライトのデフューズ成分)
Ls:LightSpecular(ライトのスペキュラー成分)
Lo=_Color(マテリアルカラー)
Lo(s) =_Speculer(マテリアルのスペキュラ)


頂点シェーダでの例です 関数で動作がわからない箇所はHLSLの対応表をUnityのビルトイン関数は"UnityCG.cginc"ファイルを参照して検証してみてください。
v2fはstructで定義された構造体です。構造体名”o”で定義される値がv2f型で頂点シェーダに戻ります


v2f vert( appdata_base i)
{
v2f o;
o.position = mul( glstate.matrix.mvp, i.vertex );
float3 n =mul(glstate.matrix.invtransview[0],float4(i.normal,1));
float LdotN=saturate( dot( glstate.light[0].position,n));
o.color = glstate.lightmodel.ambient*_Color
o.color += LdotN * _Color * glstate.light[0].diffuse;

float3 L = ObjSpaceLightDir(i.vertex);
float3 V = ObjSpaceViewDir( i.vertex);
float3 H = normalize(L + V );

float NdotH = dot(i.normal , H);
o.color += _Specular * pow(NdotH,_Shiniess *128);
return o;
}


http://en.wikipedia.org/wiki/Blinn%E2%80%93Phong_shading_model

http://ja.wikipedia.org/wiki/Phong%E3%81%AE%E5%8F%8D%E5%B0%84%E3%83%A2%E3%83%87%E3%83%AB

http://es.scribd.com/doc/90634580/109/The-Blinn-Phong-Shaders

http://www.arcsynthesis.org/gltut/Illumination/Tut11%20BlinnPhong%20Model.html


http://asura.iaigiri.com/OpenGL/gl28.html



●スクリプトからシェーダーへのアクセス

シェーダにアクセスする場合 最初に
renderer.material.shader=Shader.Find( "シェーダの名前" );
を使用してどのシェーダにアクセスするかを定義します。
オブジェクトにmaterialが複数使用されている場合もありますのでこれを実行してください。
※使用されているシェーダ名プロパティがわからない場合シェーダファイルを調べても良いですが
アニメーションウィンドウを開いてマテリアルのアニメーションキーの名前を調べても良いです。
シェーダにプロパティが存在するか問い合わせるにはHasPropertyを使用して
renderer.material.HasProperty("プロパティ名") とします戻り値はboolean型です。

例:シェーダにフロート型の値を設定する場合

function Start () {   // ”Glossy ”というシェーダを探してマテリアルに設定します

    renderer.material.shader = Shader.Find(" Glossy");
}

function Update () {
//値Shininessのアニメーション ここではtimeと1.0の間を往復させています
var shininess : float =
Mathf.PingPong (Time.time, 1.0);
renderer.material.SetFloat( "_Shininess", shininess );
}

マテリアルクラスのメンバ


Variables
shader:使用されているマテリアルに設定されたシェーダ.
color:メインマテリアルのカラー.
mainTexture:メインマテリアルのテクスチャ.
mainTextureOffset:メインテクスチャのオフセット.
mainTextureScale:メインテクスチャのスケール.
passCount:現在使用しているマテリアルのパス数取得(Read Only)
renderQueue: 現在のマテリアルのレンダーキューを設定する(int型)


Constructors

Material:Textにshaderの本体を記述してrenderer.material = new Material( shaderText );でマテリアルをテンポラリとして扱える。要するにスクリプトからマテリアルを一時的に生成できます。


Functions
SetColor:指定したカラーの値をセットします.
GetColor:指定したカラーから値を取得します.
SetVector:指定したベクトル値に値をセットします.
GetVector:指定したベクトル値から値を取得します.
SetTexture:指定したテクスチャプロパティにテクスチャをセットします.
GetTexture:指定したテクスチャプロパティからテクスチャを取得します(返り値はtexture型)
SetTextureOffset:指定したテクスチャのオフセット変位を与えます 移動量はvector2型.
GetTextureOffset:指定したテクスチャのオフセット変位を取得します.
SetTextureScale:指定したテクスチャにスケール値を与えます .
GetTextureScale:指定したテクスチャからスケール値を取得します .
SetMatrix:シェーダーにマトリックス変換行列をセットします.
GetMatrix:シェーダからマトリックス変換行列を取得します.
SetFloat:指定した値にフロート型の値をセットします.
GetFloat:指定した値からフロート型の値を取得します.
HasProperty:シェーダで指定した名前のプロパティが使用されているか問い合わせます.
GetTag:シェーダからタグを取得します (返り値はString型)
Lerp:2つのマテリアルの名前とリープ値を設定することでマテリアルをブレンドします.
SetPass:Activate the given pass for rendering.
CopyPropertiesFromMaterial:他のマテリアルから現在のマテリアルにプロパティをコピーします.


追記:関連して”テクスチャの回転”で検索がありましたので サンプルを載せておきます。


■MatrixRotation サンプル


http://docs.unity3d.com/Documentation/ScriptReference/Material.SetMatrix.html



var rotateSpeed = 30;
var texture : Texture;

function Start() {

var m : Material = new Material (
"Shader \"Rotating Texture\" {" +
"Properties { _MainTex (\"Base\", 2D) = \"white\" {} }" +
"SubShader {" +
" Pass {" +
" Material { Diffuse (1,1,1,0) Ambient (1,1,1,0) }" +
" Lighting On" +
" SetTexture [_MainTex] {" +
" matrix [_Rotation]" +
" combine texture * primary double, texture" +
" }" +
" }" +
"}" +
"}"
);
m.mainTexture = texture;
renderer.material = m;
}

function Update() {

var rot = Quaternion.Euler (0, 0, Time.time * rotateSpeed);
var m = Matrix4x4.TRS (Vector3.zero, rot, Vector3(1,1,1) );
renderer.material.SetMatrix ("_Rotation", m);
}


マトリクスのトランスフォーム


TRS (pos : Vector3, q : Quaternion, s : Vector3) : Matrix4x4


新規にマテリアルを生成してUpDate文の中でMatrixに回転成分を与えて_Rotationに値を設定します。TRS関数は(移動、回転(Quartterinion),スケール)を設定することでMatrixに値を返します。マトリックス変換の基本となる変換行列は以下のとおりです。



no title
no title
no title



マトリックス変換をシェーダの中でも行うことができます。


例:
void surf (Input IN, inout SurfaceOutput o) {

   float4 oColor;

   float2 UV;

UV =IN.uv_MainTex;

float4 sine = sin(_Time*_StretchTime);
float4 cosine = cos(_Time*_StretchTime);
pulsateMatrix._m00 = pulsateMatrix._m11 = cosine;
pulsateMatrix._m10 = -sine;
pulsateMatrix._m01 = sine;

float4 temp = float4 (UV.x, UV.y, 0, 0);
temp = mul(pulsateMatrix,temp);

UV.x = temp.x;
UV.y = temp.y;

oColor = tex2D(_MainTex, UV);
o.Albedo = oColor.rgb;
o.Alpha = oColor.a
}


先に説明した通り4×4行列のメンバに変換のための三角関数を埋めこむのでz軸回転の行列を
使用してx,yの値をU,vに代入します

_m00, _m01, _m02, _m03    cosθ -sinθ 0 0
_m10, _m11, _m12, _m13 →  sinθ cosθ 0 0
_m20, _m21, _m22, _m23    0  0 0 0
_m30, _m31, _m32, _m33    0  0 0 0

_m00 =_m11 = cosine;
_m10 = -sine;  
_m01 = sine;


シェーダでのテクスチャのマトリックス変換は可能ですが、GPUに負荷がかかるため
GPUの強力なPCでの開発に関しては問題無さそうですが モバイル機器の開発では
スクリプトで計算をしてシェーダに値を設定したほうが安定しそうな気はします。

こんにちは 長らくほったらかしだった ”うにばな”記事を再開したいと思います。
今回は要望が多めのシェーダに関するおはなしです。 といってもシェーダだけで分厚い本が何冊も書けるほどの情報量があるうえに進化し続けている分野ですので、とりあえずかいつまんで記事にしました。
非常に急いで書いたため すこし意味が通らなかったり説明が不十分なところがあるかと思いますが 今後も記事に手を入れていきますのでご容赦くださいませ。。UnityのShaderLabですが 基本的な記述はダイレクトXのHLSL またはOpenGLのGLSLどちらでも記述することが出来るという柔軟性のあるものです。3Dソフトなどでカスタムシェーダを使用することができるcgfxというシェーダもありますが 労せずシェーダを移植することは

可能ですので、開発時に3Dソフト上で確認しながらモデリングをすることも出来ると思います。学んだ知識がのちのち応用が効くという意味では

非常に開発者にとってはありがたいことです。

記事は3部にわけて

1.初めてのシェーダ (基礎知識 といってもちょっと難しい)
2.Unity公式シェーダリファレンスの意訳(必要なとこだけ)
3.シェーダを書いてみます(strumpyエディタ)など

時間があればイメージエフェクトなどもやっていきたいかな
(テクスチャレンダはプロ版のみなのでフィルタ形式のものはインディ版では難しいですが カメラの前にシェーダ入れた板ポリを置く貧乏シェーダは可能なんですよw)
今回はこちら↓を参考にシェーダの初歩的な知識について書かせていただきました





http://gameprogramming.me.land.to/PDF/ESL502.pdf

http://download.unity3d.com/support/resources/files/Unite08_Shaders.pdf


参考動画 チュートリアルなど

http://www.youtube.com/watch?v=37pc-eif24g&feature=plcp

http://www.youtube.com/user/JessyUV/videos?view=0

http://www.youtube.com/user/animate4evr



no title


大まかにシェーダファイルに記述するシェーダは2つに分けられます。上手のようにGPUのvetexプロセッサに命令を送るのがvertexshader。Pixelプロセッサに命令を送るのがPixel シェーダです。


Vertex Shaders(頂点シェーダ)


  • オブジェクト空間(ローカルな座標)からワールド座標へオブジェクトを変換します。

  • 頂点ごとにどのようなデータも計算することができます。

  • その他の入力データはすり抜けます。


no title



Pixel Shaders(サーフェスシェーダ)


  • 頂点シェーダから送られたデータを使用することができます。

  • ピクセル単位の光源計算を行います

  • 設定されたテクスチャを用いて最終的なピクセルの色を決定します


no title



Tips: VertexシェーダもPixelシェーダもほぼ同様の関数で記述できるのですが、Pixel シェーダはデータ量が多いため vertexシェーダ内部にPixel シェーダの内容を移動して記述してしまえば 高速化が期待できます。


 


シェーダの記述


大まかにはC/C++とほとんど同じで、以下のような構成をしています。


シェーダの名前の定義

プロパティの定義
 {
  構造体の定義
   グローバル変数の宣言
      関数の定義
     Subshader(一般的にはテクニック の定義)
     {
       パスの定義
        …
      }

 }


シェーダの記述例



Shader "Tutorial/Textured Colored" { //シェーダファイル名の定義

Properties { //プロパティの定義(インスペクタに表示される)
_Color ("Main Color", Color) = (1,1,1,0.5)
_MainTex ("Texture", 2D) = "white" { }
}
SubShader { //サブシェーダの記述ここから

Pass {//パスの定義ここから

CGPROGRAM //Cgプログラムの開始

#pragma vertex vert //頂点シェーダの名前の定義 ここでは”vert”
#pragma fragment frag //サーフェスシェーダ(フラグメントシェーダ)の定義 ここでは”frag”
#include "UnityCG.cginc" //外部ファイルのインクルード”UnityCg.cginc”はUnityのシェーダの関数

float4 _Color; //float4型の”_Color”という名前の変数の定義(r,g,b,a)
sampler2D _MainTex; //sanpler2D型 テクスチャのUV値の定義

struct v2f { //構造体(いくつかの値をまとめて型として定義する)ここでは”v2”f型
float4 pos : SV_POSITION; //float4型の SV_POSITION型の宣言(頂点座標) ここでは"pos"
float2 uv : TEXCOORD0; //float2型の TEXCOORD型の宣言(テクスチャUV) ここでは”uv"
};
float4 _MainTex_ST;

v2f vert (appdata_base v) //※1
{
v2f o; //構造体v2f型のローカルな値”o"を定義
o.pos = mul (UNITY_MATRIX_MVP, v.vertex); //UNITY_MATRIX_MVP(現 行モデル*ビュー*射影行列)を用いて頂点座標の変換
o.uv = TRANSFORM_TEX (v.texcoord, _MainTex); //※2
return o;
}
half4 frag (v2f i) : COLOR //サーフェスシェーダここから(戻り値COLOR型)
{
half4 texcol = tex2D (_MainTex, i.uv); //harf4型の変数”texcol"にテクスチャの色を設定
return texcol * _Color; //戻り値にtexcol (テクスチャの色)* _Color(マテリアルの色)を設定 いわゆるカラー乗算
}
ENDCG //Cgプログラムの終了
}
}
Fallback "VertexLit" //フォールバック関数(シェーダが実行できない時に呼ばれるシェーダ なるべく 標準的なシェーダを選択することで検索が早くなるようです)
}


※1: UnityCg.cgincファイルを開いて確認すると構造体 appdata_baseは

struct appdata_base {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 texcoord : TEXCOORD0;
};
と定義されています。


※2:      UnityCg.cgincファイルを開いて確認すると

// Transforms 2D UV by scale/bias property  // テクスチャUVにスケールとバイアスを付与する関数です。

#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)



プロパティの定義


http://docs.unity3d.com/Documentation/Components/SL-Properties.html


シェーダの名前の後にプロパティというシェーダに与える値を宣言します。実際に型宣言はサブシェーダの中で行いますが、プロパティの宣言ではサブシェーダで設定する値それぞれに対応させてインスペクタ上での表示と最初に代入される初期値を設定します。


例:_Color ("Main Color", Color) = (1,1,1,0.5)


Properties { Property [Property ...] }

使用する値の名前(”インスペクタ上の表示の名前 ”,インスペクタ上のエディット表示の種類)=(設定する値の初期値)


 
name ("display name", Range (min, max)) = number
float型のプロパティを定義し、インスペクタではスライダーとして表示されます

name ("display name", Color) = (number,number,number,number)


colorプロパティを定義します。
name ("display name", 2D) = "name" { options }
2Dテクスチャプロパティを定義します。
name ("display name", Rect) = "name" { options }
長方形のテクスチャプロパティを定義します。
name ("display name", Cube) = "name" { options }
キューブマップテクスチャのプロパティを定義します。
name ("display name", Float) = number
float型のプロパティを定義します。
name ("display name", Vector) = (number,number,number,number)
4成分ベクトルのプロパティを定義します。


例:


Shader "VertexLit" {
Properties {

_Color ("Main Color", Color) = (1,1,1,0.5)
_SpecColor ("Spec Color", Color) = (1,1,1,1)
_Emission ("Emmisive Color", Color) = (0,0,0,0)
_Shininess ("Shininess", Range (0.01, 1)) = 0.7
_MainTex ("Base (RGB)", 2D) = "white" { }
}

SubShader {

Pass {
Material {
Diffuse [_Color]
Ambient [_Color]
Shininess [_Shininess]
Specular [_SpecColor]
Emission [_Emission]

}

Lighting On
SeparateSpecular On
SetTexture [_MainTex] {
constantColor [_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
}
}



SubShaderの定義
(一般的にはテクニックと呼ばれる の定義)

SubShaderとは、Cg関数で書かれたレンダリング方法を記述するパスを複数まとめたものです。


1つのファイルに複数のSubShaderを定義することができます。またSubShader内には、複数 のパスを定義することができます。


Shader "MyShaderName" {
Properties {
... プロパティを書きます...
}
SubShader {
... サブシェーダーコマンドを記述 ...
Pass {
... Passコマンドを定義...
}
... 必要ならば続けてパスを記述...
}
SubShader {
... サブシェーダーコマンドを記述...
}
...フォールバックオプションを記述...
FallBack "VertexLit"
}




パスの定義


パスとは、バーテックスシェーダー(vertex)とピクセルシェーダー(fragment)をセットにしたものです。

CGPROGRAM …ENDCG の間で#pragma 名前でバーテックスシェーダーとピクセルシェーダーが最初に実行する関数とバージョンを指定します。 パス内部でシェーダーのメインとなる関数名とバージョ ンを設定するものです。


Pass {

...Pass の初期設定
...


CGPROGRAM

#pragma vertex vert

#pragma fragment frag


…Cg コードを記述します


ENDCG 


}


関数の定義

HLSLでは、C/C++と同様に、関数を定義し呼び出すことができます。定義方法も同じです。


戻り値の型関数名(引数の型変数名[ : セマンティクス],

引数の型変数名[ : セマンティクス], … ) [ 戻り値のセマンティクス]
{
(関数の処理)

return 戻り値;
}


関数内で別の関数を呼び出すことや、別ファイルで定義されている関数をインクルードして使うこと

もできます。


#include "UnityCG.cginc"


windowsの場合 Unityインストールパス /Data/ CGIncludes / UnityCG.cginc

Macの場合 /Applications/Unity/Unity.app/Contents/CGIncludes/UnityCG.cginc


 "UnityCG.cginc"ファイルに記述された関数はビルトイン関数と呼ばれます。


例:

float4 _Time (t/20, t, t*2, t*3) //時間を返す関数
float4 _SinTime (t/8, t/4, t/2, t) //サインあたりの時間を返す関数

float4 _CosTime (t/8, t/4, t/2, t)  //コサイン・・



_ModelLightColor[N] (モデルのマテリアルカラー*ライトカラー)
_SpecularLightColor[N] (モデルのスペキュラカラー*ライトカラー)


PositionFog() //頂点のフォグの値を計算
ObjSpaceLightDir() //モデルを中心としてライトの方向を計算
ObjSpaceViewDir() //モデルを中心としてビュー方向(カメラの方向)を計算
TRANSFORM_TEX() //テクスチャの移動などの変換
DifuseLight() or SpecularLight() //ライブラリにない?







HLSLでは、C/C++と同様に、関数内のローカル変数やシェーダー全体で参照できるグローバル変数、
構造体、配列を宣言できます。宣言方法も同じで、以下のように行います。

型名変数名

標準のHLSLでは、変数の型として以下のものを使用できます。

bool - true またはfalse
int - 32 ビット符号付き整数
uint - 32 ビット符号なし整数
float - 32 ビット浮動小数点値


●float:高精度浮動小数点。一般的に32ビット、普通のプログラミング言語のfloat型。

●half:培地精度浮動小数点。精度は-60000から60000 3.3桁の範囲で、一般的に16ビット。

●fixed:低精度の固定小数点。-2.0から2.0の範囲と256分1の精度で、一般的に11ビット。

double - 64 ビット浮動小数点値
sampler2D-uv座標
samplerCUBE-cubeマップ座標
uniform−入力専用の定数データ


ベクトルは、vectorもしくは型名のあとに数字をつけることによって表現します。4次元ベクトルま でサポートしているので、数字は1〜4の範囲で指定します。
int2 pos2d; // int 型の2次元ベクトル
float3 pos3d; // float 型の3次元ベクトル
vector <double, 4> vec4; // double型の4次元ベクトル


各メンバは”x, y, z, w”または”r, g, b, a”を組み合わせることによりアクセスします。
pos.xyz pos.xy pos.xz pos.x; col.rgb col.rg col.gb col.a;
x〜w、r〜aは自由に組み合わせて記述できます。


行列は、最大4×4まで定義できます。行と列の数が異なる行列も扱うことができます。 matrixまたは 型名のあとに ”行数x列数” で宣言します。
int3x2 mat_i; int 型3行2列
float4x4 mat_f;  float 型4行4列
matrix <double, 3, 3> mat_d; double型3行3列


行列のメンバへのアクセスは、メンバ名を指定することにより行えます。



●4×4の行列の場合

_m00, _m01, _m02, _m03
_m10, _m11, _m12, _m13
_m20, _m21, _m22, _m23
_m30, _m31, _m32, _m33



ほかに 
_11, _12, _13, _14
_21, _22, _23, _24
_31, _32, _33, _34
_41, _42, _43, _44


どちらを使用しても良いです



制御文

HLSLでは、プログラムの流れを制御する「フロー制御文」も使用できます。DirectX9.0では、 if-else, for, while, do-while文を使うことができます。記述方法もC/C++とまったく同じです。 ただし、brake, countinue, (switch-case文)は、DirectX10以降でなければ使用できません。


セマンティクス

シェーダーが最初に実行する関数の引数と構造体のメンバに、変数の使用目的を示した「セマンティ クス」という文字列を指定できます。 関数が呼び出される際、頂点またはピクセルの情報は、該当するセマンティクスが付いた変数に渡されます。これにより、頂点またはピクセル情報の中から必要なものだけを受け取ることができます。なお、頂点にもピクセル情報にも含まれていないセマンティクスが付いている変数は、0が格納されます。また、シェーダーが最初に実行する関数の引数すべてに、セマンティクスを指定する必要があります。



float4 : POSITION //頂点座標(x,y,z,w)※wはスケールの値
float4 : TANGENT //ノーマルマップのタンジェント座標
float4: NORMAL; //頂点法線(x,y,z,w)
float4 : TEXCOORD0 //テクスチャ座標(UV)
float4: TEXCOORD1// テクスチャ座標2つ目(テクスチャUVはXBOX360では1メッシュあたり7つまでサポートしている?ようです)
float4 : COLOR //カラーの値(r,g,b,a)

一部ですが 上記のものがあります




組み込み関数一覧


HLSL標準関数の表ですがUnityのシェーダ上でのすべての関数についての検証はしていません。シェーダエディターなどで関数の機能が分からない時に対応表として使用することは出来ると思います。


http://msdn.microsoft.com/ja-jp/library/bb509611(v=vs.85).aspx



no title

Unity3Dでの配列に関しての検索が多かったので記事を書きました。

具体的にどのような場面で使用するかといえばやはり配列とそのソートでしょうか
ゲーム中でアイテムの登録やアイテムの整理やらのデータの並べ替えなどが問題になることが多いです。

どんなことに使用するの?ということで いちおう例をあげておきます。

現在作成中のDota系ゲームではミニオンのジェネレート地点から敵のベースまでの移動経路に配置された
waypointデータをソートするのに配列ソートを使用しています。
まず最初にwaypointにwaypoint本体の値(Transform)と敵のベースまでの距離をclass作成して
距離をキーとしてwaypointの並べ替えを行なっています。
Inspectorで一個づつ近い順に登録してももちろん有効ですけど データが変わるたびにその労力はいやでしょ?w

なのであらかじめ
WaypointMasterというスクリプトを親につけて複数のwaypointをその子供にして 

■WaypointMasterの冒頭

class Destination{


var distance:float;
var trans:Transform;

}

function Start(){
for( var trans:Transform in transform){
distination = new Destination();

destination.distance = Vector3.Distance(trans.transform.position,DestinationPoint);
destination.trans = trans;

DistinationArray.Push(new DestinationResult(distination));
}
         ・
         ・
         ・

}


これをdistanceをキーにしてソートするなどというように使用しています。 
まあ細かいところはそのうち解説いたしますので 今回は配列のお話を。。


■Unity3Dで扱える配列

http://robotduck.wordpress.com/2009/11/04/88/   //配列に関する記事
この記事を読めばほとんどわかってしまう内容なのですが、リンク先の記事を眺めてから読んでみてください。

Unity3Dであつかえる配列には以下のようなものがあります

■ビルトイン配列
■Javaスクリプトタイプの配列
■ハッシュテーブル
■ArrayLists(辞書型配列)
■Generic配列
■2次元配列 など



■ビルトイン配列
ビルトイン配列(builtin_Array)は大きさが決まっていない配列で高速に処理できるものです。
var values : int[]];
var values : float[];
var values : String[];
var values : GameObject[];


などのようにほとんどの型がこの指定で配列化できます。ただしこのまま使用するとエラーが出ると思います。
なぜならばデータに大きさがないため初期化のプロセスが必要だからです。
これはAwake()またはStart() のfunctionで


var values:int[];

function Awake(){

values = new int[10];


として配列を確保してやる必要があります。
初期化と同時に数値を代入したい場合は

var values:int[]=[0,0,0,0,0,0,0,0,0,0]

としてやると配列宣言と初期化を同時にしていることになります。

まだこのブログでは解説していませんがclassを使用する場合


var CLASSArray : CLASS[];

class CLASS{

 var1:int;
var2:float;
var3:String;


function Awake(){

CLASSArray = new CLASS[1]; // 配列の領域確保

CLASSArray[0] = new CLASS(); // 空のクラスを代入

CLASSArray[0].var1 = 5;
CLASSArray[0].var2 = 5.0;
CLASSArray[0].var3 = "test";



クラスを代入のところを私は

for(i=0; i<CLASSArray.length; i++){

CLASSArray[i] = new CLASS();

}

なんて風に書いてるんですけど他の書き方があったらだれかヘルプミーです。



■Javaスクリプトタイプの配列

リファレンスの解説を翻訳(意訳)しました

function Start () {
var arr = new Array ();
  
 arr.Push ("Hello");    // elementの追加
arr.length = 2;      // arrayのリサイズ
 arr[1] = "World";     // セカンドエレメントの追加

  for (var value : String in arr) {  // 配列から値を取り出す
print(value);
}
}



/////////////////////////////////////////////

var values : float[]; // インスペクタで値を登録します

function Start () {

for (var value in values) {  // forループを使用してvaluesからvalueに値をひとつづつコピー
print(value);
}

values = new float[10]; //valuesに10個分の領域を確保

values[1] = 5.0;   //valuesの2番目の値(インデックスは0が初めで2番目が1)
}


/////////////////////////////////////////////

●配列のビルトイン配列への変換方法


function Start () {
var array = new Array (Vector3(0, 0, 0), Vector3(0, 0, 1));
array.Push(Vector3(0, 0, 2));
array.Push(Vector3(0, 0, 3));


var builtinArray : Vector3[] = array.ToBuiltin(Vector3); //配列をビルトイン配列に変換  <.ToBuiltinを使用して変換 型は 左側=右側にあわせる

var newarr = new Array (builtinArray); //ビルトイン配列を配列に変換


print (newarr);
}



■配列型のリファレンス

●Variables
length 配列の長さを返す

●Constructors
Array 配列型の指定

●Functions
Concat    配列の結合
Join      値をひとつだけ結合
Push   配列の最後に値を追加する
Add      同上
Pop      配列の最後の値を取り出す
Shift       配列の最初の値を取り出して返す
RemoveAt インデックス(0,1,2‥)で指定した値を削除する
Unshift   配列先頭にひとつ以上の値を追加?配列の長さを更新する。
Clear    配列の値を0に初期化する
Reverse    配列並びを反転する
Sort      配列を規則にそってソートする(abc順、値が小さい順など)


Q.ビルトイン配列にArrayみたいに値を追加したいときはどうすんの?

話が前後しますがビルトイン配列には通常の配列操作のような機能(function)がついていないようです。
それで一度新しく配列をつくってそちらで操作する方法があるようです。

var builtinArray : MyClass[];

function Start () {
var enemies = new Array(builtinArray);
enemies.push(new MyClass());
}


でここまで覚えた所でビルトイン配列のソートですが↓のリンク
http://answers.unity3d.com/questions/22261/sorting-builtin-arrays.html

例の中ではクラスを使ったものが使いやすかったです。

import System;

class RaycastResult implements IComparable
{
var distance : float;
var collider : Collider;

function RaycastResult(hit : RaycastHit)
{
distance = hit.distance;
collider = hit.collider;
}

function CompareTo(other : System.Object) : int
{
if (!(other instanceof RaycastResult)) return;
var raycastResultOther : RaycastResult = other;
return distance.CompareTo(raycastResultOther);
}
}

var raycastResults : Array = new Array();

function SortDistances()
{
raycastResults = new Array();
var hits : RaycastHit[] = Physics.RaycastAll(transform.position, transform.forward, 100.0);

for(var hit : RaycastHit in hits)
{
raycastResults.push(new RaycastResult(hit));
}
raycastResults.sort();

}

これは一度にPhysics.RaycastAll()を使用してオブジェクト前方に例キャストしてヒットしたものを配列hitsに
格納してしまっています。
それをraycastResultsにpushで登録して ソートするわけですが。
CompareTo()のファンクションの中のdistance.CompareTo(raycastResultOther)が距離distanceをほかの値と比較して
小さい方を返す部分です。 distanceを他の値に変えれば条件が変わります。

■ArrayList


ArrayListクラス
http://msdn.microsoft.com/ja-jp/library/7x4b0a97(v=vs.80).aspx

Javaの配列に操作が似ているけれどJavaとC#両方からつかえる点が異なるようです。
リンク先のメンバの解説を読んでつかってみてください。 なげやりw
 

■ハッシュテーブル

http://msdn.microsoft.com/ja-jp/library/system.collections.hashtable.aspx
http://msdn.microsoft.com/ja-jp/library/system.collections.hashtable_methods.aspx


ハッシュテーブルは連想配列とも呼ぶようで辞書の一種ですね 普通の配列と少し違って
『キー と 値』の 2つでワンセットに格納されます。

import System.Collections.Hashtable; //JavaScriptの場合importでC#の場合はusingでライブラリ仕様宣言します


myhash = {"name":"John", "occupation":"programmer"};

Debug.Log( myhash["name"] ); //displays "John"


ハッシュテーブルのメソッド
Count ハッシュテーブルの長さ


Clear     値をクリアー テーブル長さを0に
Remove     キーと値の削除
ContainsValue  与える値と同じ値がテーブルに入っているか確認
ContainsKey   与えるキーと同じキーががテーブルに入っているか確認
Contains      キーか値の確認でいいのかな?.
Add        キーと値をテーブルに追加


●ハッシュテーブル(辞書)のソート

http://forum.unity3d.com/threads/6335-associative-arrays-hashtables



function SortHashtable (inpHash : Hashtable) {

var iVal; var key; var arrayValues = new Array(); var valuesKeysHash = new Hashtable(); var returnArray = new Array();



for (key in inpHash.Keys) {

arrayValues[arrayValues.length] = inpHash[key];

valuesKeysHash.Add(inpHash[key], key);

}


arrayValues.Sort();


for (iVal in arrayValues)

returnArray[returnArray.length] = valuesKeysHash[iVal];

return returnArray;

}




■Generic配列

かなり様々な操作が行える上に早いので なれれば使いやすい?
C#のひとは断然これをつかうんでしょうね

●参照
http://answers.unity3d.com/questions/166789/iterate-through-generic-list.html

http://msdn.microsoft.com/ja-jp/library/6sh2ey19(v=vs.80).aspx

Listのメソッド
http://msdn.microsoft.com/ja-jp/library/s6hkc2c4(v=vs.80).aspx


■基本仕様例

var myList = new List.<型>();

myList.Add(値);

myList[インデックス] = 値;

myList.RemoveAt(インデックス);




例)

import System.Collections.Generic; // ライブラリのインポート

var phraseList = new List.();

function OnGUI () {

var i : int = 0;
for (str in phraseList) {
if (GUI.Button (Rect (100, 20 * i, 200, 20), str)) {
Debug.Log ("Box #" + i + " is working!");
}
i++;
}

}

function Update () {
if (Input.GetKeyUp ("z")) {
phraseList.Add("Test string 1");
phraseList.Add("Test string 2");
phraseList.Add("Test string 3");

}

}




■2次元配列

2次元の配列ですよ 指定方法はカンマで区切って宣言するだけです。

var myArray = new string[0,1];

var width = myArray.GetUpperBound(0); //2次元配列の次元の上限を返す
var length = myArray.GetUpperBound(1);//2次元配列の次元の下限を返す




調べていたらちょっと特殊な2次元配列の使い方をみつけたのでピックアップ
GameObject型に2次元配列を使用してステージを作るやり方ですかね なんかにつかえそう。

http://answers.unity3d.com/questions/168822/2d-array-of-gameobjects.html


public var spawn = 15;
public var rows = 10;
public var cols = 10;
public var gemObject: GameObject;
public var pieces: GameObject [,];
public var gems: Object [];

function Start () {
//Initializing variables
pieces = new GameObject[rows, cols];

//load's gems from the resources Gem folder to this object array
gems = Resources.LoadAll("Gems", GameObject);

for (i = 0; i < gems.Length; i++)
Debug.Log("gems #" + i + "is: " + gems[i].name);

for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++){
pieces[i,j] = Instantiate (gems[i], Vector2(i,j), Quaternion.identity);
}
}
}



以上私も全部の機能を使ったわけではないので間違いがあるかも知れませんので ご容赦ください。
で そろそろ本業のビジュアル的な部分や企画的なことなどもやっていこうかなーと 思っています。。。。。。。仕事が入らなければ


すっかりご無沙汰していましたがトラックバックをつけてもらいました オブジェクトの非表示を
子供だけに行いたいということらしかったのですが 説明が不足してました
ゲームオブジェクトの親子の操作方法についてお話しします。

var object:GameObject = gameObject.find("オブジェクトネーム")
これは ゲームオブジェクトを取得することができます。
objectの階層下の子供を取得したい場合は
transform.Find("子供の名前")を使用します。
階層が深い場合は
transform.Find( 直下の子供の名前/・・ /取得したい子の名前)というように
ツリー構造をファイル操作のディレクトリと同じように記述します。
例)
function Update() {
aFinger = transform.Find("LeftShoulder/Arm/Hand/Finger");
aFingerに対する実行文
}

指定オブジェクトに操作をおこなうときはgameObject .transform.Find()のように 指定するゲームオブジェクトの後につづけて記述します。

で 問題の点ですがGameObjectを消さないで操作を行いたいという点です。
gameObject.SetActiveRecursively(false);
を使用するとgameObjectの階層は全て消えてしまいますので次回から
Findでのサーチができなくなるという点です
そこでtransformの下にはgameObject自分本体の指定が格納されているということを利用して
transform.gameObject.SetActiveRecursively(false);
を使用したらよいのではということですね これでも問題ないと思います。
わたしの場合は高速化のためにゲーム中で低速なFind関数を使用せずにstart()の中で一度
var Player:GameObject;
start(){
Player = GameObject.Find("player");
Player.SetActiveRecursively(false);
}

として表示の時は
Player.SetActiveRecursively(true);
というように変数に一旦格納して表示させるのでこのような問題にあったことがありませんでした。

ほかにもプレファブを使用する場合は
var prefab:GameObject;
var object:GameObject = Instansiate(prefab,transform.position,transform.rotation);

objectはプレファブのコピーです
などというように 一度配列や変数にとってしまったほうが効率よくスクリプトかけるような気がします。
注)SetActiveRecursively()はRendererがコンポーネント登録されていないと機能しません。

注目点はTransform型はGameObject型の代わりに使用することができて
transform.gameObject と gameObject(this.gameObject)
 が同じものを示しているというところです。
よくUnityのスクリプトでvarにモデルを登録するのがGameObjectではなくTransformだったりするのはtransform.gameObjectで代用できるからなのですね。




ゲームオブジェクトのtransformの下にはrootやparentといった階層のルート(一番上の階層)のtransformや親階層(階層の一つ上)のtransformが格納されています。

例えば transform.parent = transform
とすれば 親子構造を作ることができます。この場合transformはゲームオブジェクトの自分自身です 
親から切り離されたいときは transform.parent = null;
としてやればよいです。

他にも例えば 子を破壊せずに切り離したいときはTransform.DetachChildrenを用いて
transform.DetachChildren();
Destroy(gameObject);

のようにします。 これは公式のFPSチュートリアルでロケットランチャーから爆発前にトレイル煙を切り離す部分に使用されています。

■子の検索
子の検索はGetComponentInChildren()を使用して

var allChildren:Transform[] = gameObject.GetComponentsInChildren(Transform);
for (var child : Transform in allChildren) {
    実行文
}

のように行います。allChildrenはTransformの配列型指定である点に注意してください。
何度も呼び出されるこのような実行部分はstatic関数にしてしまうと効率が良いです。
例)
static function FindTransform(parent:Transform, name:String)
{
if (parent.name == name ) return parent;
for (var child in parent)
{
result:Transform = FindTransform(child, name);
if (result != null) return result;
}
return null;
}



そして例えば子だけを削除してしまいたい場合は

for(var child in allChildren)  Destroy(child);

Destroy()文をかえれば

スクリプト系の場合
child.active = false;

または

Rendererなど表示系の場合
child.enabled = false;


万能な非表示系
child.SetActiveRecursively(false);

というように子を非表示にすることが出来ます。

ここでallChildrenは配列型ですが Transform型かGameObject型かで書き方を
変えなければいけません。

例)
var allChildren:Transform[];
allChidren = gameObject.GetComponentsInChildren(Transform);

for (var child in allChildren)
{
Destroy(child.gameObject);
}


この場合Transform型でallChildrenに登録していますので child=子 はTransform型なので.gameObjectをつけてオブジェクト本体を指定します。


その他
■タグのチェンジ 
ゲームオブジェクトのタグを変更する場合。インスペクタに
登録されているタグを付け替えることができます。
gameObject.tag = "Player";
または
transform.gameObject.tag = "Player";
など 同じオブジェクトを用いて敵と自機を分けたいときなどに使用出来ます。


■レイヤーチェンジ

例)
function ChangeLayersRecursively(var trans : Transform, var name : String)
{
for (var child : Transform in trans)
{
child.gameObject.layer = LayerMask.NameToLayer(name);
ChangeLayersRecursively(child, name);
}
}

この例のスクリプトをどこで見たのか忘れてしまいましたがLayerMask.NameToLayerを使用してオブジェクトのレイヤーも変更することができます。
この例ではtransのに子のTransformをいれてレイヤーの名前をnameにいれて
コールするとレイヤーを変更します。for文の中で再帰的な呼び出し(関数が自分自身を呼び出すこと)をしています。



ということでした。久しぶりの記事なのでちょっとボリュームたりませんでしたかね?
記事の文中おそらく間違えているところも色々あると思いますので指摘していただいてかまいませんよー。
ちなみにどこかのブログで現役プログラマーと紹介されていましたが わたしCGデザイナーです。

あとこのブログはライブドアのテンプレートで書いているのですがフォントサイズが小さいという指摘を
以前受けました。現在たいていのブラウザは ctrl+”+” で記事を拡大することができますので試してみてくださいね。

↑このページのトップヘ