// WL-816j2 — ① 띠 텍스처·머티리얼·물 머티리얼 E 를 만든다 (에디트 모드) // 🔴 원본 셰이더/FI/씬 0줄. 전부 새 에셋(`Assets/WL/Look/Farm/**`)이다. using System.Text; using UnityEngine; using UnityEditor; public static class WL816j2_Build { const string MatDir = "Assets/WL/Look/Farm/Materials/"; const string TexDir = "Assets/WL/Look/Farm/Textures/"; const string TexPath = TexDir + "WL_ShoreFoam.png"; const string FoamMat = MatDir + "Farm_ShoreFoam.mat"; const string R_FoamScale = "Vector1_11671d7a4cdb4d059a6c69e39f3f7b3f"; const string R_FoamSpeed = "Vector1_3089b6a325a44c1686907af4ee3b0776"; const string R_FoamDistance = "Vector1_101dc4546b684d40bc2ff2fc59ab43d5"; const string R_FoamEdge = "Vector1_34f757bec6b8422b9aef3b9d97117a6e"; const string C_Light = "Color_1ec6cccf8ead489ebb917674f7e8b3b1"; const string C_Foam = "Color_e1f155248d144786a62fc1585ca21e9a"; const string C_FoamShadow = "Color_2323d69962e04c499c5d5f0925432b55"; public static void Run() { var sb = new StringBuilder(); // ── ① 거품 띠 텍스처 (가로 = 해안을 따라 · 세로 v0 = 물가, v1 = 바깥) ── System.IO.Directory.CreateDirectory(TexDir); const int W = 512, H = 128; var tex = new Texture2D(W, H, TextureFormat.RGBA32, false); var px = new Color32[W * H]; for (int y = 0; y < H; y++) { float v = y / (float)(H - 1); float t = 1f - v; // 물가에서 1, 바깥에서 0 for (int x = 0; x < W; x++) { float n = Fbm(x, y, W); // 0~1 · 가로로 이어진다 // 🔴 Mathf.SmoothStep(a,b,t) 는 「a~b 사이 보간」이지 GLSL smoothstep 이 아니다(816j2 에서 이걸로 한 번 틀렸다). float k = Mathf.Clamp01((t - 0.18f) / 0.30f + (n - 0.5f) * 1.0f); float a = k * k * (3f - 2f * k); // 물가 = 1 · 바깥으로 가며 거품이 부서진다 px[y * W + x] = new Color32(255, 255, 255, (byte)Mathf.RoundToInt(Mathf.Clamp01(a) * 255f)); } } tex.SetPixels32(px); tex.Apply(); System.IO.File.WriteAllBytes(TexPath, tex.EncodeToPNG()); Object.DestroyImmediate(tex); AssetDatabase.ImportAsset(TexPath, ImportAssetOptions.ForceUpdate); var imp = AssetImporter.GetAtPath(TexPath) as TextureImporter; if (imp != null) { imp.textureType = TextureImporterType.Default; imp.alphaIsTransparency = true; imp.alphaSource = TextureImporterAlphaSource.FromInput; imp.wrapModeU = TextureWrapMode.Repeat; imp.wrapModeV = TextureWrapMode.Clamp; imp.filterMode = FilterMode.Bilinear; imp.mipmapEnabled = true; imp.maxTextureSize = 512; imp.SaveAndReimport(); } sb.AppendLine("띠 텍스처 = " + TexPath + " (" + W + "×" + H + " · U 반복 / V 고정 · mip on)"); // ── ② 띠 머티리얼 (URP Unlit · 반투명 · ZWrite off · 물보다 뒤에) ── var sh = Shader.Find("Universal Render Pipeline/Unlit"); if (sh == null) { Debug.LogError("[816j2] URP Unlit 셰이더 없음"); return; } var fm = AssetDatabase.LoadAssetAtPath(FoamMat); if (fm == null) { fm = new Material(sh); AssetDatabase.CreateAsset(fm, FoamMat); } fm.shader = sh; fm.SetTexture("_BaseMap", AssetDatabase.LoadAssetAtPath(TexPath)); fm.SetColor("_BaseColor", new Color(1f, 1f, 1f, 0.92f)); fm.SetFloat("_Surface", 1f); // Transparent fm.SetFloat("_Blend", 0f); // Alpha fm.SetFloat("_AlphaClip", 0f); fm.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.SrcAlpha); fm.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); fm.SetFloat("_ZWrite", 0f); fm.SetFloat("_Cull", (float)UnityEngine.Rendering.CullMode.Off); fm.SetFloat("_QueueOffset", 50f); // 물(3000)보다 뒤 fm.SetFloat("_QueueControl", 0f); // 0 = 사용자 지정(URP 가 자동으로 되돌리지 못하게) fm.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); fm.DisableKeyword("_ALPHATEST_ON"); fm.DisableKeyword("_ALPHAPREMULTIPLY_ON"); fm.renderQueue = 3050; // 물(3000)보다 뒤에 그린다 EditorUtility.SetDirty(fm); sb.AppendLine("띠 머티리얼 = " + FoamMat + " · shader " + fm.shader.name + " · queue " + fm.renderQueue); // ── ③ 물 머티리얼 E = D 복사본 + 무늬 선 약화 + 깊이 거품 낮춤 ── string dst = MatDir + "Farm_Water_WL_E.mat"; if (AssetDatabase.LoadAssetAtPath(dst) == null) { AssetDatabase.CopyAsset(MatDir + "Farm_Water_WL_D.mat", dst); AssetDatabase.ImportAsset(dst); } var e = AssetDatabase.LoadAssetAtPath(dst); // ⓐ 무늬를 1칸 2.5 m → **1.0 m** 로 더 잘게(「금 간 유리」 → 잔물결) · 흐름 0.8 m/s 유지 const float cellM = 1.0f; float size = 1000f / (cellM * 6f); e.SetFloat("Vector1_07238257334b4a149e675e2451801030", size); // Size e.SetFloat("Vector1_99cc56b5c6fd474082f07f00a6df94f9", 0.3085f / size); // Strength e.SetFloat("Vector1_8e22a98f75c94b218c49e6c4805e799d", 0.8f * size / 1000f); // Speed e.SetFloat("Vector1_ad7e0fcd37f44d68b68de6f06bb43b96", 0.8f * size / 1000f); // NoiseSpeed // ⓑ 선 세기 — 밝은 쪽(LightColor)은 물색 가까이, 어두운 쪽(FoamShadowColor)은 흰색으로 없앤다 e.SetColor(C_Light, new Color(0.42f, 0.72f, 0.845f, 1f)); e.SetColor(C_FoamShadow, Color.white); // ⓒ 깊이 기반 거품은 **끈다** — 둘레 띠가 대신한다(둘 다 켜면 분홍 얼룩이 된다) e.SetColor(C_Foam, e.GetColor("Color_9af4ad59934f40d1ae6565e6ab22c45e")); // = WaterColor e.SetFloat(R_FoamDistance, 2.2f); e.SetFloat(R_FoamEdge, 4f); EditorUtility.SetDirty(e); sb.AppendLine("물 E = " + dst + " · Size " + size.ToString("F2") + "(무늬 1칸 " + cellM + " m)" + " · LightColor " + e.GetColor(C_Light) + " · FoamShadowColor 흰색" + " · FoamColor = 물색(깊이 거품 off) · FoamDistance " + e.GetFloat(R_FoamDistance) + " · FoamEdge " + e.GetFloat(R_FoamEdge)); // ── ④ SO ── var cfg = AssetDatabase.LoadAssetAtPath( "Assets/WL/Look/Farm/Resources/WL/WLIslandLookSettings.asset"); var so = new SerializedObject(cfg); so.FindProperty("waterTo").objectReferenceValue = e; so.FindProperty("shoreFoamMaterial").objectReferenceValue = fm; so.FindProperty("shoreFoamEnabled").intValue = 1; so.ApplyModifiedPropertiesWithoutUndo(); EditorUtility.SetDirty(cfg); AssetDatabase.SaveAssets(); sb.AppendLine("SO waterTo = " + cfg.waterTo.name + " · shoreFoamMaterial = " + cfg.shoreFoamMaterial.name + " · 폭 " + cfg.shoreFoamWidth + " m · 격자 " + cfg.shoreFoamCell + " m"); System.IO.File.WriteAllText("AgentScripts/WL816j2_BUILD.txt", sb.ToString()); Debug.Log("[816j2 build]\n" + sb); } // 가로로 이어지는(타일링) 값 노이즈 fbm static float Fbm(int x, int y, int W) { float s = 0f, amp = 0.5f; int period = 32; for (int o = 0; o < 3; o++) { s += amp * Value(x, y, W, period); amp *= 0.5f; period *= 2; } return Mathf.Clamp01(s + 0.25f); } static float Value(int x, int y, int W, int period) { float fx = x / (float)period, fy = y / (float)period; int x0 = Mathf.FloorToInt(fx), y0 = Mathf.FloorToInt(fy); float tx = fx - x0, ty = fy - y0; tx = tx * tx * (3f - 2f * tx); ty = ty * ty * (3f - 2f * ty); int wrap = Mathf.Max(1, W / period); float a = H2(Mod(x0, wrap), y0), b = H2(Mod(x0 + 1, wrap), y0); float c = H2(Mod(x0, wrap), y0 + 1), d = H2(Mod(x0 + 1, wrap), y0 + 1); return Mathf.Lerp(Mathf.Lerp(a, b, tx), Mathf.Lerp(c, d, tx), ty); } static int Mod(int a, int m) { int r = a % m; return r < 0 ? r + m : r; } static float H2(int x, int y) { uint h = (uint)(x * 374761393 + y * 668265263 + 1442695040); h = (h ^ (h >> 13)) * 1274126177u; return ((h ^ (h >> 16)) & 0xFFFFFF) / (float)0xFFFFFF; } }