Advertisement

[ComputeShader]实例化大网格

阅读量:

最终效果

思路

图解ComputeShader

这个图片就是ComputeShader,值得注意的是Thread最多为1024个。应该是考虑到当前显卡的最低线程数的关系。

数据交换的效率

有图可鉴,数据最好是单向输出的,尽量避免交换。

网格实例化思路

  1. 脚本中根据核心数对需要实例化的网格进行顶点排序并记录在uv信息中,然后合并网格,也可以利用dx11的SV_InstanceID(相关API可以查看MSCN的HLSL)。
  2. 编写ComputeShader,利用GPU对大量数据进行实时运算。
  3. 编写延迟光照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;
    
     }
    
     }
    
 }

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;
    }

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"
    }

全部评论 (0)

还没有任何评论哟~