前回はポストエフェクトについて。

※Unity Proじゃないとできません。

しかし、前回の方法ではできないポストエフェクトがある。

それは、対象物だけ描画してその対象物だけエフェクトをかけるという事。

たとえば、太陽だけ描画して太陽だけに放射ブラーをかける。

ゴットレイってやつですね。

あとは白黒のマスク画面が必要なので太陽以外を黒でレンダリングする。

といった手順が必要です。

んで厄介なのがUnityのマルチレンダ。

1番気に食わなかったのがカメラを1つ増やさないとマルチレンダができない事。

絶対にカメラを増やさないとマルチレンダできません。

絶対にです。

なぜ気に食わないかというと、ドローコールが2倍になったからです。

そりゃカメラ2つでレンダリングしてれば2倍になりますわな。

そこで!

レンダリングするときにレンダリングする奴を決める事ができます。

カメラの関数で

camera.RenderwithShader(第1引数:シェーダ、第2引数:ターゲット)

とかいうものと

camera.SetReplacementShader(第1引数:シェーダ、第2引数:ターゲット)

という今表示されている奴を1つのシェーダで全部描画するというものです。

置き換えシェーダとよばれています。

ttp://unityshader.hatenablog.com/entry/2013/12/14/015247

このサイトにも書いてあります。

んでいまこいつらに苦しめられています。

まずこの二つの関数の違いがよくわからない。

たぶんなんだけど、SetReplacementShaderは画面に表示されているものすべてにって感じ

恐らく、2D含め。だから正直微妙?UIなんでレンダリングしなくていい。

つかうのはRenderwithShaderになるのかな?

そしてわからん事がUnityでステート?ドローコールとか表示されているやつ。

あれがレンダテクスチャ側に描画されてもどってこない罠。

たぶんResetReplacementShaderっていう使ったシェーダの初期化

とかいうのやらないとなんだけど、どのタイミングでやればいいかよく分からない状況。

これは公式
ttp://docs-jp.unity3d.com/Documentation/Components/SL-ShaderReplacement.html

置き換えシェーダについての注意点がかいてあります。

シェーダのサブシェーダにタグは必ずつけろよ的な話。

Sunshader
{
Tags{"RenderType" = "オペーク"}
}

みたいなやつ。んでUnityの標準シェーダに全部ついているから描画されない心配はないよ!

だって。

さっきはった(公式じゃないほう)場所にもそれについて書いてあります。


RenderwithShader(shader,"RenderType");

ってやるとレンダータイプってタグがつけられたシェーダが描画される。

とでも思っているのか?

使うシェーダのタグがRenderType = オペーク だったら

レンダータイプってタグのやつのオペークってやつらを描画する設定。

だからRenderType = Transpose みたいな事かいてるとオペークじゃないので

そいつは描画されません。

あくまでも置き換えシェーダとおなじタグの同じ値?文字列?の奴しか描画されない。

なので太陽だけ書きたい!ってなったら太陽のシェーダに

Tags{"RenderType" = "sun"}

とかかいといて、置き換えシェーダにもTags{"RenderType" = "sun"} って書いておけば

描画されるって信じている。これまだやってないから確証ないです。

明日やるんで、明日また書きます。

つまりこれができたら太陽だけレンダリングしているレンダテクスチャができるから

レンダテクスチャにしても描画コストは2倍にならないというわけ。

普通にカメラのターゲットテクスチャを何も考えないで画面に表示すると2倍になってしますけど。

まだまだこれについてはわからないことがたくさんあるのでまた書きます。

まじでUnityのシェーダ周りの日本語記事が少ない。

テッセレーションについては皆無だし。

テッセレーションもできたらいつか書きます。

それでは。

最後までかいたのに消しちゃった・・・。

やる気なくなったから重要なところだけ。

まずUnityProじゃないとできません。

Unityでポストエフェクトかけたい。

ちなみにエッジフィルターかけます。

マーベッリックプロジェクトのエッジフィルターの奴やります。

カメラのスクリプト張ります。

using UnityEngine;
using System.Collections;

public class Camera : MonoBehaviour {

public Shader shader; // フィルター系のシェーダ

// Use this for initialization
void Start () {
// 深度テクスチャモードON!
camera.depthTextureMode = DepthTextureMode.Depth;
}

// Update is called once per frame
void Update () {

}

// すべてが描画された後に呼ばれる関数
// 引数は今画面に表示されている物を画像として取得してきている
// たぶん、第1が入力で第2が出力だとおもう
void OnRenderImage( RenderTexture src, RenderTexture dest)
{
// マテリアルをシェーダで生成する。(こんな事できたんだね
Material mat = new Material(shader);

// 神関数
// srcのテクスチャをdestのテクスチャにコピーする。
// コピーする時にマテリアルに適用されたシェーダの効果を使ってコピーする。
// そしてそれを画面に出力する。
// くそ便利関数。これがないとポストエフェクトははじまらない!
Graphics.Blit(src, dest, mat);
}
}

これをカメラに適用します

深度テクスチャの取得方法はシェーダ側で

sumpler2D DptheTexture; みたいな事かけば取得できます。

用意されてる変数があるんです。スペル間違ってたらスマソ。


これでカメラスクリプトにエッジフィルターのシェーダを適用します。

シェーダ内で注意することがあります。

頂点シェーダの所は座標変換をいつもどおりしてください。

2D画像だからいらねぇや!って思うんですけど、Unityはそうゆう感じじゃないみたい。

んであとはピクセルシェーダでマーベリックさんの所でやってるソースかけばいけます。

これができるということは、普通のテクスチャはシェーダ内におくられてきています。

なのでブラーとかセピア調にしたりいろいろできます。

今回は1枚をうけとって2枚目の深度テクスチャはシェーダ内で受け取れていたので問題はない

ですが、2枚のテクスチャをうけとりたい!ってなったらまた別の方法をやらないといけないと

思います。

カメラクラスでレンダーパスとかレンダーバッファとかを切り替えるのがあるのでそれを

なんとか駆使すればいけるような。だれか出来たら教えて。

そいじゃー。
俺用の記事。

注意:みてもすごいわかりにくいよ!本当に知りたいときはコメントでも残してください。
   後色々まちがってるよ!参考にしないほうがいいよ!

仕事場にソース持ち込めないからここに貼り付ける。


今は暇だからUniytのシェーダいじくりまわしている。

んでサーフェスシェーダ使わないで、頂点とフラグメントでライティングやらしている。

てかバンプ用意されてるからやる必要ないんだけどねw

でもシェーダの勉強だから頂点とフラグメントで計算する事にしている。


んでバンプマップでつまづいている。

シェーダソースは悪くないとおもう。

とか思ってたらCPU側でやる処理があったのね・・・。

学生の頃に普通にやってたから詰む意味がわからんとかおもってたらそうゆうことだった。


ソースはDirextXのHLSLです。

ソース張る。

D3DXMATRIX mtxRot; // 回転マトリクス
D3DXMATRIX mtxTlans; // 平行移動マトリクス
D3DXMatrixIdentity(&m_WorldMat); // ワールドマトリクスの初期化
D3DXMatrixScaling(&m_WorldMat,m_Scale.x, m_Scale.y, m_Scale.z); // 拡大、縮小
D3DXMatrixRotationYawPitchRoll(&mtxRot, m_Rot.y, m_Rot.x, m_Rot.z); // 回転処理
D3DXMatrixTranslation(&mtxTlans,m_Pos.x,m_Pos.y,m_Pos.z); // 平行移動

// マトリクスの計算
m_WorldMat = m_WorldMat * mtxRot * mtxTlans;

// モデルの逆用列用
D3DXMATRIX modelmatInv;
// モデルの逆行列を生成
D3DXMatrixInverse(&modelmatInv,NULL,&m_WorldMat);

// テクスチャのポインタ取得
CTexture *pTex = CManager::GetTexture();

CCamera *pCamera = CManager::GetCamera();
D3DXMATRIX mtxview = pCamera->GetMatView();
D3DXMATRIX mtxprj = pCamera->GetMatPrj();

// ビューとプロジェクションをかける
m_WorldMat = m_WorldMat * mtxview * mtxprj;

// 逆行列変換用ライトベクトル
D3DXVECTOR3 InvLightDirection;

// ライトベクトルを出す
// LightVec = LightVec - m_Pos;
// D3DXVec3Normalize(&LightVec, &LightVec);// ノーマライズ

// ライトベクトルとモデルの逆行列をかけたやつをつくる。
D3DXVec3TransformNormal(&InvLightDirection, &LightVec, &modelmatInv);

// カメラの座標を取得
D3DXVECTOR3 CP = CManager::GetCamera()->GetPos();
D3DXVECTOR4 InvCameraPos;

// カメラ座標と逆行列を掛け合わせたやーつを作る
D3DXVec3Transform(&InvCameraPos, &CP, &modelmatInv);

// 頂点シェーダーに送るライトベクトルを送る
m_pCT->SetFloatArray(pD3Ddev, "InvLightDirection", (float*)&InvLightDirection, 4);

// 頂点シェーダーに送るライトベクトルを送る
m_pCT->SetFloatArray(pD3Ddev, "InvCameraPos", (float*)&InvCameraPos, 4);

// WVPを送信
m_pCT->SetMatrix(pD3Ddev, "WorldViewProjection", &m_WorldMat);

CPU側でシェーダに送るデータで一番重要なのは、

ライトベクトルとカメラベクトルをモデルのワールドマトリクスの逆行列と計算する事

// モデルの逆行列を生成
D3DXMatrixInverse(&modelmatInv,NULL,&m_WorldMat);

// 逆行列変換用ライトベクトル
D3DXVECTOR3 InvLightDirection;
// ライトベクトルとモデルの逆行列をかけたやつをつくる。
D3DXVec3TransformNormal(&InvLightDirection, &LightVec, &modelmatInv);

// カメラの座標を取得
D3DXVECTOR3 CP = CManager::GetCamera()->GetPos();
D3DXVECTOR4 InvCameraPos;

// カメラ座標と逆行列を掛け合わせたやーつを作る
D3DXVec3Transform(&InvCameraPos, &CP, &modelmatInv);

あとは上記のやつを送るらしい。

俺はこれをすっかりわすれてたし、シェーダの記事みててもCPU側の事を一切きにしてなかった

次シェーダソース

頂点シェーダ

void VS(
in float4 inPosition : POSITION,
in float2 inTexcoord : TEXCOORD0,
in float3 inNormal : NORMAL0,
in float3 inTangent : TANGENT,
in float3 inBinormal : BINORMAL,
out float4 outPosition : POSITION,
out float2 outTexcoord : TEXCOORD0,
out float3 outLight : TEXCOORD1,
out float3 outEye : TEXCOORD2)
{
outPosition = mul( inPosition, WorldViewProjection );
outTexcoord = inTexcoord;

// こうやって計算すると行列変換ようのマトリクスを入れrう、
float4x4 mat = { float4( -inBinormal, 0.0f ),
float4( -inTangent, 0.0f ),
float4( inNormal, 0.0f ),
float4( 0.0f, 0.0f, 0.0f, 1.0f ) };

float4x4 invmat = transpose( mat );


// モデルの座標系とテクスチャの座標系を2回変換しないといけない
outLight = mul( InvLightDirection, invmat);

outLight = normalize(outLight);

// Eyeベクトルを出す
float4 eye = inPosition - InvCameraPos;

// ノーマライズ
eye = normalize(eye);

outEye = mul(eye, invmat);
}

ピクセルシェーダ

void PS2(in float2 inTexcoord : TEXCOORD0,in float3 inLight :TEXCOORD1,in float3 inEye:TEXCOORD2,
out float4 outDiffuse : COLOR )
{
// 高さ
//float height;// = (tex2D(TexHeight,inTexcoord).x - 0.0f) * 0.1f;

float depth = -inEye.z * 0.1f;

// アイベクトルやら高さやつかったテクスチャ座標を出す。
float2 TexCoord = inTexcoord.xy + inEye.xy * 0.1f;

//↓法線ベクトルを調整させるため
float3 N = tex2D( TexBump, TexCoord).xyz * 2.0f - 1.0f;

outDiffuse = -dot( N, inLight );
outDiffuse.a = 1.0f;

// 基本画像を合成
outDiffuse = outDiffuse * tex2D( TexSampler, TexCoord );


}

正直ピクセルシェーダはうごくかは謎。

どうやらこのシェーダは視差マッピングやったあとにバンプをやろうとして

視差マッピングのシェーダを改造させたものらしい。なんでやったかは忘れた。

あとはコレを仕事場でみてなんとかしな。

Unityで頂点とピクセルシェーダでこんな事している奴はたぶん少ない。

ゆえに記事がなくて困る。


そして記事よく記事に惑わされる。

特に一番困ったのが、ライトベクトルとカメラベクトルの取得方法。

Unityには定義済みのマクロが用意されている。

その中になんだったかわすれたけど、ライトベクトルを出すヘルパ関数と

カメラベクトルを出すヘルパ関数があるんだけど、そいつらは使わないほうがいいと思います。

もうシェーダのプロパティにスクリプト側からシェーダに座標関連を送ればいいと思う。

あとマルチレンダもまじで意味分からん。


Unityでマルチレンダに関する記事もかなり少ない。

英語ならなるかもしれないけど英語とか読む気にならないわ。

こうゆうところが日本人のプログラム技術が発展しにくい所だと思う。

あとマルチレンダ関連はUnityProじゃないとできない。

マーベリックプロジェクトっていうサイトでエッジフィルターのシェーダやってたら

Unityでマルチレンダってどうやるんだ?ってなったは。

まずメインとは別にカメラを作る。

そこにレンダテクスチャをあてる。

これで現在表示されている画面のテクスチャが生成できる。

そしてこのカメラはメインのカメラと座標が同じになるようにスクリプトでもくむ。


んでここからがやっかい。

フィルターだから2Dシェーダをつくらないといけないわけなんだけど・・・。

2Dにマテリアルどうやってあてるねんwww

そもそもマテリアルを入れる場所がない。

だからスプライトとかいうのにマテリアルがいれられるからそこにシェーダを適用した

マテリアルを無理やりつっこんだ。

このやり方が2Dシェーダを使うときにあっているかどうかはわからん。

でもできた。

んで深度テクスチャなんだけど、

シェーダー側に用意されてるっていうね・・・。

これもさっきつくったカメラにカメラモード=デプステクチャとかいうソースを書いて

んで2Dシェーダ側で

sampler2D DepthTexture;

とかいう変数宣言すると、普通にとってこれるっていう謎。

定義済みマクロらしい。(さっきヘルパ関数使わないって言った奴だれだ)

これで深度テクスチャはとれた。

んで2枚のテクスチャをあとは合成してエッジやらなんやら検出すればいけるっていう話。

まじでUnityでシェーダをばりばり書いている人いねぇかな・・・・。

記事すくなすぎて辛すぎ。

俺の記事参考にならないから見ないほうが良いよ。

UnityだからCg言語でやってるんだけど、ほぼシェーダの記述方式は変わらないから

そこは困ってない。ただ、CPU側でやる処理がDirectXとOpenGLと流れとか

全然把握できない。Unity謎多すぎ。