分型中文链接:链接,英文链接:链接
有机品种中文链接:链接,英文链接:链接
这一节主要学习了,从简单到复杂,构建分型,思路很重要!
知识点:采用Job系统和Burst优化CPU,采用实例化优化GPU。
代码:
FractalPart:
using Unity.Mathematics;
using UnityEngine;
public struct FractalPart
{
public float3 worldPosition;
public Quaternion rotation, worldRotation;
public float maxSagAngle, spinAngle, spinVelocity;
}
Fractal2:
using Unity.Burst;
using Unity.Collections;
using Unity.Jobs;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
//using float4x4 = Unity.Mathematics.float4x4;
using quaternion = Unity.Mathematics.quaternion;
using Random = UnityEngine.Random;
public class Fractal2 : MonoBehaviour
{
[BurstCompile(FloatPrecision.Standard, FloatMode.Fast, CompileSynchronously = true)]
struct UpdateFractalLevelJob : IJobFor
{
//public float spinAngleDelta;
public float scale;
public float deltaTime;
[ReadOnly]
public NativeArray<FractalPart> parents;
public NativeArray<FractalPart> parts;
[WriteOnly]
public NativeArray<float3x4> matrices;
public void Execute(int i)
{
FractalPart parent = parents[i / 5];
FractalPart part = parts[i];
part.spinAngle += part.spinVelocity * deltaTime;
float3 upAxis = mul(mul(parent.worldRotation, part.rotation), up());
float3 sagAxis = cross(up(), upAxis);
//sagAxis = normalize(sagAxis);
float sagMagnitude = length(sagAxis);
quaternion baseRotation;
if (sagMagnitude > 0f)
{
quaternion sagRotation = quaternion.AxisAngle(sagAxis, part.maxSagAngle * sagMagnitude);
baseRotation = mul(sagRotation, parent.worldRotation);
}
else
{
baseRotation = parent.worldRotation;
}
part.worldRotation = mul(baseRotation, mul(part.rotation, quaternion.RotateY(part.spinAngle)));
//part.worldPosition = parent.worldPosition + (Vector3)mul(parent.worldRotation, 1.5f * scale * part.direction);
part.worldPosition = parent.worldPosition + mul(part.worldRotation, float3(0f, 1.5f * scale, 0f));
parts[i] = part;
float3x3 r = float3x3(part.worldRotation) * scale;
matrices[i] = float3x4(r.c0, r.c1, r.c2, part.worldPosition);
//matrices[i] = Matrix4x4.TRS(part.worldPosition, part.worldRotation, float3(scale));
}
}
[SerializeField]
Mesh mesh = default;
[SerializeField]
Mesh leafMesh = default;
[SerializeField]
Material material = default;
[SerializeField]
Gradient gradientA = default, gradientB = default;
[SerializeField]
Color leafColorA = default, leafColorB = default;
[SerializeField, Range(0f, 90f)]
float maxSagAngleA = 15f, maxSagAngleB = 25f;
[SerializeField, Range(0f, 90f)]
float spinSpeedA = 20f, spinSpeedB = 25f;
[SerializeField, Range(0f, 1f)]
float reverseSpinChance = 0.25f;
[SerializeField, Range(3, 8)]
int depth = 4;
//static Vector3[] directions =
//{
// up(),right(),left(),forward(),back()
//};
static Quaternion[] rotations =
{
quaternion.identity,
quaternion.RotateZ(-0.5f*PI),quaternion.RotateZ(0.5f*PI),
quaternion.RotateX(0.5f*PI), quaternion.RotateX(-0.5f*PI),
};
//FractalPart[][] parts;
NativeArray<FractalPart>[] parts;
//Matrix4x4[][] matrices;
NativeArray<float3x4>[] matrices;
ComputeBuffer[] matricesBuffers;
static readonly int
colorAId = Shader.PropertyToID("_ColorA"),
colorBId = Shader.PropertyToID("_ColorB"),
matricesId = Shader.PropertyToID("_Matrices"),
sequenceNumbersId = Shader.PropertyToID("_SequenceNumbers");
static MaterialPropertyBlock propertyBlock;
Vector4[] sequenceNumbers;
private void OnEnable()
{
parts = new NativeArray<FractalPart>[depth];
matrices = new NativeArray<float3x4>[depth];
matricesBuffers = new ComputeBuffer[depth];
sequenceNumbers = new Vector4[depth];
int stride = 12 * 4;
for (int i = 0, length = 1; i < parts.Length; i++, length *= 5)
{
parts[i] = new NativeArray<FractalPart>(length, Allocator.Persistent);
matrices[i] = new NativeArray<float3x4>(length, Allocator.Persistent);
matricesBuffers[i] = new ComputeBuffer(length, stride);
sequenceNumbers[i] = new Vector4(Random.value, Random.value, Random.value, Random.value);
}
parts[0][0] = CreatePart(0);
for (int i = 1; i < parts.Length; i++)
{
NativeArray<FractalPart> levelParts = parts[i];
for (int j = 0; j < levelParts.Length; j += 5)
{
for (int k = 0; k < 5; k++)
{
levelParts[j + k] = CreatePart(k);
}
}
}
if (propertyBlock == null)
{
propertyBlock = new MaterialPropertyBlock();
}
}
private void OnDisable()
{
for (int i = 0; i < matricesBuffers.Length; i++)
{
matricesBuffers[i].Release();
parts[i].Dispose();
matrices[i].Dispose();
}
parts = null;
matrices = null;
matricesBuffers = null;
sequenceNumbers = null;
}
private void OnValidate()
{
if (parts != null && enabled)
{
OnDisable();
OnEnable();
}
}
private FractalPart CreatePart(int childIndex)
{
return new FractalPart
{
maxSagAngle = radians(Random.Range(maxSagAngleA, maxSagAngleB)),
//direction = directions[childIndex],
rotation = rotations[childIndex],
spinVelocity = (Random.value < reverseSpinChance ? -1f : 1f) * radians(Random.Range(spinSpeedA, spinSpeedB))
};
}
private void Update()
{
//Quaternion deltaRotation = Quaternion.Euler(0f, 22.5f * Time.deltaTime, 0f);
//float spinAngleDelta = 0.125f * PI * Time.deltaTime;
float deltaTime = Time.deltaTime;
FractalPart rootPart = parts[0][0];
//rootPart.rotation *= deltaRotation;
rootPart.spinAngle += rootPart.spinVelocity * deltaTime;
//rootPart.worldRotation = rootPart.rotation * Quaternion.Euler(0f, rootPart.spinAngle, 0f);
//添加游戏对象的转换
rootPart.worldRotation = mul(transform.rotation, mul(rootPart.rotation, quaternion.RotateY(rootPart.spinAngle)));
rootPart.worldPosition = transform.position;
parts[0][0] = rootPart;
float objectScale = transform.lossyScale.x;
float3x3 r = float3x3(rootPart.worldRotation) * objectScale;
matrices[0][0] = float3x4(r.c0, r.c1, r.c2, rootPart.worldPosition);
//matrices[0][0] = Matrix4x4.TRS(rootPart.worldPosition, rootPart.worldRotation, float3(objectScale));
float scale = objectScale;
JobHandle jobHandle = default;
for (int i = 1; i < parts.Length; i++)
{
scale *= 0.5f;
jobHandle = new UpdateFractalLevelJob
{
//spinAngleDelta = spinAngleDelta,
deltaTime = deltaTime,
scale = scale,
parents = parts[i - 1],
parts = parts[i],
matrices = matrices[i]
}.ScheduleParallel(parts[i].Length, 5, jobHandle);
//job.Schedule(parts[i].Length, default).Complete();
//jobHandle = job.Schedule(parts[i].Length, jobHandle);
//NativeArray<FractalPart> parentParts = parts[i - 1];
//NativeArray<FractalPart> levelParts = parts[i];
//NativeArray<Matrix4x4> levelMatrices = matrices[i];
//for (int j = 0; j < parts[i].Length; j++)
//{
// job.Execute(j);
// Transform parentTf = parentParts[j / 5].transform;
// //FractalPart parent = parentParts[j / 5];
// //FractalPart part = levelParts[j];
// part.rotation *= deltaRotation;
// //part.spinAngle += spinAngleDelta;
// //part.worldRotation = parent.worldRotation * (part.rotation * Quaternion.Euler(0f, part.spinAngle, 0f));
// //part.worldPosition = parent.worldPosition + parent.worldRotation * (1.5f * scale * part.direction);
// //levelParts[j] = part;
// //levelMatrices[j] = Matrix4x4.TRS(part.worldPosition, part.worldRotation, scale * Vector3.one);
//}
}
jobHandle.Complete();
var bounds = new Bounds(rootPart.worldPosition, 3f * objectScale * Vector3.one);
int leafIndex = matricesBuffers.Length - 1;
for (int i = 0; i < matricesBuffers.Length; i++)
{
ComputeBuffer buffer = matricesBuffers[i];
buffer.SetData(matrices[i]);
Color colorA, colorB;
Mesh instanceMesh;
if (i == leafIndex)
{
colorA = leafColorA;
colorB = leafColorB;
instanceMesh = leafMesh;
}
else
{
float gradientInterpolator = i / (matricesBuffers.Length - 2f);
colorA = gradientA.Evaluate(gradientInterpolator);
colorB = gradientB.Evaluate(gradientInterpolator);
instanceMesh = mesh;
}
propertyBlock.SetColor(colorAId, colorA);
propertyBlock.SetColor(colorBId, colorB);
propertyBlock.SetBuffer(matricesId, buffer);
propertyBlock.SetVector(sequenceNumbersId, sequenceNumbers[i]);
Graphics.DrawMeshInstancedProcedural(instanceMesh, 0, material, bounds, buffer.count, propertyBlock);
}
}
}
Shader:
FractalGPU.hlsl
#if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
StructuredBuffer<float3x4> _Matrices;
#endif
void ConfigureProcedural() {
#if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
float3x4 m = _Matrices[unity_InstanceID];
unity_ObjectToWorld._m00_m01_m02_m03 = m._m00_m01_m02_m03;
unity_ObjectToWorld._m10_m11_m12_m13 = m._m10_m11_m12_m13;
unity_ObjectToWorld._m20_m21_m22_m23 = m._m20_m21_m22_m23;
unity_ObjectToWorld._m30_m31_m32_m33 = float4(0.0, 0.0, 0.0, 1.0);
#endif
}
float4 _ColorA, _ColorB;
float4 _SequenceNumbers;
float4 GetFractalColor()
{
#if defined(UNITY_PROCEDURAL_INSTANCING_ENABLED)
//return (unity_InstanceID % 10) / 9.0;
float4 color;
color.rgb = lerp(_ColorA.rgb, _ColorB.rgb, frac(unity_InstanceID * _SequenceNumbers.x + _SequenceNumbers.y));
color.a = lerp(_ColorA, _ColorB, frac(unity_InstanceID * _SequenceNumbers.z + _SequenceNumbers.w));
return color;
#else
return _ColorA;
#endif
}
void ShaderGraphFunction_float(float3 In, out float3 Out, out float4 FractalColor) {
Out = In;
FractalColor = GetFractalColor();
}
void ShaderGraphFunction_half(half3 In, out half3 Out, out float4 FractalColor) {
Out = In;
FractalColor = GetFractalColor();
}
Fractal URP GPU:shader graph
和ComputeShader你的差不多,这里把光滑度储存在颜色的a通道了而已。
第一个自定义节点的Body:
#pragma instancing_options procedural:ConfigureProcedural
#pragma editor_sync_compilation
Out = In;
这里的自定义节点:用的是上面的FractalGPU.hlsl