🦔
URPのシェーダー入門
ベースとなるUnlitShaderを用意
UnlitShader
Shader "Custom/Unlit"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
CBUFFER_END
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
half4 baseColor = tex2D(_MainTex, i.uv);
return baseColor;
}
ENDHLSL
}
}
}
NormalMapを適用した版
NormalShader
Shader "Custom/Normal"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
[Normal] _NormalTex ("Normal map", 2D) = "bump" {}
_Specular ("Specular", Range(1, 100)) = 0
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
half3 normal : NORMAL;
half4 tangent : TANGENT;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
half3 normal : TEXCOORD1; // 法線
half3 tangent : TEXCOORD2; // 接線
half3 binormal : TEXCOORD3; // 従法線
float4 worldPos : TEXCOORD4;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
sampler2D _NormalTex;
float _Specular;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
CBUFFER_END
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
o.normal = TransformObjectToWorldNormal(i.normal);
o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
// 法線と接線から従法線を計算
o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
o.worldPos = i.pos;
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
float3 lightDir = normalize(_MainLightPosition.xyz);
half3 lightColor = _MainLightColor.xyz;
half3 normalmap = UnpackNormal(tex2D(_NormalTex, i.uv));
// 法線を合成
float3 normal = (i.tangent * normalmap.x) + (i.binormal * normalmap.y) + (i.normal * normalmap.z);
float t = dot(normal, lightDir);
t = max(0, t); // t >= 0にする
// 内積が0に近いほど黒く
float3 diffuseLight = lightColor * t;
// ライト方向と法線ベクトルから反射ベクトルを計算
float3 reflectVec = reflect(-lightDir, normal);
float3 eyeVec = normalize(GetCameraPositionWS() - i.worldPos); // 視線ベクトル
t = dot(reflectVec, eyeVec);
t = max(0, t); // t >= 0にする
t = pow(t, _Specular);
float3 specularLight = lightColor * t;
float4 color = tex2D(_MainTex, i.uv);
color.xyz *= (specularLight + diffuseLight);
return color;
}
ENDHLSL
}
}
}
NormalMap適用した版で影を表示する
NormalShader
Shader "Custom/Normal"
{
Properties
{
[MainTexture] _MainTex ("Texture", 2D) = "white" {}
[Normal] _NormalTex ("Normal map", 2D) = "bump" {}
_Specular ("Specular", Range(1, 100)) = 0
}
SubShader
{
Tags
{
"RenderPipeline"="UniversalPipeline"
"RenderType"="Opaque"
}
Pass
{
Name "Universal Forward"
Tags
{
"LightMode" = "UniversalForward"
}
Cull Back
ZTest LEqual
ZWrite On
HLSLPROGRAM
#pragma target 4.5
#pragma vertex vert
#pragma fragment frag
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile_instancing
#pragma multi_compile _ DOTS_INSTANCING_ON
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Input.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct appdata
{
float4 pos: POSITION;
float2 uv : TEXCOORD0;
half3 normal : NORMAL;
half4 tangent : TANGENT;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
half3 normal : TEXCOORD1; // 法線
half3 tangent : TEXCOORD2; // 接線
half3 binormal : TEXCOORD3; // 従法線
float4 worldPos : TEXCOORD4;
float4 shadowCoord : TEXCOORD5;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
sampler2D _MainTex;
sampler2D _NormalTex;
float _Specular;
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
CBUFFER_END
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
o.normal = TransformObjectToWorldNormal(i.normal);
o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
// 法線と接線から従法線を計算
o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
o.worldPos = i.pos;
o.shadowCoord = GetShadowCoord(vInput);
return o;
}
half4 frag(v2f i) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(i);
float3 lightDir = normalize(_MainLightPosition.xyz);
half3 lightColor = _MainLightColor.xyz;
half3 normalmap = UnpackNormal(tex2D(_NormalTex, i.uv));
// 法線を合成
float3 normal = (i.tangent * normalmap.x) + (i.binormal * normalmap.y) + (i.normal * normalmap.z);
float t = dot(normal, lightDir);
t = max(0, t); // t >= 0にする
// 内積が0に近いほど黒く
float3 diffuseLight = lightColor * t;
// ライト方向と法線ベクトルから反射ベクトルを計算
float3 reflectVec = reflect(-lightDir, normal);
float3 eyeVec = normalize(GetCameraPositionWS() - i.worldPos); // 視線ベクトル
t = dot(reflectVec, eyeVec);
t = max(0, t); // t >= 0にする
t = pow(t, _Specular);
float3 specularLight = lightColor * t;
Light light = GetMainLight(i.shadowCoord);
half shadow = light.shadowAttenuation * light.distanceAttenuation;
float4 color = tex2D(_MainTex, i.uv) * half4(shadow, shadow, shadow, 1);
color.xyz *= (specularLight + diffuseLight);
return color;
}
ENDHLSL
}
UsePass "Universal Render Pipeline/Lit/ShadowCaster"
}
}
ライトの影響を受けるために以下の機能を追加
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS_CASCADE
fragmentシェーダーにシャドウマップの情報を渡す
struct v2f
{
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
half3 normal : TEXCOORD1; // 法線
half3 tangent : TEXCOORD2; // 接線
half3 binormal : TEXCOORD3; // 従法線
float4 worldPos : TEXCOORD4;
float4 shadowCoord : TEXCOORD5;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
v2f vert(appdata i)
{
v2f o;
UNITY_SETUP_INSTANCE_ID(i);
UNITY_TRANSFER_INSTANCE_ID(i, o);
VertexPositionInputs vInput = GetVertexPositionInputs(i.pos.xyz);
o.pos = vInput.positionCS;
o.uv = TRANSFORM_TEX(i.uv, _MainTex);
o.normal = TransformObjectToWorldNormal(i.normal);
o.tangent = normalize(mul(unity_ObjectToWorld, i.tangent)).xyz;
// 法線と接線から従法線を計算
o.binormal = cross(i.normal, i.tangent) * i.tangent.w;
o.binormal = normalize(mul(unity_ObjectToWorld, o.binormal));
o.worldPos = i.pos;
o.shadowCoord = GetShadowCoord(vInput);
return o;
}
GetShadowCoord()
を使用するためにLighting.hlsl
をインクルード
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
ShadowCaster
のパスを追加
UsePass "Universal Render Pipeline/Lit/ShadowCaster"
Discussion