【Unity2018】LWRPでUnirtyちゃんをフィギュアっぽく描画するShaderを書いた
概要
皆さんは「フィギュアっぽくしたいけど、PBRにしたら全然思ってたのと違う…!」みたいな経験はないでしょうか?私はあります。 ということで、LWRPを使ってUnityちゃんをいい感じに描画するシェーダーを書いてみました。
ちなみに、あくまでそれっぽく見せるのが目的で、あまり正確なPBRではないため、しっかりとした品質を求める人はHDRPを使うのをオススメします。
After
Before
Half-Lambert
左が通常のLambertで、右がHalf-Lambertです。 通常のLambertでは光源に対して垂直な面は暗くなりますが、Half-Lambertでは、光源に対して真逆を向くまでリニアにグラデーションさせることで、人肌のような「少し透けている」印象を持たせることが出来ます。
Lambert
Half-Lambert
疑似SSS
影を計算するとき、グラデーションの赤要素を補正でガッと持ち上げてあげることで、血潮が透けている感じを出すことができます。 相当大胆な近似ですが、まあそれっぽければええやろの精神です。
疑似SSSなし
疑似SSSあり
コード
基本的には前回の記事のように、LWRPのStandardLitをコピってきて一部書き換えています。 LightingPhysicallyBased関数を書き換え、Half-Lambertと疑似SSSを実装しています。
Shader "LightweightPipeline/Skin" { Properties { // Specular vs Metallic workflow [HideInInspector] _WorkflowMode("WorkflowMode", Float) = 1.0 _Color("Color", Color) = (0.5,0.5,0.5,1) _MainTex("Albedo", 2D) = "white" {} _Cutoff("Alpha Cutoff", Range(0.0, 1.0)) = 0.5 _Glossiness("Smoothness", Range(0.0, 1.0)) = 0.5 _GlossMapScale("Smoothness Scale", Range(0.0, 1.0)) = 1.0 _SmoothnessTextureChannel("Smoothness texture channel", Float) = 0 [Gamma] _Metallic("Metallic", Range(0.0, 1.0)) = 0.0 _MetallicGlossMap("Metallic", 2D) = "white" {} _SpecColor("Specular", Color) = (0.2, 0.2, 0.2) _SpecGlossMap("Specular", 2D) = "white" {} [ToggleOff] _SpecularHighlights("Specular Highlights", Float) = 1.0 [ToggleOff] _GlossyReflections("Glossy Reflections", Float) = 1.0 _BumpScale("Scale", Float) = 1.0 _BumpMap("Normal Map", 2D) = "bump" {} _Parallax("Height Scale", Range(0.005, 0.08)) = 0.02 _ParallaxMap("Height Map", 2D) = "black" {} _OcclusionStrength("Strength", Range(0.0, 1.0)) = 1.0 _OcclusionMap("Occlusion", 2D) = "white" {} _EmissionColor("Color", Color) = (0,0,0) _EmissionMap("Emission", 2D) = "white" {} _DetailMask("Detail Mask", 2D) = "white" {} _DetailAlbedoMap("Detail Albedo x2", 2D) = "grey" {} _DetailNormalMapScale("Scale", Float) = 1.0 _DetailNormalMap("Normal Map", 2D) = "bump" {} [Enum(UV0,0,UV1,1)] _UVSec("UV Set for secondary textures", Float) = 0 // Blending state [HideInInspector] _Surface("__surface", Float) = 0.0 [HideInInspector] _Blend("__blend", Float) = 0.0 [HideInInspector] _AlphaClip("__clip", Float) = 0.0 [HideInInspector] _SrcBlend("__src", Float) = 1.0 [HideInInspector] _DstBlend("__dst", Float) = 0.0 [HideInInspector] _ZWrite("__zw", Float) = 1.0 [HideInInspector] _Cull("__cull", Float) = 2.0 [HDR] _SSSColor ("SSS Color", Color) = (0, 0, 0) } SubShader { // Lightweight Pipeline tag is required. If Lightweight pipeline is not set in the graphics settings // this Subshader will fail. One can add a subshader below or fallback to Standard built-in to make this // material work with both Lightweight Pipeline and Builtin Unity Pipeline Tags{"RenderType" = "Opaque" "RenderPipeline" = "LightweightPipeline" "IgnoreProjector" = "True"} LOD 300 // ------------------------------------------------------------------ // Forward pass. Shades all light in a single pass. GI + emission + Fog Pass { // Lightmode matches the ShaderPassName set in LightweightPipeline.cs. SRPDefaultUnlit and passes with // no LightMode tag are also rendered by Lightweight Pipeline Name "StandardLit" Tags{"LightMode" = "LightweightForward"} Blend[_SrcBlend][_DstBlend] ZWrite[_ZWrite] Cull[_Cull] HLSLPROGRAM // Required to compile gles 2.0 with standard SRP library // All shaders must be compiled with HLSLcc and currently only gles is not using HLSLcc by default #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma target 2.0 // ------------------------------------- // Material Keywords #pragma shader_feature _NORMALMAP #pragma shader_feature _ALPHATEST_ON #pragma shader_feature _ALPHAPREMULTIPLY_ON #pragma shader_feature _EMISSION #pragma shader_feature _METALLICSPECGLOSSMAP #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A #pragma shader_feature _OCCLUSIONMAP #pragma shader_feature _SPECULARHIGHLIGHTS_OFF #pragma shader_feature _GLOSSYREFLECTIONS_OFF #pragma shader_feature _SPECULAR_SETUP // ------------------------------------- // Lightweight Pipeline keywords #pragma multi_compile _ _ADDITIONAL_LIGHTS #pragma multi_compile _ _VERTEX_LIGHTS #pragma multi_compile _ _MIXED_LIGHTING_SUBTRACTIVE #pragma multi_compile _ _SHADOWS_ENABLED #pragma multi_compile _ _LOCAL_SHADOWS_ENABLED #pragma multi_compile _ _SHADOWS_SOFT #pragma multi_compile _ _SHADOWS_CASCADE // ------------------------------------- // Unity defined keywords #pragma multi_compile _ DIRLIGHTMAP_COMBINED #pragma multi_compile _ LIGHTMAP_ON #pragma multi_compile_fog //-------------------------------------- // GPU Instancing #pragma multi_compile_instancing #pragma vertex LitPassVertex #pragma fragment frag #include "LWRP/ShaderLibrary/InputSurfacePBR.hlsl" #include "LWRP/ShaderLibrary/LightweightPassLit.hlsl" float3 _SSSColor; half3 SkinLighting(BRDFData brdfData, half3 lightColor, half3 lightDirectionWS, half lightAttenuation, half3 normalWS, half3 viewDirectionWS) { half NdotL = saturate(dot(normalWS, lightDirectionWS) * 0.5 + 0.5); half3 radiance = lightColor * pow(lightAttenuation * NdotL, 1.0 / _SSSColor); return DirectBDRF(brdfData, normalWS, lightDirectionWS, viewDirectionWS) * radiance; } half3 SkinLighting(BRDFData brdfData, Light light, half3 normalWS, half3 viewDirectionWS) { return SkinLighting(brdfData, light.color, light.direction, light.attenuation, normalWS, viewDirectionWS); } half4 SkinPBR(InputData inputData, half3 albedo, half metallic, half3 specular, half smoothness, half occlusion, half3 emission, half alpha) { BRDFData brdfData; InitializeBRDFData(albedo, metallic, specular, smoothness, alpha, brdfData); Light mainLight = GetMainLight(); mainLight.attenuation = MainLightRealtimeShadowAttenuation(inputData.shadowCoord); MixRealtimeAndBakedGI(mainLight, inputData.normalWS, inputData.bakedGI, half4(0, 0, 0, 0)); half3 color = GlobalIllumination(brdfData, inputData.bakedGI, occlusion, inputData.normalWS, inputData.viewDirectionWS); color += SkinLighting(brdfData, mainLight, inputData.normalWS, inputData.viewDirectionWS); #ifdef _ADDITIONAL_LIGHTS int pixelLightCount = GetPixelLightCount(); for (int i = 0; i < pixelLightCount; ++i) { Light light = GetLight(i, inputData.positionWS); light.attenuation *= LocalLightRealtimeShadowAttenuation(light.index, inputData.positionWS); color += SkinLighting(brdfData, light, inputData.normalWS, inputData.viewDirectionWS); } #endif color += inputData.vertexLighting * brdfData.diffuse; color += emission; return half4(color, alpha); } // Used in Standard (Physically Based) shader half4 frag(LightweightVertexOutput IN) : SV_Target { UNITY_SETUP_INSTANCE_ID(IN); SurfaceData surfaceData; InitializeStandardLitSurfaceData(IN.uv, surfaceData); InputData inputData; InitializeInputData(IN, surfaceData.normalTS, inputData); half4 color = SkinPBR(inputData, surfaceData.albedo, surfaceData.metallic, surfaceData.specular, surfaceData.smoothness, surfaceData.occlusion, surfaceData.emission, surfaceData.alpha); ApplyFog(color.rgb, inputData.fogCoord); return color; } ENDHLSL } Pass { Name "ShadowCaster" Tags{"LightMode" = "ShadowCaster"} ZWrite On ZTest LEqual Cull[_Cull] HLSLPROGRAM // Required to compile gles 2.0 with standard srp library #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma target 2.0 // ------------------------------------- // Material Keywords #pragma shader_feature _ALPHATEST_ON //-------------------------------------- // GPU Instancing #pragma multi_compile_instancing #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A #pragma vertex ShadowPassVertex #pragma fragment ShadowPassFragment #include "LWRP/ShaderLibrary/InputSurfacePBR.hlsl" #include "LWRP/ShaderLibrary/LightweightPassShadow.hlsl" ENDHLSL } Pass { Name "DepthOnly" Tags{"LightMode" = "DepthOnly"} ZWrite On ColorMask 0 Cull[_Cull] HLSLPROGRAM // Required to compile gles 2.0 with standard srp library #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma target 2.0 #pragma vertex DepthOnlyVertex #pragma fragment DepthOnlyFragment // ------------------------------------- // Material Keywords #pragma shader_feature _ALPHATEST_ON #pragma shader_feature _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A //-------------------------------------- // GPU Instancing #pragma multi_compile_instancing #include "LWRP/ShaderLibrary/InputSurfacePBR.hlsl" #include "LWRP/ShaderLibrary/LightweightPassDepthOnly.hlsl" ENDHLSL } // This pass it not used during regular rendering, only for lightmap baking. Pass { Name "Meta" Tags{"LightMode" = "Meta"} Cull Off HLSLPROGRAM // Required to compile gles 2.0 with standard srp library #pragma prefer_hlslcc gles #pragma exclude_renderers d3d11_9x #pragma vertex LightweightVertexMeta #pragma fragment LightweightFragmentMeta #pragma shader_feature _SPECULAR_SETUP #pragma shader_feature _EMISSION #pragma shader_feature _METALLICSPECGLOSSMAP #pragma shader_feature _ _SMOOTHNESS_TEXTURE_ALBEDO_CHANNEL_A #pragma shader_feature EDITOR_VISUALIZATION #pragma shader_feature _SPECGLOSSMAP #include "LWRP/ShaderLibrary/InputSurfacePBR.hlsl" #include "LWRP/ShaderLibrary/LightweightPassMetaPBR.hlsl" ENDHLSL } } FallBack "Hidden/InternalErrorShader" }