Created
March 11, 2020 11:03
-
-
Save stilllisisi/37bfbe2c8a9b1ff213e1cfb2083d459e to your computer and use it in GitHub Desktop.
【游戏-网格】使用ComputeShader实现实例化大网格,主要也是借助了ComputeShader对于运行的图形库的支持,包括DX11、DX12、OpenGL4.3+、OpenGLES3等,在涉及到大量的数学计算时,并且是可以并行的没有很多分支的计算,都可以采用ComputeShader。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//ComputeShader代码: | |
#pragma kernel Init | |
#pragma kernel Emit | |
#pragma kernel Update | |
#include "./ComputeBuffer.cginc" | |
RWStructuredBuffer<Particle> _Particles; | |
int _xMod, _yMod, _zMod; | |
float4 _Scale; | |
float4 _Pos; | |
float _Time; | |
float _Speed; | |
float _Height; | |
float4 _LocalToWorld; | |
inline uint Index(uint3 id) | |
{ | |
return id.x + id.y * _xMod + id.z * _xMod * _yMod; | |
} | |
inline float Random(float2 seed) | |
{ | |
return frac(sin(dot(seed.xy, float2(12.9898, 78.233))) * 43758.5453); | |
} | |
inline float3 Random3(float3 seed) | |
{ | |
return float3(Random(seed.yz), Random(seed.xz), Random(seed.xy)); | |
} | |
[numthreads(10, 10, 10)] | |
void Init(uint3 id : SV_DispatchThreadID) | |
{ | |
uint index = Index(id); | |
Particle p = _Particles[index]; | |
p.position = id * _Scale.xyz; | |
p.direction = float3(0, 0, 1); | |
p.scale = _Scale.xyz; | |
p.uv = p.position.xy / (float2(_xMod, _yMod)*_Scale.xy); | |
float z = p.position.z / (_zMod *_Scale.z); | |
p.color = float4(z, z, z, 1); | |
p.lifeTime = -Random(id.xy); | |
_Particles[index] = p; | |
} | |
[numthreads(10, 10, 10)] | |
void Update(uint3 id : SV_DispatchThreadID) | |
{ | |
uint index = Index(id); | |
Particle p = _Particles[index]; | |
if (p.lifeTime > 0 && p.lifeTime < _Time) | |
{ | |
p.position = id * _Scale.xyz; | |
p.lifeTime = -Random(id.xy); | |
} | |
p.lifeTime -= _Time; | |
if (p.lifeTime < 0) | |
{ | |
p.position += sin(p.lifeTime * 10)*float3(0, 0, 0.02f); | |
} | |
else | |
{ | |
p.position += p.direction * _Time; | |
} | |
_Particles[index] = p; | |
} | |
[numthreads(10, 10, 10)] | |
void Emit(uint3 id : SV_DispatchThreadID) | |
{ | |
uint index = Index(id); | |
Particle p = _Particles[index]; | |
float3 pos = id * _Scale.xyz; | |
float dis = clamp((20 - distance(pos.xy, _Pos.xy)) / 20, 0, 1); | |
dis = dis * dis * dis; | |
if (dis > 0.1) | |
{ | |
float rand = Random(pos.xy); | |
float z = 1 - pos.z / (_zMod *_Scale.z); | |
p.position = float3(pos.x, pos.y, pos.z + z * _Height * rand * dis); | |
p.direction = float3(0, 0, -_Height * z * rand * dis); | |
p.lifeTime = 1; | |
} | |
_Particles[index] = p; | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//网格实例化思路 | |
//脚本中根据核心数对需要实例化的网格进行顶点排序并记录在uv信息中,然后合并网格,也可以利用dx11的SV_InstanceID(相关API可以查看MSCN的HLSL)。 | |
//编写ComputeShader,利用GPU对大量数据进行实时运算。 | |
//编写延迟光照Shader,根据处理后的数据对网格进行实时变动。 | |
//脚本控制代码: | |
using UnityEngine; | |
using System.Collections; | |
using System.Runtime.InteropServices; | |
using System.Collections.Generic; | |
using UnityEngine.Assertions; | |
#if UNITY_EDITOR | |
using UnityEditor; | |
#endif | |
namespace MatrixParticle | |
{ | |
public struct _Particle | |
{ | |
Vector3 position; | |
Vector3 direction; | |
Vector3 scale; | |
Vector2 uv; | |
Vector4 color; | |
float lifeTime; | |
}; | |
public class MatrixParticles : MonoBehaviour | |
{ | |
const int VERTEX_MAX = 65534; | |
public ComputeShader shader; | |
public Material mat; | |
public Mesh mesh; | |
[SerializeField] | |
private int xMod = 1, yMod = 1, zMod = 1; | |
[SerializeField] | |
private Vector3 scale = Vector3.one; | |
private ComputeBuffer particlesBuffer; | |
private int initKernal, updateKernal, emitKernal; | |
private int maxKernal; | |
private List<MaterialPropertyBlock> propertyBlocks = new List<MaterialPropertyBlock>(); | |
private int perMeshNum, comMeshNum; | |
private Mesh combinedMesh; | |
void Start() | |
{ | |
maxKernal = xMod * yMod * zMod * 1000; | |
shader.SetInt("_xMod", xMod * 10); | |
shader.SetInt("_yMod", yMod * 10); | |
shader.SetInt("_zMod", zMod * 10); | |
shader.SetVector("_Scale", scale); | |
initKernal = shader.FindKernel("Init"); | |
updateKernal = shader.FindKernel("Update"); | |
emitKernal = shader.FindKernel("Emit"); | |
particlesBuffer = new ComputeBuffer(maxKernal, Marshal.SizeOf(typeof(_Particle)), ComputeBufferType.Default); | |
CreateMesh(); | |
InitParticles(); | |
} | |
void CreateMesh() | |
{ | |
perMeshNum = VERTEX_MAX / mesh.vertexCount; | |
comMeshNum = (int)Mathf.Ceil((float)maxKernal / perMeshNum); | |
combinedMesh = CreateCombinedMesh(mesh, perMeshNum); | |
for (int i = 0; i < comMeshNum; i++) | |
{ | |
MaterialPropertyBlock property = new MaterialPropertyBlock(); | |
property.SetFloat("_Offset", perMeshNum * i); | |
propertyBlocks.Add(property); | |
} | |
} | |
void Update() | |
{ | |
UpdateParticles(); | |
DrawParticles(Camera.main); | |
#if UNITY_EDITOR | |
if (SceneView.lastActiveSceneView) | |
{ | |
DrawParticles(SceneView.lastActiveSceneView.camera); | |
} | |
#endif | |
} | |
void InitParticles() | |
{ | |
shader.SetBuffer(initKernal, "_Particles", particlesBuffer); | |
shader.Dispatch(initKernal, xMod, yMod, zMod); | |
} | |
void UpdateParticles() | |
{ | |
shader.SetFloat("_Time", Time.deltaTime); | |
shader.SetBuffer(updateKernal, "_Particles", particlesBuffer); | |
shader.Dispatch(updateKernal, xMod, yMod, zMod); | |
} | |
public void EmitParticles(Vector3 pos, float height) | |
{ | |
shader.SetVector("_Pos", pos); | |
shader.SetFloat("_Height", -height); | |
shader.SetBuffer(emitKernal, "_Particles", particlesBuffer); | |
shader.Dispatch(emitKernal, xMod, yMod, zMod); | |
} | |
void DrawParticles(Camera camera) | |
{ | |
mat.SetBuffer("_Particles", particlesBuffer); | |
for (int i = 0; i < comMeshNum; ++i) | |
{ | |
var props = propertyBlocks[i]; | |
props.SetFloat("_IdOffset", perMeshNum * i); | |
Graphics.DrawMesh(combinedMesh, transform.position, transform.rotation, mat, 0, camera, 0, props); | |
} | |
} | |
void OnDestroy() | |
{ | |
particlesBuffer.Release(); | |
} | |
Mesh CreateCombinedMesh(Mesh mesh, int num) | |
{ | |
int[] meshIndices = mesh.GetIndices(0); | |
int indexNum = meshIndices.Length; | |
List<Vector3> verts = new List<Vector3>(); | |
int[] indices = new int[num * indexNum]; | |
List<Vector3> normals = new List<Vector3>(); | |
List<Vector4> tans = new List<Vector4>(); | |
List<Vector2> uv0 = new List<Vector2>(); | |
List<Vector2> uv1 = new List<Vector2>(); | |
for (int i = 0; i < num; i++) | |
{ | |
verts.AddRange(mesh.vertices); | |
normals.AddRange(mesh.normals); | |
tans.AddRange(mesh.tangents); | |
uv0.AddRange(mesh.uv); | |
for (int n = 0; n < indexNum; n++) | |
{ | |
indices[i * indexNum + n] = i * mesh.vertexCount + meshIndices[n]; | |
} | |
for (int n = 0; n < mesh.uv.Length; n++) | |
{ | |
uv1.Add(new Vector2(i, i)); | |
} | |
} | |
Mesh combinedMesh = new Mesh(); | |
combinedMesh.SetVertices(verts); | |
combinedMesh.SetIndices(indices, MeshTopology.Triangles, 0); | |
combinedMesh.SetNormals(normals); | |
combinedMesh.SetTangents(tans); | |
combinedMesh.SetUVs(0, uv0); | |
combinedMesh.SetUVs(1, uv1); | |
combinedMesh.RecalculateBounds(); | |
Vector3 size = new Vector3(xMod * 10 * scale.x, yMod * 10 * scale.y, zMod * 10 * scale.z); | |
combinedMesh.bounds = new Bounds(transform.position + size * 0.5f, size); | |
return combinedMesh; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//Shader代码: | |
Shader "QQ/Mesh" | |
{ | |
Properties | |
{ | |
_Color("Color",color)=(0.5,0.5,0.5,1) | |
_MainTex("Texture", 2D) = "white" {} | |
} | |
SubShader | |
{ | |
Tags{ "RenderType" = "Opaque" } | |
CGINCLUDE | |
#pragma multi_compile_fog | |
#pragma target 5.0 | |
#include "UnityCG.cginc" | |
#include "AutoLight.cginc" | |
#include "./ComputeBuffer.cginc" | |
uniform StructuredBuffer<Particle> _Particles; | |
uniform float _IdOffset; | |
uniform fixed4 _Color; | |
uniform sampler2D _MainTex; | |
uniform float4 _MainTex_ST; | |
uniform float4 _LightColor0; | |
inline int GetID(float2 uv) | |
{ | |
return (int)(uv.x + 0.5 + _IdOffset); | |
} | |
ENDCG | |
Pass | |
{ | |
Tags{"LightMode" = "Deferred"} | |
CGPROGRAM | |
#pragma vertex vert | |
#pragma fragment frag | |
#pragma multi_compile_shadowcaster | |
#pragma multi_compile ___ UNITY_HDR_ON | |
struct G_Buffer | |
{ | |
fixed4 diffuse : SV_Target0; | |
float4 specSmoothness : SV_Target1; | |
float4 normal : SV_Target2; | |
fixed4 emission : SV_Target3; | |
}; | |
struct a2v | |
{ | |
float4 vertex : POSITION; | |
float3 normal : NORMAL; | |
float2 uv : TEXCOORD0; | |
float2 id : TEXCOORD1; | |
}; | |
struct v2f | |
{ | |
float4 pos : SV_POSITION; | |
float3 normal : NORMAL; | |
float2 uv : TEXCOORD0; | |
float4 color : TEXCOORD1; | |
}; | |
v2f vert(a2v v) | |
{ | |
Particle p = _Particles[GetID(v.id)]; | |
v.vertex.xyz *= p.scale; | |
v.vertex.xyz += p.position; | |
v2f o; | |
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); | |
o.uv = p.uv; | |
o.color = p.color; | |
o.normal = UnityObjectToWorldNormal(v.normal); | |
return o; | |
} | |
G_Buffer frag(v2f i) | |
{ | |
i.normal = normalize(i.normal); | |
fixed4 col = tex2D(_MainTex, i.uv)*i.color; | |
clip(col.a - 0.2); | |
G_Buffer g; | |
g.diffuse = _Color; | |
g.specSmoothness = 0; | |
g.normal = half4(i.normal * 0.5 + 0.5, 1); | |
g.emission = col; | |
#ifndef UNITY_HDR_ON | |
g.emission.rgb = exp2(-g.emission.rgb); | |
#endif | |
return g; | |
} | |
ENDCG | |
} | |
Pass | |
{ | |
Tags{ "LightMode" = "ShadowCaster" } | |
ZWrite On | |
ZTest LEqual | |
Offset 1, 1 | |
CGPROGRAM | |
#pragma vertex vert_ | |
#pragma fragment frag_ | |
#pragma multi_compile_shadowcaster | |
struct a2v_ { | |
float4 vertex : POSITION; | |
float2 uv : TEXCOORD0; | |
float2 id : TEXCOORD1; | |
}; | |
struct v2f_ { | |
V2F_SHADOW_CASTER; | |
float2 uv : TEXCOORD1; | |
}; | |
v2f_ vert_(a2v_ v) { | |
Particle p = _Particles[GetID(v.id)]; | |
v.vertex.xyz *= p.scale; | |
v.vertex.xyz += p.position; | |
v2f_ o; | |
o.pos = mul(UNITY_MATRIX_MVP, v.vertex); | |
o.uv = p.uv; | |
TRANSFER_SHADOW_CASTER(o) | |
return o; | |
} | |
float4 frag_(v2f_ i) : COLOR{ | |
float4 col = tex2D(_MainTex,i.uv); | |
clip(col.a - 0.2); | |
SHADOW_CASTER_FRAGMENT(i) | |
} | |
ENDCG | |
} | |
} | |
FallBack "Diffuse" | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gameinstitute.qq.com/community/detail/128589