// BSD license // //Copyright( c ) 2019, lena antler(猫のしもべ), less //All rights reserved. // //Redistribution and use in source and binary forms, with or without //modification, are permitted provided that the following conditions are met : // //1. Redistributions of source code must retain the above copyright notice, //this list of conditions and the following disclaimer. //2. Redistributions in binary form must reproduce the above copyright notice, //this list of conditions and the following disclaimer in the documentation //and / or other materials provided with the distribution. // //THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND //ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED //WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE //DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR //ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES //( INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; //LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION ) HOWEVER CAUSED AND //ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT //( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF THIS //SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // //The views and conclusions contained in the software and documentation are those //of the authors and should not be interpreted as representing official policies, //either expressed or implied, of the FreeBSD Project. // //------------------------------------------------------------------------------ // // MMDモデル(pmd/pmx/x)を表示するためのシェーダー。だと思うヨ。 // //------------------------------------------------------------------------------ // 基本ルート署名定義 // // t0:toonテクスチャ // t1:sphereテクスチャ // t2:材質テクスチャ // t3:追加テクスチャ1 // t4:追加テクスチャ2 // t5:追加テクスチャ3 // t6:セルフ影マップ // // b0:wvp行列やライトなどのシーン属性 // b1:マテリアル個別の属性 // // s0:ANISOTROPIC/Wrapサンプラー。 // s1:ANISOTROPIC/Clampサンプラー。 // s2:MIN_MAG_MIP_LINEAR/Wrapサンプラー。 // s3:MIN_MAG_MIP_LINEAR/Clampサンプラー。 // s4:MIN_MAG_MIP_POINT/Wrapサンプラー。 // s5:MIN_MAG_MIP_POINT/Clampサンプラー。 // s6:COMPARISON ANISOTROPIC/Wrapサンプラー。 // s7:COMPARISON ANISOTROPIC/Clampサンプラー。 // s8:COMPARISON MIN_MAG_MIP_LINEAR/Wrapサンプラー。 // s9:COMPARISON MIN_MAG_MIP_LINEAR/Clampサンプラー。 // s10:COMPARISON MIN_MAG_MIP_POINT/Wrapサンプラー。 // s11:COMPARISON MIN_MAG_MIP_POINT/Clampサンプラー。 //------------------------------------------------------------------------------ // //------------------------------------------------------------------------------ // Shader behavior. //------------------------------------------------------------------------------ //#define SOFTWARE_PCF 1 #define SHADING_MODEL 1 // 1:MMD 2:Phong with Fresnel //#define BINALIZE_SHADOW_FILTER 0.25 //#define LENEAR_WORKFLOW //------------------------------------------------------------------------------ // シーン属性 //------------------------------------------------------------------------------ cbuffer cbScene : register(b0) { float4x4 g_wvp; float4x4 g_viewMatrix; float4x4 g_projectionMatrix; float4x4 g_viewProjectionMatrix; float3 g_cameraPos; float3 g_cameraCenter; float3 g_cameraUp; float3 g_cameraDirection; float g_fov; float4x4 g_lwvp; float4x4 g_groundShadowWvp; float4 g_groundShadowColor; float3 g_lightDirection; float3 g_lightColor; float g_lightRange; float g_near; float g_far; uint g_shadowBufferSize; }; //------------------------------------------------------------------------------ // マテリアル属性 //------------------------------------------------------------------------------ #define NND_SPHERE_DISABLED (0) // スフィア無効(排他) #define NND_SPHERE_MULTIPLICATION (1) // 加算スフィアが有効(排他) #define NND_SPHERE_ADDTION (2) // 乗算スフィアが有効(排他) cbuffer cbMaterial : register(b1) { float4 g_diffuse; float3 g_specular; float g_shininess; float3 g_ambient; uint g_uvCount; uint g_toonFileEnabled; uint g_sphereFlag; uint g_edgeEnabled; float g_edgeSize; float4 g_edgeColor; uint g_groundShadowEnabled; uint g_shadowCastEnabled; uint g_shadowRecvEnabled; uint g_doubleSided; }; //------------------------------------------------------------------------------ // Outer parameter. //------------------------------------------------------------------------------ Texture2D g_toonTex : register(t0); Texture2D g_sphereTex : register(t1); Texture2D g_materialTex[4] : register(t2); Texture2D g_shadowTex : register(t6); Texture2D g_extraTex[4] : register(t7); SamplerState g_wrapAnisotropicSampler : register(s0); SamplerState g_clampAnisotropicSampler : register(s1); SamplerState g_wrapLinerSampler : register(s2); SamplerState g_clampLinerSampler : register(s3); SamplerState g_wrapPointSampler : register(s4); SamplerState g_clampPointSampler : register(s5); SamplerComparisonState g_wrapComparisonAnisotropicSampler : register(s6); SamplerComparisonState g_clampComparisonAnisotropicSampler : register(s7); SamplerComparisonState g_wrapComparisonLinerSampler : register(s8); SamplerComparisonState g_clampComparisonLinerSampler : register(s9); SamplerComparisonState g_wrapComparisonPointSampler : register(s10); SamplerComparisonState g_clampComparisonPointSampler : register(s11); struct VSInput { float3 position : NND_POSITION; float3 normal : NND_NORMAL; float2 uv[4] : NND_TEXCOORD; float edgeBias : NND_EDGEBIAS; }; // カラーマップ用のWRAP-Sampler #define ColorTextureSampler g_wrapAnisotropicSampler // Toonマップ用のCLAMP-Sampler #define ToonTextureSampler g_clampAnisotropicSampler // Sphereマップ用のCLAMP-Sampler(分ける必要は無いかも知れないが一応分けておく) #define SphereTextureSampler g_clampAnisotropicSampler // シャドウマップ用のサンプラー #define ShadowMapTextureSampler g_clampLinerSampler #define SoftShadowMapTextureSampler g_clampComparisonLinerSampler // トゥーン度合いを調整する定数。小さいほど影が伸びてグラデーションが出る。 // トゥーンテクスチャの解像度次第で影の出方が変わる。16以上は差が出ないと思われる。 // // 3 MMDと同じ色合い #define ToonFactor 3 static float3 lightDiffuse = float3(0.0f, 0.0f, 0.0f); static float3 lightAmbient = g_lightColor; static float3 lightSpecular = g_lightColor; static float4 materialDiffuseColor = g_diffuse; static float3 materialAmbientColor = g_diffuse.rgb; static float3 materialSpecularColor = g_specular; static float4 diffuseColor = materialDiffuseColor * float4(lightDiffuse, 1.0f); static float3 ambientColor = saturate( materialAmbientColor * g_lightColor + g_ambient ); static float3 specularColor = materialSpecularColor * g_lightColor; static float shadowTexStemp = 1.0f / g_shadowBufferSize; //------------------------------------------------------------------------------ // Functions. //------------------------------------------------------------------------------ // Texture sample for linearize workflow. float4 SampleColorTexture(Texture2D textureObject, SamplerState state, float2 uv) { #ifdef LENEAR_WORKFLOW return pow(textureObject.Sample(state, uv), 2.2f); #else return textureObject.Sample(state, uv); #endif } // Linearize color. float3 Linearize(float3 color) { #ifdef LENEAR_WORKFLOW return pow(color, 2.2f); #else return color; #endif } // Unlinearize color. float3 Unlinearize(float3 color) { #ifdef LENEAR_WORKFLOW return pow(color, 0.454545f); #else return color; #endif } //------------------------------------------------------------------------------ // マテリアル描画用 //------------------------------------------------------------------------------ struct PSInput { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; float2 sphereUv : TEXCOORD2; float3 view : TEXCOORD3; float4 zCalcTex : TEXCOORD4; float4 color : COLOR0; float3 shadowColor : COLOR1; }; PSInput VSMain( VSInput input ) { PSInput result; result.position = mul( float4(input.position, 1.0f), g_wvp ); //result.position = mul( float4(input.position, 1.0f), g_groundShadowWvp ); result.normal = normalize( input.normal ); // For normal offset shadows. result.zCalcTex = mul( float4(input.position + result.normal * g_lightRange / 1000, 1.0f), g_lwvp ); result.view = g_cameraPos - input.position; result.color.rgb = Linearize(ambientColor); result.shadowColor = result.color.rgb; float3 diffuse = Linearize(diffuseColor.rgb); if ( g_toonFileEnabled == 1 ) { result.color.rgb += max( 0.0f, dot( input.normal, -g_lightDirection ) ) * diffuse; } result.color.a = diffuseColor.a; result.color.rgb = saturate( result.color.rgb ); result.uv = input.uv[0]; result.sphereUv = float2(0.0f, 0.0f); if ( g_sphereFlag != NND_SPHERE_DISABLED ) { float2 normalWv = mul( result.normal, (float3x3)g_viewMatrix ).xy; result.sphereUv = normalWv * float2(0.5f, -0.5f) + 0.5f; } return result; } float4 PSMain( PSInput input ) : SV_TARGET { //return float4(0.5f + 0.5f * input.normal, 1.0f ); //return float4(input.normal, 1.0 ); // For now, let's normalize values we don't know is normalized. float3 normal = normalize( input.normal ); float3 light = -g_lightDirection; float3 view = normalize( input.view ); // ------------------------------------------------------------ // Material color. // ------------------------------------------------------------ // Specular color. float3 tempSpecularColor = Linearize(specularColor); float3 halfVec = normalize( view + light ); float3 specular = pow( max( 0.0f, dot( halfVec, normal ) ), g_shininess ) * tempSpecularColor; float ndotlFull = dot( normal, light ); float ndotv = clamp(dot( normal, view ), 0.0f, 1.0f); float ndotl = clamp(ndotlFull, 0.0f, 1.0f); // Base color. float4 color = input.color; float3 shadowColor = input.shadowColor; // Texture mapping. [branch] if ( 0 < g_uvCount ) { float4 texColor = SampleColorTexture(g_materialTex[0], ColorTextureSampler, input.uv); color *= texColor; shadowColor *= texColor.rgb; } // Sphere mapping. [branch] if ( g_sphereFlag != NND_SPHERE_DISABLED ) { float4 sphereTexColor = SampleColorTexture(g_sphereTex, SphereTextureSampler, input.sphereUv); [branch] if ( g_sphereFlag == NND_SPHERE_ADDTION ) { color.rgb += sphereTexColor.rgb; shadowColor += sphereTexColor.rgb; } else { color.rgb *= sphereTexColor.rgb; shadowColor *= sphereTexColor.rgb; } color.a *= sphereTexColor.a; } // Add specular. // Specular appears only light side. color.rgb += specular; // ------------------------------------------------------------ // Self shadowing. // ------------------------------------------------------------ float3 comp = 1.0f; [branch] if (g_shadowRecvEnabled == 1) { float depth = input.zCalcTex.z / input.zCalcTex.w; float2 texCoord; texCoord.x = (1.0f + input.zCalcTex.x / input.zCalcTex.w) * 0.5f; texCoord.y = (1.0f - input.zCalcTex.y / input.zCalcTex.w) * 0.5f; [branch] if ( any( saturate( texCoord ) != texCoord ) ) { // Out of shadow map square. } else { comp = 0.0f; float bias = 0.005f * clamp(tan(acos(ndotl)), 0.0f, 0.01f); #ifdef SOFTWARE_PCF [loop] for (int loopY = -SOFTWARE_PCF; loopY <= SOFTWARE_PCF; ++loopY) { [loop] for (int loopX = -SOFTWARE_PCF; loopX <= SOFTWARE_PCF; ++loopX) { comp += g_shadowTex.SampleCmpLevelZero(SoftShadowMapTextureSampler, float2(loopX, loopY) * shadowTexStemp + texCoord, depth); } } comp /= (SOFTWARE_PCF * 2 + 1) * (SOFTWARE_PCF * 2 + 1); #else comp += g_shadowTex.SampleCmpLevelZero(SoftShadowMapTextureSampler, texCoord, depth); // comp += g_shadowTex.SampleCmpLevelZero(SoftShadowMapTextureSampler, texCoord, depth); #endif } comp = clamp(comp, 0.0f, 1.0f); // ------------------------------------------------------------ // Toon(lut) mapping. // ------------------------------------------------------------ [branch] if ( g_toonFileEnabled == 1 ) { float4 ToonTex = SampleColorTexture(g_toonTex, ToonTextureSampler, float2(0.0f, 1.0f)); #if SHADING_MODEL == 1 comp = min(saturate(ndotl * ToonFactor), comp); #else // Without toon mapping. comp = (1.0f - ndotv) * tempSpecularColor + ndotl * comp; #endif color.a *= ToonTex.a; shadowColor.rgb *= ToonTex.rgb; } } else { // No shadow receiver material. float4 ToonTex = 1.0f; [branch] if ( g_toonFileEnabled == 1 ) { #if SHADING_MODEL == 1 // Toon without self-shadowing material method. ToonTex = SampleColorTexture(g_toonTex, ToonTextureSampler, float2(0.0f, 0.5f - ndotlFull * 0.5f)); color *= ToonTex; #else comp = (1.0f - ndotv) * tempSpecularColor + ndotl; ToonTex = SampleColorTexture(g_toonTex, ToonTextureSampler, float2(0.0f, 1.0f)); color.a *= ToonTex.a; #endif shadowColor.rgb *= ToonTex.rgb; } } comp = saturate(comp); #ifdef BINALIZE_SHADOW_FILTER comp = step(BINALIZE_SHADOW_FILTER, comp); #endif [branch] if (color.a == 0) discard; //return float4(Unlinearize(comp), color.a); color.rgb = lerp(shadowColor, color.rgb, comp); color.rgb = Unlinearize(color.rgb); return color; } //------------------------------------------------------------------------------ // エッジ描画用 //------------------------------------------------------------------------------ struct PSEdgeInput { float4 position : SV_POSITION; float4 color : COLOR; }; PSEdgeInput VSEdgeMain( VSInput input ) { PSEdgeInput result; float3 eye = g_cameraPos - input.position; //float distance = length( eye ) / (g_far - g_near); float distance = length( eye ) / 1000; float3 tempNormal = mul( normalize( float3(input.normal.xy, 0) ), (float3x3)g_wvp ); float edgeWeigt = 5.0f * g_edgeSize * input.edgeBias * distance * (degrees( g_fov ) / 180); float3 pos = input.position + input.normal * edgeWeigt; result.position = mul( float4(pos, 1.0f), g_wvp ); result.color = g_edgeColor; return result; } float4 PSEdgeMain( PSEdgeInput input ) : SV_TARGET { //return float4(0.0f, 0.0f, 0.0f, 0.0f); return input.color; } //------------------------------------------------------------------------------ // セルフ影描画用 //------------------------------------------------------------------------------ struct PSShadowInput { float4 position : SV_POSITION; float2 uv : TEXCOORD0; float alpha : COLOR; }; PSShadowInput VSShadowMain( VSInput input ) { PSShadowInput result; result.position = mul( float4(input.position, 1.0f), g_lwvp ); result.alpha = diffuseColor.a; result.uv = input.uv[0]; return result; } float4 PSShadowMain( PSShadowInput input ) : SV_TARGET { if (g_shadowCastEnabled == 0) { discard; } [branch] if ( 0 < g_uvCount ) { float alpha = input.alpha * g_materialTex[0].Sample( ColorTextureSampler, input.uv ).a; clip(alpha < 0.1f ? -1 : 1); } return float4(0.0f, 0.0f, 0.0f, 1.0f); } //------------------------------------------------------------------------------ // 地面影描画用 //------------------------------------------------------------------------------ struct PSGroundShadowInput { float4 position : SV_POSITION; }; PSGroundShadowInput VSGroundShadowMain( VSInput input ) { PSGroundShadowInput result; result.position = mul( float4(input.position, 1.0f), g_groundShadowWvp ); return result; } float4 PSGroundShadowMain( PSGroundShadowInput input ) : SV_TARGET { [branch] if ( g_groundShadowEnabled == 0 ) { discard; } return float4(g_groundShadowColor.rgb, 1.0f); }