zoukankan      html  css  js  c++  java
  • 【Unity】Compute Shader粒子效果模拟

    在UE4引擎中,已经实现了GPU的粒子系统,可以快速计算数百万的粒子及其碰撞。在Unity中,可以简单的使用Compute Shader,来尝试实现GPU粒子的效果。

    实现一个简单的立方体粒子效果,图片压缩的很厉害……粒子数量在6w+

    第一步,我们实现一个脚本,挂在在摄像机组件上,这个脚本我们用来控制粒子的渲染。

     1 using System.Collections;
     2 using System.Collections.Generic;
     3 using UnityEngine;
     4 
     5 public class CBufferTest : MonoBehaviour {
     6     public Shader shader;
     7     public ComputeShader computeShader;
     8 
     9     private ComputeBuffer offsetBuffer;
    10     private ComputeBuffer outputBuffer;
    11     private ComputeBuffer constantBuffer;
    12     private ComputeBuffer colorBuffer;
    13     private int _kernel;
    14     private Material material;
    15 
    16     public const int VertCount = 65536; //64*64*4*4 (Groups*ThreadsPerGroup)
    17 
    18     //We initialize the buffers and the material used to draw.
    19     void Start()
    20     {
    21         CreateBuffers();
    22         CreateMaterial();
    23         _kernel = computeShader.FindKernel("CSMain");
    24     }
    25 
    26     //When this GameObject is disabled we must release the buffers or else Unity complains.
    27     private void OnDisable()
    28     {
    29         ReleaseBuffer();
    30     }
    31 
    32     //After all rendering is complete we dispatch the compute shader and then set the material before drawing with DrawProcedural
    33     //this just draws the "mesh" as a set of points
    34     void OnPostRender()
    35     {
    36         Dispatch();
    37 
    38         material.SetPass(0);
    39         material.SetBuffer("buf_Points", outputBuffer);
    40         material.SetBuffer("buf_Colors", colorBuffer);
    41         Graphics.DrawProcedural(MeshTopology.Points, VertCount);
    42     }
    43 
    44     //To setup a ComputeBuffer we pass in the array length, as well as the size in bytes of a single element.
    45     //We fill the offset buffer with random numbers between 0 and 2*PI.
    46     void CreateBuffers()
    47     {
    48         offsetBuffer = new ComputeBuffer(VertCount, 4); //Contains a single float value (OffsetStruct)
    49 
    50         float[] values = new float[VertCount];
    51         for (int i = 0; i < VertCount; i++)
    52         {
    53             values[i] = Random.value * 2 * Mathf.PI;
    54         }
    55 
    56         offsetBuffer.SetData(values);
    57 
    58         constantBuffer = new ComputeBuffer(1, 4); //Contains a single element (time) which is a float
    59         colorBuffer = new ComputeBuffer(VertCount, 12);
    60         outputBuffer = new ComputeBuffer(VertCount, 12); //Output buffer contains vertices (float3 = Vector3 -> 12 bytes)
    61     }
    62 
    63     //For some reason I made this method to create a material from the attached shader.
    64     void CreateMaterial()
    65     {
    66         material = new Material(shader);
    67     }
    68 
    69     //Remember to release buffers and destroy the material when play has been stopped.
    70     void ReleaseBuffer()
    71     {
    72         constantBuffer.Release();
    73         offsetBuffer.Release();
    74         outputBuffer.Release();
    75 
    76         DestroyImmediate(material);
    77     }
    78 
    79     //The meat of this script, it sets the constant buffer (current time) and then sets all of the buffers for the compute shader.
    80     //We then dispatch 32x32x1 groups of threads of our CSMain kernel.
    81     void Dispatch()
    82     {
    83         constantBuffer.SetData(new[] { Time.time });
    84 
    85         computeShader.SetBuffer(_kernel, "cBuffer", constantBuffer);
    86         computeShader.SetBuffer(_kernel, "offsets", offsetBuffer);
    87         computeShader.SetBuffer(_kernel, "output", outputBuffer);
    88         computeShader.SetBuffer(_kernel, "color", colorBuffer);
    89         computeShader.Dispatch(_kernel, 64, 64, 1);
    90     }
    91 }
    Particles

    第二步,实现Compute Shader,用来计算粒子的位置以及颜色。

     1 #pragma kernel CSMain
     2 //We define the size of a group in the x and y directions, z direction will just be one
     3  #define thread_group_size_x 4
     4  #define thread_group_size_y 4
     5  
     6  //A struct that simple holds a position
     7 struct PositionStruct
     8 {
     9     float3 pos;
    10 };
    11  
    12 //A struct containing an offset for use by Wave function
    13 struct OffsetStruct
    14 {
    15     float offset;
    16 };
    17  
    18 //A constant buffer struct that holds a time variable sent from Unity
    19 struct CBufferStruct
    20 {
    21     float t;
    22 };
    23  
    24 //We keep three buffers accessed by the kernel, a constant buffer that is the same for every computation,
    25 //an offset buffer with a value to offset the wave, and an output buffer that is written to by the kernel
    26 RWStructuredBuffer<CBufferStruct> cBuffer;
    27 RWStructuredBuffer<OffsetStruct> offsets;
    28 RWStructuredBuffer<PositionStruct> output;
    29 RWStructuredBuffer<float3> color;
    30 //A simple sine modulation of the z coordinate, with an offset by a random value between 0 and 2PI
    31 float3 Wave(float3 p, int idx,uint3 id)
    32 {
    33     p.x=cos(cBuffer[0].t+id.x);
    34     p.y=sin(cBuffer[0].t+id.y);
    35     p.z = sin(cBuffer[0].t + offsets[idx].offset);
    36     return p;
    37 }
    38  float3 SetColor(float3 p,uint3 id)
    39  {
    40     p.x=abs(sin(cBuffer[0].t+id.x));
    41     p.y=abs(sin(cBuffer[0].t+id.y));
    42     p.z=abs(sin(cBuffer[0].t+id.x+id.y));
    43     return p;
    44  }
    45 //The kernel for this compute shader, each thread group contains a number of threads specified by numthreads(x,y,z)
    46 //We lookup the the index into the flat array by using x + y * x_stride
    47 //The position is calculated from the thread index and then the z component is shifted by the Wave function
    48 [numthreads(thread_group_size_x,thread_group_size_y,1)]
    49 void CSMain (uint3 id : SV_DispatchThreadID)
    50 {
    51     int idx = id.x + id.y * thread_group_size_x * 32;
    52     float spacing = 1;
    53  
    54     float3 pos = float3(id.x*spacing, id.y*spacing, id.z*spacing);
    55     pos = Wave(pos, idx,id);
    56     color[idx]=SetColor(pos,id);
    57     output[idx].pos = pos;
    58 }
    Compute Shader

    第三步,实现简单的V&F Shader,用于渲染像素到屏幕。

     1 Shader "Custom/CBufferTest" {
     2 Properties {
     3         _MainTex ("Albedo (RGB)", 2D) = "white" {}
     4     }
     5 SubShader {
     6     Tags { 
     7     "Queue"="Transparent" 
     8     "IgnoreProjector"="True" 
     9     "RenderType"="Transparent" 
    10     }
    11     LOD 200
    12     Cull Off
    13     blend srcAlpha one
    14     Pass {
    15       CGPROGRAM
    16             #pragma target 5.0
    17  
    18             #pragma vertex vert
    19             #pragma fragment frag
    20  
    21             #include "UnityCG.cginc"
    22  
    23             //The buffer containing the points we want to draw.
    24             StructuredBuffer<float3> buf_Points;
    25             StructuredBuffer<float3> buf_Colors;
    26             //A simple input struct for our pixel shader step containing a position.
    27             struct ps_input {
    28                 float4 pos : SV_POSITION;
    29                 half3 color:COLOR;
    30             };
    31  
    32             //Our vertex function simply fetches a point from the buffer corresponding to the vertex index
    33             //which we transform with the view-projection matrix before passing to the pixel program.
    34             ps_input vert (uint id : SV_VertexID)
    35             {
    36                 ps_input o;
    37                 float3 worldPos = buf_Points[id];
    38                 o.color=buf_Colors[id];
    39                 o.pos = mul (UNITY_MATRIX_VP, float4(worldPos,1.0f));
    40 
    41                 return o;
    42             }
    43  
    44             //Pixel function returns a solid color for each point.
    45             float4 frag (ps_input i) : COLOR
    46             {
    47                 return float4(i.color,0.5);
    48             }
    49  
    50             ENDCG
    51     }
    52         
    53 } 
    54     FallBack "Diffuse"
    55 }
    CBufferTest

    可以搜索不同的几何体算法,来实现不同的效果。

  • 相关阅读:
    windown reids
    redis 类型、方法
    nginx 路由配置
    http status code
    beego orm mysql
    thinkphp5
    beego
    MAC 更新brew 镜像源
    php session存入redis
    ios项目开发— iOS8 定位功能API改变
  • 原文地址:https://www.cnblogs.com/jaffhan/p/7391491.html
Copyright © 2011-2022 走看看