matrix World; // ワールド変換行列 matrix View; // ビュー変換行列 matrix Projection; // 透視変換行列 float4 Diffuse; // ディフューズ色 float3 Light; // 光源座標(ビュー座標系) Texture2D Tex2D; // テクスチャ // テクスチャ・サンプラ SamplerState smpWrap : register(s0); // ************************************************** // 3Dオブジェクトの描画 // ************************************************** // 頂点シェーダの入力データ定義 struct VS_INPUT { float3 Pos : POSITION; // 頂点座標(モデル座標系) float3 Norm : NORMAL; // 法線ベクトル(モデル座標系) float2 Tex : TEXTURE; // テクスチャ座標 }; // ピクセル シェーダの入力データ定義 struct PS_INPUT { float4 Pos : SV_POSITION; // 頂点座標(透視座標系) float3 PosView : POSVIEW; // 頂点座標(ビュー座標系) float3 Norm : NORMAL; // 法線ベクトル(ビュー座標系) float2 Tex : TEXTURE; // テクスチャ座標 }; // 頂点シェーダの関数 PS_INPUT VS(VS_INPUT input) { PS_INPUT output; // 頂点座標 モデル座標系→透視座標系 float4 pos4 = float4(input.Pos, 1.0); pos4 = mul(pos4, World); pos4 = mul(pos4, View); output.PosView = pos4.xyz / pos4.w; output.Pos = mul(pos4, Projection); // 法線ベクトル モデル座標系→ビュー座標系 float3 Norm = mul(input.Norm, (float3x3)World); output.Norm = mul(Norm, (float3x3)View); // テクスチャ座標 output.Tex = input.Tex; // 出力 return output; } // ライティング計算 float lighting(PS_INPUT input) { // 光源ベクトル float3 light = Light - input.PosView; // 距離 float leng = length(light); // 明るさ return 30 * dot(normalize(light), normalize(input.Norm)) / pow(leng, 2); } // ピクセル シェーダの関数 float4 PS(PS_INPUT input) : SV_TARGET { // ライティング計算 float bright = lighting(input); // テクスチャ取得 float4 texCol = Tex2D.Sample(smpWrap, input.Tex); // テクセル読み込み // 色 return saturate(bright * texCol * Diffuse); } // ************************************************** // シャドウ・ボリュームの描画 // ************************************************** struct GS_INPUT_SV { float3 Pos : SV_POSITION; }; struct PS_INPUT_SV { float4 Pos : SV_POSITION; }; // 頂点シェーダの関数 float3 VS_SV(VS_INPUT input) : POSITION { // 頂点座標 モデル座標系→ビュー座標系 float4 Pos = float4(input.Pos, 1.0); Pos = mul(Pos, World); Pos = mul(Pos, View); return Pos.xyz / Pos.w; } // ジオメトリ・シェーダの関数@ [maxvertexcount(12)] void GS_SV_1(triangleadj float3 input[6] : POSITION, inout TriangleStream TriStream) { PS_INPUT_SV output = (PS_INPUT_SV)0; // 法線ベクトルの計算 float3 faceNorm = normalize(cross(input[2] - input[0], input[4] - input[0])); // 光源ベクトルの計算 float3 lightVec = Light - (input[0] + input[2] + input[4]) / 3.0; // 光源を向いている? if (dot(faceNorm, lightVec) >= 0.0) return; /* ********************************************* // 以下のコードはなぜか正しく動作しない(GeForce 8800 GTX)。[2007/6/21] // 各辺の判定 for (uint iA=0; iA<6; iA+=2) { uint iB = iA+1, iC = (iA+2) % 6; // 光源ベクトルの計算 lightVec = Light - (input[iA] + input[iB] + input[iC]) / 3.0; // 隣接面の法線ベクトルの計算 faceNorm = cross(input[iB] - input[iA], input[iC] - input[iA]); // 境界判定 if (dot(faceNorm, lightVec) >= 0.0) { // シャドウ・ボリュームを生成 output.Pos = mul(float4(input[iA], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iA] + (input[iA] - Light) * 1000, 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC] + (input[iC] - Light) * 1000, 1.0), Projection); TriStream.Append(output); TriStream.RestartStrip(); } } ********************************************* */ // 以上のコードが正しく動作しないので、以下にforループを展開する。 uint iA = 0; { uint iB = iA+1, iC = (iA+2) % 6; // 光源ベクトルの計算 lightVec = Light - (input[iA] + input[iB] + input[iC]) / 3.0; // 隣接面の法線ベクトルの計算 faceNorm = cross(input[iB] - input[iA], input[iC] - input[iA]); // 境界判定 if (dot(faceNorm, lightVec) >= 0.0) { // シャドウ・ボリュームを生成 output.Pos = mul(float4(input[iA], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iA] + (input[iA] - Light) * 1000, 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC] + (input[iC] - Light) * 1000, 1.0), Projection); TriStream.Append(output); TriStream.RestartStrip(); } } iA = 2; { uint iB = iA+1, iC = (iA+2) % 6; // 光源ベクトルの計算 lightVec = Light - (input[iA] + input[iB] + input[iC]) / 3.0; // 隣接面の法線ベクトルの計算 faceNorm = cross(input[iB] - input[iA], input[iC] - input[iA]); // 境界判定 if (dot(faceNorm, lightVec) >= 0.0) { // シャドウ・ボリュームを生成 output.Pos = mul(float4(input[iA], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iA] + (input[iA] - Light) * 1000, 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC] + (input[iC] - Light) * 1000, 1.0), Projection); TriStream.Append(output); TriStream.RestartStrip(); } } iA = 4; { uint iB = iA+1, iC = (iA+2) % 6; // 光源ベクトルの計算 lightVec = Light - (input[iA] + input[iB] + input[iC]) / 3.0; // 隣接面の法線ベクトルの計算 faceNorm = cross(input[iB] - input[iA], input[iC] - input[iA]); // 境界判定 if (dot(faceNorm, lightVec) >= 0.0) { // シャドウ・ボリュームを生成 output.Pos = mul(float4(input[iA], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iA] + (input[iA] - Light) * 1000, 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[iC] + (input[iC] - Light) * 1000, 1.0), Projection); TriStream.Append(output); TriStream.RestartStrip(); } } } // ジオメトリ・シェーダの関数A [maxvertexcount(12)] void GS_SV_2(triangle float3 input[3] : POSITION, inout TriangleStream TriStream) { PS_INPUT_SV output; // 法線ベクトルの計算 float3 faceNorm = cross(input[1] - input[0], input[2] - input[0]); // 光源ベクトルの計算 float3 lightVec = Light - (input[0] + input[1] + input[2]) / 3.0; // 光源を向いている? if (dot(faceNorm, lightVec) > 0.0) return; // 各辺を引き延ばす for (uint i1=0; i1<3; ++i1) { uint i2 = (i1+1)%3, i3 = (i1+2)%3; // シャドウ・ボリュームを生成 output.Pos = mul(float4(input[i1], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[i3], 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[i1] + (input[i1] - Light) * 100, 1.0), Projection); TriStream.Append(output); output.Pos = mul(float4(input[i3] + (input[i3] - Light) * 100, 1.0), Projection); TriStream.Append(output); TriStream.RestartStrip(); } } // ************************************************** // シャドウの描画 // ************************************************** // 頂点シェーダの関数 float4 VS_S(uint id : SV_VertexID) : SV_POSITION{ switch(id) { case 0: return float4(-1, 1, 1, 1); case 1: return float4( 1, 1, 1, 1); case 2: return float4(-1, -1, 1, 1); default: case 3: return float4( 1, -1, 1, 1); } } // ピクセル・シェーダの関数 float4 PS_S(float4 pos : SV_POSITION) : SV_TARGET { return float4(0.0, 0.0, 0.0, 0.5); }