Unity3D|[ComputeShader]实例化大网格
最终效果 【Unity3D|[ComputeShader]实例化大网格】
思路
图解ComputeShader
文章图片
这个图片就是ComputeShader,值得注意的是Thread最多为1024个。应该是考虑到当前显卡的最低线程数的关系。 数据交换的效率
文章图片
有图可鉴,数据最好是单向输出的,尽量避免交换。 网格实例化思路
- 脚本中根据核心数对需要实例化的网格进行顶点排序并记录在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;
#endifnamespace 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 propertyBlocks = new List();
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 verts = new List();
int[] indices = new int[num * indexNum];
List normals = new List();
List tans = new List();
List uv0 = new List();
List uv1 = new List();
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;
}
}
}
ComputeShader代码
#pragma kernel Init
#pragma kernel Emit
#pragma kernel Update
#include "./ComputeBuffer.cginc"RWStructuredBuffer _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;
}
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 _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_ONstruct 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, 1CGPROGRAM
#pragma vertex vert_
#pragma fragment frag_
#pragma multi_compile_shadowcasterstruct 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"
}
推荐阅读
- LSTM网络层详解及其应用实例
- Python-类和对象
- SpringBoot整合MongoDB完整实例代码
- MySQL|MySQL 存储过程语法及实例
- thinkphp3.2下实现阿里云视频点播实例(客户端JavaScript上传)
- Servlet原理|Servlet原理 二(Web应用与创建Servlet实例)
- (4)Canal多实例使用
- C# 接口实例
- locust实例
- RxSwift官方实例八(UITableVIew)