ToonShader

https://roystan.net/articles/toon-shader

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
Shader "URP/Unlit/URP_ToonShader"
{
Properties
{
[MainTexture] _BaseMap("Texture", 2D) = "white" {}
[MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1)
[NormalMap] _NormalMap("Normal Map", 2D) = "bump" {}
[HDR] _AmbientColor("Ambient Color", Color) = (0.4, 0.4, 0.4, 1)
[HDR] _SpecularColor("Specular Color", Color) = (0.9, 0.9, 0.9, 1)
_Glossiness("Glossiness", Float) = 32
[HDR] _RimColor("Rim Color", Color) = (1, 1, 1, 1)
_RimAmount("Rim Amount", Range(0, 1)) = 0.5
_RimThreshold("Rim Threshold", Range(0, 1)) = 0.1
[OcclusionMap] _OcclusionMap("Occlusion Map", 2D) = "white" {}
_OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0
[Toggle] _EnableEmission("Enable Emission", Float) = 0
[MainTexture] _EmissionMap("Emission Map", 2D) = "black" {}
[HDR] _EmissionColor("Emission Color", Color) = (1, 1, 1, 1)
_EmissionIntensity("Emission Intensity", Float) = 1.0
}
SubShader
{
Tags { "RenderType" = "Oqueue" "Queue" = "Geometry" "RenderPipeline" = "UniversalPipeline" }
Pass
{
Name "URP_ToonShader"
Tags { "LightMode" = "UniversalForward" }
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _BaseColor;
float4 _AmbientColor;
float4 _SpecularColor;
float _Glossiness;
float4 _RimColor;
float _RimAmount;
float _RimThreshold;
float4 _OcclusionMap_ST;
float _OcclusionStrength;
float _EnableEmission;
float4 _EmissionColor;
float _EmissionIntensity;
CBUFFER_END
TEXTURE2D(_BaseMap);
SAMPLER(sampler_BaseMap);
TEXTURE2D(_NormalMap);
SAMPLER(sampler_NormalMap);
TEXTURE2D(_OcclusionMap);
SAMPLER(sampler_OcclusionMap);
TEXTURE2D(_EmissionMap);
SAMPLER(sampler_EmissionMap);
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float2 uv : TEXCOORD0;
float4 tangentOS : TANGENT;
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float2 uv : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float3 tangentWS : TEXCOORD2;
float3 bitangentWS : TEXCOORD3;
float3 lightDir : TEXCOORD4;
float3 viewDir : TEXCOORD5;
// 그림자 관련 변수
float4 shadowCoord : TEXCOORD6;
};
Varyings vert(Attributes IN)
{
Varyings OUT = (Varyings)0;
OUT.positionCS = TransformObjectToHClip(IN.positionOS);
OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
OUT.tangentWS = TransformObjectToWorldNormal(IN.tangentOS.xyz);
OUT.bitangentWS = cross(OUT.normalWS, OUT.tangentWS) * IN.tangentOS.w;
OUT.lightDir = normalize(_MainLightPosition.xyz);
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
OUT.viewDir = normalize(_WorldSpaceCameraPos.xyz - positionWS);
OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
VertexPositionInputs positions = GetVertexPositionInputs(IN.positionOS.xyz);
OUT.shadowCoord = GetShadowCoord(positions);
return OUT;
}
float3 GetNormal(Varyings IN)
{
float3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv));
float3x3 TBN = float3x3(IN.tangentWS, IN.bitangentWS, IN.normalWS);
return normalize(mul(normalTS, TBN));
}
float4 frag(Varyings IN) : SV_Target
{
float4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;
// 오클루전 맵 샘플링
float occlusion = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, IN.uv).r;
occlusion = lerp(1.0, occlusion, _OcclusionStrength);
float3 normal = GetNormal(IN);
float3 viewDir = normalize(IN.viewDir);
// 그림자 계산
float shadow = MainLightRealtimeShadow(IN.shadowCoord);
float NdotL = saturate(dot(normal, IN.lightDir));
float lightIntensity = smoothstep(0.0, 0.01, NdotL) * shadow; // 그림자 적용
float4 light = lightIntensity * _MainLightColor * occlusion;
float3 halfVector = normalize(IN.lightDir + viewDir);
float NdotH = saturate(dot(normal, halfVector));
float specularIntensity = pow(NdotH, _Glossiness * _Glossiness);
float specularIntensitySmooth = smoothstep(0.005, 0.01, specularIntensity);
float4 specular = specularIntensitySmooth * _SpecularColor * occlusion;
float rimDot = 1.0 - saturate(dot(viewDir, normal));
float rimIntensity = rimDot * pow(NdotL, _RimThreshold);
rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);
float4 rim = rimIntensity * _RimColor * occlusion;
// Emission Map 적용
float4 emission = float4(0, 0, 0, 0);
if (_EnableEmission > 0.5)
{
float4 emissionMapValue = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, IN.uv);
emission = emissionMapValue * _EmissionColor * _EmissionIntensity;
}
col.xyz *= (light.xyz + _AmbientColor.xyz + specular.xyz + rim.xyz);
col.xyz += emission.xyz; // Emission 추가
return col;
}
ENDHLSL
}
UsePass "Universal Render Pipeline/Lit/ShadowCaster"
}
}
Shader "URP/Unlit/URP_ToonShader" { Properties { [MainTexture] _BaseMap("Texture", 2D) = "white" {} [MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1) [NormalMap] _NormalMap("Normal Map", 2D) = "bump" {} [HDR] _AmbientColor("Ambient Color", Color) = (0.4, 0.4, 0.4, 1) [HDR] _SpecularColor("Specular Color", Color) = (0.9, 0.9, 0.9, 1) _Glossiness("Glossiness", Float) = 32 [HDR] _RimColor("Rim Color", Color) = (1, 1, 1, 1) _RimAmount("Rim Amount", Range(0, 1)) = 0.5 _RimThreshold("Rim Threshold", Range(0, 1)) = 0.1 [OcclusionMap] _OcclusionMap("Occlusion Map", 2D) = "white" {} _OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0 [Toggle] _EnableEmission("Enable Emission", Float) = 0 [MainTexture] _EmissionMap("Emission Map", 2D) = "black" {} [HDR] _EmissionColor("Emission Color", Color) = (1, 1, 1, 1) _EmissionIntensity("Emission Intensity", Float) = 1.0 } SubShader { Tags { "RenderType" = "Oqueue" "Queue" = "Geometry" "RenderPipeline" = "UniversalPipeline" } Pass { Name "URP_ToonShader" Tags { "LightMode" = "UniversalForward" } HLSLPROGRAM #pragma target 4.5 #pragma vertex vert #pragma fragment frag #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl" CBUFFER_START(UnityPerMaterial) float4 _BaseMap_ST; float4 _BaseColor; float4 _AmbientColor; float4 _SpecularColor; float _Glossiness; float4 _RimColor; float _RimAmount; float _RimThreshold; float4 _OcclusionMap_ST; float _OcclusionStrength; float _EnableEmission; float4 _EmissionColor; float _EmissionIntensity; CBUFFER_END TEXTURE2D(_BaseMap); SAMPLER(sampler_BaseMap); TEXTURE2D(_NormalMap); SAMPLER(sampler_NormalMap); TEXTURE2D(_OcclusionMap); SAMPLER(sampler_OcclusionMap); TEXTURE2D(_EmissionMap); SAMPLER(sampler_EmissionMap); struct Attributes { float4 positionOS : POSITION; float3 normalOS : NORMAL; float2 uv : TEXCOORD0; float4 tangentOS : TANGENT; }; struct Varyings { float4 positionCS : SV_POSITION; float2 uv : TEXCOORD0; float3 normalWS : TEXCOORD1; float3 tangentWS : TEXCOORD2; float3 bitangentWS : TEXCOORD3; float3 lightDir : TEXCOORD4; float3 viewDir : TEXCOORD5; // 그림자 관련 변수 float4 shadowCoord : TEXCOORD6; }; Varyings vert(Attributes IN) { Varyings OUT = (Varyings)0; OUT.positionCS = TransformObjectToHClip(IN.positionOS); OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS); OUT.tangentWS = TransformObjectToWorldNormal(IN.tangentOS.xyz); OUT.bitangentWS = cross(OUT.normalWS, OUT.tangentWS) * IN.tangentOS.w; OUT.lightDir = normalize(_MainLightPosition.xyz); float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz); OUT.viewDir = normalize(_WorldSpaceCameraPos.xyz - positionWS); OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap); VertexPositionInputs positions = GetVertexPositionInputs(IN.positionOS.xyz); OUT.shadowCoord = GetShadowCoord(positions); return OUT; } float3 GetNormal(Varyings IN) { float3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv)); float3x3 TBN = float3x3(IN.tangentWS, IN.bitangentWS, IN.normalWS); return normalize(mul(normalTS, TBN)); } float4 frag(Varyings IN) : SV_Target { float4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor; // 오클루전 맵 샘플링 float occlusion = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, IN.uv).r; occlusion = lerp(1.0, occlusion, _OcclusionStrength); float3 normal = GetNormal(IN); float3 viewDir = normalize(IN.viewDir); // 그림자 계산 float shadow = MainLightRealtimeShadow(IN.shadowCoord); float NdotL = saturate(dot(normal, IN.lightDir)); float lightIntensity = smoothstep(0.0, 0.01, NdotL) * shadow; // 그림자 적용 float4 light = lightIntensity * _MainLightColor * occlusion; float3 halfVector = normalize(IN.lightDir + viewDir); float NdotH = saturate(dot(normal, halfVector)); float specularIntensity = pow(NdotH, _Glossiness * _Glossiness); float specularIntensitySmooth = smoothstep(0.005, 0.01, specularIntensity); float4 specular = specularIntensitySmooth * _SpecularColor * occlusion; float rimDot = 1.0 - saturate(dot(viewDir, normal)); float rimIntensity = rimDot * pow(NdotL, _RimThreshold); rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity); float4 rim = rimIntensity * _RimColor * occlusion; // Emission Map 적용 float4 emission = float4(0, 0, 0, 0); if (_EnableEmission > 0.5) { float4 emissionMapValue = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, IN.uv); emission = emissionMapValue * _EmissionColor * _EmissionIntensity; } col.xyz *= (light.xyz + _AmbientColor.xyz + specular.xyz + rim.xyz); col.xyz += emission.xyz; // Emission 추가 return col; } ENDHLSL } UsePass "Universal Render Pipeline/Lit/ShadowCaster" } }
Shader "URP/Unlit/URP_ToonShader"
{
    Properties
    {
        [MainTexture] _BaseMap("Texture", 2D) = "white" {}
        [MainColor] _BaseColor("Color", Color) = (1, 1, 1, 1)
        [NormalMap] _NormalMap("Normal Map", 2D) = "bump" {}
        [HDR] _AmbientColor("Ambient Color", Color) = (0.4, 0.4, 0.4, 1)
        [HDR] _SpecularColor("Specular Color", Color) = (0.9, 0.9, 0.9, 1)
        _Glossiness("Glossiness", Float) = 32
        [HDR] _RimColor("Rim Color", Color) = (1, 1, 1, 1)
        _RimAmount("Rim Amount", Range(0, 1)) = 0.5
        _RimThreshold("Rim Threshold", Range(0, 1)) = 0.1
        [OcclusionMap] _OcclusionMap("Occlusion Map", 2D) = "white" {}
        _OcclusionStrength("Occlusion Strength", Range(0, 1)) = 1.0

        [Toggle] _EnableEmission("Enable Emission", Float) = 0
        [MainTexture] _EmissionMap("Emission Map", 2D) = "black" {}
        [HDR] _EmissionColor("Emission Color", Color) = (1, 1, 1, 1)
        _EmissionIntensity("Emission Intensity", Float) = 1.0
    }

    SubShader
    {
        Tags { "RenderType" = "Oqueue" "Queue" = "Geometry" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            Name "URP_ToonShader"
            Tags { "LightMode" = "UniversalForward" }

            HLSLPROGRAM
            #pragma target 4.5
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE _MAIN_LIGHT_SHADOWS_SCREEN

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseColor;
                float4 _AmbientColor;
                float4 _SpecularColor;
                float _Glossiness;
                float4 _RimColor;
                float _RimAmount;
                float _RimThreshold;
                float4 _OcclusionMap_ST;
                float _OcclusionStrength;
                float _EnableEmission;
                float4 _EmissionColor;
                float _EmissionIntensity;
            CBUFFER_END

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);
            TEXTURE2D(_NormalMap);
            SAMPLER(sampler_NormalMap);
            TEXTURE2D(_OcclusionMap);
            SAMPLER(sampler_OcclusionMap);
            TEXTURE2D(_EmissionMap);
            SAMPLER(sampler_EmissionMap);

            struct Attributes
            {
                float4 positionOS : POSITION;
                float3 normalOS : NORMAL;
                float2 uv : TEXCOORD0;
                float4 tangentOS : TANGENT;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
                float2 uv : TEXCOORD0;
                float3 normalWS : TEXCOORD1;
                float3 tangentWS : TEXCOORD2;
                float3 bitangentWS : TEXCOORD3;
                float3 lightDir : TEXCOORD4;
                float3 viewDir : TEXCOORD5;

                // 그림자 관련 변수
                float4 shadowCoord : TEXCOORD6;
            };

            Varyings vert(Attributes IN)
            {
                Varyings OUT = (Varyings)0;
                OUT.positionCS = TransformObjectToHClip(IN.positionOS);
                OUT.normalWS = TransformObjectToWorldNormal(IN.normalOS);
                OUT.tangentWS = TransformObjectToWorldNormal(IN.tangentOS.xyz);
                OUT.bitangentWS = cross(OUT.normalWS, OUT.tangentWS) * IN.tangentOS.w;
                OUT.lightDir = normalize(_MainLightPosition.xyz);
                float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
                OUT.viewDir = normalize(_WorldSpaceCameraPos.xyz - positionWS);
                OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);

                VertexPositionInputs positions = GetVertexPositionInputs(IN.positionOS.xyz);
                OUT.shadowCoord = GetShadowCoord(positions);
                return OUT;
            }

            float3 GetNormal(Varyings IN)
            {
                float3 normalTS = UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, IN.uv));
                float3x3 TBN = float3x3(IN.tangentWS, IN.bitangentWS, IN.normalWS);
                return normalize(mul(normalTS, TBN));
            }

            float4 frag(Varyings IN) : SV_Target
            {
                float4 col = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv) * _BaseColor;

                // 오클루전 맵 샘플링
                float occlusion = SAMPLE_TEXTURE2D(_OcclusionMap, sampler_OcclusionMap, IN.uv).r;
                occlusion = lerp(1.0, occlusion, _OcclusionStrength);

                float3 normal = GetNormal(IN);
                float3 viewDir = normalize(IN.viewDir);

                // 그림자 계산
                float shadow = MainLightRealtimeShadow(IN.shadowCoord);

                float NdotL = saturate(dot(normal, IN.lightDir));
                float lightIntensity = smoothstep(0.0, 0.01, NdotL) * shadow; // 그림자 적용
                float4 light = lightIntensity * _MainLightColor * occlusion;

                float3 halfVector = normalize(IN.lightDir + viewDir);
                float NdotH = saturate(dot(normal, halfVector));
                float specularIntensity = pow(NdotH, _Glossiness * _Glossiness);
                float specularIntensitySmooth = smoothstep(0.005, 0.01, specularIntensity);
                float4 specular = specularIntensitySmooth * _SpecularColor * occlusion;

                float rimDot = 1.0 - saturate(dot(viewDir, normal));
                float rimIntensity = rimDot * pow(NdotL, _RimThreshold);
                rimIntensity = smoothstep(_RimAmount - 0.01, _RimAmount + 0.01, rimIntensity);
                float4 rim = rimIntensity * _RimColor * occlusion;

                // Emission Map 적용
                float4 emission = float4(0, 0, 0, 0);
                if (_EnableEmission > 0.5)
                {
                    float4 emissionMapValue = SAMPLE_TEXTURE2D(_EmissionMap, sampler_EmissionMap, IN.uv);
                    emission = emissionMapValue * _EmissionColor * _EmissionIntensity;
                }

                col.xyz *= (light.xyz + _AmbientColor.xyz + specular.xyz + rim.xyz);
                col.xyz += emission.xyz; // Emission 추가
                return col;
            }

            ENDHLSL
        }

        UsePass "Universal Render Pipeline/Lit/ShadowCaster"
    }
}

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다