champions-of-luneraria/Assets/Luneraria/Shaders/LuneSurface.shader
Martin Stickelbroeck 07fa4572e0 U5.3: Gebäude, Boss und Ringborke auf PBR; Bildfehler aus U5 behoben
Fortsetzung des angefangenen U5.3-Stands vom 2. Oktober.

- Fünf neue Sätze aus bake_pbr.py (metal, cloth, hide, bone, wood) für
  Türme, Ancients, Grotte und die Schlundkrone. Ringborkes Rinde (RB_Bark)
  auf ihren eigenen UVs mit dem 4k-Satz (Rauigkeit zu ORM gepackt),
  Normalen im Tangentenraum des Meshes. Aufnahmen aller drei Lanes.
- Befund: Seit U5 war die Welt halb so hell und ohne Grün (Mid-Lane
  Helligkeit 0,27, Grün 0 %). Ursachen und Korrekturen:
  - Die PBR-Albedos wurden über die Godot-Farben multipliziert (Rasen
    etwa x0,05). Jetzt teilt der Shader jede Karte durch ihren Mittel-
    wert; die Godot-Farbe bleibt der Durchschnitt, die Karte liefert
    Struktur. Ebenso die Rauigkeit, wo das Material eine eigene hat.
  - Normal- und ORM-Karten waren als sRGB-Farbe importiert. Jetzt
    Normalkarte bzw. linear.
  - bake_pbr.py verzerrte alle Muster 10:1 (Regenstreifen). Stein, Erde,
    Laub, Wasser und die neuen Sätze ohne Maserung neu gebacken.
  - Der Treffer-Flash ersetzte die Grundfarbe aller Materialplätze; im
    Nahkampf waren die Armeen weiß und der Boss gelb. Jetzt Tönung der
    eigenen Farbe je Platz, wenig Emission, 16 Stufen.
  - Nebel 0,0085 verschleierte die Kartenansicht zu 35-50 %; jetzt 0,0045.
  Ergebnis Mid-Lane: Helligkeit 0,41-0,44, Grün 31-39 % (U3: 0,45 / 27 %).
- Tests: EditMode 29/29, PlayMode 13/13, Verify 718/0, Kerntests .NET
  457/457, Fingerabdrücke unverändert (DCE95C96556F3A1A, 7B8E43AEFC0D60C5).
- Leistung: A/B mit einem U5.2-Player unter gleicher Last, kein messbarer
  Unterschied (91,4 gegen 93,8 fps Karte, 124,9 gegen 125,3 fps Mid).
- Messwerkzeug tools/port/png_stats.py; Doku U5-VISUAL (U5.3),
  PORT-STATUS, PORT-QA; Bilder screenshots/u5.3/ als JPEG.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 19:34:02 +02:00

515 lines
23 KiB
Text

// Shared surface shader of Champions of Luneraria in URP (U2B, PBR since U5). Ports the Godot
// world shaders (scripts/luneraria/view/lune_terrain.gdshader, scripts/presentation/sanctuary.gdshader,
// river.gdshader, materials/lune_vein.gdshader) and a simplified G2 building look: one
// shader, the look chosen per material with _Mode.
// 0 plain base colour (x vertex colour with _VertexColor), metal/emission from parameters
// 1 terrain zone colour from the vertex colour, rockiness in vertex alpha, triplanar limestone
// 2 stone Kluft stone: triplanar limestone relief
// 3 paving lane paving
// 4 soil packed earth
// 5 bark tree bark ridges
// 6 foliage leaves with wind
// 7 water river surface with moving waves
// 8 vein energy vein of the Ancient plaza (UV.x along, UV.y across)
// 9 crystal glowing crystal with rim light
// World-space patterns use Unity world coordinates (Godot x is mirrored); the look is the same.
// U5 PBR set (tools/blender/bake_pbr.py): albedo, tangent normal, ORM (R ao, G roughness,
// B metallic), triplanar like the stone map. _StoneMap stays as a cheap relief fallback.
// U5.3: the maps are detail around the Godot look. Albedo is divided by its mean colour
// (_AlbedoMean) and roughness by its mean (_OrmMean.g) where the material keeps its own
// roughness, so the Godot colours and roughness stay the average and the maps add variation.
Shader "Luneraria/Surface"
{
Properties
{
_BaseColor("Base Colour", Color) = (1, 1, 1, 1)
[NoScaleOffset] _AlbedoMap("Albedo", 2D) = "white" {}
[NoScaleOffset] _NormalMap("Normal", 2D) = "bump" {}
[NoScaleOffset] _OrmMap("ORM (ao, rough, metal)", 2D) = "white" {}
[NoScaleOffset] _StoneMap("Stone Map", 2D) = "grey" {}
_StoneScale("Stone Scale", Float) = 0.23
_Mode("Mode", Float) = 0
_VertexColor("Multiply Vertex Colour", Float) = 0
_Metallic("Metallic", Range(0, 1)) = 0
_Roughness("Roughness", Range(0, 1)) = 0.88
[HDR] _EmissionColor("Emission", Color) = (0, 0, 0, 1)
_Relief("Relief Strength", Float) = 0.016
_NormalStrength("Normal Strength", Float) = 1
_Quality("Quality (1 lane PBR, 0 edge)", Float) = 1
_UvMaps("UV Maps (1 character, 0 world triplanar)", Float) = 0
_AlbedoMean("Albedo Mean (linear)", Vector) = (1, 1, 1, 1)
_OrmMean("ORM Mean (linear)", Vector) = (1, 1, 1, 1)
_Energy("Energy (veins)", Float) = 1
_Flicker("Flicker (veins)", Float) = 0
_Seed("Seed", Float) = 0
[Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 2
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "Queue" = "Geometry" }
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
half4 _BaseColor;
float _StoneScale;
float _Mode;
float _VertexColor;
half _Metallic;
half _Roughness;
half4 _EmissionColor;
float _Relief;
float _NormalStrength;
float _Quality;
float _UvMaps;
half4 _AlbedoMean;
half4 _OrmMean;
float _Energy;
float _Flicker;
float _Seed;
float _Cull;
CBUFFER_END
TEXTURE2D(_AlbedoMap);
SAMPLER(sampler_AlbedoMap);
TEXTURE2D(_NormalMap);
SAMPLER(sampler_NormalMap);
TEXTURE2D(_OrmMap);
SAMPLER(sampler_OrmMap);
TEXTURE2D(_StoneMap);
SAMPLER(sampler_StoneMap);
float LuneHash(float3 p)
{
p = frac(p * 0.1031);
p += dot(p, p.yzx + 33.33);
return frac((p.x + p.y) * p.z);
}
float LuneNoise(float3 p)
{
float3 i = floor(p);
float3 f = frac(p);
f = f * f * (3.0 - 2.0 * f);
return lerp(lerp(lerp(LuneHash(i), LuneHash(i + float3(1, 0, 0)), f.x), lerp(LuneHash(i + float3(0, 1, 0)), LuneHash(i + float3(1, 1, 0)), f.x), f.y),
lerp(lerp(LuneHash(i + float3(0, 0, 1)), LuneHash(i + float3(1, 0, 1)), f.x), lerp(LuneHash(i + float3(0, 1, 1)), LuneHash(i + float3(1, 1, 1)), f.x), f.y), f.z);
}
float LuneHash2(float2 p) { return frac(sin(dot(p, float2(127.1, 311.7))) * 43758.5453); }
float LuneNoise2(float2 p)
{
float2 i = floor(p);
float2 f = frac(p);
f = f * f * (3.0 - 2.0 * f);
return lerp(lerp(LuneHash2(i), LuneHash2(i + float2(1, 0)), f.x), lerp(LuneHash2(i + float2(0, 1)), LuneHash2(i + float2(1, 1)), f.x), f.y);
}
// Foliage sways a little with height (Godot: VERTEX.x += wind * 0.065 * clamp(y * 0.3)).
float3 LuneWind(float3 positionWS)
{
if (_Mode > 5.5 && _Mode < 6.5)
{
float t = _Time.y;
float wind = sin(t * 0.8 + positionWS.x * 0.55 + positionWS.z * 0.32) * sin(t * 0.37 + positionWS.z * 0.6);
positionWS.x += wind * 0.065 * clamp(positionWS.y * 0.3, 0.15, 1.0);
}
return positionWS;
}
ENDHLSL
Pass
{
Name "ForwardLit"
Tags { "LightMode" = "UniversalForward" }
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex Vert
#pragma fragment Frag
#pragma multi_compile _ _MAIN_LIGHT_SHADOWS _MAIN_LIGHT_SHADOWS_CASCADE
#pragma multi_compile _ _ADDITIONAL_LIGHTS_VERTEX _ADDITIONAL_LIGHTS
#pragma multi_compile _ _CLUSTER_LIGHT_LOOP
#pragma multi_compile_fragment _ _ADDITIONAL_LIGHT_SHADOWS
#pragma multi_compile_fragment _ _SHADOWS_SOFT _SHADOWS_SOFT_LOW _SHADOWS_SOFT_MEDIUM _SHADOWS_SOFT_HIGH
#pragma multi_compile_fragment _ _SCREEN_SPACE_OCCLUSION
#pragma multi_compile_fog
#pragma multi_compile_instancing
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
float4 tangentOS : TANGENT;
float4 color : COLOR;
float2 uv : TEXCOORD0;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 positionWS : TEXCOORD0;
float3 normalWS : TEXCOORD1;
float4 color : TEXCOORD2;
float2 uv : TEXCOORD3;
float fogFactor : TEXCOORD4;
// Mesh tangent (xyz) and bitangent sign (w) for the authored UV normal maps (U5.3).
float4 tangentWS : TEXCOORD5;
UNITY_VERTEX_INPUT_INSTANCE_ID
UNITY_VERTEX_OUTPUT_STEREO
};
Varyings Vert(Attributes input)
{
Varyings output = (Varyings)0;
UNITY_SETUP_INSTANCE_ID(input);
UNITY_TRANSFER_INSTANCE_ID(input, output);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
float3 positionWS = LuneWind(TransformObjectToWorld(input.positionOS.xyz));
output.positionWS = positionWS;
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
output.tangentWS = float4(TransformObjectToWorldDir(input.tangentOS.xyz), input.tangentOS.w * GetOddNegativeScale());
output.positionCS = TransformWorldToHClip(positionWS);
output.color = input.color;
output.uv = input.uv;
output.fogFactor = ComputeFogFactor(output.positionCS.z);
return output;
}
half4 Frag(Varyings input, bool frontFace : SV_IsFrontFace) : SV_Target
{
UNITY_SETUP_INSTANCE_ID(input);
float3 world = input.positionWS;
float3 n = normalize(input.normalWS);
if (!frontFace)
n = -n;
float3 weights = pow(abs(n), 5.0);
weights /= max(dot(weights, 1.0), 0.0001);
float3 p = world * _StoneScale;
// U5 PBR set (lane tier): triplanar albedo, ORM and normal. Edge tier (Weg 2
// Stufen) skips the maps and keeps the flat U2 response of colour + stone relief.
// _UvMaps samples the authored UVs of a character (Ringborke bark) instead.
half3 texAlbedo = half3(1, 1, 1);
half3 texOrm = half3(1.0, _Roughness, _Metallic);
half3 nX = half3(0, 0, 1), nY = half3(0, 0, 1), nZ = half3(0, 0, 1);
// Variation of the map around its mean; 1 where no map is sampled (edge tier).
half roughDetail = 1.0;
half metalDetail = 1.0;
if (_Quality > 0.5)
{
if (_UvMaps > 0.5)
{
texAlbedo = SAMPLE_TEXTURE2D(_AlbedoMap, sampler_AlbedoMap, input.uv).rgb;
texOrm = SAMPLE_TEXTURE2D(_OrmMap, sampler_OrmMap, input.uv).rgb;
// Authored UV normal (Ringborke bark) in the mesh's own tangent frame
// (Mikktspace tangents of the FBX import, skinned with the figure).
float3 tangent = normalize(input.tangentWS.xyz - n * dot(n, input.tangentWS.xyz));
float3 bitangent = cross(n, tangent) * (input.tangentWS.w < 0.0 ? -1.0 : 1.0);
half3 uvNormal = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.uv), _NormalStrength);
n = normalize(uvNormal.x * tangent + uvNormal.y * bitangent + uvNormal.z * n);
}
else
{
texAlbedo = SAMPLE_TEXTURE2D(_AlbedoMap, sampler_AlbedoMap, p.zy).rgb * weights.x
+ SAMPLE_TEXTURE2D(_AlbedoMap, sampler_AlbedoMap, p.xz).rgb * weights.y
+ SAMPLE_TEXTURE2D(_AlbedoMap, sampler_AlbedoMap, p.xy).rgb * weights.z;
texOrm = SAMPLE_TEXTURE2D(_OrmMap, sampler_OrmMap, p.zy).rgb * weights.x
+ SAMPLE_TEXTURE2D(_OrmMap, sampler_OrmMap, p.xz).rgb * weights.y
+ SAMPLE_TEXTURE2D(_OrmMap, sampler_OrmMap, p.xy).rgb * weights.z;
nX = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, p.zy), _NormalStrength);
nY = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, p.xz), _NormalStrength);
nZ = UnpackNormalScale(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, p.xy), _NormalStrength);
}
// Detail around the Godot colour: the map's mean becomes 1.
texAlbedo /= max(_AlbedoMean.rgb, half3(0.002, 0.002, 0.002));
roughDetail = texOrm.g / max(_OrmMean.g, 0.05);
metalDetail = texOrm.b / max(_OrmMean.b, 0.05);
}
half3 stone = SAMPLE_TEXTURE2D(_StoneMap, sampler_StoneMap, p.zy).rgb * weights.x
+ SAMPLE_TEXTURE2D(_StoneMap, sampler_StoneMap, p.xz).rgb * weights.y
+ SAMPLE_TEXTURE2D(_StoneMap, sampler_StoneMap, p.xy).rgb * weights.z;
float relief = dot(stone, 0.3333);
half3 albedo = _BaseColor.rgb * texAlbedo;
half roughness = texOrm.g;
half metallic = texOrm.b;
half ao = texOrm.r;
half3 emission = _EmissionColor.rgb;
float bump = 0.0;
int mode = (int)round(_Mode);
// World-space modes keep their Godot look on top of the PBR set.
if (mode == 1)
{
float broad = LuneNoise(world * 0.35);
float grain = LuneNoise(world * 6.0);
float rock = input.color.a;
half3 soil = input.color.rgb * texAlbedo * lerp(0.84, 1.14, broad) * lerp(0.9, 1.07, grain);
half3 rocky = input.color.rgb * texAlbedo * lerp(0.72, 1.45, stone);
albedo = lerp(soil, rocky, rock);
roughness = saturate(lerp(0.93, 0.84, rock) * roughDetail);
metallic = texOrm.b;
bump = 0.006 + rock * 0.014;
}
else if (mode == 2)
{
albedo = _BaseColor.rgb * texAlbedo * lerp(0.65, 1.6, stone);
roughness = texOrm.g;
bump = _Relief;
}
else if (mode == 3)
{
albedo = _BaseColor.rgb * texAlbedo * lerp(0.82, 1.3, stone);
roughness = texOrm.g;
bump = _Relief;
}
else if (mode == 4)
{
float broad = LuneNoise(world * 0.8);
albedo = _BaseColor.rgb * texAlbedo * lerp(0.82, 1.12, broad);
albedo = lerp(albedo, half3(0.12, 0.17, 0.075) * texAlbedo, smoothstep(0.54, 0.8, broad) * 0.16);
roughness = texOrm.g;
bump = _Relief;
}
else if (mode == 5)
{
float ridges = LuneNoise(world * float3(13, 1.2, 13));
float grain = LuneNoise(world * 21.0);
albedo = _BaseColor.rgb * texAlbedo * lerp(0.45, 1.2, ridges) * lerp(0.8, 1.08, grain);
relief = ridges;
roughness = texOrm.g;
bump = 0.025;
}
else if (mode == 6)
{
float broad = LuneNoise(world * 0.8);
albedo = _BaseColor.rgb * texAlbedo * lerp(0.75, 1.2, broad);
roughness = texOrm.g;
// Godot BACKLIGHT (albedo * 0.32): light through thin leaves, approximated as a small glow.
emission += albedo * 0.32 * saturate(dot(-n, _MainLightPosition.xyz)) * _MainLightColor.rgb;
}
else if (mode == 7)
{
float2 flow = world.xz * 0.75 + float2(_Time.y * 0.037, -_Time.y * 0.075);
float waves = LuneNoise2(flow) * 0.6 + LuneNoise2(flow * 2.13 + float2(-_Time.y * 0.06, 0)) * 0.3 + LuneNoise2(flow * 4.17) * 0.1;
float fresnel = pow(1.0 - saturate(dot(n, GetWorldSpaceNormalizeViewDir(world))), 4.0);
// Godot river colours (U2/U3) with the water set as detail.
albedo = lerp(half3(0.022, 0.094, 0.098), half3(0.042, 0.145, 0.15), waves) * texAlbedo + half3(0.045, 0.07, 0.075) * fresnel;
roughness = lerp(texOrm.g, 0.22, fresnel * 0.5);
metallic = texOrm.b;
relief = waves;
bump = 0.055;
}
else if (mode == 8)
{
float across = abs(input.uv.y - 0.5) * 2.0;
float core = 1.0 - smoothstep(0.15, 0.7, across);
float lip = smoothstep(0.72, 0.95, across);
float flow = frac(input.uv.x * 0.35 + _Time.y * 0.45 + _Seed);
float pulse = smoothstep(0.0, 0.12, flow) * (1.0 - smoothstep(0.12, 0.45, flow));
float live = _Energy;
if (_Flicker > 0.0)
live *= lerp(1.0, step(0.4, LuneNoise(float3(_Time.y * 4.0, input.uv.x * 0.3, _Seed))), _Flicker);
half3 carved = half3(0.08, 0.08, 0.1) + half3(0.3, 0.29, 0.27) * lip;
albedo = lerp(carved, _BaseColor.rgb * 0.2, core * 0.6);
roughness = lerp(0.85, 0.3, core);
metallic = 0.0;
ao = 1.0;
emission = _BaseColor.rgb * core * live * (0.55 + pulse * 1.6);
}
else if (mode == 9)
{
float rim = pow(1.0 - saturate(dot(n, GetWorldSpaceNormalizeViewDir(world))), 2.5);
albedo = _BaseColor.rgb * 0.35;
roughness = 0.08;
metallic = 0.0;
ao = 1.0;
emission = _EmissionColor.rgb * (0.75 + rim * 1.2);
}
else
{
// plain: keep the material's own metal/roughness; the set adds variation around them.
roughness = saturate(_Roughness * roughDetail);
metallic = saturate(_Metallic * metalDetail);
}
if (_VertexColor > 0.5)
albedo *= input.color.rgb;
// Tangent-space normal of the PBR set (lane tier only), triplanar, on top of the
// geometric normal. UV characters (Ringborke) already blended their normal above.
if (_Quality > 0.5 && _UvMaps < 0.5)
{
// Whiteout blend of the three projections into world space.
nX = half3(nX.xy + n.zy, abs(nX.z) * n.x);
nY = half3(nY.xy + n.xz, abs(nY.z) * n.y);
nZ = half3(nZ.xy + n.xy, abs(nZ.z) * n.z);
n = normalize(nX.zyx * weights.x + nY.xzy * weights.y + nZ.xyz * weights.z);
}
if (bump > 0.0)
{
// Relief from the pattern's screen derivatives (surface gradient, as in Godot).
float3 dx = ddx(world);
float3 dy = ddy(world);
float3 rx = cross(dy, n);
float3 ry = cross(n, dx);
float det = dot(dx, rx);
float3 gradient = ddx(relief) * rx + ddy(relief) * ry;
n = normalize(abs(det) * n - sign(det) * gradient * bump);
}
InputData inputData = (InputData)0;
inputData.positionWS = world;
inputData.positionCS = input.positionCS;
inputData.normalWS = n;
inputData.viewDirectionWS = GetWorldSpaceNormalizeViewDir(world);
inputData.shadowCoord = TransformWorldToShadowCoord(world);
inputData.fogCoord = input.fogFactor;
inputData.bakedGI = SampleSH(n);
inputData.normalizedScreenSpaceUV = GetNormalizedScreenSpaceUV(input.positionCS);
inputData.shadowMask = half4(1, 1, 1, 1);
SurfaceData surface = (SurfaceData)0;
surface.albedo = albedo;
surface.metallic = metallic;
surface.specular = half3(0, 0, 0);
surface.smoothness = 1.0 - roughness;
surface.occlusion = ao;
surface.emission = emission;
surface.alpha = 1.0;
surface.normalTS = half3(0, 0, 1);
half4 color = UniversalFragmentPBR(inputData, surface);
color.rgb = MixFog(color.rgb, inputData.fogCoord);
return color;
}
ENDHLSL
}
Pass
{
Name "ShadowCaster"
Tags { "LightMode" = "ShadowCaster" }
ZWrite On
ZTest LEqual
ColorMask 0
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex ShadowVert
#pragma fragment ShadowFrag
#pragma multi_compile_instancing
#pragma multi_compile_vertex _ _CASTING_PUNCTUAL_LIGHT_SHADOW
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Shadows.hlsl"
float3 _LightDirection;
float3 _LightPosition;
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 ShadowVert(Attributes input) : SV_POSITION
{
UNITY_SETUP_INSTANCE_ID(input);
float3 positionWS = LuneWind(TransformObjectToWorld(input.positionOS.xyz));
float3 normalWS = TransformObjectToWorldNormal(input.normalOS);
#if _CASTING_PUNCTUAL_LIGHT_SHADOW
float3 lightDirectionWS = normalize(_LightPosition - positionWS);
#else
float3 lightDirectionWS = _LightDirection;
#endif
float4 positionCS = TransformWorldToHClip(ApplyShadowBias(positionWS, normalWS, lightDirectionWS));
return ApplyShadowClamping(positionCS);
}
half4 ShadowFrag() : SV_Target { return 0; }
ENDHLSL
}
Pass
{
Name "DepthOnly"
Tags { "LightMode" = "DepthOnly" }
ZWrite On
ColorMask R
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex DepthVert
#pragma fragment DepthFrag
#pragma multi_compile_instancing
struct Attributes
{
float4 positionOS : POSITION;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
float4 DepthVert(Attributes input) : SV_POSITION
{
UNITY_SETUP_INSTANCE_ID(input);
return TransformWorldToHClip(LuneWind(TransformObjectToWorld(input.positionOS.xyz)));
}
half DepthFrag() : SV_Target { return 0; }
ENDHLSL
}
Pass
{
Name "DepthNormals"
Tags { "LightMode" = "DepthNormals" }
ZWrite On
Cull [_Cull]
HLSLPROGRAM
#pragma target 3.0
#pragma vertex NormalsVert
#pragma fragment NormalsFrag
#pragma multi_compile_instancing
#pragma multi_compile_fragment _ _GBUFFER_NORMALS_OCT
struct Attributes
{
float4 positionOS : POSITION;
float3 normalOS : NORMAL;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
struct Varyings
{
float4 positionCS : SV_POSITION;
float3 normalWS : TEXCOORD0;
};
Varyings NormalsVert(Attributes input)
{
UNITY_SETUP_INSTANCE_ID(input);
Varyings output;
output.positionCS = TransformWorldToHClip(LuneWind(TransformObjectToWorld(input.positionOS.xyz)));
output.normalWS = TransformObjectToWorldNormal(input.normalOS);
return output;
}
half4 NormalsFrag(Varyings input) : SV_Target
{
float3 normalWS = NormalizeNormalPerPixel(input.normalWS);
#if defined(_GBUFFER_NORMALS_OCT)
float2 octNormalWS = PackNormalOctQuadEncode(normalWS);
float2 remapped = saturate(octNormalWS * 0.5 + 0.5);
return half4(PackFloat2To888(remapped), 0.0);
#else
return half4(normalWS, 0.0);
#endif
}
ENDHLSL
}
}
}