Graduate lightning presentation into EveUnity
This commit is contained in:
parent
d1240f9fab
commit
d6bdeb8fe9
20 changed files with 931 additions and 2 deletions
|
|
@ -21,6 +21,9 @@ Retained provider feedback is lowered without becoming Unity authority.
|
|||
The generic `EveUnityShotTrajectoryRenderer` renders provider-authored origin,
|
||||
endpoint, duration, and hit outcome, then expires the visual locally. It never
|
||||
uses Unity collision to infer damage or report a hit back to the provider.
|
||||
The reusable `LightningCompute` renderer and its shader/material bundle live in
|
||||
this runtime package with their original Unity GUIDs. Provider asset bundles may
|
||||
therefore reference the effect without importing Aetheria gameplay code.
|
||||
|
||||
`fields.surface` projection consumes the plugin-owned
|
||||
`org.gamecult.eve.plugin-fields` contracts. `EveFieldsSplatRasterizer` owns the
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 25f11aab7bd34496af779bfab1c22e65
|
||||
folderAsset: yes
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6849f3e40d0646ac992c9c2bc394cb93
|
||||
folderAsset: yes
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Lightning 1
|
||||
m_Shader: {fileID: 4800000, guid: 21dd485e846b4d3a8213cf5551832776, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BranchIntensity: 10
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Intensity: 100
|
||||
- _LeaderIntensity: 100
|
||||
- _LeaderProgress: 2
|
||||
- _LeaderSize: 0.025
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EndColor: {r: 1, g: 0.049999997, b: 0.049999997, a: 1}
|
||||
- _StartColor: {r: 1, g: 0.20970267, b: 0.051643193, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 476f7453551d79b469dda2d6926b93fd
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,139 @@
|
|||
// Each #kernel tells which function to compile; you can have many kernels
|
||||
#pragma kernel UpdateNodes
|
||||
#pragma kernel CreateWidth
|
||||
|
||||
#define NUM_THREAD_X 64
|
||||
|
||||
#include "LightningVertex.cginc"
|
||||
#include "Assets/Plugins/GPU Noise/SimplexNoise2D.cginc"
|
||||
|
||||
struct Node
|
||||
{
|
||||
float3 pos;
|
||||
int bolt;
|
||||
float time;
|
||||
};
|
||||
|
||||
struct Bolt
|
||||
{
|
||||
float3 startPos;
|
||||
float3 endPos;
|
||||
float startTime;
|
||||
int startIndex;
|
||||
float3 startOffset;
|
||||
float3 endOffset;
|
||||
};
|
||||
|
||||
RWStructuredBuffer<Node> _NodeBuffer;
|
||||
RWStructuredBuffer<Vertex> _VertexBuffer;
|
||||
RWStructuredBuffer<Bolt> _BoltBuffer;
|
||||
|
||||
float _NoiseAmplitude;
|
||||
float _NoiseFrequency;
|
||||
float _NoiseGain;
|
||||
float _NoiseLacunarity;
|
||||
float _Time;
|
||||
float _StartWidth;
|
||||
float _EndWidth;
|
||||
int _TrunkNodeCount;
|
||||
//int _BranchCount;
|
||||
int _BranchNodeCount;
|
||||
float3 _CameraPos; // for perspective camera
|
||||
|
||||
float fbm(float2 p)
|
||||
{
|
||||
float freq = _NoiseFrequency, amp = _NoiseAmplitude;
|
||||
float sum = 0;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
sum += snoise(p * freq) * amp;
|
||||
freq *= _NoiseLacunarity;
|
||||
amp *= _NoiseGain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float3 fbm3(float2 p)
|
||||
{
|
||||
return float3(
|
||||
fbm(p),
|
||||
fbm(p+10),
|
||||
fbm(p+20));
|
||||
}
|
||||
|
||||
inline float3 calcToCameraDir(float3 pos)
|
||||
{
|
||||
return normalize(_CameraPos - pos);
|
||||
}
|
||||
|
||||
[numthreads(NUM_THREAD_X,1,1)]
|
||||
void UpdateNodes (uint3 id : SV_DispatchThreadID)
|
||||
{
|
||||
int idx = id.x;
|
||||
|
||||
Node node = _NodeBuffer[idx];
|
||||
Bolt bolt = _BoltBuffer[node.bolt];
|
||||
|
||||
float3 samplePos = lerp(bolt.startPos, bolt.endPos, node.time);
|
||||
|
||||
float distance = length(bolt.startPos - samplePos);
|
||||
|
||||
node.pos = samplePos + fbm3(float2(distance, _Time + node.bolt * 10));
|
||||
if(node.bolt == 0) node.pos += bolt.startOffset * max(.5 - node.time, 0) * 2 + bolt.endOffset * max(node.time - .5, 0) * 2;
|
||||
else node.pos += bolt.startOffset;
|
||||
|
||||
_NodeBuffer[idx] = node;
|
||||
}
|
||||
|
||||
[numthreads(NUM_THREAD_X,1,1)]
|
||||
void CreateWidth (uint3 id : SV_DispatchThreadID)
|
||||
{
|
||||
int idx = id.x;
|
||||
|
||||
Vertex v0 = GetDefaultVertex();
|
||||
Vertex v1 = GetDefaultVertex();
|
||||
|
||||
Node node = _NodeBuffer[idx];
|
||||
Bolt bolt = _BoltBuffer[node.bolt];
|
||||
|
||||
float3 pos = node.pos;
|
||||
|
||||
float3 dirPrev = float3(0,0,0);
|
||||
float3 dirNext = float3(0,0,0);
|
||||
|
||||
if ( idx!=bolt.startIndex )
|
||||
{
|
||||
Node nodePrev = _NodeBuffer[idx-1];
|
||||
dirPrev = normalize(pos - nodePrev.pos);
|
||||
}
|
||||
|
||||
bool enableNext;
|
||||
if(idx < _TrunkNodeCount)
|
||||
enableNext = idx < _TrunkNodeCount - 1;
|
||||
else enableNext = (idx - _TrunkNodeCount) % _BranchNodeCount < _BranchNodeCount - 1;
|
||||
if ( enableNext )
|
||||
{
|
||||
Node nodeNext = _NodeBuffer[idx+1];
|
||||
dirNext = normalize(nodeNext.pos - pos);
|
||||
}
|
||||
|
||||
float3 dir = normalize(dirPrev + dirNext);
|
||||
|
||||
float width;
|
||||
if(idx < _TrunkNodeCount) width = lerp(_StartWidth, _EndWidth, node.time);
|
||||
else width = (1-node.time) * lerp(_StartWidth, _EndWidth, bolt.startTime);
|
||||
|
||||
float3 toCameraDir = calcToCameraDir(pos);
|
||||
float3 right = normalize(cross(dir, toCameraDir)) * width * 0.5f;
|
||||
|
||||
float uvx = bolt.startTime + node.time * (1-bolt.startTime);
|
||||
v0.pos = pos - right;
|
||||
v0.uv = float2(uvx, 0 + node.bolt);
|
||||
|
||||
v1.pos = pos + right;
|
||||
v1.uv = float2(uvx, 1 + node.bolt);
|
||||
|
||||
uint vId = idx * 2;
|
||||
_VertexBuffer[vId] = v0;
|
||||
_VertexBuffer[vId+1] = v1;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a974189fea3fe9a46a7859ef6ea8da09
|
||||
ComputeShaderImporter:
|
||||
externalObjects: {}
|
||||
currentAPIMask: 4
|
||||
preprocessorOverride: 0
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
%YAML 1.1
|
||||
%TAG !u! tag:unity3d.com,2011:
|
||||
--- !u!21 &2100000
|
||||
Material:
|
||||
serializedVersion: 6
|
||||
m_ObjectHideFlags: 0
|
||||
m_CorrespondingSourceObject: {fileID: 0}
|
||||
m_PrefabInstance: {fileID: 0}
|
||||
m_PrefabAsset: {fileID: 0}
|
||||
m_Name: Lightning
|
||||
m_Shader: {fileID: 4800000, guid: 21dd485e846b4d3a8213cf5551832776, type: 3}
|
||||
m_ShaderKeywords:
|
||||
m_LightmapFlags: 4
|
||||
m_EnableInstancingVariants: 0
|
||||
m_DoubleSidedGI: 0
|
||||
m_CustomRenderQueue: -1
|
||||
stringTagMap: {}
|
||||
disabledShaderPasses: []
|
||||
m_SavedProperties:
|
||||
serializedVersion: 3
|
||||
m_TexEnvs:
|
||||
- _BumpMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailAlbedoMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailMask:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _DetailNormalMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _EmissionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MainTex:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _MetallicGlossMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _OcclusionMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
- _ParallaxMap:
|
||||
m_Texture: {fileID: 0}
|
||||
m_Scale: {x: 1, y: 1}
|
||||
m_Offset: {x: 0, y: 0}
|
||||
m_Floats:
|
||||
- _BranchIntensity: 10
|
||||
- _BumpScale: 1
|
||||
- _Cutoff: 0.5
|
||||
- _DetailNormalMapScale: 1
|
||||
- _DstBlend: 0
|
||||
- _GlossMapScale: 1
|
||||
- _Glossiness: 0.5
|
||||
- _GlossyReflections: 1
|
||||
- _Intensity: 10
|
||||
- _LeaderIntensity: 100
|
||||
- _LeaderProgress: 0.81259537
|
||||
- _LeaderSize: 0.025
|
||||
- _Metallic: 0
|
||||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
- _UVSec: 0
|
||||
- _ZWrite: 1
|
||||
m_Colors:
|
||||
- _Color: {r: 1, g: 1, b: 1, a: 1}
|
||||
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
|
||||
- _EndColor: {r: 0.062478006, g: 0.11552096, b: 0.801, a: 0}
|
||||
- _StartColor: {r: 0.83699995, g: 0.043821972, b: 0.57845026, a: 1}
|
||||
m_BuildTextureStacks: []
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c94ec17d8118c2749ad885c2b378ea01
|
||||
NativeFormatImporter:
|
||||
externalObjects: {}
|
||||
mainObjectFileID: 2100000
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
Shader "Aetheria/GPU Lightning" {
|
||||
Properties {
|
||||
_StartColor("StartColor", Color) = (1,1,1,1)
|
||||
_EndColor("EndColor", Color) = (0,0,0,1)
|
||||
_Intensity("Intensity", Float) = 1
|
||||
_LeaderIntensity("LeaderIntensity", Float) = 10
|
||||
_LeaderProgress("Leader Progress", Float) = 1
|
||||
_LeaderSize("Leader Size", Float) = .01
|
||||
_BranchIntensity("Branch Intensity", Float) = 1
|
||||
}
|
||||
|
||||
SubShader {
|
||||
Pass{
|
||||
Tags { "RenderType"="Opaque" }
|
||||
|
||||
CGPROGRAM
|
||||
#pragma target 5.0
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "LightningVertex.cginc"
|
||||
#include "Assets/Shaders/Dither Functions.cginc"
|
||||
|
||||
float _Intensity;
|
||||
float _LeaderIntensity;
|
||||
float _LeaderProgress;
|
||||
float _LeaderSize;
|
||||
float _BranchIntensity;
|
||||
fixed4 _StartColor;
|
||||
fixed4 _EndColor;
|
||||
StructuredBuffer<uint> _IndexBuffer;
|
||||
StructuredBuffer<Vertex> _VertexBuffer;
|
||||
|
||||
Vertex GetVertex(uint indexBufferIdx) {
|
||||
uint idx = _IndexBuffer[indexBufferIdx];
|
||||
return _VertexBuffer[idx];
|
||||
}
|
||||
|
||||
struct vs_out {
|
||||
float4 pos : SV_POSITION;
|
||||
float4 col : COLOR;
|
||||
float2 uv : TEXCOORD;
|
||||
float4 screenPos : TEXCOORD1;
|
||||
};
|
||||
|
||||
vs_out vert (uint id : SV_VertexID, uint iId : SV_InstanceID)
|
||||
{
|
||||
vs_out Out;
|
||||
Vertex vtx = GetVertex(id);
|
||||
|
||||
Out.pos = UnityObjectToClipPos(float4(vtx.pos, 1.0));
|
||||
Out.uv = vtx.uv;
|
||||
Out.col = lerp(_StartColor, _EndColor, vtx.uv.x);
|
||||
Out.screenPos = ComputeScreenPos(float4(vtx.pos, 1.0));
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
fixed4 frag (vs_out In) : COLOR0
|
||||
{
|
||||
// Discard fragments that are further than the leader progress
|
||||
clip(_LeaderProgress-In.uv.x);
|
||||
|
||||
// Simple quadratic curve "texture"
|
||||
float x = 2 * (In.uv.y % 1) - 1;
|
||||
float a = 1 - x * x;
|
||||
|
||||
float i = In.uv.y > 1 ? _BranchIntensity : _Intensity;
|
||||
a *= lerp(i, _LeaderIntensity, smoothstep(_LeaderProgress - _LeaderSize * 1.5, _LeaderProgress - _LeaderSize, In.uv.x));
|
||||
|
||||
// Apply screen space dithering
|
||||
float2 screenUV = In.screenPos.xy / In.screenPos.w;
|
||||
ditherClip(screenUV, a);
|
||||
|
||||
return In.col * a;
|
||||
}
|
||||
|
||||
ENDCG
|
||||
|
||||
}
|
||||
Pass{
|
||||
Name "ShadowCaster"
|
||||
Tags { "LightMode" = "ShadowCaster" }
|
||||
|
||||
CGPROGRAM
|
||||
#pragma target 5.0
|
||||
|
||||
#pragma vertex vert
|
||||
#pragma fragment frag
|
||||
|
||||
#include "UnityCG.cginc"
|
||||
#include "LightningVertex.cginc"
|
||||
#include "Assets/Shaders/Dither Functions.cginc"
|
||||
|
||||
float _LeaderProgress;
|
||||
float _LeaderSize;
|
||||
StructuredBuffer<uint> _IndexBuffer;
|
||||
StructuredBuffer<Vertex> _VertexBuffer;
|
||||
|
||||
Vertex GetVertex(uint indexBufferIdx) {
|
||||
uint idx = _IndexBuffer[indexBufferIdx];
|
||||
return _VertexBuffer[idx];
|
||||
}
|
||||
|
||||
struct vs_out {
|
||||
float4 pos : SV_POSITION;
|
||||
float4 col : COLOR;
|
||||
float2 uv : TEXCOORD;
|
||||
float4 screenPos : TEXCOORD1;
|
||||
};
|
||||
|
||||
vs_out vert (uint id : SV_VertexID, uint iId : SV_InstanceID)
|
||||
{
|
||||
vs_out Out;
|
||||
Vertex vtx = GetVertex(id);
|
||||
|
||||
Out.pos = UnityObjectToClipPos(float4(vtx.pos, 1.0));
|
||||
Out.uv = vtx.uv;
|
||||
Out.col = 1;
|
||||
Out.screenPos = ComputeScreenPos(float4(vtx.pos, 1.0));
|
||||
|
||||
return Out;
|
||||
}
|
||||
|
||||
fixed4 frag (vs_out In) : COLOR0
|
||||
{
|
||||
// Discard fragments that are further than the leader progress
|
||||
clip(_LeaderProgress-In.uv.x);
|
||||
|
||||
// Simple quadratic curve "texture"
|
||||
float x = 2 * (In.uv.y % 1) - 1;
|
||||
float a = 1 - x * x;
|
||||
|
||||
// Apply screen space dithering
|
||||
float2 screenUV = In.screenPos.xy / In.screenPos.w;
|
||||
ditherClip(screenUV, a);
|
||||
|
||||
SHADOW_CASTER_FRAGMENT(In)
|
||||
}
|
||||
|
||||
ENDCG
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Fallback Off
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 21dd485e846b4d3a8213cf5551832776
|
||||
timeCreated: 1612395387
|
||||
|
|
@ -0,0 +1,355 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using Unity.Mathematics;
|
||||
using static Unity.Mathematics.math;
|
||||
using static Unity.Mathematics.noise;
|
||||
using Random = UnityEngine.Random;
|
||||
|
||||
public class LightningCompute : MonoBehaviour
|
||||
{
|
||||
public Camera Camera;
|
||||
|
||||
[Header("Bolt Properties")]
|
||||
public Vector3 StartPosition;
|
||||
public Vector3 EndPosition;
|
||||
public float StartWidth;
|
||||
public float EndWidth;
|
||||
public float BranchLength;
|
||||
public float MorphSpeed;
|
||||
public float NoiseAmplitude;
|
||||
public float NoiseFrequency;
|
||||
public float NoiseGain;
|
||||
public float NoiseLacunarity;
|
||||
public bool FixedEndpoint;
|
||||
public bool Animate;
|
||||
|
||||
[Header("Animation Properties")]
|
||||
public float LeaderWidth;
|
||||
public float LeaderDuration;
|
||||
public float LeaderIntensity;
|
||||
public float PulseDuration;
|
||||
public float PulseIntensity;
|
||||
public float PulseWidth;
|
||||
public AnimationCurve PulseTrunkIntensityCurve;
|
||||
public AnimationCurve PulseBranchIntensityCurve;
|
||||
public AnimationCurve PulseWidthCurve;
|
||||
|
||||
[Header("Buffer Sizes")]
|
||||
public int TrunkNodeCount;
|
||||
public int BranchCount;
|
||||
public int BranchNodeCount;
|
||||
|
||||
[Header("Shaders")]
|
||||
public Material RenderMaterial;
|
||||
public ComputeShader ComputeShader;
|
||||
|
||||
public bool DebugDrawNodes;
|
||||
public bool DebugDrawVertices;
|
||||
|
||||
public Action OnLeaderComplete;
|
||||
public Action OnPulseComplete;
|
||||
|
||||
private Transform _cameraTransform;
|
||||
|
||||
private ComputeBuffer _nodeBuffer;
|
||||
private ComputeBuffer _vertexBuffer;
|
||||
private ComputeBuffer _indexBuffer;
|
||||
private ComputeBuffer _boltBuffer;
|
||||
private Bolt[] _bolts;
|
||||
private Vector3[] _branchEndpoints;
|
||||
|
||||
private const int GROUP_SIZE = 64;
|
||||
private int _updateKernel;
|
||||
private int _createWidthKernel;
|
||||
private float _animationStartTime;
|
||||
private float _time;
|
||||
private bool _leaderComplete;
|
||||
private bool _pulseComplete;
|
||||
private bool _initialized;
|
||||
private Material _materialInstance;
|
||||
|
||||
private int NodeBufferSize => TrunkNodeCount + BranchCount * BranchNodeCount;
|
||||
private int VertexBufferSize => NodeBufferSize * 2;
|
||||
private int IndexBufferSize => (NodeBufferSize - (BranchCount + 1)) * 6;
|
||||
|
||||
public struct Node
|
||||
{
|
||||
public Vector3 pos;
|
||||
public int bolt;
|
||||
public float time;
|
||||
}
|
||||
|
||||
public struct Vertex
|
||||
{
|
||||
public Vector3 pos;
|
||||
public Vector2 uv;
|
||||
}
|
||||
|
||||
public struct Bolt
|
||||
{
|
||||
public Vector3 startPos;
|
||||
public Vector3 endPos;
|
||||
public float startTime;
|
||||
public int startIndex;
|
||||
public Vector3 startOffset;
|
||||
public Vector3 endOffset;
|
||||
}
|
||||
|
||||
void Start()
|
||||
{
|
||||
Initialize();
|
||||
_materialInstance = Instantiate(RenderMaterial);
|
||||
}
|
||||
|
||||
void Initialize()
|
||||
{
|
||||
if (_initialized) return;
|
||||
_initialized = true;
|
||||
|
||||
if (!Camera) Camera = Camera.main;
|
||||
if (!Camera) throw new InvalidOperationException("EveUnity lightning presentation requires an active camera.");
|
||||
_cameraTransform = Camera.transform;
|
||||
_updateKernel = ComputeShader.FindKernel("UpdateNodes");
|
||||
_createWidthKernel = ComputeShader.FindKernel("CreateWidth");
|
||||
|
||||
_nodeBuffer = new ComputeBuffer(NodeBufferSize, Marshal.SizeOf(typeof(Node)));
|
||||
var nodeList = new List<Node>();
|
||||
for (int i = 0; i < TrunkNodeCount; i++)
|
||||
{
|
||||
nodeList.Add(new Node{bolt = 0, pos = Vector3.zero, time = (float)i/(TrunkNodeCount-1)});
|
||||
}
|
||||
|
||||
for (int branch = 0; branch < BranchCount; branch++)
|
||||
{
|
||||
for (int branchNode = 0; branchNode < BranchNodeCount; branchNode++)
|
||||
{
|
||||
nodeList.Add(new Node{bolt = branch+1, pos = Vector3.zero, time = (float)branchNode/(BranchNodeCount-1)});
|
||||
}
|
||||
}
|
||||
|
||||
_nodeBuffer.SetData(nodeList.ToArray());
|
||||
|
||||
|
||||
_boltBuffer = new ComputeBuffer(BranchCount + 1, Marshal.SizeOf(typeof(Bolt)));
|
||||
_branchEndpoints = new Vector3[BranchCount];
|
||||
_bolts = new Bolt[BranchCount + 1];
|
||||
_bolts[0] = new Bolt
|
||||
{
|
||||
startPos = StartPosition,
|
||||
endPos = EndPosition,
|
||||
startTime = 0
|
||||
};
|
||||
|
||||
_boltBuffer.SetData(_bolts);
|
||||
|
||||
_vertexBuffer = new ComputeBuffer(VertexBufferSize, Marshal.SizeOf(typeof(Vertex))); // 1 node to 2 vtx(left,right)
|
||||
_vertexBuffer.SetData(Enumerable.Repeat(default(Vertex), _vertexBuffer.count).ToArray());
|
||||
|
||||
var indexData = new int[IndexBufferSize];
|
||||
var index = 0;
|
||||
for (var iNode = 0; iNode < TrunkNodeCount - 1; iNode++)
|
||||
{
|
||||
var offset = iNode * 2;
|
||||
indexData[index++] = 0 + offset;
|
||||
indexData[index++] = 1 + offset;
|
||||
indexData[index++] = 2 + offset;
|
||||
indexData[index++] = 2 + offset;
|
||||
indexData[index++] = 1 + offset;
|
||||
indexData[index++] = 3 + offset;
|
||||
}
|
||||
for (int iBranch = 0; iBranch < BranchCount; iBranch++)
|
||||
{
|
||||
for (int iBranchNode = 0; iBranchNode < BranchNodeCount - 1; iBranchNode++)
|
||||
{
|
||||
var offset = iBranchNode * 2 + (TrunkNodeCount) * 2 + iBranch * (BranchNodeCount) * 2;
|
||||
indexData[index++] = 0 + offset;
|
||||
indexData[index++] = 1 + offset;
|
||||
indexData[index++] = 2 + offset;
|
||||
indexData[index++] = 2 + offset;
|
||||
indexData[index++] = 1 + offset;
|
||||
indexData[index++] = 3 + offset;
|
||||
}
|
||||
}
|
||||
|
||||
_indexBuffer = new ComputeBuffer(indexData.Length, Marshal.SizeOf(typeof(uint))); // 1 node to 2 triangles(6vertexs)
|
||||
_indexBuffer.SetData(indexData);
|
||||
}
|
||||
|
||||
public void StartAnimation()
|
||||
{
|
||||
Initialize();
|
||||
_animationStartTime = Time.time;
|
||||
_time = Random.value * 1000;
|
||||
_leaderComplete = false;
|
||||
_pulseComplete = false;
|
||||
|
||||
for (int i = 0; i < BranchCount; i++)
|
||||
{
|
||||
_branchEndpoints[i] = Random.insideUnitSphere * BranchLength;
|
||||
_bolts[i+1] = new Bolt
|
||||
{
|
||||
startTime = Random.value * .8f
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
float fbm(float2 p)
|
||||
{
|
||||
float freq = NoiseFrequency, amp = NoiseAmplitude;
|
||||
float sum = 0;
|
||||
for(int i = 0; i < 4; i++)
|
||||
{
|
||||
sum += snoise(p * freq) * amp;
|
||||
freq *= NoiseLacunarity;
|
||||
amp *= NoiseGain;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
float3 fbm3(float2 p)
|
||||
{
|
||||
return float3(
|
||||
fbm(p),
|
||||
fbm(p+10),
|
||||
fbm(p+20));
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
_time += Time.deltaTime * MorphSpeed;
|
||||
|
||||
if(Animate)
|
||||
{
|
||||
var leaderLerp = (Time.time - _animationStartTime) / LeaderDuration;
|
||||
if (leaderLerp > 1 && !_leaderComplete)
|
||||
{
|
||||
OnLeaderComplete?.Invoke();
|
||||
_leaderComplete = true;
|
||||
}
|
||||
|
||||
var pulseLerp = (Time.time - _animationStartTime - LeaderDuration) / PulseDuration;
|
||||
if (pulseLerp > 1 && !_pulseComplete)
|
||||
{
|
||||
OnPulseComplete?.Invoke();
|
||||
_pulseComplete = true;
|
||||
}
|
||||
ComputeShader.SetFloat("_StartWidth", leaderLerp < 1 ? LeaderWidth : StartWidth * PulseWidthCurve.Evaluate(pulseLerp)*PulseWidth);
|
||||
ComputeShader.SetFloat("_EndWidth", leaderLerp < 1 ? LeaderWidth : EndWidth * PulseWidthCurve.Evaluate(pulseLerp)*PulseWidth);
|
||||
|
||||
_materialInstance.SetFloat("_LeaderProgress", leaderLerp);
|
||||
_materialInstance.SetFloat("_BranchIntensity", leaderLerp < 1 ? LeaderIntensity : PulseBranchIntensityCurve.Evaluate(pulseLerp) * PulseIntensity);
|
||||
_materialInstance.SetFloat("_Intensity", leaderLerp < 1 ? LeaderIntensity : PulseTrunkIntensityCurve.Evaluate(pulseLerp) * PulseIntensity);
|
||||
}
|
||||
else
|
||||
{
|
||||
ComputeShader.SetFloat("_StartWidth", StartWidth);
|
||||
ComputeShader.SetFloat("_EndWidth", EndWidth);
|
||||
}
|
||||
var diff = EndPosition - StartPosition;
|
||||
var dist = diff.magnitude;
|
||||
_bolts[0].startPos = StartPosition;
|
||||
_bolts[0].startOffset = -(Vector3) fbm3(float2(0, _time));
|
||||
_bolts[0].endPos = EndPosition;
|
||||
_bolts[0].endOffset = FixedEndpoint ? -(Vector3) fbm3(float2(dist, _time)) : Vector3.zero;
|
||||
|
||||
for (int i = 0; i < BranchCount; i++)
|
||||
{
|
||||
var root = Vector3.Lerp(StartPosition, EndPosition, _bolts[i + 1].startTime);
|
||||
var rootDist = _bolts[i + 1].startTime * dist;
|
||||
_bolts[i + 1].startPos =
|
||||
root + (Vector3) fbm3(float2(rootDist, _time)) +
|
||||
_bolts[0].startOffset * (max(.5f - _bolts[i + 1].startTime, 0) * 2) +
|
||||
_bolts[0].endOffset * (max(_bolts[i + 1].startTime - .5f, 0) * 2);
|
||||
_bolts[i + 1].startOffset = -(Vector3) fbm3(float2(0, _time + (i + 1) * 10));
|
||||
_bolts[i + 1].endPos = root + _branchEndpoints[i];
|
||||
}
|
||||
|
||||
_boltBuffer.SetData(_bolts);
|
||||
|
||||
ComputeShader.SetBuffer(_updateKernel, "_NodeBuffer", _nodeBuffer);
|
||||
ComputeShader.SetBuffer(_updateKernel, "_BoltBuffer", _boltBuffer);
|
||||
|
||||
ComputeShader.SetFloat("_Time", _time);
|
||||
ComputeShader.SetFloat("_NoiseAmplitude", NoiseAmplitude);
|
||||
ComputeShader.SetFloat("_NoiseFrequency", NoiseFrequency);
|
||||
ComputeShader.SetFloat("_NoiseGain", NoiseGain);
|
||||
ComputeShader.SetFloat("_NoiseLacunarity", NoiseLacunarity);
|
||||
|
||||
ComputeShader.Dispatch(_updateKernel, NodeBufferSize/GROUP_SIZE, 1, 1);
|
||||
|
||||
ComputeShader.SetInt("_TrunkNodeCount", TrunkNodeCount);
|
||||
ComputeShader.SetInt("_BranchNodeCount", BranchNodeCount);
|
||||
ComputeShader.SetVector("_CameraPos", _cameraTransform.position);
|
||||
|
||||
ComputeShader.SetBuffer(_createWidthKernel, "_NodeBuffer", _nodeBuffer);
|
||||
ComputeShader.SetBuffer(_createWidthKernel, "_BoltBuffer", _boltBuffer);
|
||||
ComputeShader.SetBuffer(_createWidthKernel, "_VertexBuffer", _vertexBuffer);
|
||||
|
||||
ComputeShader.Dispatch(_createWidthKernel, NodeBufferSize/GROUP_SIZE, 1, 1);
|
||||
|
||||
_materialInstance.SetBuffer("_IndexBuffer", _indexBuffer);
|
||||
_materialInstance.SetBuffer("_VertexBuffer", _vertexBuffer);
|
||||
Graphics.DrawProcedural(
|
||||
_materialInstance,
|
||||
new Bounds((EndPosition + StartPosition) / 2, (EndPosition - StartPosition).magnitude * Vector3.one),
|
||||
MeshTopology.Triangles,
|
||||
IndexBufferSize,
|
||||
1,
|
||||
Camera,
|
||||
null,
|
||||
ShadowCastingMode.Off,
|
||||
false,
|
||||
0);
|
||||
//
|
||||
// if(pulseLerp > 2)
|
||||
// StartAnimation();
|
||||
}
|
||||
|
||||
#region cleanup
|
||||
void OnDestroy()
|
||||
{
|
||||
if(_indexBuffer!=null)
|
||||
{
|
||||
_indexBuffer.Release();
|
||||
_boltBuffer.Release();
|
||||
_nodeBuffer.Release();
|
||||
_vertexBuffer.Release();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void OnDrawGizmosSelected()
|
||||
{
|
||||
if (DebugDrawNodes && _nodeBuffer != null)
|
||||
{
|
||||
Gizmos.color = Color.magenta;
|
||||
var data = new Node[_nodeBuffer.count];
|
||||
_nodeBuffer.GetData(data);
|
||||
foreach(var node in data)
|
||||
{
|
||||
Gizmos.DrawWireSphere(node.pos, StartWidth);
|
||||
};
|
||||
}
|
||||
|
||||
if (DebugDrawVertices && _vertexBuffer != null)
|
||||
{
|
||||
Gizmos.color = Color.yellow;
|
||||
var data = new Vertex[_vertexBuffer.count];
|
||||
_vertexBuffer.GetData(data);
|
||||
|
||||
var num = _vertexBuffer.count / 2;
|
||||
for(var i=0; i< num; ++i)
|
||||
{
|
||||
var v0 = data[2*i];
|
||||
var v1 = data[2*i +1];
|
||||
|
||||
Gizmos.DrawLine(v0.pos, v1.pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 044c2be5da0fc2340a09f856be731cea
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 58
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
#ifndef LIGHTNING_VERTEX_INCLUDED
|
||||
#define LIGHTNING_VERTEX_INCLUDED
|
||||
|
||||
struct Vertex
|
||||
{
|
||||
float3 pos;
|
||||
float2 uv;
|
||||
};
|
||||
|
||||
inline Vertex GetDefaultVertex() {
|
||||
|
||||
Vertex ret;
|
||||
ret.pos = (0).xxx;
|
||||
ret.uv = (-1).xx;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#endif // LIGHTNING_VERTEX_INCLUDED
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 73ccd50b02d44a0185deea93c36ffb55
|
||||
timeCreated: 1612367245
|
||||
|
|
@ -62,6 +62,14 @@ namespace GameCult.Eve.UnityScene
|
|||
effect.transform.position = origin;
|
||||
var direction = endpoint - origin;
|
||||
if (direction.sqrMagnitude > 0.0001f) effect.transform.rotation = Quaternion.LookRotation(direction.normalized);
|
||||
var lightning = effect.GetComponentInChildren<LightningCompute>();
|
||||
if (lightning != null)
|
||||
{
|
||||
lightning.StartPosition = origin;
|
||||
lightning.EndPosition = endpoint;
|
||||
lightning.Animate = true;
|
||||
lightning.StartAnimation();
|
||||
}
|
||||
}
|
||||
var impactPrefab = ResolveRolePrefab("effect.impact." + receipt.ImpactKind);
|
||||
var travels = string.Equals(receipt.PresentationKind, "bolt", StringComparison.Ordinal) ||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,8 @@
|
|||
"rootNamespace": "GameCult.Eve.UnityScene",
|
||||
"references": [
|
||||
"GameCult.Eve.Surface",
|
||||
"GameCult.Eve.PluginFields"
|
||||
"GameCult.Eve.PluginFields",
|
||||
"Unity.Mathematics"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
using NUnit.Framework;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace GameCult.Eve.UnityScene.Tests
|
||||
{
|
||||
public sealed class EveUnityLightningEffectTests
|
||||
{
|
||||
[Test]
|
||||
public void GraduatedLightningBundleLoadsWithoutAetheriaAssemblies()
|
||||
{
|
||||
const string root = "Packages/org.gamecult.eve.unity-scene/Runtime/Effects/Lightning/";
|
||||
var compute = AssetDatabase.LoadAssetAtPath<ComputeShader>(root + "Lightning.compute");
|
||||
var material = AssetDatabase.LoadAssetAtPath<Material>(root + "Lightning.mat");
|
||||
var host = new GameObject("lightning-import-proof");
|
||||
try
|
||||
{
|
||||
var effect = host.AddComponent<LightningCompute>();
|
||||
effect.ComputeShader = compute;
|
||||
effect.RenderMaterial = material;
|
||||
|
||||
Assert.That(compute, Is.Not.Null);
|
||||
Assert.That(material, Is.Not.Null);
|
||||
Assert.That(effect.GetType().Assembly.GetName().Name, Is.EqualTo("GameCult.Eve.UnityScene"));
|
||||
}
|
||||
finally
|
||||
{
|
||||
Object.DestroyImmediate(host);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: c20d5fe870774a2a9e664bdd12e6c726
|
||||
|
|
@ -7,7 +7,8 @@
|
|||
"dependencies": {
|
||||
"org.gamecult.cultlib": "1.0.3",
|
||||
"org.gamecult.eve.plugin-fields": "0.1.1",
|
||||
"org.gamecult.eve.surface": "0.1.0"
|
||||
"org.gamecult.eve.surface": "0.1.0",
|
||||
"com.unity.mathematics": "1.3.2"
|
||||
},
|
||||
"keywords": [
|
||||
"Eve",
|
||||
|
|
|
|||
Reference in a new issue