Study log on Chayan Vinayak Goswami's ShaderDev
- https://shaderdev.com/
- Chayan Vinayak Goswami
- TA 8 years
์์ด๋๋ ํ๋ฉด์ ์ถ๋ ฅํ ํฝ์
์ ์์น์ ์์์ ๊ณ์ฐํ๋ ํจ์
์์ด๋(shader)๋ '์์ ๋๋ด, ์์กฐ, ๋ช
์ ํจ๊ณผ๋ฅผ ์ฃผ๋ค.'๋ผ๋ ๋ป์ ๊ฐ์ง shade๋ ๋์ฌ์ ํ๋์ ์ฃผ์ฒด๋ฅผ ๋ํ๋ด๋ ์ ๋ฏธ์ฌ '-er'์ ํผํฉํ ๋จ์ด์
๋๋ค.
์ฆ, ์์ ๋๋ด, ์์กฐ, ๋ช
์ ๋ฑ์ ํจ๊ณผ๋ฅผ ์ฃผ๋ ์ฃผ์ฒด๊ฐ ์์ด๋๋ ๋ป
- https://kblog.popekim.com/2011/11/01-part-1.html
์ฝ์ด๊ฐฏ์ | ์ฐ์ฐ | |
---|---|---|
CPU | ๋ช๊ฐ | serial operation |
GPU | ์์ฒ๊ฐ | parallel operation |
๊ฐ๋ ฅํ ๋ง์ดํฌ๋ก ํ๋ก์ธ์๋ฅผ ๋ช๊ฐ ๋๋ ํฐ ํ์ดํ๋ฅผ ์ฐ๋ ๋์ ,
๋งค์ฐ ์์ ๋ง์ดํฌ๋ก ํ๋ก์ธ์๋ค์ ํ๋ฒ์ ๋๋ฆฌ๋ ๊ฒ์ด๋ค. ๊ทธ๊ฒ์ด ๋ฐ๋ก GPU(Graphic Processor Unit).
- https://thebookofshaders.com/01/?lan=kr
์์ด๋ | ๊ธฐ๋ฅ |
---|---|
Vertex | |
Geometry | input primitive |
Fragment / Pixel | |
Compute | ๋ ๋๋ง ํ์ดํ๋ผ์ธ์ ์ํด ์์ง ์์. GPU ๋ณ๋ ฌ ์ฒ๋ฆฌ ๋ชฉ์ |
Tessellation / Hull | OpenGL 4, DirectX3D 11, Metal |
- Vertex Shader Input
- Position(Local/Object Space)
- Normal
- Color
- ...
- Vertex Shader Output
- Position(Clip Space)
- other infos
- geometry์ ์ด๋ค Sample๋ค์ด ํ๋ฉด์ ๊ทธ๋ ค์ง๋์ง ๊ฒฐ์ ํ๊ณ (sampling)
- Vertex-Output์ ์ด์ฉํ์ฌ ๋ฐ์ดํฐ(Fragment)๋ค์ interpolateํ์ฌ Fragment Shader๋ก ๋๊น.
Fragment๋ฅผ ์ด์ฉํ์ฌ, ๊ฐ ํฝ์ ์ ๊ฐ(์)์ ๊ตฌํ๋ค.
Shader "ShaderName"
{
Properties
{
}
Sub-Shader
{
Tags
{
}
Pass
{
hardware features(support graphics api(metal / gles / xbox360))
[#pragma onlyrenderer metal]
occlusion pass
lighting pass
beauty(diffuse, color) pass
Vertext-Shader-Input
Fragment-Shader-Input
Vertex-Shader
Fragment-Shader
}
}
Fallback
}
Shader "ShaderDevURP/BareBone"
{
Properties
{
_Color("Main Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags
{
"RenderType" = "Opaque"
"RenderPipeline" = "UniversalPipeline"
}
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
half4 _Color;
CBUFFER_END
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
};
Varyings vert(Attributes IN)
{
Varyings OUT = (Varyings)0;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
return OUT;
}
half4 frag() : SV_Target
{
return _Color;
}
ENDHLSL
}
}
}
Coordinate System | ์ขํ๊ณ | ์ขํ๋ฅผ ์ด์ฉํ์ฌ, ํน์ ์์์ ์์น๋ฅผ ์ ์. |
Coordinate Space | ์ขํ๊ณต๊ฐ | ํ์ ์ ์์น๊ฐ ๋ค๋ฅธ ์ ๊ณผ์ ๊ด๊ณ๋ก ๊ท์ . |
-
Vector4 ์ด์
(x, y, z, w) w == 1 (x,y,z,1) position in space w == 0 (x,y,z,0) direction -
Matrix 4x4 ์ด์
์ ํ๋ณํ์ผ๋ก ํ๋/์ถ์, ํ์ , ์ฐ๊ทธ๋ฌ๋จ๋ฆฌ๊ธฐ(skewing)์ ์กฐํฉ์ ํํํ ์ ์๋ค๊ณ ๋ฐฐ์ ์ฃ . ์ ํ๋ณํ๋ง์ผ๋ก๋ ํํ์ด๋์ ๋ง๋ค์ด๋ผ ์ ์๋ค๋ ๊ฒ์ด ๋ฌธ์ ์ ๋๋ค ... ์ ํ๋ณํ์ ์์ํญ์ด ์ฒจ๊ฐ๋ ๋ณํ์ affine ๋ณํ์ด๋ผ๊ณ ... - https://gpgstudy.com/forum/viewtopic.php?t=25011
-
์) ์ด๋ ๋งคํธ๋ฆญ์ค x ๋ฐฉํฅ๋ฒกํฐ
-
์) ์ด๋ ๋งคํธ๋ฆญ์ค x ์์น๋ฐฑํฐ
-
Z-Sorting -> Render Queue -> Painter's algorithm(์นด๋ฉ๋ผ์ ๊ฑฐ๋ฆฌ ๊ธฐ๋ฐ)
-
Render Queue
Tags { "Queue" = "Geometry-1" }
min | default | max | |
---|---|---|---|
0 | 100 | 1499 | Background |
1500 | 2000 | 2399 | Geometry |
2400 | 2450 | 2699 | AlphaTest |
2700 | 3000 | 3599 | Transparent |
3600 | 4000 | 5000 | Overlay |
0 | Rendered First | Back |
5000 | Rendered Last | Front |
-
Tags๋
,
๋ก ๊ตฌ๋ถํ์ง ์๋๋ค.(๊ณต๋ฐฑ์ผ๋ก ๊ตฌ๋ถ.)Tags { ___ = ___ ___ = ___ }
"IgnoreProjector" = "True" "IgnoreProjector" = "False"
-
์์ด๋ ๋ณ๊ฒฝ์ ์ฐ์ด๋ ํค ์ง์ .
-
์ผ๋ฐ์ ์ผ๋ก RenderType๋ Queue์ ์ด๋ฆ๊ณผ ๋์ผํ๊ฒ ์ค์
"Queue" = "Transparent" "RenderType" = "Transparent"
// Opaque๋ฅผ "X-rayShader"๋ก ๋ฐ๊พธ์ด๋ผ. Camera.main.SetReplacement("X-rayShader", "Opaque")
Blend(srcFactor, blendOp, dstFactor)
srcFactor: ์์
๋์ [0 ~ 1]
dstFactor: ์ปฌ๋ฌ๋ฒํผ์ ์๋ ๊ฐ๋ค [0 ~ 1]
blendOp: Add(default), Sub, RevSub, Min, Max ...
Merged Pixel = blendOp((srcColor * srcFactor), (dstColor * dstFactor))
// Texture ์์ฑ
// Wrap Mode - Clamp / Repeat
Properties
{
_MainTex("Main Texture", "2D") = "white" {}
}
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
float4 texcoord : TEXCOORD0;
_MainTex_ST.xy; // Tiling
_MainTex_ST.zw; // Offset
float4 color = tex2D(_MainTex, texcoord);
// UnityCG.cginc
// Transforms 2D UV by scale/bias property
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
out.texcoord.xy = in.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
out.texcoord.xy = TRANSFORM_TEX(in.texcoord, _MainTex);
Blend SrcAlpha OneMinusSrcAlpha
TEXTURE2D(_MainTex); SAMPLER(sampler_MainTex);
CBUFFER_START(UnityPerMaterial)
float4 _MainTex_ST;
CBUFFER_END
struct Attributes
{
float4 uv : TEXCOORD0;
};
struct Varyings
{
float4 uv : TEXCOORD0;
};
Varyings vert(Attributes IN)
{
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex);
}
half4 frag() : SV_Target
{
float4 tex = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, IN.uv);
}
// com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl
#define TEXTURE2D(textureName) Texture2D textureName
#define SAMPLER(samplerName) SamplerState samplerName
#define SAMPLE_TEXTURE2D(textureName, samplerName, coord2) textureName.Sample(samplerName, coord2)
// com.unity.render-pipelines.core/ShaderLibrary/Macros.hlsl
#define TRANSFORM_TEX(tex, name) ((tex.xy) * name##_ST.xy + name##_ST.zw)
Direct X
UV ์ขํ๊ณ๊ฐ ํ
์ค์ฒ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์๊ณผ ์ผ์นํ๋ค๋ ์ฅ์
๋ฐ๋ผ์ UV์ขํ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ํ
์ค์ฒ ์กฐ์(texture manipulation)์ ํ๋ฉด ๋งค์ฐ ์ง๊ด์ ์ด๊ณ ์ฌ์
- https://blog.popekim.com/ko/2012/06/11/unity-i-love-you-but-your-uv-is-horrible.html
(0,0) (1,0)
+-----+-----+
| | |
| | |
+-----+-----+
| | |
| | |
+-----+-----+
(0,1) (1,1)
OpenGL / UnityEngine
(0,1) (1,1)
+-----+-----+
| | |
| | |
+-----+-----+
| | |
| | |
+-----+-----+
(0,0) (1,0)
Quad, Plane์ UV๋งตํ์ด ๋ค๋ฅด๋ค. Quad๋ ์ขํ๋จ. Plane์ ์ฐํ๋จ
- Mac: Grapher
- Online: https://www.desmos.com/calculator
- sqrt / sin / cos / tan ๊ทธ๋ํ
half DrawLine(half2 uv, half start, half end)
{
if (start < uv.x && uv.x < end)
{
return 1;
}
return 0;
}
half DrawCircle(half2 uv, half2 cp, half r)
{
// cp : center position
// r : radius
half x2y2 = pow((uv.x - cp.x), 2) + pow((uv.y - cp.y), 2);
half r2 = pow(r, 2);
if (x2y2 > r2)
{
return 0;
}
return 1;
}
float smoothstep(float a, float b, float x)
{
float t = saturate((x - a)/(b - a));
return t * t * (3.0 - (2.0 * t));
}
smoothstep(1, 0.5, uv.x);
+----------+----------+----------+----------+
1(from) 0.75 0.5(to) 0.25 0
from | to | ||
---|---|---|---|
1 | 0.5 | texcoord.x | return |
1 | 0.5 | 0 | 1 |
1 | 0.5 | 0.25 | 1 |
1 | 0.5 | 0.5 | 1 |
1 | 0.5 | 0.75 | 0.5 |
1 | 0.5 | 1 | 0 |
float drawCircleFade(float2 uv, float2 cp, float r, float feather)
{
float x2y2 = pow((uv.x - cp.x), 2) + pow((uv.y - cp.y), 2);
float r2 = pow(r, 2);
if (x2y2 > r2)
{
return 0;
}
return smoothstep(r2, r2 - feather, x2y2);
}
sin / cos ํจ์์ _Time๋ณ์๋ฅผ ์ด์ฉํ ์๋์๋ฏธ์ .
Name | Type | Value |
---|---|---|
_Time | float4 | Time since level load (t/20, t, t * 2, t * 3) , use to animate things inside the shaders. |
_SinTime | float4 | Sine of time: (t/8, t/4, t/2, t). |
_CosTime | float4 | Cosine of time: (t/8, t/4, t/2, t). |
unity_DeltaTime | float4 | Delta time: (dt, 1/dt, smoothDt, 1/smoothDt). |
- ๊น๋ฐ์ vertex position์ sin์ผ๋ก ํ๋ค๊ณ ์ค์ฌ์ ์์น ๋ณด์ .
half3 VertexFlagAnim(half3 positionOS, half2 uv)
{
positionOS.z += sin((uv.x - (_Time.y * _FlagSpeed)) * _FlagFrequency) * (uv.x * _FlagAmplitude);
return positionOS;
}
- vertex normal
- face normal
๊ตฌํ๊ณ ์ ํ๋ vertex๋ฅผ ํฌํจํ๊ณ ์๋ face normal์ ๋ชจ๋ ๊ตฌํ๊ณ normalize๋ฅผ ํ๋ฉด vertex normal์ ์ป์ ์ ์๋ค.
half3 VertexAnimNormal(half3 positionOS, half3 normalOS, half2 uv)
{
positionOS += sin((normalOS - (_Time.y * _Speed)) * _Frequency) * (normalOS * _Amplitude);
return positionOS;
}
- Material
- Vertex shader
- Pixel shader
- texture
- Lighting setting
RenderState์ ๋ณํ.
์)
[RenderStateX [Draw A] [Draw B] [Draw C]]
DrawCall : 3๋ฒ (A, B, C)
RenderState : RenderStateX
Batches : 1๋ฒ (A, B, C๊ฐ ๋์ผํ RenderState)
Saved by batching : 2. (A๋ค์์ ์ค๋ B, C๋ฅผ ๊ทธ๋ฆด ๋์ RenderState๋ณํ๊ฐ ์์.)
ZTest
-> Pass <Z-Buffer>
-> ๋ธ๋๋ฉ
-> [์ ํ์ ]์คํ
์ค ํ
์คํธ
-> Pass
-> [์ ํ์ ] Color Mask
-> Final Color
-> <Color Buffer>
ZWrite | [On]/Off | Z-Buffer ๊ฐ์ ์์ฑํ ์ง ์ํ ์ง. Off์ Z-Buffer ๋ณ๊ฒฝ์ํจ. On์ ZTestํต๊ณผ์ Z-Buffer๊ฐ์ ํ์ฌ ZTest๊ฐ์ผ๋ก ์ค์ (๋ถํฌ๋ช ์ค๋ธ์ ํธ์์ ๋ง์ด ์ฐ์) |
Cull | [Back] / Front / Off | ํด๋น ๋ฉด์ ๋ ๋๋ง ํ์ง ์์ |
ZTest | [(<=)LEqual]/Less/NotEqual/Always ... | if ( Z-Buffer xop Z-Depth ) { Zwrite } |
- forward/backward face๊ธฐ๋ฐ(+z, -z).
- Normal์ xyz๋ฅผ rgb๋ก Texture์ ์ ์ฅ.
- ๋ฐ๋ผ์ x, y, z๊ฐ 0~1์ฌ์ด์ ๋ชจ๋ ๋ฐฉํฅ์ผ๋ก ์ ์ ํ๊ฒ ๋ถ๋ฐฐ๋์ด ์๋ก๋ฌ๋กํ๊ฒ ๋ณด์.
- forward face๊ธฐ๋ฐ.
- Tangent Vector๋ Normal Vector์ ์์ง์ธ ๋ฒกํฐ์ด๋ค(์ฌ๋ฌ๊ฐ...). ๋ฐ๋ผ์ ํต์์ ์ผ๋ก UV ์ขํ์ ๋น๊ตํ์ฌ
- Tangent Vector: U ์ขํ์ ์ผ์นํ๋ Vector
- BiTangent Vector: V ์ขํ์ ์ผ์นํ๋ Vector
TBN-matrix
TBN = | Tx Ty Tz | // Tangent | U
| Bx By Bz | // Binormal | V
| Nx Ny Nz | // Normal
Software | Red | Green | Blue |
---|---|---|---|
Unity |
X+ | Y+ | Z+ |
Maya | X+ | Y+ | Z+ |
Blender | X+ | Y+ | Z+ |
Unreal |
X+ | Y- | Z+ |
3ds Max | X+ | Y- | Z+ |
- Right handedness, which coincides with OpenGL is indicated with a plus sign (ex. +Y)
- Left handedness, which coincides with DirectX, is indicated with a negative sign (ex. -Y)
- ๋ด์ ๊ณผ ์ธ์ ๊ณต์.
- ๋ด์ ๊ณผ ์ธ์ ์ ์๊ฐ์ ์ผ๋ก ์๊ฐํ ์ ์์ด์ผ ํจ.
- ์ด๊ฑฐ ์ด๋ฆ ํ๊ฐ๋ฆฌ๊ธฐ ์ฌ์.
- ๋ท์ ์ ์ด๋๊น ๋ชจ์ด๋๊ฑด ๋ด์
- ์ ์ด๋๊น ๋๊ฐ ๋ชจ์์ ํ๋๊ฐ ๋จ.
- ํ๋๋ก ๋ชจ์ด๋ ๋ ๋ฒกํฐ ์ฌ์ด์ ๊ฐ๋๋ฅผ ๊ตฌํ ์ ์์.
- ๊ฐ๋๋๊น cos์ฐ์ฐ ๋ค์ด๊ฐ.
- https://rfriend.tistory.com/145
- ๊ตํ๋ฒ์น์ด ์ฑ๋ฆฝ
| ๊ฐ๋ | ๊ฐ |
| ---- | --- |
| 0 | 1 |
| 90 | 0 |
| 180 | -1 |
| -270 | 0 |
1
|
|
0-------+------ 0
|
|
-1
- ํฌ๋ก์ค๋ ์์ฃฝํ๋๊น ์ธ์ ์ผ๋ก ์ธ์ธ๊ป.
- X ๋๊น ์์ ๋์ด.
- X๊ฐ ์ง๊ฐ์ด๋ ์์ง ๊ตฌํ ๋ ์.
- https://rfriend.tistory.com/146
- ๊ตํ๋ฒ์น ์ฑ๋ฆฝ์ํจ
TBN : (Tangent Binormal Normal)
VS
obj_TBN = float3(Input.T, cross(Input.N, Input.T), Input.N); // object to tangent TBN
PS
obj_N = mul(inverse(obj_TBN), tangent_N);
= mul(transpose(obj_TBN), tangent_N);
= mul(tangent_N, obj_TBN);
world_N = mul(obj_N, unity_World2Object);
world TBN๊ธฐ๋ฐ์ผ๋ก world Normal์ ์ป๊ธฐ์ํด PS์์ ํ๋ฒ๋ง ๊ณฑํ๋ฉด ๋๋ค.
VS
world_T = mul(unity_Object2World, Input.T); // ํ๋ฉด์ ๋ถ์ด์์ผ๋ฏ๋ก shifting๊ณผ ๋ฌด๊ด
world_N = mul(Input.N , unity_World2Object); // shifting ๋ฐฉ์ง.
world_B = cross(world_N, world_T);
world_TBN = float3(world_T, world_B, world_N); // world to tangent TBN.
PS
world_N = mul(tangent_N, world_TBN);
M == unity_Object2World;
M_want == transpose( inverse(unity_Object2World) )
== transpose( unity_World2Object )
mul(matrix, vector) == mul(vector, transpose(matrix))
mul(M_want, v) == mul( transpose( unity_World2Object ), V )
== mul( V, unity_World2Object )
inline void ExtractTBN(in half3 normalOS, in float4 tangent, inout half3 T, inout half3 B, inout half3 N)
{
N = TransformObjectToWorldNormal(normalOS);
T = TransformObjectToWorldDir(tangent.xyz);
B = cross(N, T) * tangent.w * unity_WorldTransformParams.w;
}
inline half3 CombineTBN(in half3 tangentNormal, in half3 T, in half3 B, in half3 N)
{
return mul(tangentNormal, float3x3(normalize(T), normalize(B), normalize(N)));
}
float3x3 tangentToWorld = CreateTangentToWorld(unnormalizedNormalWS, tangentWS.xyz, tangentWS.w > 0.0 ? 1.0 : -1.0);
// com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl
real3x3 CreateTangentToWorld(real3 normal, real3 tangent, real flipSign)
{
// For odd-negative scale transforms we need to flip the sign
real sgn = flipSign * GetOddNegativeScale();
real3 bitangent = cross(normal, tangent) * sgn;
return real3x3(tangent, bitangent, normal);
}
float3 TransformObjectToWorldNormal(float3 normalOS, bool doNormalize = true)
{
#ifdef UNITY_ASSUME_UNIFORM_SCALING
return TransformObjectToWorldDir(normalOS, doNormalize);
#else
// Normal need to be multiply by inverse transpose
float3 normalWS = mul(normalOS, (float3x3)GetWorldToObjectMatrix());
if (doNormalize)
return SafeNormalize(normalWS);
return normalWS;
#endif
}
float3 TransformObjectToWorldDir(float3 dirOS, bool doNormalize = true)
{
#ifndef SHADER_STAGE_RAY_TRACING
float3 dirWS = mul((float3x3)GetObjectToWorldMatrix(), dirOS);
#else
float3 dirWS = mul((float3x3)ObjectToWorld3x4(), dirOS);
#endif
if (doNormalize)
return SafeNormalize(dirWS);
return dirWS;
}
- ์ ๋ํฐ์ rgb ์ ๋ ฅ ๋ฒ์๋ [0 ~ 1]
- ์ ๋ํฐ์ ๋ ธ๋ฉ์ ๋ฒ์๋ [-1 ~ 1]
- n๋ฐ๋ผ์ rgb์์ ๋ ธ๋ฉ์ ๊ตฌํ ๋ ค๋ฉด ๋ฒ์๋ฅผ 2๋ฐฐ๋ก ๋๋ฆฌ๊ณ , -1๋งํผ ์ด๋์์ผ์ค์ผํจ.
- (color channel * 2) - 1
PS
tangent_N = tex2D(_N_Texture, Input.mUV).rgb;
tangent_N = (tangent_N * 2) - 1;
world_N = mul(tangent_N, world_TBN);
- S3 Texture Compression
- DXT Compression
- 4x4 ํฝ์ ์ค์, ์ 2๊ฐ๋ฅผ ๊ณ ๋ฆ. 2๊ฐ์ ์์ interpolation์์ผ์ 4x4 color ์ธ๋ฑ์ค๋ฅผ ๋ง๋ฌ.
- ์์ค์์ถ.
V | color | channel | bit |
---|---|---|---|
X | R | color0 | 16 |
Y | G | color1 | 16 |
Z | B | x | 0 |
XYZ๊ฐ normalize ๋์๋ค๋ฉด, X์ Y๋ง ์์๋ Z๋ฅผ ๊ตฌํ ์ ์๋ค.
1 = (X * X) + (Y * Y) + (Z * Z)
Z = sqrt(1 - ((X * X) + (Y * Y)))
// com.unity.render-pipelines.core/ShaderLibrary/Packing.hlsl
real3 UnpackNormal(real4 packedNormal)
{
#if defined(UNITY_ASTC_NORMALMAP_ENCODING)
return UnpackNormalAG(packedNormal, 1.0);
#elif defined(UNITY_NO_DXT5nm)
return UnpackNormalRGBNoScale(packedNormal);
#else
// Compiler will optimize the scale away
return UnpackNormalmapRGorAG(packedNormal, 1.0);
#endif
}
// Unpack normal as DXT5nm (1, y, 0, x) or BC5 (x, y, 0, 1)
real3 UnpackNormalmapRGorAG(real4 packedNormal, real scale = 1.0)
{
// Convert to (?, y, 0, x)
packedNormal.a *= packedNormal.r;
return UnpackNormalAG(packedNormal, scale);
}
real3 UnpackNormalAG(real4 packedNormal, real scale = 1.0)
{
real3 normal;
normal.xy = packedNormal.ag * 2.0 - 1.0;
normal.z = max(1.0e-16, sqrt(1.0 - saturate(dot(normal.xy, normal.xy))));
normal.xy *= scale;
return normal;
}
uniform sampler2D _MainTex;
uniform float4 _MainTex_ST;
PS
Output.T = normalize(mul(Input.T, unity_ObjectToWorld));
Output.N = normalize(mul(Input.N, unity_WorldToObject));
Output.B = normalize(cross(Output.N, Output.T) * Input.T.w);
VS
// ์ผ๋ฐ ํ์ ํธ๋งต ๋ฒ์ .
float3 colorT = tex2D(_Tangent_Map, Input.mUV).rgb;
float3 tangent_N = colorT * 2 - 1;
// DXT5nm ๋ฒ์ .
float3 colorT = tex2D(_Tangent_Map_DXT5nm, Input.mUV).rgb;
float3 tangent_N = float(colorT.a * 2 - 1, colorT.g * 2 - 1, 0);
tangent_N.z = sqrt(1 - dot(tangent_N.xy, tangent_N.xy));
float3x3 world_TBN = float3x3(Input.T, Input.B, Input.N); // world to tangent TBN
float3 world_N = mul(inverse(world_TBN), tangent_N);
= mul(transpose(world_TBN), tangent_N);
= mul(tangent_N, world_TBN);
UV
,ST
๋๋์ฒด ๋ญ์ผ.- 3d ์ขํ๊ณ์์ xyzw ์ทจํจ. uv๋จ์. st๋จ์.
- uv - ํ ์ค์ฒ ์ขํ๊ณ
- st - ํ ์ (texel = Texture + pixel) ์ขํ๊ณ
UV - texture's coordinate
+-------+ (1, 1)
| |
| |
(0, 0) +-------+
ST - surface's coordinate space.
+-------+ (32, 32)
| |
| |
(0, 0) +-------+
o.texcoord.xy = (v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw)
| xy | tiling |
| zw | offset |
| 1 0 0 0 |
| 0 1 0 0 |
| 0 0 1 0 |
| 0 0 0 1 |
| 1 0 0 x |
| 0 1 0 y |
| 0 0 1 z |
| 0 0 0 1 |
| x 0 0 0 |
| 0 y 0 0 |
| 0 0 z 0 |
| 0 0 0 1 |
2์ฐจ์ ํ์
| x' | = | cos$ -sin$||x|
| y' | | sin$ cos$||y|
3์ฐจ์ ํ์
- ํ์ ํ๋ ฌ ๊ธฐ์ค์ผ๋ก ์๊ฐํ๋ค.
- 2์ฐจ์ ํ์ ํ๋ ฌ์ ๊ธฐ์ตํ๋ค.
- ๊ธฐ์ค ํ ์๋๊ฐ
-sin$
์ด๋ค.
X
| 1(x) 0 0 0 | (x ์๋ -sin$)
| 0 cos$ -sin$ 0 |
| 0 sin$ cos$ 0 |
| 0 0 0 1 |
Y
| cos$ 0 sin$ 0 |
| 0 1(y) 0 0 | (y ์๋ -sin$)
| -sin$ 0 cos$ 0 |
| 0 0 0 1 |
Z
| cos$ -sin$ 0 0 |
| sin$ cos$ 0 0 |
| 0 0 1(z) 0 | (z์ ์๋ ๋ง์ง๋ง ์ด์ ๋น์์ผํ๋ ๋งจ ์๊ฐ -sin$)
| 0 0 0 1 |
- Pass 1
- ๊ธฐ์ค ๋ชจ๋ธ ์ค์ผ์ผ ์ .
- outline ์์์ผ๋ก ์น ํ๊ธฐ
- Pass 2
- ๊ทธ ์์ ๋ง ๊ทธ๋ฆฌ๊ธฐ
- normal ์ฌ์ถ๋ฐฉ์.
- ๋จ์ , ๋ฉด๋ค ์ฌ์ด์ ๊ฐญ๋ค์ด ๋ณด์.
- ๋งคํธ๋ฆญ์ค๋ฅผ ํตํ vertex ํ๋.
Zwrite Off
Cull Front
half4 Outline(half4 vertexPosition, half w)
{
half4x4 m;
m[0][0] = 1.0 + w; m[0][1] = 0.0; m[0][2] = 0.0; m[0][3] = 0.0;
m[1][0] = 0.0; m[1][1] = 1.0 + w; m[1][2] = 0.0; m[1][3] = 0.0;
m[2][0] = 0.0; m[2][1] = 0.0; m[2][2] = 1.0 + w; m[2][3] = 0.0;
m[3][0] = 0.0; m[3][1] = 0.0; m[3][2] = 0.0; m[3][3] = 1.0;
return mul(m, vertexPosition);
}
// 1. HLSL ๋ฐฉ๋ฒ.
// ref: https://blog.naver.com/mnpshino/222058677191
// UniversalRenderPipelineAsset_Renderer
// - Add Feature> Render Objects
// - Add Pass Names (same as LightMode)
// 2. ShaderGraph๋ฐฉ๋ฒ.
// ref: https://walll4542.wixsite.com/watchthis/post/unityshader-14-urp-shader-graph
// - Graph Inspector
// - Alpha Clip ์ฒดํฌ
// - Two Sided ์ฒดํฌ
// - Is Front Face> Branch> Fragment's Alpha๋ก ์ํ์ ์ฉ.
[KeywordEnum(On, Off)] _UseNormal("_UserNormalMap, float) = 0
[Enum(On 1, Off)]
[Toggle]
[KeywordEnum(On, Off)] _UseNormal("_UserNormalMap, float) = 0
;; => _USE_NORMAL_ON // _USE_NORMAL_OFF
#pragma multi_compile _USE_NORMAL_ON
#pragma shader_feature _USE_NORMAL_ON
#if _USE_NORMAL_ON
#endif
-
์ฌ๋ฌ multi_compile ๊ฒฐํฉ
-
์ฌ๋ฌ๊ฐ์ multi_compile์ ์ ์ํ ์ ์๊ณ , ์์ฑ ๊ฐ๋ฅํ ๋ชจ๋ ์กฐํฉ์ ๋ํด ์ปดํ์ผ๋จ.
#pragma multi_compile A B C #pragma multi_compile D E
-
์ฒซ ๋ฒ์งธ ์ค์์ ์ธ ๊ฐ์ง ๋ฐฐ๋ฆฌ์ธํธ๊ฐ ์์ฑ๋๊ณ , ๋ ๋ฒ์งธ ์ค์์ ๋ ๊ฐ์ง ๋ฐฐ๋ฆฌ์ธํธ๊ฐ ์์ฑ๋์ด, ์ด ์ฌ์ฏ๊ฐ(3 * 2)์ ์ ฐ์ด๋ ๋ฐฐ๋ฆฌ์ธํธ(A+D, B+D, C+D, A+E, B+E, C+E)๊ฐ ์์ฑ๋จ
-
-
Difference between shader_feature and multi_compile
shader_feature is very similar to multi_compile. The only difference is that Unity does not include unused variants of shader_feature shaders in the final build. For this reason, you should use shader_feature for keywords that are set from the Materials, while multi_compile is better for keywords that are set from code globally.
[KeywordEnum(On, Off)] _UseNormal("_UserNormalMap, float) = 0
#pragma shader_feature _USE_NORMAL_ON
#if _USE_NORMAL_ON
#else
#endif
float3 normalFromColor (float4 colorVal)
{
#if defined(UNITY_NO_DXT5nm)
return colorVal.xyz * 2 - 1;
#else
// R => x => A
// G => y
// B => z => ignored
float3 normalVal;
normalVal = float3 (
colorVal.a * 2.0 - 1.0,
colorVal.g * 2.0 - 1.0,
0.0
);
normalVal.z = sqrt(1.0 - dot(normalVal, normalVal));
return normalVal;
#endif
}
float3 WorldNormalFromNormalMap(sampler2D normalMap, float2 normalTexCoord, float3 tangentWorld, float3 binormalWorld, float3 normalWorld)
{
// Color at Pixel which we read from Tangent space normal map
float4 colorAtPixel = tex2D(normalMap, normalTexCoord);
// Normal value converted from Color value
float3 normalAtPixel = normalFromColor(colorAtPixel);
// Compose TBN matrix
float3x3 TBNWorld = float3x3(tangentWorld, binormalWorld, normalWorld);
return normalize(mul(normalAtPixel, TBNWorld));
}
B
asic Lighting Model
A mbient |
์ฃผ๋ณ๊ด | ์ ์ฒด์ |
D iffuse |
๋๋ฐ์ฌ๊ด | ํน์ ๋ฐฉํฅ ์ ์ฌ, ๊ณ ๋ฅด๊ฒ ๋ฐ์ฌ |
S pecular |
์ ๋ฐ์ฌ๊ด | ํน์ ๋ฐฉํฅ ์ ์ฌ, ํน์ ๋ฐฉํฅ์ผ๋ก ์ ํํ ๋ฐ์ฌ |
E misive |
๋ฐ์ฐ | ๋ฐ๊ด์ฒด |
- https://docs.unity3d.com/Manual/RenderTech-ForwardRendering.html
- 5 object x 4 lighting = 20 draw call
- ์ต์ ํ ํด์ ๋ผ์ดํธ๊ฐ ์ํฅ์ ์ฃผ๋ ์ค๋ธ์ ํธ๋ง ๊ทธ๋ฆผ
- ์ด์จ๋ , ๋ผ์ดํธ๊ฐ ๋์ด๋ ์๋ก, ๋๋ก์ฐ ์ฝ ์๊ฐ ๋ฐฐ๋ก ๋์ด๋จ.
- ๋ชจ๋ฐ์ผ๊ฐ์ ์ฒ๋ฐํ ํ๊ฒฝ์์๋
- ๋ผ์ดํธ๋ฅผ ํ๋๋ง ์ ์ง
- ๋ผ์ดํธ๋ฅผ ๋ฏธ๋ฆฌ ํ ์ค์ณ์ ๊ตฌ์.
Tag { Queue = Transparency ...}
Pass {
LightMode = "Forward Base"
}
- 1 per pixel
directional
light rendering- Spherical harmonic lights(Light probes, Global Illumination, Sky Ambient)
Pass {
LightMode = "Forward Add"
}
- 1 per pixel additional light
directional ๋ผ์ดํธ๋ point ๋ผ์ดํธ๊ฐ ์์ผ๋ฉด,
directional light์๋ forward base๋ก
point light์๋ forward add๋ก ๋๊ฐ์ง ํจ์ค๋ฅผ ์์ฑํด์ผ ํ๋ค.
- ์ฌ์
Geometry Buffer
์ ๋ ๋๋งํ๋ค.(๋ณด์ฌ์ง๋ ๊ฐ ํฝ์ ์ depth, normal, specular power) - Light Accumulation
- ๊ฐ ๋ผ์ดํธ์ ์ํฅ์ ๋ฐ๋ ํฝ์ ์ ์ฐพ์.
- ์ง์ค๋ฉํธ๋ฆฌ ๋ฒํผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
- ๋ผ์ดํธ ๊ฐ์ ๊ณ์ฐ
Light Accumulation buffer
์ ์ ์ฅ.
- ์ฌ์ ๋ค์ ๋ ๋๋ง ํ๋ค.
- Accumulated light value + Mesh color + Ambient or Emissive light
- ์ฌ์
Geometry Buffer(g-buffer)
์ ๋ ๋๋งํ๋ค.- depth
- diffuse color
- normal(world space)
- specular color
- smoothness
- emission
- Light Accumulation
- ๊ฐ ๋ผ์ดํธ์ ์ํฅ์ ๋ฐ๋ ํฝ์ ์ ์ฐพ์.
- ์ง์ค๋ฉํธ๋ฆฌ ๋ฒํผ์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์
- ๋ผ์ดํธ ๊ฐ์ ๊ณ์ฐ
Light Accumulation buffer
์ ์ ์ฅ.- accumulated-light value์ diffuse color + spec + emission๋ฅผ ํฉ์น๋ค.
- deferred shading vs deferred lighting
- deferred shading์์๋ ์ฌ์ ๋ค์ ๋ ๋๋ง ํ์ง ์์๋ ๋๋ค.(์ด๋ฏธ ์ง์ค๋ฉํธ๋ฆฌ ๋ฒํผ์ ์ ์ฅํ๊ธฐ๋๋ฌธ์)
- Unity requirement for deferred shading
- Graphic Card with multiple render target
- Support Shader Model 3 or later
- Support for Depth-Render Texture
- ์ํ ํ์ธ๋ฆฌํ ๋จ๋ฒํธ (Johann Heinrich Lambert)
- ๋จ๋ฒํธ์ ๋ฒ์น์ ๋ง์กฑํ๋ ํ๋ฉด์ ๋จ๋ฒ์์ (Lambertian)์ด๋ผ ํจ.
- ๋จ๋ฒํธ ์ฝ์ฌ์ธ ๋ฒ์น(lambert's cosine law)
- light in reflected off a surface based on cosine-fall off.
- Fowrad Rendering ๊ธฐ๋ฐ์ผ๋ก
- ์ฌ์ ํ๋ ์ด์์ ๋ผ์ดํธ๊ฐ ์๋ค๋ฉด, ํ๋์ ๊ฐ์ฅ ๋ฐ์ directional light๊ฐ Base Pass์ ์ฌ์ฉ.
- ๋ค๋ฅธ ๋ผ์ดํธ๋ค์ Spherical Harmonics๋ก ๊ฐ์ฃผ.
๋ฒกํฐ L์ N์ ์ฌ์ด๊ฐ์ x๋ผ ํ์๋, ๋ด์ ๊ฐ์(๋ด์ ์ ๊ตํ๋ฒ์น ์ฑ๋ฆฝ)
dot(L, N) = |L| * |N| * cos(x)
L, N์ด ๋จ์๋ฐฑํฐ์ผ๋ |L|, |N|๋ 1.
cos(x) = dot(L, N)
๋ฐ๋ผ์ lambert ๊ฐ์
lambert = cos(x)
= dot(L, N)
VS์์ lambert๊ฐ์ ๊ตฌํ๋ฉด ๋ณด๊ฐ๊ฐ์ ์ด์ฉํ๊ธฐ์, PS์์ lambert๊ตฌํ ๊ฒ๊ณผ์ ์ฐจ์ด๊ฐ ์๋ค.
์
์ฌ๋ฒกํฐ(incident)๋ฅผ I๋ผ ํ์๋ reflect(I, N)์
R = reflect(I, N)
= I - 2 * N * dot(I, N)
N: Normal
L: Light
R: Reflect
V: Viewport
H: Halfway( normalize(L + V) )
L N R
\ | /
\|/
---+---
\
\
-L
Phong Reflection Model : max(0, dot(R, N))^S
Blinn-Phong Reflection Model : max(0, dot(H, N))^S
-
์ ์ฌ๊ด์ ๊ฐ๋/๋ฐฉํฅ
-
Normal
-
๋ทฐํฌํธ/๋์ ๊ฐ๋/๋ฐฉํฅ
-
tex2D, tex2Dlod
// tex2D - pixel shader only
// float4 specularMap = tex2D(_SpecularMap, o.texcoord.xy);
// tex2Dlod - 2D texture lookup with specified level of detail and optional texel offset.
// samp : Sampler to lookup.
// s.xy : Coordinates to perform the lookup.
// s.w : Level of detail. (0 ~ n-1) (๊ฐ์ฅํฐ)์ฒซ๋ฒ์งธ ๋ฐ๋งต์ด 0
#pragma target 3.0
float4 specularMap = tex2Dlod(_SpecularMap, o.texcoord);
#define SAMPLE_TEXTURE2D_LOD(textureName, samplerName, coord2, lod) textureName.SampleLevel(samplerName, coord2, lod)
half specularColor = SAMPLE_TEXTURE2D_LOD(_SpecularMap, sampler_SpecularMap, OUT.uv, 0).r;
- pragma
- https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
#pragma target 2.5 (default)
Windows> Ligiting> Environment> Environment Lighting> Source> Color
Windows> Ligiting> Environment> Environment Lighting> Ambient Color> ์ง์ .
์ฃผ๋ณ(Ambient) ๋ฐ์ฌ๊ด.
A = ambient property of material * global ambient
= KA * UNITY_LIGHTMODEL_AMBIENT
// ref: [Get Ambient Color in custom shader](https://forum.unity.com/threads/get-ambient-color-in-custom-shader.994786/)
// half3 ambientColor = _AmbientFactor * UNITY_LIGHTMODEL_AMBIENT;
half3 ambientColor = _AmbientFactor * half3(unity_SHAr.w, unity_SHAg.w, unity_SHAb.w);
// ref: com.unity.render-pipelines.universal/ShaderLibrary/ShaderGraphFunctions.hlsl
// #define SHADERGRAPH_AMBIENT_SKY unity_AmbientSky
half3 ambientColor = _AmbientFactor * unity_AmbientSky;
unity_AmbientSky | fixed4 | Sky ambient lighting color in gradient ambient lighting case. |
unity_AmbientEquator | fixed4 | Equator ambient lighting color in gradient ambient lighting case. |
unity_AmbientGround | fixed4 | Ground ambient lighting color in gradient ambient lighting case. |
UNITY_LIGHTMODEL_AMBIENT | fixed4 | Ambient lighting color (sky color in gradient ambient case). Legacy variable. |
๊ธฐ๋ณธ ๋ผ์ดํธ ๋ชจ๋ธ์๋ ๋ญ๊ฐ ์ข ๋ถ์กฑํ๋ค.
- ๊ทธ๋ฆผ์(Shadow)
- ์ค๋ธ์ ํธ๊ฐ ์ํธ์์ฉ(Inter Object interaction)
- ์๋์ง ๊ท ํ(Energy Balance)
- Energy balanced shading model
- ์ค๋ธ์ ํธ๋ ๊ตฌ์ ์ผํฐ์ ๋น์ ๋ฐฉํฅ์ด ๊ตฌ์ ๋ฐ๊นฅ์์ ์์ชฝ์ผ๋ก ๋น์ถ๋ค ๊ฐ์ .
- Hemispherical(๋ฐ๊ตฌํ) Lighting Model
- 2 half sphere
upper | sky | N.L > 0 |
lower | ground | N.L <= 0 |
๋ง์ ๋ผ์ดํธ๋ฅผ ์ค์๊ฐ์ผ๋ก ๊ณ์ฐํ๊ธฐ์๋ ๋ฌด๋ฆฌ
- light์ ๋ณด๋ฅผ ํ
์ค์ณ์ ์ ์ฅ
- Chrome ball
- ๋์ ๊ฐ๊ธ์ ๋ง์ ๊ตฌ์ญ์ ๋ด์ ์ ์๋๋ก ์นด๋ฉ๋ผ๋ฅผ ๋ฉ๋ฆฌ ๋ฐฐ์น
- ๋งต์ ํฌ๋กฌ๋ณผ(chrome ball)์ ๋ฐฐ์น(๋์ ๋ณด์ด๋ ๋ฐฉํฅ์ผ๋ก๋ถํฐ ๋ชจ๋ ๋ผ์ดํธ ์ ๋ณด๋ฅผ ์ ์ฅ)(์ด๋ฌํ ํฌ๋กฌ๋ณผ์ light probe๋ผ ํ๋ค)
- Fish eye lens
- 185๋๊น์ง ์บก์ณ ๊ฐ๋ฅํ ๋ ์ฆ๊ฐ ์์.
- 2๊ฐ๋ก ๋ฌถ์ด 360๋๋ฅผ ์บก์ณ. ํ๊ฒฝ๋งต์ ๋ง๋ฌ.
- Chrome ball
- light probe๋ก๋ถํฐ ํ๊ฒฝ๋งต(sphere, cube)์ ์ ์
- ํ๊ฒฝ ๋งต์ผ๋ก๋ถํฐ light๊ณ์ฐ
- ๋ณต์ฌ์กฐ๋ ํ๊ฒฝ๋งต
- Irradiance : ๋ฌด์ธ๊ฐ๋ก๋ถํฐ ๋์ค๋ ๋น์ ์์ ๋ํ๋ด๋ ๋จ์
- IBL(Image Based Rendering) : ํ๊ฒฝ๋งต์ด ์๋ฐ๋๋ ํ ํฌ๋
texCube - ์ด๋ค ํ
์
์ด ๋
ธ๋ฉ ๋ฐฉํฅ๊ณผ ๋ง๋๊ฒ ๋๋๊ฐ.
color = texCube(_Cube_Texture, NormalDirection);
- ๋น์ ์ ๋ณด(์์น, ๋ฐ๊ธฐ๋ฑ)์ ํ ์ ์ ์ ์ฅ
์ค๋ธ์ ํธ๋ค์ ์ด๋ฏธ์ง๋ฅผ ์ ์ฅ.
- ์ฌ์ ๋ชจ๋ ๋ฏธ๋ฆฌ ๊ณ์ฐ๋ ๋น์ ์ ๋ณด๋ฅผ ํ ์ค์ณ์ ์ ์ฅ.
- ์ค๋ธ์ ํธ์ N๊ณผ ๋น์ L์ ๊ธฐ๋ฐํ์ฌ, ๋ฏธ๋ฆฌ ๊ณ์ฐํ์ฌ ์ ์ฅํ์๊ธฐ์ ๋์ค์ ์ค๋ธ์ ํธ๊ฐ ํ์ ํ๊ฑฐ๋ ๋ณํ๊ฒ๋๋ฉด ๋ฏธ๋ฆฌ ๊ณ์ฐํ ๊ฐ๊ณผ ๋ง์ง ์๊ฒ๋๋ค.
์๋์ผํฐ์ ๊ตฌ์ N๊ณผ ํ๊ฒฝ๋งต์ L์ ๋ํ diffuse๊ฐ(dot(N, L))์ ์ ์ฅ.
- ํ๊ฒฝ๋งต(light)์ ๋ชจ๋ ํ ์ ์ด, ์๋์ผํฐ์ ๊ตฌ๊ฐ ์๋ค๊ณ ๊ฐ์ .
- ๊ตฌ์ N๊ณผ ํ ์ ์ L์ ๋ํด dot(N, L)์ ๊ตฌํ๊ณ .
- ๊ทธ์ ๋ํ ์๊น์ Irradiance Environment Map์ ํ ์ ์ ์ ์ฅ.
- Irradiance Map์ ๊ฐ ํ
์
๋ง๋ค
- ์ผํฐ๋ก๋ถํฐ ํ ์ ๋ก์ ๋ฐฉํฅ(Normal๊ฐ)์ ๊ตฌํ๊ณ
- Lighting Environment map์ ๊ฐ ํ
์
๋ง๋ค
- ๋ฏธ๋ฆฌ ์กฐ๋ช ์ฒ๋ฆฌ๋ diffuse๊ฐ์ ๊ฐ์ฐํ๊ณ
- ๊ณ์ฐ๋ diffuse ๊ฐ์ Irradiance Map์ ํ ์ ์ ์ ์ฅํ๋ค.
foreach (irr_texel in irradiance_map.texels)
{
N = calculateDirectionFromCenter(irr_texel);
diffuseColor = 0
foreach (light_texel in lighting_environment_map.texels)
{
L = calculateDirectionFromCenter(light_texel)
diffuseColor += Vector.Dot(N, L)
}
irr_texel.color = diffuseColor;
}
return irradiance_map
-
image from https://heinleinsgame.tistory.com/29
-
IBL-Reflection
- IBL(Image Based Lighting)
- Reflection: ๋ฐ์ฌ
๊ตฌํ ๋ฐ์ฌ์ฒด๋ฅผ ํ๋ธ๋งต ์์ ์์น์ํค๊ณ , ๋ฐ์ฌ์ฒด์์ ๋ฐ์ฌ๋์ด ํ๋ธ๋งต์ ๋ง๋ฟ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๋ค.
V : ์๋ ๋ทฐ ๋ฐฉํฅ N : ์๋ ์ค๋ธ์ ํธ์ ํ๋ฉด Normal VR : ์๋ ๋ทฐ ๋ฐฉํฅ์ ๋ฐ์ฌ ๋ฐฉํฅ
texCUBElod - LOD๋ (level of detail)๋ฅผ ์๋ฏธ.
texel = texCUBElod(cubeMap, VR.xyzw);
xyz : direction
w : detail(max: 1)
float3 IBL_Reflection(
samplerCUBE cubeMap,
half detail,
float3 reflect,
float exeposure,
float factor)
{
float4 c = texCUBElod(cubeMap, float4(reflect, detail));
return factor * c.rgb * (c.a * exeposure);
}
// com.unity.render-pipelines.core/ShaderLibrary/API/D3D11.hlsl
#define TEXTURECUBE_PARAM(textureName, samplerName) TEXTURECUBE(textureName), SAMPLER(samplerName)
#define SAMPLE_TEXTURECUBE(textureName, samplerName, coord3) textureName.Sample(samplerName, coord3)
#define SAMPLE_TEXTURECUBE_LOD(textureName, samplerName, coord3, lod) textureName.SampleLevel(samplerName, coord3, lod)
half3 IBL_Reflection(TEXTURECUBE_PARAM(cubeMap, sampler_cubeMap), half detail, half3 worldRefl, half exposure, half reflectionFactor)
{
half4 cubeMapCol = SAMPLE_TEXTURECUBE_LOD(cubeMap, sampler_cubeMap, worldRefl, detail).rgba;
return reflectionFactor * cubeMapCol.rgb * (cubeMapCol.a * exposure);
}
- IBL-Refraction
- IBL(Image Based Lighting)
- Refraction: ๊ตด์
์ ๋ฆฌ๋ ๋ฌผ์ ๋ฐ์ฌ์ ๊ตด์ ๋ชจ๋๋ฅผ ๊ฐ๋๋ฐ...
- Snell's law ๊ณต์ ์ ๋ ์ค๋ช .
- Willebrord Snellius
- nvidia: refract
float3 refract(float3 i, float3 n, float eta)
{
float cosi = dot(-i, n);
float cost2 = 1.0f - eta * eta * (1.0f - cosi * cosi);
float3 t = eta * i + ((eta * cosi - sqrt(abs(cost2))) * n);
return t * (float3)(cost2 > 0);
}
// ๋งค์ง์ ๋ํ ๋ฐ์ฌ
float3 world_reflect_V = reflect(-world_V, world_N);
// ๋งค์ง์ ๋ํ ๊ตด์
float3 world_refract_V = refract(-world_V, world_N, 1 / _RefractiveIndex);
half3 V = normalize(GetWorldSpaceViewDir(IN.positionWS));
// dot(V, N)๋ ๊ฐ๋๊ฐ ์ข์์๋ก 1๋ก ์๋ ดํ๋,
// ๊ฐ๋๊ฐ ์ปค์ง๋ฉด ๋ ๋ฐ๋๋ก One Minus.
half fresnelWeight = 1 - saturate(dot(V, N));
// ๊ฑฐ๋ฆฌ์ ๋ฐ๋ฅธ ๋น์จ ๋ณด๊ฐ.
half fresnel = smoothstep(1 - _FresnelWidth, 1, fresnelWeight);
half3 reflectVN = reflect(-V, N);
half3 reflectColor = IBL_Reflection(_CubeMap, sampler_CubeMap, _ReflectionDetail, reflectVN, _ReflectionExposure, _ReflectionFactor);
// fresnel๋ก ๋ณด๊ฐํจ๊ณผ
finalColor.rgb = lerp(finalColor.rgb, finalColor.rgb * reflectColor, fresnel);
H = normalize( V + L )
spec = pow(saturate(dot(H, N)), power);
Blinn-phong์์๋ ๋ค์๊ณผ ๊ฐ์ ๊ทธ๋ฆผ์์ Half-vector๋ฅผ ์ด์ฉํ๊ธฐ์ ๋ฐ์ฌ๋๋ ํฌ๊ธฐ๊ฐ ๊ณ์ฐ์ ๊ฐ๊ฒ ๋์ฌ๊ฒ์ด๋ค. ํ์ง๋ง, ํ์ค์์๋ L๊ณผ V์ ๊ฐ๋๊ฐ ์ปค์ง๋ฉด ๋ฐ์ฌ๋์ด ๋ฐ๊ฒ ๋น๋๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
Fresnel๋ก ๋ฆผ๋ผ์ดํธํจ๊ณผ์ ๋ฐ์ฌ๊ดํจ๊ณผ๋ฅผ ํ๋ด๋ผ ์์๋ค.
๊ตด์ ๋ฅ ์ด n1์ธ ๋งค์ง์์ n2์ธ ๋งค์ง๋ก ๋น์ด ํฌ๊ณผํ ๋ ๋ฐ์ฌ์ ๊ตด์ ์ด ์ผ์ด๋๋ค. ํ๋ ๋ฌ ๋ฐฉ์ ์์ ์ด ์ฑ์ง์ ๋ฐ์ฌ๊ณ์, ํฌ๊ณผ๊ณ์๋ก ๋๋์ด ์ฑ๋ถ์ ๋ถ์ํ์ฌ ํํํ ๋ฐฉ์ ์์ด๋ค
// H is the standard half-angle vector.
// F0 is reflectance at normal incidence (for skin use 0.028).
float fresnelReflectance( float3 H, float3 V, float F0 )
{
float base = 1.0 - dot( V, H );
float exponential = pow( base, 5.0 );
return exponential + F0 * ( 1.0 - exponential );
}
NDC(Normalized Device Coordinate) [-1, 1]
-1, 1 1, 1
+--------------------+
| |
| |
| 0,0 |
| |
| |
+--------------------+
-1, -1 1, -1
Texture Coord [0, 1]
D3D
(0,0) (1,0)
+-----+-----+
| | |
| | |
+-----+-----+
| | |
| | |
+-----+-----+
(0,1) (1,1)
OpenGL
(0,1) (1,1)
+-----+-----+
| | |
| | |
+-----+-----+
| | |
| | |
+-----+-----+
(0,0) (1,0)
[-1, 1] => [0, 1] = (NDC + 1) / 2
NDC.x = proj_X / proj_W = x / w
U = (x / w + 1) * 0.5
= (x + w) / w * 0.5
= (1 / w) * (x + w) * 0.5
V = (1 / w) * (y + w) * 0.5
_ProjectionParams.x : -1 TopLeft D3D
1 BottomLeft Opengl
UNITY_HALF_TEXEL_OFFSET์ธ ๊ฒฝ์ฐ _ScreenParams.zw๋ฅผ ๊ณฑํด์ค๋ค.
inline float4 ProjectionToTextureSpace(float4 pos)
{
float4 textureSpacePos = pos;
#if defined(UNITY_HALF_TEXEL_OFFSET)
textureSpacePos.xy = float2 (textureSpacePos.x, textureSpacePos.y * _ProjectionParams.x) + textureSpacePos.w * _ScreenParams.zw;
#else
textureSpacePos.xy = float2 (textureSpacePos.x, textureSpacePos.y * _ProjectionParams.x) + textureSpacePos.w;
#endif
textureSpacePos.xy = float2 (textureSpacePos.x/textureSpacePos.w, textureSpacePos.y/textureSpacePos.w) * 0.5f;
return textureSpacePos;
}
// https://github.com/Unity-Technologies/Graphics/pull/2529
// com.unity.render-pipelines.universal/ShaderLibrary/ShaderVariablesFunctions.deprecated.hlsl
// Deprecated: A confusingly named and duplicate function that scales clipspace to unity NDC range. (-w < x(-y) < w --> 0 < xy < w)
// Use GetVertexPositionInputs().positionNDC instead for vertex shader
// Or a similar function in Common.hlsl, ComputeNormalizedDeviceCoordinatesWithZ()
float4 ComputeScreenPos(float4 positionCS)
{
float4 o = positionCS * 0.5f;
o.xy = float2(o.x, o.y * _ProjectionParams.x) + o.w;
o.zw = positionCS.zw;
return o;
}
-
Z-depth๊ตฌํ๊ธฐ
-
์ฌ ๋ ๋๋ง
-
Z-depth๋ฅผ ๊น์ด๋ฒํผ์ ์ ์ฅํ๋ค(depth map)
world > View[Light] > Proj[Light] Light's View Matrix > Light's Projection Matrix > transform NDC > transform texture Space
-
-
๊ทธ๋ฆผ์๊ทธ๋ฆฌ๊ธฐ
-
์ฌ ๋ ๋๋ง
-
๊น์ด๋ฒํผ๋ Z-depth ํ ์คํธ
if (fragment Z-depth > sampled Z-depth) { shadow : 0 } else { lit : 1 }
-
// Built-in(legacy)
Tag {"LightMode" = "ShadowCaster"} => _ShadowMapTexture
TODO ๋ฐ๋ก ์ ๋ฆฌํ ํ์ด์ง๋ก ๋งํฌ.
- Mesh Renderer> Lighting> Cast Shadow> On
- ShadowCaster Pass
- frag ํจ์์์ shadow(0) / lit(1) ๋ฐํ
world > View[Camera] > Proj[Camera]
Camera's View Matrix > Camera's Projection Matrix
> transform texture Space
// builtin(legacy)
inline float4 ProjectionToTextureSpace(float4 positionHCS)
{
float4 textureSpacePos = positionHCS;
#if defined(UNITY_HALF_TEXEL_OFFSET)
textureSpacePos.xy = float2 (textureSpacePos.x, textureSpacePos.y * _ProjectionParams.x) + textureSpacePos.w * _ScreenParams.zw;
#else
textureSpacePos.xy = float2 (textureSpacePos.x, textureSpacePos.y * _ProjectionParams.x) + textureSpacePos.w;
#endif
textureSpacePos.xy = float2 (textureSpacePos.x/textureSpacePos.w, textureSpacePos.y/textureSpacePos.w) * 0.5f;
return textureSpacePos;
}
half4 shadowCoord = ProjectionToTextureSpace(positionHCS)
half shadowAttenuation = tex2D(_ShadowMapTexture, shadowCoord).a;
Pass
{
Name "ShadowCaster"
Tags { "Queue" = "Opaque" "LightMode" = "ShadowCaster" }
ZWrite On
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct vertexInput
{
float4 vertex : POSITION;
};
struct vertexOutput
{
float4 pos : SV_POSITION;
};
vertexOutput vert (vertexInput v)
{
vertexOutput o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
float4 frag(vertexOutput i) : SV_Target
{
return 0;
}
ENDCG
}
- Builtin(Legacy)์์๋
_ShadowMapTexture
๋ฅผ ํ์ฉํ๋, URP์์๋ API๋ ๋ฐ๋๊ณ ๋ด๋ถ์ ์ผ๋ก_MainLightShadowmapTexture
๋ฅผ ํ์ฉํ๋๋ก ๋ฐ๋์๋ค. - com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl
half4 shadowCoord = TransformWorldToShadowCoord(IN.positionWS);
ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
half4 shadowParams = GetMainLightShadowParams();
half shadowAttenuation = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
finalColor.rgb *= shadowAttenuation;
// URP
half4 shadowCoord = TransformWorldToShadowCoord(positionWS);
// or
// VertexPositionInputs vertexInput = GetVertexPositionInputs(IN.positionOS.xyz);
// half4 shadowCoord = GetShadowCoord(vertexInput);
half shadowAttenuation = MainLightRealtimeShadow(shadowCoord);
// or
// ShadowSamplingData shadowSamplingData = GetMainLightShadowSamplingData();
// half4 shadowParams = GetMainLightShadowParams();
// half shadowAttenuation = SampleShadowmap(TEXTURE2D_ARGS(_MainLightShadowmapTexture, sampler_MainLightShadowmapTexture), shadowCoord, shadowSamplingData, shadowParams, false);
// or
// Light mainLight = GetMainLight(i.shadowCoord);
// half shadowAttenuation = mainLight.shadowAttenuation;
Pass
{
Name "ShadowCaster"
Tags
{
"LightMode" = "ShadowCaster"
}
ZWrite On
Cull Back
HLSLPROGRAM
#pragma target 3.5
#pragma vertex shadowVert
#pragma fragment shadowFrag
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl" // real
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/CommonMaterial.hlsl" // LerpWhiteTo
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl" // ApplyShadowBias
struct Attributes
{
float4 positionOS : POSITION;
float4 normal : NORMAL;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
};
Varyings shadowVert(Attributes IN)
{
Varyings OUT = (Varyings)0;
float3 positionWS = TransformObjectToWorld(IN.positionOS.xyz);
float3 normalWS = TransformObjectToWorldNormal(IN.normal.xyz);
OUT.positionHCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, _MainLightPosition.xyz));
return OUT;
}
half4 shadowFrag(Varyings IN) : SV_Target
{
return 0;
}
ENDHLSL
}
// Legacy
#pragma multi_compile_fwdbase
#if _SHADOWMODE_ON
#if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X) || defined(UNITY_COMPILER_HLSLCC)
Texture2D _ShadowMapTexture;
SamplerComparisonState sampler_ShadowMapTexture;
#elif defined(SHADER_TARGET_GLSL)
sampler2DShadow _ShadowMapTexture;
#else
sampler2D _ShadowMapTexture;
#endif
#endif
#if _SHADOWMODE_ON
// ์๋ํฐ๋ cascading shadows๋ฅผ ์ฌ์ฉ. ๋ชจ๋ฐ์ผ ํ๋ ํผ์ ์ฌ์ฉํ์ง ์์.
#if defined(UNITY_NO_SCREENSPACE_SHADOWS)
o.shadowCoord = mul(unity_WorldToShadow[0], o.posWorld);
#else
o.shadowCoord = ProjectionToTextureSpace(o.pos);
#endif
#endif
#if _SHADOWMODE_ON
#if defined(SHADER_TARGET_GLSL)
float shadow = shadow2D(_ShadowMapTexture, i.shadowCoord);
#else
// Factor in shadow strength using _LightShadowData.r
#if defined(SHADER_API_D3D11) || defined(SHADER_API_D3D11_9X) || defined(UNITY_COMPILER_HLSLCC)
float shadow = (_ShadowMapTexture.SampleCmpLevelZero (sampler_ShadowMapTexture, i.shadowCoord.xy, i.shadowCoord.z)? 1.0 : _LightShadowData.r);
#elif defined(UNITY_NO_SCREENSPACE_SHADOWS)
float shadow = ((tex2D(_ShadowMapTexture, i.shadowCoord.xy).r < i.shadowCoord.z) ? 1.0 : _LightShadowData.r);
#else
float shadow = tex2D(_ShadowMapTexture, i.shadowCoord).a;
#endif
#endif
#endif
BRDF ๋ "Bidirectional Reflectance Distribution Function" ์ ๋จธ๋ฆฌ๊ธ์์
๋๋ค.
์ฐ๋ฆฌ ๋ง๋ก๋ "์๋ฐฉํฅ ๋ฐ์ฌ์จ ๋ถํฌ ํจ์"์
๋๋ค.
์ด BRDF ๋ Diffuse BRDF ์ Specular BRDF ๋ก ๋๋ฉ๋๋ค.
์ถ์ฒ: https://lifeisforu.tistory.com/386 [๊ทธ๋ฅ ๊ทธ๋ฐ ๋ธ๋ก๊ทธ]
BRDF - ex) ๋ทฐ ๋ฐฉํฅ๊ณผ ๋ผ์ดํธ ๋ฐฉํฅ์ผ๋ก๋ถํฐ, ๋ถํฌ๋ช ํ ํ๋ฉด์ ๋ฐ์ฌ๋๋ ๋ฐฉ์์ ๊ตฌํจ.
ํน์ ๋ฐฉํฅ(๋ ํน์ ์นด๋ฉ๋ผ )์ผ๋ก ๋ฐ์ฌ๋ ๋น์ ์
-------------------------------------------------------------
ํน์ ๋ฐฉํฅ(ํน์ ํฌ์ธํฐ๋ก๋ถํฐ์ ๋น์ ๋ฐฉํฅ)์ผ๋ก๋ถํฐ ๋๋ฌํ ๋น์ ์
Radiance
= -------------
Irradiance
BRDF
๋ฐ์ฌ๋ ๋น์ ์
= --------------------
Li * ๋ฉด์ x Cos
๋ฑ๋ฐฉ์ฑ( isotropic): ๋ฐ์ฌ๋๊ฐ ํ๋ฉด์ด ํฅํ๋ ๋ฐฉํฅ์ด๋ ํ์ ์ ์ํด ๋ณํ์ง ์์.
๋น๋ฑ๋ฐฉ์ฑ(anisotropic): ๋ฐ์ฌ๋๊ฐ ํ๋ฉด์ด ํฅํ๋ ๋ฐฉํฅ์ด๋ ํ์ ์ ์ํด ๋ณํจ.
Microfacet Theory
Radiance | ๋ณต์ฌํ๋ | ๋น์ ํ๋ฉด์ ๋จ์๋ฉด์ ๋น ๋ฐฉ์ถ๋ ์๋์ง(๋จ์ ์๊ฐ๋น ํน์ ๋ฐฉํฅ) |
Irradiance | ๋ณต์ฌ์กฐ๋ | ๋ฐ์ ์๋์ง(๋จ์ ๋ฉด์ ) |
๊ตฌ๋ฉด์ขํ :
์๋ฐ์ฑ reciprocity
- Ashikhimin-Shirley && Ashikhimin-Premoze
- Ashikhimin-Shirley ์ ์
- Ashikhimin-Premoze์ ์ํด ์์
H = Halfway
T = Tangent
B = Bitangent
half AshikhminShirleyPremoze_BRDF(half nU, half nV, half reflectionFactor, half3 N, half3 T, half3 L, half3 V);
float AshikhminShirleyPremoze_BRDF(float nU, float nV, float3 tangentDir, float3 normalDir, float3 lightDir, float3 viewDir, float reflectionFactor)
{
float pi = 3.141592;
float3 halfwayVector = normalize(lightDir + viewDir);
float3 NdotH = dot(normalDir, halfwayVector);
float3 NdotL = dot(normalDir, lightDir);
float3 NdotV = dot(normalDir, viewDir);
float3 HdotT = dot(halfwayVector, tangentDir);
float3 HdotB = dot(halfwayVector, cross(tangentDir, normalDir));
float3 VdotH = dot(viewDir, halfwayVector);
float power = nU * pow(HdotT,2) + nV * pow(HdotB,2);
power /= 1.0 - pow(NdotH,2);
float spec = sqrt((nU + 1) * (nV + 1)) * pow(NdotH, power);
spec /= 8.0 * pi * VdotH * max(NdotL, NdotV);
float Fresnel = reflectionFactor + (1.0 - reflectionFactor) * pow(1.0 - VdotH, 5.0);
spec *= Fresnel;
return spec;
}
float4 tangentMap = tex2D(_TangentMap, o.texcoord.xy);
float3 specular = AshikhminShirleyPremoze_BRDF(_AnisoU, _AnisoV, tangentMap.xyz, o.normal, lightDir, worldViewDir, _ReflectionFactor);
- Xcode ๋๋ฒ๊ทธ
- ์นด๋ฉ๋ผ๋ฒํผ
- RenderCommandEncoder ๋๋ธํด๋ฆญ