AndroidでComputeShaderのテスト
この記事は2017年06月15日にqiitaに投稿した内容です。
環境
Unity5.6.1f1
概要
AndroidでComputeShaderのテストをしてみました
追記(2017/06/22)
最近のスマホだとPSIZEがちゃんと動作するみたいなのでコードを変更しました
スマホ対応状況
以下で確認できます http://hwstats.unity3d.com/mobile/gpu.html
OpenGlES3.1の設定
・「BuildSettings」でAndroidに「SwitchPlatform」する ・「PlayerSettings」>「OtherSettings」>「AutoGraphicsAPI」のチェックを外す ・「GraphicAPIs」リストから「OpenGLES2」を削除する ・「RequireES3.1」もしくは「RequireES3.1+AEP」のどちらかにチェックをする
ComputeShaderの作成
・プロジェクトビューで右クリックし、「Create」>「Shader」>「ComputeShader」を押す
エディタでエラー「Platform does not support compute shaders」
・解決できないので、エディタ上でテストする時はPCに「SwitchPlatform」する
プログラム
Assets/TestComputeShader.cs デフォルトのコンピュートシェーダ(シェルピンスキーのギャスケット的なの)とポイントクラウドです
using System.Collections;
using System.Runtime.InteropServices;
using UnityEngine;
using UnityEngine.UI;
public class TestComputeShader : MonoBehaviour
{
[SerializeField] private ComputeShader _computeShader = null;
[SerializeField] private RawImage _rawImage = null;
[SerializeField] private Material _materialParticle = null;
[SerializeField] private RawImage _rawImageCamera = null;
private RenderTexture _renderTexture = null;
private ComputeBuffer _particles = null;
private IEnumerator Start()
{
yield return TestDefault();
yield return TestParticle();
}
private void OnDestroy()
{
if( _particles != null )
_particles.Release();
}
private IEnumerator TestDefault()
{
var groupNum = 32;
var threadNum = 8; //computeShaderのnumthreadsの値
var texSize = groupNum * threadNum;
_renderTexture = new RenderTexture( texSize, texSize, 0, RenderTextureFormat.ARGB32 );
_renderTexture.enableRandomWrite = true;
_renderTexture.Create();
_rawImage.texture = _renderTexture;
var kernelIndex = 0;
var kernelName = "TestDefault";
kernelIndex = _computeShader.FindKernel( kernelName );
_computeShader.SetTexture( 0, "Result", _renderTexture );
_computeShader.Dispatch( kernelIndex, groupNum, groupNum, 1 );
yield break;
}
struct Particle
{
Vector3 position;
Color color;
};
private IEnumerator TestParticle()
{
var camera = Camera.main;
var texHeight = 1920;
var texWidth = texHeight * camera.aspect;
var renderTexture = new RenderTexture( (int)texWidth, texHeight, 0, RenderTextureFormat.ARGB32 );
camera.targetTexture = renderTexture;
_rawImageCamera.texture = renderTexture;
var groupNum = 4;
var threadNum = 8; //computeShaderのnumthreadsの値
var size = groupNum * threadNum;
var particleBufferSize = size * size * size;
_particles = new ComputeBuffer( particleBufferSize, Marshal.SizeOf( typeof( Particle ) ) );
var kernelIndex = 0;
var kernelName = "TestParticle";
kernelIndex = _computeShader.FindKernel( kernelName );
_computeShader.SetInt( "Size", size );
_computeShader.SetBuffer( kernelIndex, "Particles", _particles );
var elements = new float[16];
while( true )
{
var rot = Quaternion.Euler( 0.0f, transform.localRotation.eulerAngles.y + 1.0f, 0.0f );
transform.localRotation = rot;
var pos = transform.localPosition;
pos.z = Mathf.Sin( Time.time ) * 5.0f;
transform.localPosition = pos;
var wm = transform.localToWorldMatrix;
for( int i = 0; i < elements.Length; i++ )
elements[ i ] = wm[ i ];
_computeShader.SetFloats( "ObjectToWorld", elements );
_computeShader.Dispatch( kernelIndex, groupNum, groupNum, groupNum );
yield return null;
}
yield break;
}
private void OnRenderObject()
{
if( _particles == null )
return;
_materialParticle.SetPass( 0 );
_materialParticle.SetBuffer( "Particles", _particles );
Graphics.DrawProcedural( MeshTopology.Points, _particles.count );
}
}
Assets/ComputeShader.compute
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
#pragma kernel TestDefault
RWTexture2D<float4> Result;
[numthreads( 8, 8, 1 )]
void TestDefault( uint3 id : SV_DispatchThreadID )
{
Result[ id.xy ] = float4( id.x & id.y, ( id.x & 15 ) / 15.0, ( id.y & 15 ) / 15.0, 1.0 );
}
//-----------------------------------------------------------------------------
//
//-----------------------------------------------------------------------------
#pragma kernel TestParticle
struct Particle
{
float3 position;
float4 color;
};
RWStructuredBuffer<Particle> Particles;
int Size;
float4x4 ObjectToWorld;
[numthreads( 8, 8, 8 )]
void TestParticle( uint3 id : SV_DispatchThreadID )
{
int index = ( id.z * ( Size * Size ) ) + ( id.y * Size ) + id.x;
Particles[ index ].position = float3( id.xyz ) / float( Size - 1 ) - 0.5;
Particles[ index ].position = mul( ObjectToWorld, float4( Particles[ index ].position, 1.0 ) ).xyz;
Particles[ index ].color = float4( float3( id.xyz ) / float( Size ), 1.0 );
}
Assets/Particle.shader #pragma target 5.0を入れないと正常に表示されません
Shader "Custom/TestParticle"
{
Properties
{
}
SubShader
{
Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" }
LOD 100
Blend SrcAlpha OneMinusSrcAlpha
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#pragma target 5.0
#include "UnityCG.cginc"
struct v2f
{
float4 vertex : SV_POSITION;
float4 color : COLOR;
float psize : PSIZE;
};
struct Particle
{
float3 position;
float4 color;
};
StructuredBuffer<Particle> Particles;
v2f vert( uint id : SV_VertexID )
{
v2f o;
o.vertex = UnityObjectToClipPos( float4( Particles[ id ].position, 1.0 ) );
o.color = Particles[ id ].color;
o.psize = 16.0 / o.vertex.w;
return o;
}
fixed4 frag( v2f i ) : SV_Target
{
return i.color;
}
ENDCG
}
}
}