summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--3ner.cginc10
-rw-r--r--3ner.shader16
-rw-r--r--Impostor.shader63
-rw-r--r--Scripts/Impostors.cs16
-rw-r--r--features.cginc4
-rw-r--r--globals.cginc14
-rw-r--r--impostor.cginc151
-rw-r--r--pbr.cginc6
8 files changed, 107 insertions, 173 deletions
diff --git a/3ner.cginc b/3ner.cginc
index db10781..9b73a53 100644
--- a/3ner.cginc
+++ b/3ner.cginc
@@ -16,6 +16,7 @@
#include "interpolators.cginc"
#include "ray_marching.cginc"
#include "vertex.cginc"
+#include "impostor.cginc"
v2f vert(appdata v) {
#if defined(SHADOW_CASTER_PASS) && !defined(_SHADOW_CASTER)
@@ -28,6 +29,12 @@ v2f vert(appdata v) {
UNITY_TRANSFER_INSTANCE_ID(v, o);
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+#if defined(_IMPOSTORS)
+ float3 impostorWorldPos = mul(unity_ObjectToWorld, v.vertex);
+ impostor_vert(v.vertex, impostorWorldPos);
+ v.vertex = mul(unity_WorldToObject, float4(impostorWorldPos, 1));
+#endif
+
#if defined(_TESSELLATION)
o.tpos = v.vertex;
#endif
@@ -53,6 +60,7 @@ v2f vert(appdata v) {
deform_normal(o.objPos, o.normal.xyz, o.tangent.xyz);
#endif
deform(o.objPos);
+
propagateObjPos(o);
UNITY_TRANSFER_LIGHTING(o, v.uv1);
@@ -63,7 +71,7 @@ v2f vert(appdata v) {
TRANSFER_SHADOW(o);
#endif
- return o;
+ return o;
}
//ifex _Tessellation_Enabled==0
diff --git a/3ner.shader b/3ner.shader
index 4818c08..b210532 100644
--- a/3ner.shader
+++ b/3ner.shader
@@ -377,6 +377,22 @@ Shader "yum_food/3ner"
[HideInInspector] m_end_Marble("Marble", Float) = 0
//endex
+ //ifex _Impostors_Enabled==0
+ [HideInInspector] m_start_Impostors("Impostors", Float) = 0
+ [ThryToggle(_IMPOSTORS)] _Impostors_Enabled("Enable", Float) = 0
+ _Impostors_Atlas("Atlas", 2D) = "white" {}
+ _Impostors_Normal_Atlas("Normal Atlas", 2D) = "bump" {}
+ _Impostors_Metallic_Gloss_Atlas("Metallic Gloss Atlas", 2D) = "white" {}
+ _Impostors_Depth_Atlas("Depth Atlas", 2D) = "white" {}
+ _Impostors_Grid_Resolution("Grid Resolution", Int) = 5
+ _Impostors_Sphere_Radius("Sphere Radius", Float) = 1.0
+ _Impostors_Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5
+
+ [Toggle] _Impostors_Debug_Mode("Debug Mode", Float) = 0
+ [Toggle] _Impostors_Debug_Depth("Debug Depth", Float) = 0
+ [HideInInspector] m_end_Impostors("Impostors", Float) = 0
+ //endex
+
[HideInInspector] m_end_Gimmicks("Gimmicks", Float) = 0
//ifex _Tessellation_Enabled==0
diff --git a/Impostor.shader b/Impostor.shader
deleted file mode 100644
index f2eb602..0000000
--- a/Impostor.shader
+++ /dev/null
@@ -1,63 +0,0 @@
-Shader "yum_food/Gimmicks/Impostors"
-{
- Properties
- {
- _ImpostorAtlas("Impostor Atlas", 2D) = "white" {}
- _ImpostorDepthAtlas("Impostor Depth Atlas", 2D) = "white" {}
- _GridResolution("Grid Resolution", Int) = 5
- _SphereRadius("Sphere Radius", Float) = 1.0
- _Cutoff("Alpha Cutoff", Range(0, 1)) = 0.5
- _Color("Tint", Color) = (1, 1, 1, 1)
-
- [Toggle] _DebugMode("Debug Mode", Float) = 0
- [Toggle] _DebugDepth("Debug Depth", Float) = 0
-
- [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull", Float) = 0
- [Enum(Off, 0, On, 1)] _ZWrite("ZWrite", Int) = 1
- }
-
- SubShader
- {
- Tags { "RenderType" = "TransparentCutout" "Queue" = "AlphaTest" }
-
- Pass
- {
- Name "FORWARD"
- Tags { "LightMode" = "ForwardBase" }
-
- Cull [_Cull]
- ZWrite [_ZWrite]
-
- CGPROGRAM
- #pragma target 3.0
- #pragma vertex vert
- #pragma fragment frag
- #pragma multi_compile_fog
- #pragma multi_compile_instancing
-
- #include "impostor.cginc"
- ENDCG
- }
-
- Pass
- {
- Name "SHADOW"
- Tags { "LightMode" = "ShadowCaster" }
-
- Cull [_Cull]
-
- CGPROGRAM
- #pragma target 3.0
- #pragma vertex vert
- #pragma fragment frag
- #pragma multi_compile_shadowcaster
- #pragma multi_compile_instancing
-
- #define IMPOSTOR_SHADOW_PASS
- #include "impostor.cginc"
- ENDCG
- }
- }
-
- FallBack "Transparent/Cutout/Diffuse"
-}
diff --git a/Scripts/Impostors.cs b/Scripts/Impostors.cs
index e8513eb..b82dc8f 100644
--- a/Scripts/Impostors.cs
+++ b/Scripts/Impostors.cs
@@ -312,10 +312,10 @@ public class Impostors : MonoBehaviour
var exportSettings = new (Texture2D atlas, TextureExportSettings settings, string materialProp)[]
{
- (albedoAtlas, new TextureExportSettings("albedo", mipmaps: true, sRGB: true, alphaTransparency: true), "_ImpostorAtlas"),
- (normalAtlas, new TextureExportSettings("normal", mipmaps: true, sRGB: false), "_ImpostorNormalAtlas"),
- (metallicGlossAtlas, new TextureExportSettings("metallic_gloss", mipmaps: true, sRGB: false), "_ImpostorMetallicGlossAtlas"),
- (depthAtlas, new TextureExportSettings("depth", isEXR: true, mipmaps: false, sRGB: false, filter: FilterMode.Bilinear, uncompressed: true), "_ImpostorDepthAtlas")
+ (albedoAtlas, new TextureExportSettings("albedo", mipmaps: true, sRGB: true, alphaTransparency: true), "_Impostors_Atlas"),
+ (normalAtlas, new TextureExportSettings("normal", mipmaps: true, sRGB: false), "_Impostors_Normal_Atlas"),
+ (metallicGlossAtlas, new TextureExportSettings("metallic_gloss", mipmaps: true, sRGB: false), "_Impostors_Metallic_Gloss_Atlas"),
+ (depthAtlas, new TextureExportSettings("depth", isEXR: true, mipmaps: false, sRGB: false, filter: FilterMode.Bilinear, uncompressed: true), "_Impostors_Depth_Atlas")
};
string[] paths = new string[exportSettings.Length];
@@ -336,14 +336,16 @@ public class Impostors : MonoBehaviour
{
DestroyExistingImpostor();
- Shader shader = Shader.Find("yum_food/Gimmicks/Impostors");
+ Shader shader = Shader.Find("yum_food/3ner");
if (shader == null) { Debug.LogError("Shader not found"); return; }
impostorMaterial = new Material(shader);
for (int i = 0; i < textures.Length; i++)
impostorMaterial.SetTexture(exportSettings[i].materialProp, textures[i]);
- impostorMaterial.SetInt("_GridResolution", gridResolution);
- impostorMaterial.SetFloat("_SphereRadius", sphere_radius_);
+ impostorMaterial.SetInt("_Impostors_Grid_Resolution", gridResolution);
+ impostorMaterial.SetFloat("_Impostors_Sphere_Radius", sphere_radius_);
+ impostorMaterial.SetFloat("_Impostors_Enabled", 1);
+ impostorMaterial.SetFloat("_Cull", (float)UnityEngine.Rendering.CullMode.Front);
AssetDatabase.CreateAsset(impostorMaterial, Path.Combine(OutputFolder, $"{baseName}_mat.mat"));
impostorObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
diff --git a/features.cginc b/features.cginc
index d9bc95e..36ee2bf 100644
--- a/features.cginc
+++ b/features.cginc
@@ -121,4 +121,8 @@
#pragma shader_feature_local _RAY_MARCHING_HEXAGON
//endex
+//ifex _Impostors_Enabled==0
+#pragma shader_feature_local _IMPOSTORS
+//endex
+
#endif // __FEATURES_INC
diff --git a/globals.cginc b/globals.cginc
index 10f0556..b1de3d8 100644
--- a/globals.cginc
+++ b/globals.cginc
@@ -181,6 +181,20 @@ float _Parallax_Heightmap_Bias;
float _Parallax_Heightmap_Ray_Marching_Steps;
#endif // _PARALLAX_HEIGHTMAP_RAY_MARCHING
+#if defined(_IMPOSTORS)
+Texture2D _Impostors_Atlas;
+float4 _Impostors_Atlas_TexelSize;
+Texture2D _Impostors_Normal_Atlas;
+Texture2D _Impostors_Metallic_Gloss_Atlas;
+Texture2D _Impostors_Depth_Atlas;
+int _Impostors_Grid_Resolution;
+float _Impostors_Sphere_Radius;
+float _Impostors_Cutoff;
+float _Impostors_Debug_Mode;
+float _Impostors_Debug_Depth;
+float3 _Impostors_Main_Camera_Pos;
+#endif // _IMPOSTORS
+
#if defined(_VERTEX_DEFORMATION)
float _Vertex_Deformation_Slot_0_Enabled;
int _Vertex_Deformation_Slot_0_Opcode;
diff --git a/impostor.cginc b/impostor.cginc
index 4236778..1d5c9b5 100644
--- a/impostor.cginc
+++ b/impostor.cginc
@@ -2,19 +2,10 @@
#define __IMPOSTOR_INC
#include "UnityCG.cginc"
+#include "globals.cginc"
#include "vertex_deformation.hlsl"
-SamplerState bilinear_clamp_s;
-Texture2D _ImpostorAtlas;
-float4 _ImpostorAtlas_TexelSize;
-int _GridResolution;
-float _Cutoff;
-float _SphereRadius;
-float3 _ImpostorMainCameraPos;
-
-float4 _Color;
-float _DebugMode;
-
+// Utility functions for hemispherical octahedral mapping
float2 HemiOctEncode(float3 N) {
N.y = max(N.y, 1e-4);
float3 p = hemi_octahedron_to_plane(normalize(N), 0, float3(1,0,0), float3(0,1,0), 1);
@@ -40,27 +31,7 @@ float3 DirFromCell(float2 cell, float gridRes) {
return HemiOctDecode(uv);
}
-float2 ClampUvInCell(float2 uv) {
- uv = saturate(uv);
- float2 halfTexelInCell = 0.5 * _ImpostorAtlas_TexelSize.xy * (float)_GridResolution;
- return clamp(uv, halfTexelInCell, 1.0 - halfTexelInCell);
-}
-
-float4 SampleAtlasGrad(float2 uv, float2 cell, float2 uvGradX, float2 uvGradY) {
- uv = ClampUvInCell(uv);
-
- float invGridRes = rcp((float)_GridResolution);
- float2 atlasUv = (cell + uv) * invGridRes;
-
- // Important: gradients must be computed in-cell; do not let integer cell offsets affect mip selection.
- return _ImpostorAtlas.SampleGrad(bilinear_clamp_s, atlasUv, uvGradX * invGridRes, uvGradY * invGridRes);
-}
-
-// Branchless barycentric weights in a unit square split into two triangles along the (0,0)-(1,1) diagonal.
-// Returns weights for the 4 square corners:
-// .x = (0,0), .y = (0,1), .z = (1,0), .w = (1,1)
-// Only three weights are non-zero for any point (true barycentric within one triangle),
-// but sampling both possible "middle" vertices avoids triangle-selection discontinuities.
+// Branchless barycentric weights in a unit square split into two triangles
float4 GridCellBarycentric4(float2 p) {
float w00 = 1.0 - max(p.x, p.y);
float w11 = min(p.x, p.y);
@@ -86,40 +57,43 @@ float2 VirtualPlaneUV(float3 frameDir, float3 pivotToCam, float3 vertexToCam) {
return uv * -1.0 + 0.5;
}
-struct appdata {
- float4 vertex : POSITION;
- UNITY_VERTEX_INPUT_INSTANCE_ID
-#ifdef IMPOSTOR_SHADOW_PASS
- float3 normal : NORMAL;
-#endif
-};
+// General purpose ray-sphere intersection
+bool RaySphereIntersect(float3 ro, float3 rayDir, float3 origin, float radius) {
+ float3 originToRo = ro - origin;
+ float b = dot(originToRo, rayDir);
+ float c = dot(originToRo, originToRo) - radius * radius;
+ return (b * b - c) >= 0.0;
+}
-struct v2f {
-#ifdef IMPOSTOR_SHADOW_PASS
- V2F_SHADOW_CASTER;
-#else
- float4 pos : SV_POSITION;
- UNITY_FOG_COORDS(7)
-#endif
- float3 worldPos : TEXCOORD1;
- float3 centerPos : TEXCOORD2;
- UNITY_VERTEX_OUTPUT_STEREO
-};
+#if defined(_IMPOSTORS)
+
+float2 ClampUvInCell(float2 uv) {
+ uv = saturate(uv);
+ float2 halfTexelInCell = 0.5 * _Impostors_Atlas_TexelSize.xy * (float)_Impostors_Grid_Resolution;
+ return clamp(uv, halfTexelInCell, 1.0 - halfTexelInCell);
+}
+
+float4 SampleAtlasGrad(float2 uv, float2 cell, float2 uvGradX, float2 uvGradY) {
+ uv = ClampUvInCell(uv);
-v2f vert(appdata v) {
- v2f o;
- UNITY_SETUP_INSTANCE_ID(v);
- UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
+ float invGridRes = rcp((float)_Impostors_Grid_Resolution);
+ float2 atlasUv = (cell + uv) * invGridRes;
+
+ // Important: gradients must be computed in-cell; do not let integer cell offsets affect mip selection.
+ return _Impostors_Atlas.SampleGrad(bilinear_clamp_s, atlasUv, uvGradX * invGridRes, uvGradY * invGridRes);
+}
+// Billboard vertex transformation for impostors
+void impostor_vert(float4 vertexOS, inout float3 worldPos) {
float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz;
- o.centerPos = center;
+
float3 scale = float3(
length(unity_ObjectToWorld._m00_m10_m20),
length(unity_ObjectToWorld._m01_m11_m21),
length(unity_ObjectToWorld._m02_m12_m22));
-#ifdef IMPOSTOR_SHADOW_PASS
- float3 camPos = _ImpostorMainCameraPos;
+#ifdef SHADOW_CASTER_PASS
+ float3 camPos = _Impostors_Main_Camera_Pos;
#else
float3 camPos = _WorldSpaceCameraPos;
#endif
@@ -128,56 +102,35 @@ v2f vert(appdata v) {
float3 viewWS = normalize(camPos - center);
float3 right, up;
BillboardBasis(viewWS, right, up);
- float3 worldPos = center + v.vertex.x * right * scale.x + v.vertex.y * up * scale.y;
- o.worldPos = worldPos;
-
-#ifdef IMPOSTOR_SHADOW_PASS
- v.vertex = mul(unity_WorldToObject, float4(worldPos, 1));
- v.normal = -viewWS;
- TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
-#else
- o.pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1));
- UNITY_TRANSFER_FOG(o, o.pos);
-#endif
-
- return o;
+ worldPos = center + vertexOS.x * right * scale.x + vertexOS.y * up * scale.y;
}
-// General purpose ray-sphere intersection.
-bool RaySphereIntersect(float3 ro, float3 rayDir, float3 origin, float radius) {
- float3 originToRo = ro - origin;
- float b = dot(originToRo, rayDir);
- float c = dot(originToRo, originToRo) - radius * radius;
- return (b * b - c) >= 0.0;
-}
+// Sample impostor atlas with view-dependent blending
+float4 impostor_frag(float3 worldPos) {
+ // Calculate center in fragment shader to avoid extra interpolator
+ float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz;
-float4 frag(v2f i) : SV_Target {
// Sphere culling first
- float3 viewDir = normalize(i.worldPos - _WorldSpaceCameraPos);
- bool didIntersect = RaySphereIntersect(_WorldSpaceCameraPos, viewDir, i.centerPos, _SphereRadius);
- clip(didIntersect - 0.5);
-
- // Camera position for grid computation (matches billboard orientation)
-#ifdef IMPOSTOR_SHADOW_PASS
- float3 camPos = _ImpostorMainCameraPos;
+#ifdef SHADOW_CASTER_PASS
+ float3 camPos = _Impostors_Main_Camera_Pos;
#else
float3 camPos = _WorldSpaceCameraPos;
#endif
- float3 center = i.centerPos;
+ float3 viewDir = normalize(worldPos - camPos);
+ bool didIntersect = RaySphereIntersect(camPos, viewDir, center, _Impostors_Sphere_Radius);
+ clip(didIntersect - 0.5);
// For lattice lookup, use the camera-to-impostor-center direction (matches billboard orientation).
- // Using the per-fragment view ray (camera→quad point) causes the selected lattice points/weights
- // to vary across the billboard, which reads as "not smooth" while moving around the impostor.
float3x3 worldToObject = (float3x3)unity_WorldToObject;
float3 viewOS = normalize(mul(worldToObject, normalize(camPos - center)));
// Get continuous grid position and find the 4 frames
- float gridRes = (float)_GridResolution;
+ float gridRes = (float)_Impostors_Grid_Resolution;
float2 grid = GridFromDir(viewOS, gridRes);
float2 gridFloor = floor(grid);
float2 gridFrac = frac(grid);
- // Branchless barycentric blend weights (avoids discontinuities along triangle/cell boundaries).
+ // Branchless barycentric blend weights
float4 bw = GridCellBarycentric4(gridFrac);
// Frame cells (square corners)
@@ -194,7 +147,7 @@ float4 frag(v2f i) : SV_Target {
// Compute virtual plane UVs for all 4 frames
float3 pivotToCamOS = mul(worldToObject, camPos - center);
- float3 vertexPosOS = mul(worldToObject, i.worldPos - center);
+ float3 vertexPosOS = mul(worldToObject, worldPos - center);
float3 vertexToCamOS = pivotToCamOS - vertexPosOS;
float2 uv00 = VirtualPlaneUV(dir00, pivotToCamOS, vertexToCamOS);
@@ -209,19 +162,13 @@ float4 frag(v2f i) : SV_Target {
float4 s11 = SampleAtlasGrad(uv11, cell11, ddx(uv11), ddy(uv11));
float4 col = s00 * bw.x + s01 * bw.y + s10 * bw.z + s11 * bw.w;
-#ifndef IMPOSTOR_SHADOW_PASS
- if (_DebugMode > 0.5)
+ if (_Impostors_Debug_Mode > 0.5)
return float4(bw.yz, 0, 1);
-#endif
- col *= _Color;
- clip(col.a - _Cutoff);
-#ifdef IMPOSTOR_SHADOW_PASS
- SHADOW_CASTER_FRAGMENT(i)
-#else
- UNITY_APPLY_FOG(i.fogCoord, col);
+ clip(col.a - _Impostors_Cutoff);
return col;
-#endif
}
-#endif
+#endif // _IMPOSTORS
+
+#endif // __IMPOSTOR_INC
diff --git a/pbr.cginc b/pbr.cginc
index 272e460..8a611d4 100644
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -6,6 +6,7 @@
#include "instancing.cginc"
#include "interpolators.cginc"
#include "texture_utils.cginc"
+#include "impostor.cginc"
struct Pbr {
float4 albedo;
@@ -159,9 +160,14 @@ Pbr getPbr(v2f i) {
float2 uv_parallax = i.uv01.xy;
#endif // _PARALLAX_HEIGHTMAP
+#if defined(_IMPOSTORS)
+ pbr.albedo = impostor_frag(i.worldPos);
+ pbr.albedo *= _Color;
+#else
pbr.albedo = _MainTex.Sample(aniso16_trilinear_repeat_s, uv_parallax * _MainTex_ST.xy + _MainTex_ST.zw);
pbr.albedo *= _Color;
apply_marble(i.worldPos, pbr.albedo.xyz);
+#endif
float3 normal_tangent = UnpackNormal(_BumpMap.Sample(aniso16_trilinear_repeat_s, uv_parallax * _BumpMap_ST.xy));
normal_tangent.xy *= _BumpScale;