[Good for shader debug] UAV RandomWriteTarget

Send data to c# script from shader: vert/frag and surface shaders!

C# Script

It keeps a compute buffer which stores the float array, and it’s buffer size is declared to store three float values (4 byte each). The compute buffer will then bind to number 4 UAV buffer.

On each frame when you hit play, it grabs the value of the compute buffer and show them on console.

using UnityEngine;
using System.Collections;

public class UAVTest : MonoBehaviour
{
    public Material mat;
    private ComputeBuffer fieldbuf;

    private float[] fdata = new float[3];

    void OnEnable()
    {
        Setup();
    }

    void OnDisable()
    {
        if (fieldbuf != null)
        {
            fieldbuf.Release();
            fieldbuf.Dispose();
            fieldbuf = null;
        }
    }

    void Setup()
    {
        if (fieldbuf == null)
        {
            fieldbuf = new ComputeBuffer(3, sizeof(float), ComputeBufferType.Default);
        }
    }

	void OnRenderObject()
    {
        Graphics.ClearRandomWriteTargets();
        Setup();
        mat.SetPass(0);
        mat.SetBuffer("Field", fieldbuf);
        Graphics.SetRandomWriteTarget(4, fieldbuf);

        fieldbuf.GetData(fdata);
        for (int i = 0; i < fdata.Length; i++)
        {
            print(mat.name+" : " + i + ":  " + fdata[i]);
        }
    }
}

Vert and Fragment shader

It also keeps a structured float array and it’s using the number 4 UAV buffer. You can assign values to the float array in pixel shader. Things that you need to add into your shader code:

  1. #pragma target 5.0
    • Since it’s using compute buffer
  2. RWStructuredBuffer Field : register(u4);
    • Remember to match the number in C# script
  3. Field[0] = 12.3;, Field[1] = 4.56;, Field[2] = 0.789f;
    • Since you declared there are 3 float values in C# script so you are free to use them at anywhere in fragment shader
Shader "UAVTest/VertFrag"
{
	Properties
	{
		_MainTex("_MainTex (RGBA)", 2D) = "white" {}
	}
	SubShader
	{
		Tags{ "RenderType" = "Opaque" }
		LOD 200

		Pass
		{
			CGPROGRAM
			#pragma vertex vert
			#pragma fragment frag
			#pragma target 5.0

			#include "UnityCG.cginc"

			struct appdata
			{
				float4 vertex : POSITION;
				float2 uv : TEXCOORD0;
			};

			struct v2f
			{
				float2 uv : TEXCOORD0;
				float4 vertex : SV_POSITION;
			};

			sampler2D _MainTex;
			float4 _MainTex_ST;

			RWStructuredBuffer<float> Field	: register(u4);

			v2f vert(appdata v)
			{

				v2f o;
				o.vertex = UnityObjectToClipPos(v.vertex);
				o.uv = TRANSFORM_TEX(v.uv, _MainTex);

				return o;
			}

			fixed4 frag(v2f i) : SV_Target
			{
				float4 col = tex2D(_MainTex, i.uv);
                                Field[0] = 12.3;
				Field[1] = 4.56;
				Field[2] = 0.789f;

				return col;
			}
			ENDCG
		}
	}
}

Surface shader

It’s actually same as vert+frag shader. But more things need to be done:

  1. Wrap the buffer codes with #ifdef UNITY_COMPILER_HLSL … #endif
    • Surface shader is not ordinary shaders…
  2. Others are just same as vert and frag!
Shader "UAVTest/VertSurf"
{
	Properties
	{
		_Amount("Extrusion Amount", Range(-1,1)) = 0.5
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Glossiness("Smoothness", Range(0,1)) = 0.5
		_Metallic("Metallic", Range(0,1)) = 0.0
	}

	SubShader
	{
		Tags{ "RenderType" = "Opaque" }
		LOD 200

		CGPROGRAM
		#pragma surface surf Standard fullforwardshadows addshadow vertex:vert
		#pragma multi_compile_instancing
		#pragma target 5.0

		sampler2D _MainTex;

		struct Input
		{
			float2 uv_MainTex;
		};

		half _Glossiness;
		half _Metallic;

		#ifdef UNITY_COMPILER_HLSL
			RWStructuredBuffer<float> Field : register(u4);
		#endif

		//D3D 64KB * 500 Objects OPENGL 16KB * 125 Objects
		UNITY_INSTANCING_CBUFFER_START(Props)
		UNITY_DEFINE_INSTANCED_PROP(float, _Amount)
		UNITY_INSTANCING_CBUFFER_END

		void vert(inout appdata_full v, out Input o)
		{
			UNITY_INITIALIZE_OUTPUT(Input,o);

			UNITY_SETUP_INSTANCE_ID(v);

			v.vertex.xyz += v.normal * UNITY_ACCESS_INSTANCED_PROP(_Amount);
		}

		void surf(Input IN, inout SurfaceOutputStandard o)
		{
			fixed4 c = tex2D(_MainTex, IN.uv_MainTex);

			o.Albedo = c.rgb;
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			o.Alpha = c.a;

			#ifdef UNITY_COMPILER_HLSL
				Field[0] = 0.123f;
Field[1] = 0.0456f;
				Field[2] = 0.00789f;
			#endif
		}
		ENDCG
	}
}

Result

On the scene, create a sphere, create a material using either shader, assign the script, and hit play.

You will see 3 float values which comes from shader on console. 🙂

Project Link: https://drive.google.com/file/d/0BwLgUykgFtANVTdSaDF6R2FvaWs/view?usp=sharing

Shader Compilation

Import time – when you create a .shader file

  • compile the shader variants only when needed, etc. platform, variants

Build time – when you build a player

  • [level 1] ShaderLab -> glsl or hlsl or …etc
    • if platform supports, from [level 2] hlsl -> IL or MetalSL to AIR…etc
  • Cache identical shaders under Library/ShaderCache
  • compile only not-yet-ever-compiled shaders (variants for this platform only)

Run time – when you open the app

  • [level 3] glsl or hlsl or IL or AIR…etc -> driver (GPU bytecode)
    • because target device is unknown for PC/mobile at buildtime
  • Depends on driver, when to compile these codes e.g. only when it’s used
    • use shader pre-warm to “use” shaders to trigger compilation

 

Sources from lukasc, robs, Unity Manual and https://blogs.unity3d.com/2014/05/06/shader-compilation-in-unity-4-5/

UnityObjectToClipPos(use float4 instead of float3)

In the past, we uses mul(UNITY_MATRIX_MVP, v.vertex) to convert vertex position from local to world space. v.vertex is float4 which has w component.

But in most cases w is = 1. To make vertex shader run faster, Unity replaced it with UnityObjectToClipPos(float3 pos), which ignores w component even you pass a float4 position instead of float3.

For some advanced users who still need the w component in their custom shaders, here is a cheaper UnityObjectToClipPos() function which respects the w component!😄

// More efficient than computing M*VP matrix product
inline float4 UnityObjectToClipPosRespectW(in float4 pos)
{
    return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, pos));
}

This is provided by one of the Unity graphics developer, Yao.

MaterialPropertyDrawer in Shader – GUI without creating shaderGUI

16999105_467532653370479_4085466863356780898_n

https://docs.unity3d.com/ScriptReference/MaterialPropertyDrawer.html

Shader "MaterialPropertyDrawer"
{
Properties
{
_MainTex("Texture", 2D) = "white" {}

[HideInInspector] _MainTex2(“Hide Texture”, 2D) = “white” {}

[NoScaleOffset] _MainTex3(“No Scale/Offset Texture”, 2D) = “white” {}

[PerRendererData] _MainTex4(“PerRenderer Texture”, 2D) = “white” {}

[Normal] _MainTex5(“Normal Texture”, 2D) = “white” {}

_Color(“Color”, Color) = (1,0,0,1)

[HDR] _HDRColor(“HDR Color”, Color) = (1,0,0,1)

_Vector(“Vector”, Vector) = (0,0,0,0)

//Can’t go below zero
[Gamma] _GVector(“Gamma Vector”, Vector) = (0,0,0,0)

// Header creates a header text before the shader property.
[Header(A group of things)]

// Will set “_INVERT_ON” shader keyword when set
[Toggle] _Invert(“Auto keyword toggle”, Float) = 0

// Will set “ENABLE_FANCY” shader keyword when set.
[Toggle(ENABLE_FANCY)] _Fancy(“Keyword toggle”, Float) = 0

// Will show when ENABLE_FANCY is true //Feature request
//[ShowIf(ENABLE_FANCY)] _ShowIf(“Show If”, Float) = 0

// Blend mode values
[Enum(UnityEngine.Rendering.BlendMode)] _Blend(“Blend mode Enum”, Float) = 1

// A subset of blend mode values, just “One” (value 1) and “SrcAlpha” (value 5).
[Enum(One,1,SrcAlpha,5)] _Blend2(“Blend mode subset”, Float) = 1

// Each option will set _OVERLAY_NONE, _OVERLAY_ADD, _OVERLAY_MULTIPLY shader keywords.
[KeywordEnum(None, Add, Multiply)] _Overlay(“Keyword Enum”, Float) = 0
// …later on in CGPROGRAM code:
//#pragma multi_compile _OVERLAY_NONE, _OVERLAY_ADD, _OVERLAY_MULTIPLY
// …

// A slider with 3.0 response curve
[PowerSlider(3.0)] _Shininess(“Power Slider”, Range(0.01, 1)) = 0.08

// An integer slider for specified range (0 to 255)
[IntRange] _Alpha(“Int Range”, Range(0, 255)) = 100

// Default small amount of space.
[Space] _Prop1(“Small amount of space”, Float) = 0

// Large amount of space.
[Space(50)] _Prop2(“Large amount of space”, Float) = 0

}