2012年08月12日

うにばな(シェーダ的なもの1−2)


ベクトル計算の基本



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での開発に関しては問題無さそうですが モバイル機器の開発では
スクリプトで計算をしてシェーダに値を設定したほうが安定しそうな気はします。



akinow at 17:39│Comments(0)TrackBack(0) Clip to Evernote Unity3d | シリーズ講座

トラックバックURL

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔