学习笔记:分型+有机品种

分型中文链接:链接,英文链接:链接
有机品种中文链接:链接,英文链接:链接

这一节主要学习了,从简单到复杂,构建分型,思路很重要!
知识点:采用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
在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值