Files
Fishing2/Packages/com.jbooth.microsplat.procedural-texture/MicroSplatProceduralTextureUtil.cs
2025-06-04 09:09:39 +08:00

225 lines
8.1 KiB
C#

using UnityEngine;
using System.Collections;
// for sampling texturing from the CPU. If your using jobs, there's a jobified version
// included in a zip file next to this version.
namespace JBooth.MicroSplat
{
public class MicroSplatProceduralTextureUtil
{
public enum NoiseUVMode
{
UV = 0,
World,
Triplanar
}
static float PCFilter(int index, float height, float slope, float cavity, float flow, Vector3 worldPos, Vector2 uv,
Color bMask, Color bMask2, out int texIndex, Vector3 pN, MicroSplatProceduralTextureConfig config,
Texture2D procTexNoise, NoiseUVMode noiseMode, Vector4 noiseWeights)
{
var layer = config.layers [index];
Vector2 noiseUV = uv;
Color noise = new Color (0, 0, 0, 0);
if (noiseMode == NoiseUVMode.Triplanar)
{
Vector2 nUV0 = new Vector2 (worldPos.z, worldPos.y) * 0.002f * layer.noiseFrequency + new Vector2 (layer.noiseOffset, layer.noiseOffset);
Vector2 nUV1 = new Vector2 (worldPos.x, worldPos.z) * 0.002f * layer.noiseFrequency + new Vector2 (layer.noiseOffset + 0.31f, layer.noiseOffset + 0.31f);
Vector2 nUV2 = new Vector2 (worldPos.x, worldPos.y) * 0.002f * layer.noiseFrequency + new Vector2 (layer.noiseOffset + 0.71f, layer.noiseOffset + 0.71f);
Color noise0 = procTexNoise.GetPixelBilinear (nUV0.x, nUV0.y);
Color noise1 = procTexNoise.GetPixelBilinear (nUV1.x, nUV1.y);
Color noise2 = procTexNoise.GetPixelBilinear (nUV2.x, nUV2.y);
noise = noise0 * pN.x + noise1 * pN.y + noise2 * pN.z;
}
else if (noiseMode == NoiseUVMode.UV)
{
noise = procTexNoise.GetPixelBilinear (noiseUV.x * layer.noiseFrequency + layer.noiseOffset, noiseUV.y * layer.noiseFrequency + layer.noiseOffset);
}
else if (noiseMode == NoiseUVMode.World)
{
noise *= procTexNoise.GetPixelBilinear (worldPos.x * 0.002f * layer.noiseFrequency + layer.noiseOffset, worldPos.z * 0.002f * layer.noiseFrequency + layer.noiseOffset);
}
// unpack
noise.r = noise.r * 2 - 1;
noise.g = noise.g * 2 - 1;
float h0 = layer.heightCurve.Evaluate (height);
float s0 = layer.slopeCurve.Evaluate (slope);
float c0 = layer.cavityMapCurve.Evaluate (cavity);
float f0 = layer.erosionMapCurve.Evaluate (flow);
float mul = noiseWeights.w;
float noises = noise.r * noiseWeights.x + noise.g * noiseWeights.y + noise.b * noiseWeights.z;
h0 *= 1.0f + Mathf.Max(0, Mathf.Lerp (layer.noiseRange.x, layer.noiseRange.y, noises) * mul);
s0 *= 1.0f + Mathf.Max(0, Mathf.Lerp (layer.noiseRange.x, layer.noiseRange.y, noise.g) * mul);
c0 *= 1.0f + Mathf.Max(0, Mathf.Lerp (layer.noiseRange.x, layer.noiseRange.y, noise.b) * mul);
f0 *= 1.0f + Mathf.Max(0, Mathf.Lerp (layer.noiseRange.x, layer.noiseRange.y, noise.a) * mul);
if (!layer.heightActive)
h0 = 1;
if (!layer.slopeActive)
s0 = 1;
if (!layer.cavityMapActive)
c0 = 1;
if (!layer.erosionMapActive)
f0 = 1;
bMask *= layer.biomeWeights;
bMask2 *= layer.biomeWeights2;
// handle 16 mode?
float bWeight = Mathf.Max(Mathf.Max(Mathf.Max(bMask.r, bMask.g), bMask.b), bMask.a);
float bWeight2 = Mathf.Max (Mathf.Max (Mathf.Max (bMask2.r, bMask2.g), bMask2.b), bMask2.a);
texIndex = layer.textureIndex;
return Mathf.Clamp01(h0 * s0 * c0 * f0 * layer.weight * bWeight * bWeight2);
}
public struct Int4
{
public int x;
public int y;
public int z;
public int w;
}
static void PCProcessLayer(ref Vector4 weights, ref Int4 indexes, ref float totalWeight,
int curIdx, float height, float slope, float cavity, float flow, Vector3 worldPos, Vector2 uv,
Color biomeMask, Color biomeMask2, Vector3 pN,
MicroSplatProceduralTextureConfig config,
Texture2D noiseMap, NoiseUVMode noiseMode, Vector4 noiseWeights)
{
int texIndex = 0;
float w = PCFilter(curIdx, height, slope, cavity, flow, worldPos, uv, biomeMask, biomeMask2, out texIndex, pN, config, noiseMap, noiseMode, noiseWeights);
w = Mathf.Min(totalWeight, w);
totalWeight -= w;
// sort
if (w > weights.x)
{
weights.w = weights.z;
weights.z = weights.y;
weights.y = weights.x;
indexes.w = indexes.z;
indexes.z = indexes.y;
indexes.y = indexes.x;
weights.x = w;
indexes.x = texIndex;
}
else if (w > weights.y)
{
weights.w = weights.z;
weights.z = weights.y;
indexes.w = indexes.z;
indexes.z = indexes.y;
weights.y = w;
indexes.y = texIndex;
}
else if (w > weights.z)
{
weights.w = weights.z;
indexes.w = indexes.z;
weights.z = w;
indexes.z = texIndex;
}
else if (w > weights.w)
{
weights.w = w;
indexes.w = texIndex;
}
}
public static void Sample(
Vector2 uv,
Vector3 worldPos,
Vector3 worldNormal,
Vector3 up,
NoiseUVMode noiseUVMode,
Material mat,
MicroSplatProceduralTextureConfig config,
out Vector4 weights,
out Int4 indexes,
Vector4 noiseWeights = new Vector4())
{
weights = new Vector4(0, 0, 0, 0);
int layerCount = config.layers.Count;
Vector2 worldHeightRange = mat.GetVector ("_WorldHeightRange");
float worldheight = worldPos.y;
float height = Mathf.Clamp01((worldheight - worldHeightRange.x) / Mathf.Max(0.1f, (worldHeightRange.y - worldHeightRange.x)));
float slope = 1.0f - Mathf.Clamp01(Vector3.Dot(worldNormal, up) * 0.5f + 0.49f);
float cavity = 0.5f;
float flow = 0.5f;
var cavityMap = mat.HasProperty ("_CavityMap") ? (Texture2D)mat.GetTexture ("_CavityMap") : null;
if (cavityMap != null)
{
var p = cavityMap.GetPixelBilinear(uv.x, uv.y);
cavity = p.g;
flow = p.a;
}
// find 4 highest weights and indexes
indexes = new Int4();
indexes.x = 0;
indexes.y = 1;
indexes.z = 2;
indexes.w = 3;
float totalWeight = 1.0f;
var mask = mat.HasProperty ("_ProcTexBiomeMask") ? (Texture2D)mat.GetTexture ("_ProcTexBiomeMask") : null;
var mask2 = mat.HasProperty ("_ProcTexBiomeMask2") ? (Texture2D)mat.GetTexture ("_ProcTexBiomeMask2") : null;
Color biomeMask = new Color(1, 1, 1, 1);
Color biomeMask2 = new Color (1, 1, 1, 1);
if (mask != null)
{
biomeMask = mask.GetPixelBilinear(uv.x, uv.y);
}
if (mask2 != null)
{
biomeMask2 = mask2.GetPixelBilinear(uv.x, uv.y);
}
Vector3 pN = new Vector3(0, 0, 0);
if (noiseUVMode == NoiseUVMode.Triplanar)
{
Vector3 absWN = worldNormal;
absWN.x = Mathf.Abs(absWN.x);
absWN.y = Mathf.Abs(absWN.y);
absWN.z = Mathf.Abs(absWN.z);
pN.x = Mathf.Pow(absWN.x, 4);
pN.y = Mathf.Pow(absWN.y, 4);
pN.z = Mathf.Pow(absWN.z, 4);
float ttl = pN.x + pN.y + pN.z;
pN.x /= ttl;
pN.y /= ttl;
pN.z /= ttl;
}
var noiseMap = mat.HasProperty ("_ProcTexNoise") ? (Texture2D)mat.GetTexture ("_ProcTexNoise") : null;
for (int i = 0; i < layerCount; ++i)
{
PCProcessLayer(ref weights, ref indexes, ref totalWeight, i, height, slope, cavity, flow, worldPos, uv, biomeMask, biomeMask2, pN, config, noiseMap, noiseUVMode, noiseWeights);
if (totalWeight <= 0)
break;
}
}
}
}