首页 > 学院 > 开发设计 > 正文

ComputeShader(一)实例化大网格

2019-11-11 05:12:49
字体:
来源:转载
供稿:网友

最终效果

思路

图解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_EDITORusing 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<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"}
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表